diff --git a/.clang-tidy b/.clang-tidy
index 9c397309e..3d82e1db0 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -7,15 +7,12 @@ Checks: |
-modernize-avoid-c-arrays,
-modernize-pass-by-value,
-modernize-use-nodiscard,
- -modernize-concat-nested-namespaces,
cppcoreguidelines-pro-type-cstyle-cast,
cppcoreguidelines-pro-type-member-init,
cppcoreguidelines-prefer-member-initializer,
misc-const-correctness
WarningsAsErrors: '*'
-# Don't bother with wxWidgets headers
-HeaderFilterRegex: '^((?!labenski).)*$'
-AnalyzeTemporaryDtors: false
+HeaderFilterRegex: '.*'
FormatStyle: none
User: arbiter
CheckOptions:
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 000000000..a082fb340
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,15 @@
+{
+ "image": "mcr.microsoft.com/devcontainers/base:ubuntu-24.04",
+ "features": {
+ "ghcr.io/devcontainers/features/python:1": {
+ "installTools": true,
+ "version": "3.11"
+ },
+ "ghcr.io/devcontainers-contrib/features/gdbgui:2": {
+ "version": "latest"
+ },
+ "ghcr.io/rocker-org/devcontainer-features/apt-packages:1": {
+ "packages": "automake,autoconf,gdb"
+ }
+ }
+}
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 80490efbe..5d6b95ccb 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -11,7 +11,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Run clang-format style check for C/C++
- uses: jidicula/clang-format-action@v4.11.0
+ uses: jidicula/clang-format-action@v4.15.0
with:
clang-format-version: '17'
check-path: 'src'
diff --git a/.github/workflows/osxbinary.yml b/.github/workflows/osxbinary.yml
new file mode 100644
index 000000000..c2563c746
--- /dev/null
+++ b/.github/workflows/osxbinary.yml
@@ -0,0 +1,37 @@
+name: MacOS static GUI binary
+
+on:
+ push:
+ tags:
+ - 'v*'
+ schedule:
+ - cron: '0 6 * * 4'
+
+jobs:
+ macos-13:
+ runs-on: macos-13
+ if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
+ steps:
+ - uses: actions/checkout@v4
+ - name: Install dependencies
+ run: |
+ brew install automake autoconf
+ curl -L -O https://github.com/wxWidgets/wxWidgets/releases/download/v3.2.6/wxWidgets-3.2.6.tar.bz2
+ tar xjf wxWidgets-3.2.6.tar.bz2
+ cd wxWidgets-3.2.6
+ mkdir build-release
+ cd build-release
+ ../configure --disable-shared --disable-sys-libs
+ make -j4
+ sudo make install
+ - run: aclocal
+ - run: automake --add-missing
+ - run: autoconf
+ - run: ./configure
+ - run: make
+ - run: sudo make install
+ - run: make osx-dmg
+ - uses: actions/upload-artifact@v4
+ with:
+ name: artifact-osx-13
+ path: "*.dmg"
diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml
index 3cb531daa..1108d4af3 100644
--- a/.github/workflows/python.yml
+++ b/.github/workflows/python.yml
@@ -10,7 +10,7 @@ jobs:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
strategy:
matrix:
- python-version: ['3.8', '3.12']
+ python-version: ['3.9', '3.13']
steps:
- uses: actions/checkout@v4
@@ -21,10 +21,10 @@ jobs:
- name: Set up dependencies
run: |
python -m pip install --upgrade pip
- pip install setuptools cython pytest wheel lxml numpy scipy
+ pip install setuptools build cython pytest pytest-skip-slow wheel lxml numpy scipy
- name: Build source distribution
run:
- python setup.py sdist
+ python -m build
- name: Build from source distribution
run: |
cd dist
@@ -32,12 +32,58 @@ jobs:
- name: Run tests
run: pytest
+ macos-13:
+ runs-on: macos-13
+ if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
+ strategy:
+ matrix:
+ python-version: ['3.13']
+
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v5
+ with:
+ python-version: ${{ matrix.python-version }}
+ - name: Set up dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install cython pytest pytest-skip-slow wheel lxml numpy scipy
+ - name: Build extension
+ run: |
+ python -m pip install -v .
+ - name: Run tests
+ run: pytest
+
+ macos-14:
+ runs-on: macos-14
+ if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
+ strategy:
+ matrix:
+ python-version: ['3.13']
+
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v5
+ with:
+ python-version: ${{ matrix.python-version }}
+ - name: Set up dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install cython pytest pytest-skip-slow wheel lxml numpy scipy
+ - name: Build extension
+ run: |
+ python -m pip install -v .
+ - name: Run tests
+ run: pytest
+
windows:
runs-on: windows-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
strategy:
matrix:
- python-version: ['3.12']
+ python-version: ['3.13']
steps:
- uses: actions/checkout@v4
@@ -48,7 +94,7 @@ jobs:
- name: Set up dependencies
run: |
python -m pip install --upgrade pip
- pip install cython pytest wheel lxml numpy scipy
+ pip install cython pytest pytest-skip-slow wheel lxml numpy scipy
- name: Build extension
run: |
python -m pip install -v .
diff --git a/.github/workflows/tools.yml b/.github/workflows/tools.yml
index c671b9bd9..7125996bd 100644
--- a/.github/workflows/tools.yml
+++ b/.github/workflows/tools.yml
@@ -50,7 +50,7 @@ jobs:
- run: make osx-dmg
- uses: actions/upload-artifact@v4
with:
- name: artifact-macos
+ name: artifact-osx
path: "*.dmg"
windows:
diff --git a/.gitignore b/.gitignore
index 3ac8183fe..ebc21507c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,3 +36,4 @@ missing
gambit
.python-version
dist
+.venv
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 3a601efac..501002daa 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v3.2.0
+ rev: v4.6.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
@@ -11,11 +11,11 @@ repos:
hooks:
- id: ruff
- repo: https://github.com/pycqa/flake8
- rev: 7.0.0
+ rev: 7.1.0
hooks:
- id: flake8
- repo: https://github.com/MarcoGorelli/cython-lint
- rev: v0.16.0
+ rev: v0.16.2
hooks:
- id: cython-lint
- id: double-quote-cython-strings
diff --git a/ChangeLog b/ChangeLog
index 932528071..70ba908af 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,74 @@
# Changelog
+## [16.4.0] - unreleased
+
+### General
+- Officially added support for Python 3.13.
+
+### Removed
+- The deprecated functions `Game.read_game`, `Game.parse_game` and `Game.write` functions have
+ been removed as planned. (#357)
+
+### Added
+- Implement `GetPlays()` (C++) and `get_plays` (Python) to compute the set of terminal nodes consistent
+ with a node, information set, or action (#517)
+- Implement `GameStrategyRep::GetAction` (C++) and `Strategy.action` (Python) retrieving the action
+ prescribed by a strategy at an information set
+- Tests for creation of the reduced strategic form from an extensive-form game (currently only
+ for games with perfect recall)
+- Implement `Nodes` collection as a member of `GameRep`, including a C++ iterator that
+ returns nodes in depth-first traversal order (#530)
+
+
+## [16.3.1] - unreleased
+
+### Fixed
+- Corrected a regression in which information sets were prematurely invalidated (and therefore
+ `delete this` called on them) when removing the last node from an information set.
+
+
+## [16.3.0] - 2025-01-13
+
+### General
+- Dropped support for Python 3.8.
+
+### Added
+- Implemented maximum-likelihood estimation for agent logit QRE, to parallel existing support
+ for strategic logit QRE. Strategic logit QRE function names have been modified to provide
+ parallel naming. Estimation using the correspondence now supports an option to stop at the
+ first interior local maximizer found (if one exists).
+- Maximum-likelihood estimation for logit QRE using empirical payoffs has an improved internal
+ calculation of log-likelihood, and returns the estimated profile instead of just a list of
+ probabilities.
+- Reorganized naming conventions in pygambit for functions for computing QRE in both strategic
+ and agent versions, and added a corresponding section in the user guide.
+- `enumpoly_solve` has been returned to being fully supported from temporarily being experimental;
+ now available in `pygambit`.
+- `enumpoly_solve` for strategic games now uses the Porter, Nudelman, and Shoham (2004) ordering
+ of supports to search.
+- `to_arrays` converts a `Game` to a list of `numpy` arrays containing its reduced strategic form.
+ (#461)
+- Integrated support (in Python) for using `PHCpack` to solve systems of polynomial equations in
+ `enumpoly_solve` based on an implementation formerly in the `contrib` section of the
+ repository. (#165)
+- New format-specific functions `pygambit.read_*` and `pygambit.Game.to_*` functions have been
+ added to (de-)serialise games. The existing `Game.read_game` and `Game.write` functions have
+ been deprecated and will be removed in 16.4. (#357)
+
+### Changed
+- The built-in implementation of lrslib (dating from 2016) has been removed. Instead, access to
+ lrsnash is provided as an external tool via the `enummixed_solve` function, in parallel to
+ PHCpack for `enumpoly_solve`.
+
+### Fixed
+- When parsing .nfg files, check that the number of outcomes or payoffs is the expected number,
+ and raise an exception if not. (#119)
+
+### Removed
+- `Game.write()` no longer supports generation of the XML-format files for Game Theory
+ Explorer, as GTE no longer reads files in this format.
+
+
## [16.2.2] - unreleased
### Fixed
diff --git a/Makefile.am b/Makefile.am
index 674c8ce32..2a19386a9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,6 +1,6 @@
##
## This file is part of Gambit
-## Copyright (c) 1994-2023, The Gambit Project (http://www.gambit-project.org)
+## Copyright (c) 1994-2025, The Gambit Project (https://www.gambit-project.org)
##
## FILE: Makefile.am
## Top-level automake input file for Gambit
@@ -20,8 +20,6 @@
## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
##
-SUBDIRS = contrib
-
ACLOCAL_AMFLAGS = -I m4
EXTRA_DIST = \
@@ -247,14 +245,11 @@ EXTRA_DIST = \
src/README.rst
core_SOURCES = \
+ src/core/core.h \
+ src/core/util.h \
src/core/array.h \
src/core/list.h \
src/core/vector.h \
- src/core/vector.imp \
- src/core/pvector.h \
- src/core/pvector.imp \
- src/core/dvector.h \
- src/core/dvector.imp \
src/core/recarray.h \
src/core/matrix.h \
src/core/matrix.imp \
@@ -264,9 +259,6 @@ core_SOURCES = \
src/core/integer.h \
src/core/rational.cc \
src/core/rational.h \
- src/core/vector.cc \
- src/core/pvector.cc \
- src/core/dvector.cc \
src/core/matrix.cc \
src/core/sqmatrix.cc \
src/core/function.cc \
@@ -322,12 +314,7 @@ game_SOURCES = \
src/games/nash.h
linalg_SOURCES = \
- src/solvers/linalg/basis.cc \
- src/solvers/linalg/basis.h \
- src/solvers/linalg/bfs.h \
- src/solvers/linalg/btableau.cc \
src/solvers/linalg/btableau.h \
- src/solvers/linalg/btableau.imp \
src/solvers/linalg/lpsolve.cc \
src/solvers/linalg/lpsolve.h \
src/solvers/linalg/lpsolve.imp \
@@ -348,14 +335,6 @@ linalg_SOURCES = \
src/solvers/linalg/vertenum.h \
src/solvers/linalg/vertenum.imp
-lrslib_SOURCES = \
- src/solvers/lrs/lrslib.h \
- src/solvers/lrs/lrslib.c \
- src/solvers/lrs/lrsmp.h \
- src/solvers/lrs/lrsmp.c \
- src/solvers/lrs/lrsnashlib.h \
- src/solvers/lrs/lrsnashlib.c
-
gtracer_SOURCES = \
src/solvers/gtracer/cmatrix.h \
src/solvers/gtracer/cmatrix.cc \
@@ -405,81 +384,46 @@ gambit_convert_SOURCES = \
gambit_enummixed_SOURCES = \
${core_SOURCES} ${game_SOURCES} \
${linalg_SOURCES} \
- ${lrslib_SOURCES} \
src/solvers/enummixed/clique.cc \
src/solvers/enummixed/clique.h \
- src/solvers/enummixed/lrsenum.cc \
src/solvers/enummixed/enummixed.cc \
src/solvers/enummixed/enummixed.h \
+ src/tools/util.h \
src/tools/enummixed/enummixed.cc
+gambit_nashsupport_SOURCES = \
+ src/solvers/nashsupport/efgsupport.cc \
+ src/solvers/nashsupport/nfgsupport.cc \
+ src/solvers/nashsupport/nashsupport.h
+
gambit_enumpoly_SOURCES = \
- ${core_SOURCES} ${game_SOURCES} \
- src/solvers/enumpoly/gpartltr.cc \
- src/solvers/enumpoly/gpartltr.h \
- src/solvers/enumpoly/gpartltr.imp \
- src/solvers/enumpoly/gpoly.cc \
- src/solvers/enumpoly/gpoly.h \
- src/solvers/enumpoly/gpoly.imp \
- src/solvers/enumpoly/gpolylst.cc \
- src/solvers/enumpoly/gpolylst.h \
- src/solvers/enumpoly/gpolylst.imp \
- src/solvers/enumpoly/gsolver.cc \
- src/solvers/enumpoly/gsolver.h \
- src/solvers/enumpoly/gsolver.imp \
- src/solvers/enumpoly/ideal.cc \
- src/solvers/enumpoly/ideal.h \
- src/solvers/enumpoly/ideal.imp \
- src/solvers/enumpoly/ineqsolv.cc \
- src/solvers/enumpoly/ineqsolv.h \
- src/solvers/enumpoly/ineqsolv.imp \
- src/solvers/enumpoly/interval.h \
- src/solvers/enumpoly/monomial.cc \
- src/solvers/enumpoly/monomial.h \
- src/solvers/enumpoly/monomial.imp \
+ ${core_SOURCES} ${game_SOURCES} ${gambit_nashsupport_SOURCES} \
+ src/solvers/enumpoly/ndarray.h \
+ src/solvers/enumpoly/gameseq.cc \
+ src/solvers/enumpoly/gameseq.h \
+ src/solvers/enumpoly/indexproduct.h \
+ src/solvers/enumpoly/rectangle.h \
src/solvers/enumpoly/poly.cc \
src/solvers/enumpoly/poly.h \
src/solvers/enumpoly/poly.imp \
- src/solvers/enumpoly/prepoly.cc \
- src/solvers/enumpoly/prepoly.h \
- src/solvers/enumpoly/quiksolv.cc \
- src/solvers/enumpoly/quiksolv.h \
- src/solvers/enumpoly/quiksolv.imp \
- src/solvers/enumpoly/rectangl.cc \
- src/solvers/enumpoly/rectangl.h \
- src/solvers/enumpoly/rectangl.imp \
+ src/solvers/enumpoly/polysystem.h \
+ src/solvers/enumpoly/polypartial.h \
+ src/solvers/enumpoly/polypartial.imp \
+ src/solvers/enumpoly/polysolver.cc \
+ src/solvers/enumpoly/polysolver.h \
+ src/solvers/enumpoly/polyfeasible.h \
src/solvers/enumpoly/behavextend.cc \
src/solvers/enumpoly/behavextend.h \
- src/solvers/enumpoly/gcomplex.cc \
- src/solvers/enumpoly/gcomplex.h \
- src/solvers/enumpoly/gtree.h \
- src/solvers/enumpoly/gtree.imp \
- src/solvers/enumpoly/linrcomb.cc \
- src/solvers/enumpoly/linrcomb.h \
- src/solvers/enumpoly/linrcomb.imp \
- src/solvers/enumpoly/efgensup.cc \
- src/solvers/enumpoly/efgensup.h \
- src/solvers/enumpoly/gnarray.h \
- src/solvers/enumpoly/gnarray.imp \
- src/solvers/enumpoly/sfg.cc \
- src/solvers/enumpoly/sfg.h \
- src/solvers/enumpoly/sfstrat.cc \
- src/solvers/enumpoly/sfstrat.h \
- src/solvers/enumpoly/nfgensup.cc \
- src/solvers/enumpoly/nfgensup.h \
- src/solvers/enumpoly/odometer.cc \
- src/solvers/enumpoly/odometer.h \
- src/solvers/enumpoly/nfgcpoly.cc \
- src/solvers/enumpoly/nfgcpoly.h \
- src/solvers/enumpoly/nfghs.cc \
- src/solvers/enumpoly/nfghs.h \
src/solvers/enumpoly/efgpoly.cc \
src/solvers/enumpoly/nfgpoly.cc \
+ src/solvers/enumpoly/enumpoly.h \
+ src/tools/util.h \
src/tools/enumpoly/enumpoly.cc
gambit_enumpure_SOURCES = \
${core_SOURCES} ${game_SOURCES} \
src/solvers/enumpure/enumpure.h \
+ src/tools/util.h \
src/tools/enumpure/enumpure.cc
gambit_gnm_SOURCES = \
@@ -496,6 +440,7 @@ gambit_ipa_SOURCES = \
src/solvers/ipa/ipa.cc \
src/solvers/ipa/ipa.h \
src/tools/gt/nfggt.cc \
+ src/tools/util.h \
src/tools/gt/nfgipa.cc
gambit_lcp_SOURCES = \
@@ -504,6 +449,7 @@ gambit_lcp_SOURCES = \
src/solvers/lcp/efglcp.cc \
src/solvers/lcp/nfglcp.cc \
src/solvers/lcp/lcp.h \
+ src/tools/util.h \
src/tools/lcp/lcp.cc
gambit_liap_SOURCES = \
@@ -511,6 +457,7 @@ gambit_liap_SOURCES = \
src/solvers/liap/efgliap.cc \
src/solvers/liap/nfgliap.cc \
src/solvers/liap/liap.h \
+ src/tools/util.h \
src/tools/liap/liap.cc
gambit_logit_SOURCES = \
@@ -519,25 +466,25 @@ gambit_logit_SOURCES = \
src/solvers/logit/logbehav.imp \
src/solvers/logit/path.cc \
src/solvers/logit/path.h \
- src/solvers/logit/efglogit.h \
+ src/solvers/logit/logit.h \
src/solvers/logit/efglogit.cc \
- src/solvers/logit/nfglogit.h \
src/solvers/logit/nfglogit.cc \
+ src/tools/util.h \
src/tools/logit/logit.cc
gambit_lp_SOURCES = \
${core_SOURCES} ${game_SOURCES} \
${linalg_SOURCES} \
- src/solvers/lp/efglp.cc \
- src/solvers/lp/efglp.h \
- src/solvers/lp/nfglp.cc \
- src/solvers/lp/nfglp.h \
+ src/solvers/lp/lp.cc \
+ src/solvers/lp/lp.h \
+ src/tools/util.h \
src/tools/lp/lp.cc
gambit_simpdiv_SOURCES = \
${core_SOURCES} ${game_SOURCES} \
src/solvers/simpdiv/simpdiv.cc \
src/solvers/simpdiv/simpdiv.h \
+ src/tools/util.h \
src/tools/simpdiv/nfgsimpdiv.cc
gambit_SOURCES = \
@@ -691,8 +638,7 @@ msw-msi:
light -ext WixUIExtension gambit.wixobj
clang-tidy:
- clang-tidy ${top_srcdir}/src/core/*.cc -- --std=c++17 -I ${top_srcdir}/src -I ${top_srcdir}/src/labenski/include -DVERSION="" ${WX_CXXFLAGS}
- clang-tidy ${top_srcdir}/src/games/*.cc ${top_srcdir}/src/games/*/*.cc -- --std=c++17 -I ${top_srcdir}/src -I ${top_srcdir}/src/labenski/include -DVERSION="" ${WX_CXXFLAGS}
- clang-tidy ${top_srcdir}/src/solvers/*/*.cc -- --std=c++17 -I ${top_srcdir}/src -I ${top_srcdir}/src/labenski/include -DVERSION="" ${WX_CXXFLAGS}
- clang-tidy ${top_srcdir}/src/tools/*/*.cc -- --std=c++17 -I ${top_srcdir}/src -I ${top_srcdir}/src/labenski/include -DVERSION="" ${WX_CXXFLAGS}
- clang-tidy ${top_srcdir}/src/gui/*.cc -- --std=c++17 -I ${top_srcdir}/src -I ${top_srcdir}/src/labenski/include -DVERSION="" ${WX_CXXFLAGS}
+ clang-tidy ${top_srcdir}/src/core/*.cc -- --std=c++17 -I ${top_srcdir}/src -DVERSION=""
+ clang-tidy ${top_srcdir}/src/games/*.cc ${top_srcdir}/src/games/*/*.cc -- --std=c++17 -I ${top_srcdir}/src -DVERSION=""
+ clang-tidy ${top_srcdir}/src/solvers/*/*.cc -- --std=c++17 -I ${top_srcdir}/src -DVERSION=""
+ clang-tidy ${top_srcdir}/src/tools/*/*.cc -- --std=c++17 -I ${top_srcdir}/src -DVERSION=""
diff --git a/configure.ac b/configure.ac
index be1eed43e..41be2c377 100644
--- a/configure.ac
+++ b/configure.ac
@@ -20,7 +20,7 @@ dnl along with this program; if not, write to the Free Software
dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
dnl
-AC_INIT([gambit],[16.2.1])
+AC_INIT([gambit],[16.3.0])
AC_CONFIG_SRCDIR([src/gambit.h])
AM_INIT_AUTOMAKE([subdir-objects foreign])
dnl AC_CONFIG_MACRO_DIR([m4])
@@ -145,8 +145,5 @@ AC_SUBST(WX_CXXFLAGS)
REZFLAGS=`echo $CXXFLAGS $WX_CXXFLAGS | sed 's/-mthreads//g' | sed 's/-g//g' | sed 's/-O. / /g' | sed 's/-I/--include-dir /g'`
AC_SUBST(REZFLAGS)
-AC_CONFIG_FILES([Makefile
- contrib/Makefile
- contrib/scripts/Makefile
- contrib/scripts/enumpoly/Makefile])
+AC_CONFIG_FILES([Makefile])
AC_OUTPUT
diff --git a/contrib/Makefile.am b/contrib/Makefile.am
deleted file mode 100644
index c7a846959..000000000
--- a/contrib/Makefile.am
+++ /dev/null
@@ -1,23 +0,0 @@
-##
-## This file is part of Gambit
-## Copyright (c) 1994-2023, The Gambit Project (http://www.gambit-project.org)
-##
-## FILE: contrib/Makefile.am
-## automake input file for contrib subdirectory
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-##
-
-SUBDIRS = scripts
diff --git a/contrib/games/nim.efg b/contrib/games/nim.efg
index 00a941189..7f08b92a6 100644
--- a/contrib/games/nim.efg
+++ b/contrib/games/nim.efg
@@ -1,4 +1,4 @@
-EFG 2 R "Nim, starting with Five Stones." { "Player 1" "Player 2" }
+EFG 2 R "Nim-like game. One pile of 5 stones. Players alternately take 1 or 2 stones. Player to take last stone wins." { "Player 1" "Player 2" }
p "5 left" 1 1 "(1,1)" { "TAKE 1" "TAKE 2" } 0
p "4 left" 2 1 "(2,1)" { "TAKE 1" "TAKE 2" } 0
diff --git a/contrib/games/nim7.efg b/contrib/games/nim7.efg
index f1252926f..e0085cb45 100644
--- a/contrib/games/nim7.efg
+++ b/contrib/games/nim7.efg
@@ -1,4 +1,4 @@
-EFG 2 R "Nim, starting with Seven Stones." { "Player 1" "Player 2" }
+EFG 2 R "Nim-like game. One pile of 7 stones. Players alternately take 1 or 2 stones. Player to take last stone wins." { "Player 1" "Player 2" }
p "7 left" 2 1 "(2,4)" { "take 2" "take 1" } 0
p "5 left" 1 1 "(1,1)" { "take_2" "take_1" } 0
diff --git a/contrib/mac/Info.plist b/contrib/mac/Info.plist
index 16cfe66ad..148bbf25b 100644
--- a/contrib/mac/Info.plist
+++ b/contrib/mac/Info.plist
@@ -19,13 +19,13 @@
CFBundleSignature
????
CFBundleVersion
- 16.2.0
+ 16.3.0
CFBundleShortVersionString
- 16.2.0
+ 16.3.0
CFBundleGetInfoString
- Gambit version 16.2.1, (c) 1994-2025 The Gambit Project
+ Gambit version 16.3.0, (c) 1994-2025 The Gambit Project
CFBundleLongVersionString
- 16.2.1, (c) 1994-2025 The Gambit Project
+ 16.3.0, (c) 1994-2025 The Gambit Project
NSHumanReadableCopyright
Copyright 1994-2025 The Gambit Project
LSRequiresCarbon
diff --git a/contrib/scripts/Makefile.am b/contrib/scripts/Makefile.am
deleted file mode 100644
index ad24b702c..000000000
--- a/contrib/scripts/Makefile.am
+++ /dev/null
@@ -1,25 +0,0 @@
-##
-## This file is part of Gambit
-## Copyright (c) 1994-2023, The Gambit Project (http://www.gambit-project.org)
-##
-## FILE: contrib/scripts/Makefile.am
-## automake input for sample scripts directory
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-##
-
-SUBDIRS = enumpoly
-
-EXTRA_DIST =
diff --git a/contrib/scripts/enumpoly/Makefile.am b/contrib/scripts/enumpoly/Makefile.am
deleted file mode 100644
index 0441bdfb5..000000000
--- a/contrib/scripts/enumpoly/Makefile.am
+++ /dev/null
@@ -1,15 +0,0 @@
-#
-# $Source$
-# $Date: 2005-12-12 01:22:08 -0600 (Mon, 12 Dec 2005) $
-# $Revision: 6040 $
-#
-# DESCRIPTION:
-# automake input for sample scripts directory
-#
-
-EXTRA_DIST = \
- countsupport.py \
- enumpoly.py \
- phc.py \
- setup.py \
- README
diff --git a/contrib/scripts/enumpoly/README b/contrib/scripts/enumpoly/README
deleted file mode 100644
index 4b111ae2a..000000000
--- a/contrib/scripts/enumpoly/README
+++ /dev/null
@@ -1,111 +0,0 @@
-gambit-enumphc version 0.2007.03.12
-===================================
-
-This is the README for gambit-enumphc, which attempts to compute all
-(isolated) Nash equilibria of a strategic game by enumerating possible supports
-and solving the corresponding systems of polynomial equations.
-
-This program is similar in principle to the gambit-enumpoly program in
-the Gambit distribution. It differs in practice in two (useful) ways:
-
-(1) The support enumeration method is slightly different (although it
-appears to produce identical output, just in a different order).
-The correctness of the implementation here has been established
-formally, in that it will (a) visit any support at most once, and
-(b) visit any support that may harbor a Nash equilibrium. It is somewhat
-more efficient than the implementation in gambit-enumpoly, although
-the support enumeration process is typically a small portion of the
-overall running time of this method. Also, this implementation solves
-on each support as it is generated, as opposed to gambit-enumpoly, which
-first generates the list of candidate supports, and then solves them.
-
-(2) The backend is the PHCPACK solver of Jan Verschelde. This program
-has been tested with version 2.3.24 of PHCPACK. PHCPACK source, and
-binaries for several systems, can be obtained either from the main
-PHCPACK site at
-
-http://www.math.uic.edu/~jan/download.html
-
-or from the Gambit website
-
-http://econweb.tamu.edu/gambit/download.html
-
-The version of PHCPACK mirrored on the Gambit site has been tested with
-this program. Newer versions of PHCPACK may be available from the
-main site; these should also work with this program, but there is always
-the possibility of incompatible changes in PHCPACK's output.
-
-The PHCPACK "black-box" solver for computing solutions to a system of
-polynomial equations, which is called by this program, is much more
-reliable than the polynomial system solver used in gambit-enumpoly.
-
-The program is written in Python, and has been tested with Python 2.4.
-It requires that the Gambit Python extension (available from the
-Gambit website) has been installed. The program is invoked as
-
-python enumphc.py [options] < game.nfg
-
-Consult the gambit-enumpoly documentation for general information about
-the method; this program can be thought of as a "drop-in" replacement
-for that program (at least for strategic games). What follows
-documents some differences and usage notes for this program. Note that
-this program is still young, and may require some care and feeding in
-terms of the tolerances below.
-
-Additional options available:
--p: Specify a prefix for the files used to communicate with PHCPACK.
- In calling the PHCPACK executable, this program creates a file
- with the polynomial system that is passed to PHCPACK, and PHCPACK
- communicates the solutions back via another file. If you run
- multiple instances of this program at once, you will want to specify
- different prefixes for these files to use.
-
--t: Payoff tolerance. There are typically many solutions to the system
- of equations for a support that are not Nash equilibria. Some of
- these solutions fail to be Nash because a strategy not in the support
- has a higher payoff than the strategies in the support. This
- parameter adjusts the tolerance for strategies with superior payoffs
- outside the support; it defaults to 1.0e-6, meaning that a profile
- is reported to be Nash even if there exists strategies with superior
- payoffs outside the support, so long as the regret is no more than
- 1.0e-6. Note that if your payoff scales are large, you will almost
- certainly want to modify this (1.0e-6 seems about appropriate for
- games whose payoffs are on [0, 10] or so).
-
--n: Negative probability tolerance. By default, any solution with a
- negative probability, no matter how small, is not regarded as Nash.
- Giving a number greater than zero for this parameter allows solutions
- with negative probabilities no greater in magnitude than the parameter
- to be considered Nash.
-
-Both the -t and -n parameters attempt to address situations in which the
-game being solved is "almost non-generic", in the sense that there is a
-nearby game that is degenerate.
-
-By default the program only reports equilibria found. Using the -v
-command-line switch adds three additional pieces of information to the
-output:
-(1) candidate supports: as each support is visited, its contents are
- output with the tag "candidate"
-(2) singular supports: If for any reason PHCPACK returns with an error
- on a system, that support is flagged as "singular". This might
- indicate a bug in PHCPACK; more often, it may indicate that a game
- is not generic, in that this support (or a closely related one) might
- harbor a positive-dimensional continuum of equilibria. The
- program currently does not attempt to further characterize equilibria
- on such supports (but hopes to do so in the future).
-(3) non-equilibrium profiles: All solutions to the system of equations
- which are not Nash equilibria are reported, prefixed by the tag
- "noneqm". In addition, to these lines is appended the Lyapunov value
- of the profile. This is a nonnegative value which is zero exactly
- at Nash equilibria. Small values of the Lyapunov value might indicate
- that the profile is in fact an approximate Nash equilibrium, i.e.,
- there is in fact an equilibrium nearby, but the profile fails the
- equilibrium test for numerical reasons. This value can thus be used
- as a diagnostic for setting the -t and -n parameters (above) appropriately.
-
-This program is described in "Towards a Black-Box Solver for Finte
-Games: Finding All Nash Equilibria with Gambit and PHCpack," which is
-available at
-
-http://econweb.tamu.edu/turocy/papers/enumpoly.html
diff --git a/contrib/scripts/enumpoly/countsupport.py b/contrib/scripts/enumpoly/countsupport.py
deleted file mode 100644
index c172f3f03..000000000
--- a/contrib/scripts/enumpoly/countsupport.py
+++ /dev/null
@@ -1,40 +0,0 @@
-import random
-import enumphc
-import randomnfg
-
-if __name__ == '__main__':
- import sys, os
-
- #os.system("rm game-*.nfg")
-
- argv = sys.argv
- solve = True
-
- if argv[1] == '-e':
- solve = False
- argv = [ argv[0] ] + argv[2:]
-
- game = randomnfg.CreateNfg([int(x) for x in argv[2:]])
- strategies = [ strategy
- for player in game.Players()
- for strategy in player.Strategies() ]
-
- print "ownuser,ownsystem,childuser,childsystem,supports,singular,nash,nonnash"
- for iter in xrange(int(argv[1])):
- randomnfg.RandomizeGame(game, lambda: random.normalvariate(0, 1))
- #file("game-%04d.nfg" % iter, "w").write(game.AsNfgFile())
- logger = enumphc.CountLogger()
- startTime = os.times()
- enumphc.EnumViaPHC(game, "blek", logger, solve=solve)
- endTime = os.times()
-
- print ("%f,%f,%f,%f,%d,%d,%d,%d" %
- (endTime[0] - startTime[0],
- endTime[1] - startTime[1],
- endTime[2] - startTime[2],
- endTime[3] - startTime[3],
- logger.candidates,
- logger.singulars,
- logger.nash,
- logger.nonnash))
- sys.stdout.flush()
diff --git a/contrib/scripts/enumpoly/enumpoly.py b/contrib/scripts/enumpoly/enumpoly.py
deleted file mode 100644
index b8f7cbe56..000000000
--- a/contrib/scripts/enumpoly/enumpoly.py
+++ /dev/null
@@ -1,501 +0,0 @@
-#
-# Compute all equilibria of a strategic game using support enumeration.
-#
-# This program enumerates all "admissible" supports in a strategic game.
-# The main function, Enumerate(), does the enumeration, and hands off
-# each nontrivial admissible support to the Solve() method of the
-# solver object provided it.
-#
-# This program currently offers three solver objects:
-# (1) A "null" solver object (so, for instance, one can just count supports)
-# (2) A "PHCSolve" solver object, which solves the implied system using
-# PHCpack (external binary required)
-# (3) A "Bertini" solver object, which solves the implied system using
-# Bertini (external binary required)
-#
-
-import gambit
-import phc
-import time, os
-
-
-#############################################################################
-# Generate polynomial equations for a support
-#############################################################################
-
-# Use this table to assign letters to player strategy variables
-# We skip 'e' and 'i', because PHC doesn't allow these in variable names.
-playerletters = [ 'a', 'b', 'c', 'd', 'f', 'g', 'h', 'k', 'l', 'm',
- 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
- 'x', 'y', 'z' ]
-
-def Equations(support, player):
- payoffs = [ [] for strategy in support.Strategies(player) ]
-
- players = [ (pl, oplayer)
- for (pl, oplayer) in enumerate(support.GetGame().Players())
- if player != oplayer ]
- strategies = [ (st, strategy)
- for (st, strategy) in enumerate(support.Strategies(player)) ]
- for profile in gambit.StrategyIterator(support,
- support.GetStrategy(player.GetNumber(),
- 1)):
- contingency = "*".join([playerletters[pl] +
- "%d" %
- (profile.GetStrategy(oplayer).GetNumber()-1)
- for (pl, oplayer) in players])
- for (st, strategy) in strategies:
- payoffs[st].append("(%s*%s)" %
- (str(float(profile.GetStrategyValue(strategy))),
- contingency))
-
- equations = [ "(%s)-(%s);\n" % ("+".join(payoffs[0]), "+".join(s2))
- for s2 in payoffs[1:] ]
-
- # Substitute in sum-to-one constraints
- for (pl, oplayer) in enumerate(support.GetGame().Players()):
- if player == oplayer: continue
- playerchar = playerletters[pl]
- if support.NumStrategies(pl+1) == 1:
- for (i, equation) in enumerate(equations):
- equations[i] = equation.replace("*%s%d" % (playerchar,
- support.GetStrategy(pl+1, 1).GetNumber()-1),
- "")
- else:
- subvar = "1-" + "-".join( playerchar+"%d" % (strategy.GetNumber() - 1)
- for (st, strategy) in enumerate(support.Strategies(oplayer))
- if st != 0 )
- for (i, equation) in enumerate(equations):
- equations[i] = equation.replace("%s%d" % (playerchar,
- support.GetStrategy(pl+1, 1).GetNumber()-1),
- "("+subvar+")")
-
- return equations
-
-
-#############################################################################
-# Utility routines
-#############################################################################
-
-def IsNash(profile, vars):
- for i in xrange(1, profile.MixedProfileLength()+1):
- if profile[i] < -negTolerance: return False
-
- for (pl, player) in enumerate(profile.GetGame().Players()):
- playerchar = playerletters[pl]
- payoff = profile.GetPayoff(player)
- total = 0.0
- for (st, strategy) in enumerate(player.Strategies()):
- if "%s%d" % (playerchar, st) not in vars.keys():
- if profile.GetStrategyValue(strategy) - payoff > payoffTolerance:
- return False
- else:
- total += vars["%s%d" % (playerchar, st)].real
-
- return True
-
-def CheckEquilibrium(game, support, entry, logger):
- profile = game.NewMixedStrategyDouble()
-
- index = 1
-
- for (pl, player) in enumerate(game.Players()):
- playerchar = playerletters[pl]
- total = 0.0
- for (st, strategy) in enumerate(player.Strategies()):
- try:
- profile[index] = entry["vars"][playerchar + str(st)].real
- except KeyError:
- profile[index] = 0.0
- total += profile[index]
- index += 1
-
- profile[support.GetStrategy(pl+1, 1).GetId()] = 1.0 - total
- entry["vars"][playerchar + str(support.GetStrategy(pl+1,1).GetNumber()-1)] = complex(1.0 - total)
-
- x = ",".join([ "%f" % profile[i]
- for i in xrange(1, game.MixedProfileLength() + 1)])
- if IsNash(profile, entry["vars"]):
- logger.OnNashSolution(profile)
- else:
- logger.OnNonNashSolution(profile)
-
-
-def IsPureSupport(support):
- return reduce(lambda x, y: x and y,
- [ support.NumStrategies(pl+1) == 1
- for (pl, player) in enumerate(support.Players()) ])
-
-def ProfileFromPureSupport(game, support):
- profile = game.NewMixedStrategyDouble()
-
- for index in xrange(1, game.MixedProfileLength() + 1):
- profile[index] = 0.0
-
- for (pl, player) in enumerate(support.Players()):
- profile[support.GetStrategy(pl+1, 1).GetId()] = 1.0
-
- return profile
-
-
-#############################################################################
-# Solver classes
-#############################################################################
-
-class SupportSolver:
- def __init__(self, logger):
- self.logger = logger
-
- def GetLogger(self): return self.logger
-
-class NullSolver(SupportSolver):
- def __init__(self, logger):
- SupportSolver.__init__(self, logger)
-
- def Solve(support):
- pass
-
-#############################################################################
-# Solver for solving support via PHCpack
-#############################################################################
-
-class PHCSolver(SupportSolver):
- def __init__(self, prefix, logger):
- SupportSolver.__init__(self, logger)
- self.prefix = prefix
-
- def GetFilePrefix(self): return self.prefix
-
- def Solve(self, support):
- game = support.GetGame()
-
- eqns = reduce(lambda x, y: x + y,
- [ Equations(support, player)
- for player in game.Players() ])
-
- phcinput = ("%d\n" % len(eqns)) + "".join(eqns)
- #print phcinput
-
- try:
- phcoutput = phc.RunPHC("./phc", self.prefix, phcinput)
- #print "%d solutions found; checking for equilibria now" % len(phcoutput)
- for entry in phcoutput:
- CheckEquilibrium(game, support, entry, self.logger)
-
- #print "Equilibrium checking complete"
- except ValueError:
- self.logger.OnSingularSupport(support)
- except:
- self.logger.OnSingularSupport(support)
-
-
-#############################################################################
-# Solver for solving support via Bertini
-#############################################################################
-
-class BertiniSolver(SupportSolver):
- def __init__(self, logger, bertiniPath="./bertini"):
- SupportSolver.__init__(self, logger)
- self.bertiniPath = bertiniPath
-
- def GetBertiniPath(self): return self.bertiniPath
- def SetBertiniPath(self, path): self.bertiniPath = path
-
- def Solve(self, support):
- game = support.GetGame()
-
- eqns = reduce(lambda x, y: x + y,
- [ Equations(support, player)
- for player in game.Players() ])
-
- variables = [ ]
- for (pl, player) in enumerate(game.Players()):
- for st in xrange(support.NumStrategies(pl+1) - 1):
- variables.append("%c%d" % (playerletters[pl], st+1))
-
- bertiniInput = "CONFIG\n\nEND;\n\nINPUT\n\n"
- bertiniInput += "variable_group %s;\n" % ", ".join(variables)
- bertiniInput += "function %s;\n\n" % ", ".join([ "e%d" % i
- for i in xrange(len(eqns)) ])
-
- for (eq, eqn) in enumerate(eqns):
- bertiniInput += "e%d = %s\n" % (eq, eqn)
-
- bertiniInput += "\nEND;\n"
-
- #print bertiniInput
-
- infile = file("input", "w")
- infile.write(bertiniInput)
- infile.close()
-
- if os.system("%s > /dev/null" % self.bertiniPath) == 0:
- try:
- solutions = [ ]
- outfile = file("real_finite_solutions")
- lines = iter(outfile)
-
- lines.next()
- lines.next()
- while lines.next().strip() != "-1":
- solution = { }
- solution["vars"] = { }
- for var in variables:
- value = lines.next().split()
- solution["vars"][var] = complex(float(value[0]),
- float(value[1]))
- #print solution
- solutions.append(solution)
- outfile.close()
- os.remove("real_finite_solutions")
- for entry in solutions:
- CheckEquilibrium(game, support, entry, self.logger)
-
- except IOError:
- #print "couldn't open real_finite_solutions"
- self.logger.OnSingularSupport(support)
-
-
-
-#############################################################################
-# Logger classes for storing and reporting output
-#############################################################################
-
-class NullLogger:
- def OnCandidateSupport(self, support): pass
- def OnSingularSupport(self, support): pass
- def OnNashSolution(self, profile): pass
- def OnNonNashSolution(self, profile): pass
-
-class StandardLogger(NullLogger):
- def OnNashSolution(self, profile):
- print "NE," + ",".join(["%f" % max(profile[i], 0.0)
- for i in xrange(1, profile.MixedProfileLength() + 1)])
-
-class VerboseLogger(StandardLogger):
- def PrintSupport(self, support, label):
- strings = [ reduce(lambda x, y: x + y,
- [ str(int(support.Contains(strategy)))
- for strategy in player.Strategies() ])
- for player in support.Players() ]
- print label + "," + ",".join(strings)
-
- def OnCandidateSupport(self, support):
- self.PrintSupport(support, "candidate")
- def OnSingularSupport(self, support):
- self.PrintSupport(support, "singular")
-
- def OnNonNashSolution(self, profile):
- print ("noneqm," +
- ",".join([str(profile[i])
- for i in xrange(1, profile.MixedProfileLength() + 1)]) +
- "," + str(profile.GetLiapValue()))
-
-
-class CountLogger:
- def __init__(self):
- self.candidates = 0
- self.singulars = 0
- self.nash = 0
- self.nonnash = 0
-
- def OnCandidateSupport(self, support): self.candidates += 1
- def OnSingularSupport(self, support): self.singulars += 1
- def OnNashSolution(self, profile): self.nash += 1
- def OnNonNashSolution(self, profile): self.nonnash += 1
-
-
-#############################################################################
-# The main program: enumerate supports and pass them to a solver class
-#############################################################################
-
-#
-# Compute the admissible supports such that all strategies in 'setin'
-# are in the support, all the strategies in 'setout' are not in the
-# support.
-#
-# setin: Set of strategies assumed "in" the support
-# setout: Set of strategies assumed "outside" the support
-# setfree: Set of strategies not yet allocated
-# These form a partition of the set of all strategies
-#
-def AdmissibleSubsupports(game, setin, setout, setfree, skipdom = False):
- #print time.time(), len(setin), len(setout)
- support = gambit.StrategySupport(game)
- for strategy in setout:
- if not support.RemoveStrategy(strategy):
- # If the removal fails, it means we have no strategies
- # for this player; we can stop
- raise StopIteration
- if setfree == []:
- # We have assigned all strategies. Now check for dominance.
- # Note that we check these "by hand" because when eliminating
- # by "external" dominance, the last strategy for a player
- # won't be eliminated, even if it should, because RemoveStrategy()
- # will not allow the last strategy for a player to be removed.
- # Need to consider whether the API should be modified for this.
- for player in game.Players():
- for strategy in support.Strategies(player):
- if support.IsDominated(strategy, True, True):
- raise StopIteration
- yield support
- else:
- #print "Starting iterated dominance"
- if not skipdom:
- while True:
- newsupport = support.Undominated(True, True)
- if newsupport == support: break
- support = newsupport
- #print "Done with iterated dominance"
-
- for strategy in setin:
- if not support.Contains(strategy):
- # We dropped a strategy already assigned as "in":
- # we can terminate this branch of the search
- raise StopIteration
-
- subsetout = setout[:]
- subsetfree = setfree[:]
-
- for strategy in setfree:
- if not newsupport.Contains(strategy):
- subsetout.append(strategy)
- subsetfree.remove(strategy)
-
- else:
- subsetout = setout[:]
- subsetfree = setfree[:]
-
- # Switching the order of the following two calls (roughly)
- # switches whether supports are tried largest first, or
- # smallest first
- # Currently: smallest first
-
- # When we add a strategy to 'setin', we can skip the dominance
- # check at the next iteration, because it will return the same
- # result as here
-
- for subsupport in AdmissibleSubsupports(game,
- setin,
- subsetout + [subsetfree[0]],
- subsetfree[1:]):
- yield subsupport
-
- for subsupport in AdmissibleSubsupports(game,
- setin + [subsetfree[0]],
- subsetout, subsetfree[1:]):
- yield subsupport
-
-
- raise StopIteration
-
-def Enumerate(game, solver):
- game.BuildComputedValues()
-
- for support in AdmissibleSubsupports(game, [], [],
- [ strategy
- for player in game.Players()
- for strategy in player.Strategies() ]):
- solver.GetLogger().OnCandidateSupport(support)
-
- if IsPureSupport(support):
- # By definition, if this is a pure-strategy support, it
- # must be an equilibrium (because of the dominance
- # elimination process)
- solver.GetLogger().OnNashSolution(ProfileFromPureSupport(game, support))
- else:
- solver.Solve(support)
-
-########################################################################
-# Instrumentation for standalone use
-########################################################################
-
-def PrintBanner(f):
- f.write("Compute Nash equilibria by solving polynomial systems\n")
- f.write("(solved using Jan Verschelde's PHCPACK solver)\n")
- f.write("Gambit version 0.2007.03.12, Copyright (C) 2007, The Gambit Project\n")
- f.write("This is free software, distributed under the GNU GPL\n\n")
-
-def PrintHelp(progname):
- PrintBanner(sys.stderr)
- sys.stderr.write("Usage: %s [OPTIONS]\n" % progname)
- sys.stderr.write("Accepts game on standard input.\n")
- sys.stderr.write("With no options, reports all Nash equilibria found.\n\n")
- sys.stderr.write("Options:\n")
- sys.stderr.write(" -h print this help message\n")
- sys.stderr.write(" -m backend to use (null, phc, bertini)\n")
- sys.stderr.write(" -n tolerance for negative probabilities (default 0)\n")
- sys.stderr.write(" -p prefix to use for filename in calling PHC\n")
- sys.stderr.write(" -q quiet mode (suppresses banner)\n")
- sys.stderr.write(" -t tolerance for non-best-response payoff (default 1.0e-6)\n")
- sys.stderr.write(" -v verbose mode (shows supports investigated)\n")
- sys.stderr.write(" (default is only to show equilibria)\n")
- sys.exit(1)
-
-payoffTolerance = 1.0e-6
-negTolerance = 0.0
-
-if __name__ == '__main__':
- import getopt, sys
-
- verbose = False
- quiet = False
- prefix = "blek"
- backend = "phc"
-
- try:
- opts, args = getopt.getopt(sys.argv[1:], "p:n:t:hqvd:m:")
- except getopt.GetoptError:
- PrintHelp(sys.argv[0])
-
- for (o, a) in opts:
- if o == "-h":
- PrintHelp(sys.argv[0])
- elif o == "-d":
- pass
- elif o == "-v":
- verbose = True
- elif o == "-q":
- quiet = True
- elif o == "-p":
- prefix = a
- elif o == "-m":
- if a in [ "null", "phc", "bertini" ]:
- backend = a
- else:
- sys.stderr.write("%s: unknown backend `%s' passed to `-m'\n" %
- (sys.argv[0], a))
- sys.exit(1)
- elif o == "-n":
- try:
- negTolerance = float(a)
- except ValueError:
- sys.stderr.write("%s: `-n' option expects a floating-point number\n" % sys.argv[0])
- sys.exit(1)
- elif o == "-t":
- try:
- payoffTolerance = float(a)
- except ValueError:
- sys.stderr.write("%s: `-t' option expects a floating-point number\n" % sys.argv[0])
- else:
- sys.stderr.write("%s: Unknown option `-%s'.\n" %
- (sys.argv[0], o))
- sys.exit(1)
-
- if not quiet:
- PrintBanner(sys.stderr)
-
- game = gambit.ReadGame(sys.stdin.read())
- game.BuildComputedValues()
- if verbose:
- logger = VerboseLogger()
- else:
- logger = StandardLogger()
-
- if backend == "bertini":
- Enumerate(game, solver=BertiniSolver(logger,
- bertiniPath="./bertini"))
- elif backend == "phc":
- Enumerate(game, solver=PHCSolver(prefix, logger))
- else:
- Enumerate(game, solver=NullSolver(logger))
diff --git a/contrib/scripts/enumpoly/phc.py b/contrib/scripts/enumpoly/phc.py
deleted file mode 100644
index 6cb3ce458..000000000
--- a/contrib/scripts/enumpoly/phc.py
+++ /dev/null
@@ -1,143 +0,0 @@
-###########################################################################
-# A Python interface to PHCPACK
-###########################################################################
-
-# This takes the entire output of a PHCPACK run, and returns a list of
-# solutions.
-# Each solution is a Python dictionary with the following entries,
-# each containing one of the (corresponding) data from a solution:
-# * "startresidual"
-# * "iterations"
-# * "result"
-# * "t"
-# * "m"
-# * "err"
-# * "rco"
-# * "res"
-#
-# There are two other dictionary entries of interest:
-# * "type": the text string PHCPACK emits describing the output
-# (e.g., "no solution", "real regular", etc.)
-# * "vars": a dictionary whose keys are the variable names,
-# and whose values are the solution values, represented
-# using the Python `complex` type.
-def ProcessPHCOutput(output):
- # This initial phase identifies the part of the output containing
- # the solutions. It is complicated slightly because (as of Jan 2007)
- # PHCPACK's blackbox solver uses special-case code for linear and
- # sparse systems (this per email from Jan Verschelde). When these
- # methods are in effect, the output format is slightly different.
-
- startsol = output.find("THE SOLUTIONS :\n\n")
-
- if startsol == -1:
- startsol = output.find("THE SOLUTIONS :\n")
-
- solns = output[startsol:]
-
- firstequals = solns.find("solution")
- firstcut = solns[firstequals:]
-
- secondequals = firstcut.find("=====")
- if secondequals >= 0:
- secondcut = firstcut[:secondequals]
- else:
- secondequals = firstcut.find("TIMING")
- secondcut = firstcut[:secondequals]
-
- solutions = [ ]
-
- for line in secondcut.split("\n"):
- tokens = [ x.strip(" ")
- for x in line.split(" ")
- if x != "" and not x.isspace() ]
-
- if tokens == []: continue
-
- if tokens[0] == "solution":
- if len(tokens) == 3:
- # This is a solution that didn't involve iteration
- solutions.append( { "vars": { } } )
- else:
- solutions.append({ "startresidual": float(tokens[6]),
- "iterations": int(tokens[9]),
- "result": tokens[10],
- "vars": { } })
- elif tokens[0] == "t":
- solutions[-1]["t"] = complex(float(tokens[2]),
- float(tokens[3]))
- elif tokens[0] == "m":
- solutions[-1]["m"] = int(tokens[2])
- elif tokens[0] == "the":
- pass
- elif tokens[0] == "==":
- solutions[-1]["err"] = float(tokens[3])
- solutions[-1]["rco"] = float(tokens[7])
- solutions[-1]["res"] = float(tokens[11])
- try:
- solutions[-1]["type"] = " ".join([tokens[13], tokens[14]])
- except IndexError:
- # Some solutions don't have type information
- pass
- else:
- # This is a solution line
- solutions[-1]["vars"][tokens[0]] = complex(float(tokens[2]),
- float(tokens[3]))
-
- return solutions
-
-
-import os
-
-# This sets up and processes a PHC run.
-# 'phcpath' is the full path to PHC on the system;
-# 'equations' is a text string containing the system in PHC's input format
-# Returns a list of the solutions
-# (see the comments on ProcessPHCOutput() above for the description of
-# each of these solution entries)
-def RunPHC(phcpath, filename, equations):
- infilename = filename
- outfilename = filename + ".phc"
-
- infile = file(infilename, "w")
- infile.write(equations)
- infile.write("\n\n")
- infile.close()
-
- if os.system(" ".join([phcpath, "-b", infilename, outfilename])) == 0:
- outfile = file(outfilename)
- output = outfile.read()
- outfile.close()
- else:
- # For convenience, print the equation sets that cause problems
- print equations
- os.remove(infilename)
- os.remove(outfilename)
- raise ValueError, "PHC run failed"
-
- os.remove(outfilename)
- os.remove(infilename)
-
- return ProcessPHCOutput(output)
-
-if __name__ == '__main__':
- # 2x2.nfg, full support
- output = RunPHC("./phc", "foo",
- "4\n"
- "2*b1 - b2;\n"
- "b1 + b2 - 1;\n"
- "a2 - a1;\n"
- "a1 + a2 - 1;\n")
- print output
-
- # 2x2x2.nfg, full support
- output = RunPHC("./phc", "foo",
- "6\n"
- "9*b1*c1+3*b2*c2-(3*b1*c2+9*b2*c1);\n"
- "8*a1*c1+4*a2*c2-(4*a1*c2+8*a2*c1);\n"
- "12*a1*b1+2*a2*b2-(6*a1*b2+6*a2*b1);\n"
- "a1+a2-1;\n"
- "b1+b2-1;\n"
- "c1+c2-1;\n"
- )
- print output
diff --git a/contrib/scripts/enumpoly/setup.py b/contrib/scripts/enumpoly/setup.py
deleted file mode 100644
index abb8af826..000000000
--- a/contrib/scripts/enumpoly/setup.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# This is a distutils/py2exe script to build the Windows binary version
-# of gambit-enumphc
-
-from distutils.core import setup
-import py2exe
-
-setup(console=["enumphc.py"],
- data_files=[(".",
- [ "phc.exe", "README" ])])
diff --git a/doc/conf.py b/doc/conf.py
index e20f458ab..e8098594f 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -49,9 +49,9 @@
# built documents.
#
# The short X.Y version.
-version = "16.2"
+version = "16.3"
# The full version, including alpha/beta/rc tags.
-release = "16.2.1"
+release = "16.3.0"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/doc/formats.bagg.rst b/doc/formats.bagg.rst
index 356314548..ef2a20955 100644
--- a/doc/formats.bagg.rst
+++ b/doc/formats.bagg.rst
@@ -38,4 +38,4 @@ separated by whitespaces. Lines with starting '#' are treated as comments and ar
#. utility function for each action node: same as in `the AGG format`_.
-.. _the AGG format: file-formats-agg_
+.. _the AGG format: _file-formats-agg
diff --git a/doc/index.rst b/doc/index.rst
index 691e67368..2dd8f21dd 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -33,7 +33,7 @@ made.
If you are citing Gambit in a paper, we suggest a citation of the form:
Savani, Rahul and Turocy, Theodore L. (2025)
- Gambit: The package for computation in game theory, Version 16.2.1.
+ Gambit: The package for computation in game theory, Version 16.3.0.
https://www.gambit-project.org.
Replace the version number and year as appropriate if you use a
diff --git a/doc/intro.rst b/doc/intro.rst
index ad26eefc5..68c8d7caa 100644
--- a/doc/intro.rst
+++ b/doc/intro.rst
@@ -183,7 +183,7 @@ Downloading Gambit
==================
Gambit source code and built binaries can be downloaded from the project
-`GitHub repository releases section`.
+`GitHub repository releases section `_.
Older versions of Gambit can be downloaded from
`http://sourceforge.net/projects/gambit/files
diff --git a/doc/pygambit.api.rst b/doc/pygambit.api.rst
index e1a8a1710..0b9de26e9 100644
--- a/doc/pygambit.api.rst
+++ b/doc/pygambit.api.rst
@@ -28,13 +28,20 @@ Creating, reading, and writing games
.. autosummary::
:toctree: api/
+ read_gbt
+ read_efg
+ read_nfg
+ read_agg
+
Game.new_tree
Game.new_table
Game.from_arrays
+ Game.to_arrays
Game.from_dict
- Game.read_game
- Game.parse_game
- Game.write
+ Game.to_efg
+ Game.to_nfg
+ Game.to_html
+ Game.to_latex
Transforming game trees
@@ -138,6 +145,7 @@ Information about the game
Node.infoset
Node.player
Node.is_successor_of
+ Node.plays
.. autosummary::
@@ -150,6 +158,7 @@ Information about the game
Infoset.actions
Infoset.members
Infoset.precedes
+ Infoset.plays
.. autosummary::
@@ -159,6 +168,7 @@ Information about the game
Action.infoset
Action.precedes
Action.prob
+ Action.plays
.. autosummary::
@@ -168,6 +178,7 @@ Information about the game
Strategy.game
Strategy.player
Strategy.number
+ Strategy.action
Player behavior
@@ -180,7 +191,7 @@ Player behavior
Game.random_strategy_profile
Game.mixed_behavior_profile
Game.random_behavior_profile
- Game.support_profile
+ Game.strategy_support_profile
Representation of strategic behavior
@@ -205,8 +216,8 @@ Probability distributions over strategies
MixedStrategyProfile.strategy_value
MixedStrategyProfile.strategy_regret
MixedStrategyProfile.player_regret
- MixedStrategyProfile.max_regret
MixedStrategyProfile.strategy_value_deriv
+ MixedStrategyProfile.max_regret
MixedStrategyProfile.liap_value
MixedStrategyProfile.as_behavior
MixedStrategyProfile.normalize
@@ -232,14 +243,16 @@ Probability distributions over behavior
MixedBehaviorProfile.__getitem__
MixedBehaviorProfile.__setitem__
MixedBehaviorProfile.payoff
- MixedBehaviorProfile.action_regret
MixedBehaviorProfile.action_value
+ MixedBehaviorProfile.action_regret
MixedBehaviorProfile.infoset_value
+ MixedBehaviorProfile.infoset_regret
MixedBehaviorProfile.node_value
MixedBehaviorProfile.realiz_prob
MixedBehaviorProfile.infoset_prob
MixedBehaviorProfile.belief
MixedBehaviorProfile.is_defined_at
+ MixedBehaviorProfile.max_regret
MixedBehaviorProfile.liap_value
MixedBehaviorProfile.as_strategy
MixedBehaviorProfile.normalize
@@ -279,6 +292,7 @@ Computation of Nash equilibria
NashComputationResult
enumpure_solve
enummixed_solve
+ enumpoly_solve
lp_solve
lcp_solve
liap_solve
@@ -296,6 +310,8 @@ Computation of quantal response equilibria
.. autosummary::
:toctree: api/
- fit_empirical
- fit_fixedpoint
+ logit_solve_branch
+ logit_solve_lambda
+ logit_estimate
LogitQREMixedStrategyFitResult
+ LogitQREMixedBehaviorFitResult
diff --git a/doc/pygambit.user.rst b/doc/pygambit.user.rst
index 23574f6bc..26a46cc98 100644
--- a/doc/pygambit.user.rst
+++ b/doc/pygambit.user.rst
@@ -183,6 +183,29 @@ the top level index is the choice of the first player, the second level index of
and so on. Therefore, to create a two-player symmetric game, as in this example, the payoff matrix
for the second player is transposed before passing to :py:meth:`.Game.from_arrays`.
+There is a reverse function :py:meth:`.Game.to_arrays` that produces
+the players' payoff tables given a strategic game. The output is the list of ``numpy`` arrays,
+where the number of produced arrays is equal to the number of players.
+
+.. ipython:: python
+
+ m, m_transposed = g.to_arrays()
+ m
+
+The optional parameter `dtype`` controls the data type of the payoffs in the generated arrays.
+
+.. ipython:: python
+
+ m, m_transposed = g.to_arrays(dtype=float)
+ m
+
+The function supports any type which can convert from Python's `fractions.Fraction` type.
+For example, to convert the payoffs to their string representations via `str`:
+
+.. ipython:: python
+
+ m, m_transposed = g.to_arrays(dtype=str)
+ m
.. _pygambit.user.numbers:
@@ -292,7 +315,7 @@ the values are recorded as intended.
Reading a game from a file
~~~~~~~~~~~~~~~~~~~~~~~~~~
-Games stored in existing Gambit savefiles can be loaded using :meth:`.Game.read_game`:
+Games stored in existing Gambit savefiles can be loaded using :meth:`.read_efg` or :meth:`.read_nfg`:
.. ipython:: python
:suppress:
@@ -302,7 +325,7 @@ Games stored in existing Gambit savefiles can be loaded using :meth:`.Game.read_
.. ipython:: python
- g = gbt.Game.read_game("e02.nfg")
+ g = gbt.read_nfg("e02.nfg")
g
.. ipython:: python
@@ -340,7 +363,7 @@ the extensive representation. Assuming that ``g`` refers to the game
.. ipython:: python
:suppress:
- g = gbt.Game.read_game("poker.efg")
+ g = gbt.read_efg("poker.efg")
.. ipython:: python
@@ -539,7 +562,7 @@ As an example, consider solving the standard one-card poker game using
.. ipython:: python
- g = gbt.Game.read_game("poker.efg")
+ g = gbt.read_efg("poker.efg")
g.max_payoff, g.min_payoff
:py:func:`.logit_solve` is a globally-convergent method, in that it computes a
@@ -623,7 +646,7 @@ strategies for each player), :py:func:`.liap_solve` finds one of the totally-mix
.. ipython:: python
- g = gbt.Game.read_game("2x2x2.nfg")
+ g = gbt.read_nfg("2x2x2.nfg")
gbt.nash.liap_solve(g.mixed_strategy_profile())
Which equilibrium is found depends on the starting point. With a different starting point,
@@ -686,14 +709,13 @@ from different starting points.
gbt.nash.simpdiv_solve(g.random_strategy_profile(denom=10, gen=gen))
-Estimating quantal response equilibria
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Alongside computing quantal response equilibria, Gambit can also perform maximum likelihood
-estimation, computing the QRE which best fits an empirical distribution of play.
+Quantal response equilibrium
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-As an example we consider an asymmetric matching pennies game studied in [Och95]_,
-analysed in [McKPal95]_ using QRE.
+Gambit implements the idea of [McKPal95]_ and [McKPal98]_ to compute Nash equilibria
+via path-following a branch of the logit quantal response equilibrium (LQRE) correspondence
+using the function :py:func:`.logit_solve`. As an example, we will consider an
+asymmetric matching pennies game from [Och95]_ as analyzed in [McKPal95]_.
.. ipython:: python
@@ -702,13 +724,52 @@ analysed in [McKPal95]_ using QRE.
[[0, 1.1141], [1.1141, 0]],
title="Ochs (1995) asymmetric matching pennies as transformed in McKelvey-Palfrey (1995)"
)
- data = g.mixed_strategy_profile([[128*0.527, 128*(1-0.527)], [128*0.366, 128*(1-0.366)]])
+ gbt.nash.logit_solve(g)
+
+
+:py:func:`.logit_solve` returns only the limiting (approximate) Nash equilibrium found.
+Profiles along the QRE correspondence are frequently of interest in their own right.
+Gambit offers several functions for more detailed examination of branches of the
+QRE correspondence.
+
+The function :py:func:`.logit_solve_branch` uses the same procedure as :py:func:`.logit_solve`,
+but returns a list of LQRE profiles computed along the branch instead of just the limiting
+approximate Nash equilibrium.
+
+.. ipython:: python
+
+ qres = gbt.qre.logit_solve_branch(g)
+ len(qres)
+ qres[0]
+ qres[5]
-Estimation of QRE is done using :py:func:`.fit_fixedpoint`.
+:py:func:`.logit_solve_branch` uses an adaptive step size heuristic to find points on
+the branch. The parameters `first_step` and `max_accel` are used to adjust the initial
+step size and the maximum rate at which the step size changes adaptively. The step size
+used is computed as the distance traveled along the path, and, importantly, not the
+distance as measured by changes in the precision parameter lambda. As a result the
+lambda values for which profiles are computed cannot be controlled in advance.
+In some situations, the LQRE profiles at specified values of lambda are of interest.
+For this, Gambit provides :py:func:`.logit_solve_lambda`. This function provides
+accurate values of strategy profiles at one or more specified values of lambda.
.. ipython:: python
- fit = gbt.qre.fit_fixedpoint(data)
+ qres = gbt.qre.logit_solve_lambda(g, lam=[1, 2, 3])
+ qres[0]
+ qres[1]
+ qres[2]
+
+
+LQRE are frequently taken to data by using maximum likelihood estimation to find the
+LQRE profile that best fits an observed profile of play. This is provided by
+the function :py:func:`.logit_estimate`. We replicate the analysis of a block
+of the data from [Och95]_ for which [McKPal95]_ estimated an LQRE.
+
+.. ipython:: python
+
+ data = g.mixed_strategy_profile([[128*0.527, 128*(1-0.527)], [128*0.366, 128*(1-0.366)]])
+ fit = gbt.qre.logit_estimate(data)
The returned :py:class:`.LogitQREMixedStrategyFitResult` object contains the results of the
estimation.
@@ -723,7 +784,49 @@ log-likelihood is correct for use in likelihoood-ratio tests. [#f1]_
print(fit.profile)
print(fit.log_like)
+All of the functions above also support working with the agent LQRE of [McKPal98]_.
+Agent QRE are computed as the default behavior whenever the game has a extensive (tree)
+representation. For :py:func:`.logit_solve`, :py:func:`.logit_solve_branch`, and
+:py:func:`.logit_solve_lambda`, this can be overriden by passing `use_strategic=True`;
+this will compute LQRE using the reduced strategy set of the game instead.
+Likewise, :py:func:`.logit_estimate` will perform estimation using agent LQRE if the
+data passed are a :py:class:`.MixedBehaviorProfile`, and will return a
+:py:class:`.LogitQREMixedBehaviorFitResult` object.
+
.. rubric:: Footnotes
.. [#f1] The log-likelihoods quoted in [McKPal95]_ are exactly a factor of 10 larger than
those obtained by replicating the calculation.
+
+
+Using external programs to compute Nash equilbria
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Because the problem of finding Nash equilibria can be expressed in various
+mathematical formulations (see [McKMcL96]_), it is helpful to make use
+of other software packages designed specifically for solving those problems.
+
+There are currently two integrations offered for using external programs to solve
+for equilibria:
+
+- :py:func:`.enummixed_solve` supports enumeration of equilibria in
+ two-player games via `lrslib`. [#lrslib]_
+- :py:func:`.enumpoly_solve` supports computation of totally-mixed equilibria
+ on supports in strategic games via `PHCpack`. [#phcpack]_
+
+For both calls, using the external program requires passing the path to the
+executable (via the `lrsnash_path` and `phcpack_path` arguments, respectively).
+
+The user must download and compile or install these programs on their own; these are
+not packaged with Gambit. The solver calls do take care of producing the required
+input files, and reading the output to convert into Gambit objects for further
+processing.
+
+
+.. [#lrslib] http://cgm.cs.mcgill.ca/~avis/C/lrs.html
+
+.. [#phcpack] https://homepages.math.uic.edu/~jan/PHCpack/phcpack.html
+
+.. [McKMcL96] McKelvey, Richard D. and McLennan, Andrew M. (1996) Computation of equilibria
+ in finite games. In Handbook of Computational Economics, Volume 1,
+ pages 87-142.
diff --git a/doc/samples.rst b/doc/samples.rst
index 3ffd096ce..e70591203 100644
--- a/doc/samples.rst
+++ b/doc/samples.rst
@@ -60,10 +60,15 @@ Sample games
to let you switch doors, should you?
:download:`nim.efg <../contrib/games/nim.efg>`
+ This is a Nim-like game, which is a useful example of the value
+ of backward induction.
+ This version starts with one pile of five stones, and allows the
+ two players to alternately take 1 or 2 stones.
+ The player to take the last stone wins.
The classic game of
- `Nim `_, which is a useful example
- of the value of backward induction. This version starts with five
- stones. An interesting experimental study of this class of games is
+ `Nim `_ allows multiple piles,
+ and allows a player to remove any number of stones from a pile.
+ An interesting experimental study of Nim games is
`McKinney, C. Nicholas and Van Huyck, John B. (2013) Eureka
learning: Heuristics and response time in perfect information
games. Games and Economic Behavior 79:
diff --git a/doc/tools.convert.rst b/doc/tools.convert.rst
index e13804b0e..b1f3623c0 100644
--- a/doc/tools.convert.rst
+++ b/doc/tools.convert.rst
@@ -40,7 +40,7 @@ Example invocation for HTML output::
$ gambit-convert -O html 2x2.nfg
Convert games among various file formats
- Gambit version 16.2.0, Copyright (C) 1994-2024, The Gambit Project
+ Gambit version 16.3.0, Copyright (C) 1994-2025, The Gambit Project
This is free software, distributed under the GNU GPL
Two person 2 x 2 game with unique mixed equilibrium
@@ -55,7 +55,7 @@ Example invocation for LaTeX output::
$ gambit-convert -O sgame 2x2.nfg
Convert games among various file formats
- Gambit version 16.2.0, Copyright (C) 1994-2024, The Gambit Project
+ Gambit version 16.3.0, Copyright (C) 1994-2025, The Gambit Project
This is free software, distributed under the GNU GPL
\begin{game}{2}{2}[Player 1][Player 2]
diff --git a/doc/tools.enummixed.rst b/doc/tools.enummixed.rst
index 56bd0ba94..fb04021f7 100644
--- a/doc/tools.enummixed.rst
+++ b/doc/tools.enummixed.rst
@@ -70,9 +70,7 @@ in Figure 2 of Selten (International Journal of Game Theory,
$ gambit-enummixed e02.nfg
Compute Nash equilibria by enumerating extreme points
- Gambit version 16.2.0, Copyright (C) 1994-2024, The Gambit Project
- Enumeration code based on lrslib 4.2b,
- Copyright (C) 1995-2005 by David Avis (avis@cs.mcgill.ca)
+ Gambit version 16.3.0, Copyright (C) 1994-2025, The Gambit Project
This is free software, distributed under the GNU GPL
NE,1,0,0,1,0
@@ -84,9 +82,7 @@ information using the `-c` switch::
$ gambit-enummixed -c e02.nfg
Compute Nash equilibria by enumerating extreme points
- Gambit version 16.2.0, Copyright (C) 1994-2024, The Gambit Project
- Enumeration code based on lrslib 4.2b,
- Copyright (C) 1995-2005 by David Avis (avis@cs.mcgill.ca)
+ Gambit version 16.3.0, Copyright (C) 1994-2025, The Gambit Project
This is free software, distributed under the GNU GPL
NE,1,0,0,1,0
diff --git a/doc/tools.enumpoly.rst b/doc/tools.enumpoly.rst
index 03b79943f..790ae9adb 100644
--- a/doc/tools.enumpoly.rst
+++ b/doc/tools.enumpoly.rst
@@ -8,12 +8,21 @@ and inequalities.
This program searches for all Nash equilibria in a strategic game
using a support enumeration approach. This approach computes all the
supports which could, in principle, be the support of a Nash
-equilibrium, and then searches for a totally mixed equilibrium on that
-support by solving a system of polynomial equalities and inequalities
-formed by the Nash equilibrium conditions. The ordering of the
-supports is done in such a way as to maximize use of previously
-computed information, making it suited to the computation of all Nash
-equilibria.
+equilibrium. For each candidate support, it attempts to compute
+totally mixed equilibria on that support by successively subdividing
+the space of mixed strategy profiles or mixed behavior profiles (as appropriate).
+By using the fact that the equilibrium conditions imply a collection
+of equations and inequalities which can be expressed as multilinear
+polynomials, the subdivision constructed is such that each cell
+contains either no equilibria or exactly one equilibrium.
+
+For strategic games, the program searches supports in the order proposed
+by Porter, Nudelman, and Shoham [PNS04]_. For two-player games, this
+prioritises supports for which both players have the same number of
+strategies. For games with three or more players, this prioritises
+supports which have the fewest strategies in total. For many classes
+of games, this will tend to lower the average time until finding one equilibrium,
+as well as finding the second equilibrium (if one exists).
When the verbose switch `-v` is used, the program outputs each support
as it is considered. The supports are presented as a comma-separated
@@ -22,13 +31,15 @@ digit 1 represents a strategy which is present in the support, and the
digit 0 represents a strategy which is not present. Each candidate
support is printed with the label "candidate,".
-Note that the subroutine to compute a solution to the system of
-polynomial equations and inequalities will fail in degenerate cases.
+The approach of subdividing the space of totally mixed profiles assumes
+solutions to the system of equations and inequalities are isolated
+points. In the case of degeneracies in the resulting system,
When the verbose switch `-v` is used, these supports are identified on
-standard output with the label "singular,". It is possible that there
-exist equilibria, often a connected component of equilibria, on these
-singular supports.
-
+standard output with the label "singular,". This will occur
+if there is a positive-dimensional set of equilibria which all
+share the listed support. However, the converse is not true:
+not all supports labeled as "singular" will necessarily be the
+support of some set of equilibria.
.. program:: gambit-enumpoly
@@ -45,9 +56,7 @@ singular supports.
By default, the program uses an enumeration method designed to
visit as few supports as possible in searching for all equilibria.
- With this switch, the program uses a heuristic search method based on
- Porter, Nudelman, and Shoham [PNS04]_, which is designed to minimize the
- time until the first equilibrium is found. This switch only has an
+ With this switch, This switch only has an
effect when solving strategic games.
.. cmdoption:: -S
@@ -57,6 +66,20 @@ singular supports.
strategies for extensive games. (This has no effect for strategic
games, since a strategic game is its own reduced strategic game.)
+.. cmdoption:: -m
+
+ .. versionadded:: 16.3.0
+
+ Specify the maximum regret criterion for acceptance as an approximate Nash equilibrium
+ (default is 1e-4). See :ref:`pygambit-nash-maxregret` for interpretation and guidance.
+
+.. cmdoption:: -e EQA
+
+ .. versionadded:: 16.3.0
+
+ By default, the program will search all support profiles.
+ This switch instructs the program to terminate when EQA equilibria have been found.
+
.. cmdoption:: -q
Suppresses printing of the banner at program launch.
@@ -68,18 +91,17 @@ singular supports.
singular supports are identified with the label "singular." By
default, no information about supports is printed.
-Computing equilibria of the extensive game :download:`e01.efg
+Computing equilibria of the strategic game :download:`e01.nfg
<../contrib/games/e01.efg>`, the example in Figure 1 of Selten
(International Journal of Game Theory, 1975) sometimes called
"Selten's horse"::
- $ gambit-enumpoly e01.efg
+ $ gambit-enumpoly e01.nfg
Compute Nash equilibria by solving polynomial systems
- Gambit version 16.2.0, Copyright (C) 1994-2024, The Gambit Project
- Heuristic search implementation Copyright (C) 2006, Litao Wei
+ Gambit version 16.3.0, Copyright (C) 1994-2025, The Gambit Project
This is free software, distributed under the GNU GPL
+ NE,1.000000,0.000000,1.000000,0.000000,0.000000,1.000000
+ NE,0.000000,1.000000,1.000000,0.000000,1.000000,0.000000
NE,0.000000,1.000000,0.333333,0.666667,1.000000,0.000000
NE,1.000000,0.000000,1.000000,0.000000,0.250000,0.750000
- NE,1.000000,0.000000,1.000000,0.000000,0.000000,0.000000
- NE,0.000000,1.000000,0.000000,0.000000,1.000000,0.000000
diff --git a/doc/tools.enumpure.rst b/doc/tools.enumpure.rst
index a62696357..799f73cb6 100644
--- a/doc/tools.enumpure.rst
+++ b/doc/tools.enumpure.rst
@@ -66,7 +66,7 @@ Computing the pure-strategy equilibria of extensive game :download:`e02.efg
$ gambit-enumpure e02.efg
Search for Nash equilibria in pure strategies
- Gambit version 16.2.0, Copyright (C) 1994-2024, The Gambit Project
+ Gambit version 16.3.0, Copyright (C) 1994-2025, The Gambit Project
This is free software, distributed under the GNU GPL
NE,1,0,0,0,1,0
@@ -77,7 +77,7 @@ strategies::
$ gambit-enumpure -S e02.efg
Search for Nash equilibria in pure strategies
- Gambit version 16.2.0, Copyright (C) 1994-2024, The Gambit Project
+ Gambit version 16.3.0, Copyright (C) 1994-2025, The Gambit Project
This is free software, distributed under the GNU GPL
NE,1,0,0,1,0
@@ -88,7 +88,7 @@ only one information set; therefore the set of solutions is larger::
$ gambit-enumpure -A e02.efg
Search for Nash equilibria in pure strategies
- Gambit version 16.2.0, Copyright (C) 1994-2024, The Gambit Project
+ Gambit version 16.3.0, Copyright (C) 1994-2025, The Gambit Project
This is free software, distributed under the GNU GPL
NE,1,0,1,0,1,0
diff --git a/doc/tools.gnm.rst b/doc/tools.gnm.rst
index 4e906706c..508f06450 100644
--- a/doc/tools.gnm.rst
+++ b/doc/tools.gnm.rst
@@ -88,7 +88,7 @@ the reduced strategic form of the example in Figure 2 of Selten
$ gambit-gnm e02.nfg
Compute Nash equilibria using a global Newton method
Gametracer version 0.2, Copyright (C) 2002, Ben Blum and Christian Shelton
- Gambit version 16.2.0, Copyright (C) 1994-2024, The Gambit Project
+ Gambit version 16.3.0, Copyright (C) 1994-2025, The Gambit Project
This is free software, distributed under the GNU GPL
NE,1,0,2.99905e-12,0.5,0.5
diff --git a/doc/tools.ipa.rst b/doc/tools.ipa.rst
index 2b38c16f5..0dfa645b6 100644
--- a/doc/tools.ipa.rst
+++ b/doc/tools.ipa.rst
@@ -55,7 +55,7 @@ the reduced strategic form of the example in Figure 2 of Selten
$ gambit-ipa e02.nfg
Compute Nash equilibria using iterated polymatrix approximation
Gametracer version 0.2, Copyright (C) 2002, Ben Blum and Christian Shelton
- Gambit version 16.2.0, Copyright (C) 1994-2024, The Gambit Project
+ Gambit version 16.3.0, Copyright (C) 1994-2025, The Gambit Project
This is free software, distributed under the GNU GPL
NE,1.000000,0.000000,0.000000,1.000000,0.000000
diff --git a/doc/tools.lcp.rst b/doc/tools.lcp.rst
index eb81d3c68..68a9bf0fc 100644
--- a/doc/tools.lcp.rst
+++ b/doc/tools.lcp.rst
@@ -58,6 +58,12 @@ game.
which are subgame perfect. (This has no effect for strategic
games, since there are no proper subgames of a strategic game.)
+.. cmdoption:: -e EQA
+
+ By default, the program will find all equilibria accessible from
+ the origin of the polytopes. This switch instructs the program
+ to terminate when EQA equilibria have been found.
+
.. cmdoption:: -h
Prints a help message listing the available options.
@@ -73,7 +79,7 @@ Computing an equilibrium of extensive game :download:`e02.efg
$ gambit-lcp e02.efg
Compute Nash equilibria by solving a linear complementarity program
- Gambit version 16.2.0, Copyright (C) 1994-2024, The Gambit Project
+ Gambit version 16.3.0, Copyright (C) 1994-2025, The Gambit Project
This is free software, distributed under the GNU GPL
NE,1,0,1/2,1/2,1/2,1/2
diff --git a/doc/tools.liap.rst b/doc/tools.liap.rst
index db7d58a1a..6d1c61973 100644
--- a/doc/tools.liap.rst
+++ b/doc/tools.liap.rst
@@ -85,7 +85,7 @@ Computing an equilibrium in mixed strategies of :download:`e02.efg
$ gambit-liap e02.nfg
Compute Nash equilibria by minimizing the Lyapunov function
- Gambit version 16.2.0, Copyright (C) 1994-2024, The Gambit Project
+ Gambit version 16.3.0, Copyright (C) 1994-2025, The Gambit Project
This is free software, distributed under the GNU GPL
NE, 0.998701, 0.000229, 0.001070, 0.618833, 0.381167
diff --git a/doc/tools.logit.rst b/doc/tools.logit.rst
index 859ee95f0..04dc0e36a 100644
--- a/doc/tools.logit.rst
+++ b/doc/tools.logit.rst
@@ -76,7 +76,13 @@ the beliefs at such information sets as being uniform across all member nodes.
.. cmdoption:: -l
While tracing, compute the logit equilibrium points
- with parameter LAMBDA accurately.
+ with parameter LAMBDA accurately. This option may be specified multiple times,
+ in which case points are found for each successive lambda, in the order specified,
+ along the branch.
+
+ .. versionchanged:: 16.3.0
+
+ Added support for specifying multiple lambda values.
.. cmdoption:: -S
@@ -102,7 +108,7 @@ in Figure 2 of Selten (International Journal of Game Theory,
$ gambit-logit e02.nfg
Compute a branch of the logit equilibrium correspondence
- Gambit version 16.2.0, Copyright (C) 1994-2024, The Gambit Project
+ Gambit version 16.3.0, Copyright (C) 1994-2025, The Gambit Project
This is free software, distributed under the GNU GPL
0.000000,0.333333,0.333333,0.333333,0.5,0.5
diff --git a/doc/tools.lp.rst b/doc/tools.lp.rst
index a065328ce..486d50e59 100644
--- a/doc/tools.lp.rst
+++ b/doc/tools.lp.rst
@@ -65,7 +65,7 @@ strategies each, with a unique equilibrium in mixed strategies::
$ gambit-lp 2x2const.nfg
Compute Nash equilibria by solving a linear program
- Gambit version 16.2.0, Copyright (C) 1994-2024, The Gambit Project
+ Gambit version 16.3.0, Copyright (C) 1994-2025, The Gambit Project
This is free software, distributed under the GNU GPL
NE,1/3,2/3,1/3,2/3
diff --git a/doc/tools.rst b/doc/tools.rst
index 1a4bca109..d08acbebc 100644
--- a/doc/tools.rst
+++ b/doc/tools.rst
@@ -39,6 +39,7 @@ documentation.
tools.enumpure
tools.enummixed
+ tools.enumpoly
tools.lcp
tools.lp
tools.liap
diff --git a/doc/tools.simpdiv.rst b/doc/tools.simpdiv.rst
index 9b411e855..e29094444 100644
--- a/doc/tools.simpdiv.rst
+++ b/doc/tools.simpdiv.rst
@@ -92,7 +92,7 @@ Computing an equilibrium in mixed strategies of :download:`e02.efg
$ gambit-simpdiv e02.nfg
Compute Nash equilibria using simplicial subdivision
- Gambit version 16.2.0, Copyright (C) 1994-2024, The Gambit Project
+ Gambit version 16.3.0, Copyright (C) 1994-2025, The Gambit Project
This is free software, distributed under the GNU GPL
NE,1,0,0,1,0
diff --git a/gambit.wxs b/gambit.wxs
index 08876df43..c8db7f222 100644
--- a/gambit.wxs
+++ b/gambit.wxs
@@ -1,6 +1,6 @@
-
+
diff --git a/pyproject.toml b/pyproject.toml
index 3c3ff6424..fcc5107c5 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,10 +1,47 @@
[build-system]
requires = ["setuptools", "wheel", "Cython"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "pygambit"
+version = "16.3.0"
+description = "The package for computation in game theory"
+readme = "src/README.rst"
+requires-python = ">=3.9"
+license = "GPL-2.0-or-later"
+authors = [
+ {name = "Theodore Turocy", email = "ted.turocy@gmail.com"},
+ {name = "Rahul Savani", email = "rahul.savani@liverpool.ac.uk"}
+]
+keywords = ["game theory", "Nash equilibrium"]
+classifiers=[
+ "Development Status :: 5 - Production/Stable",
+ "Intended Audience :: Science/Research",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
+ "Programming Language :: Python :: Implementation :: CPython",
+ "Topic :: Scientific/Engineering :: Mathematics"
+]
+dependencies = [
+ "numpy",
+ "scipy",
+]
+
+[project.urls]
+Homepage = "https://www.gambit-project.org"
+Documentation = "https://gambitproject.readthedocs.io"
+Repository = "https://github.com/gambitproject/gambit.git"
+Issues = "https://github.com/gambitproject/gambit/issues"
+Changelog = "https://github.com/gambitproject/gambit/blob/master/ChangeLog"
+
[tool.ruff]
line-length = 99
indent-width = 4
-target-version = "py38"
+target-version = "py39"
include = ["setup.py", "src/pygambit/**/*.py", "tests/**/*.py"]
exclude = ["contrib/**.py"]
@@ -33,3 +70,16 @@ indent-style = "space"
[tool.cython-lint]
max-line-length = 99
+
+
+[tool.pytest.ini_options]
+addopts = "--strict-markers"
+markers = [
+ "nash_enummixed_strategy: tests of enummixed_solve in strategies",
+ "nash_lcp_strategy: tests of lcp_solve in strategies",
+ "nash_lcp_behavior: tests of lcp_solve in behaviors",
+ "nash_lp_strategy: tests of lp_solve in strategies",
+ "nash_lp_behavior: tests of lp_solve in behaviors",
+ "nash: all tests of Nash equilibrium solvers",
+ "slow: all time-consuming tests",
+]
diff --git a/setup.py b/setup.py
index 5766141d0..7801dd292 100644
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,6 @@
#
# This file is part of Gambit
-# Copyright (c) 1994-2023, The Gambit Project (http://www.gambit-project.org)
+# Copyright (c) 1994-2025, The Gambit Project (https://www.gambit-project.org)
#
# FILE: src/python/setup.py
# Setuptools configuration file for Gambit Python extension
@@ -26,28 +26,66 @@
import Cython.Build
import setuptools
-# By compiling this separately as a C library, we avoid problems
-# with passing C++-specific flags when building the extension
-lrslib = ("lrslib", {"sources": glob.glob("src/solvers/lrs/*.c")})
+cppgambit_include_dirs = ["src"]
+cppgambit_cflags = (
+ ["-std=c++17"] if platform.system() == "Darwin"
+ else ["/std:c++17"] if platform.system() == "Windows"
+ else []
+)
-cppgambit = (
- "cppgambit",
+cppgambit_core = (
+ "cppgambit_core",
{
- "sources": (
- glob.glob("src/core/*.cc") +
- glob.glob("src/games/*.cc") +
- glob.glob("src/games/agg/*.cc") +
- [fn for fn in glob.glob("src/solvers/*/*.cc") if "enumpoly" not in fn]
- ),
- "include_dirs": ["src"],
- "cflags": (
- ["-std=c++17"] if platform.system() == "Darwin"
- else ["/std:c++17"] if platform.system() == "Windows"
- else []
- )
+ "sources": glob.glob("src/core/*.cc"),
+ "obj_deps": {"": glob.glob("src/core/*.h") + glob.glob("src/core/*.imp")},
+ "include_dirs": cppgambit_include_dirs,
+ "cflags": cppgambit_cflags,
}
)
+cppgambit_games = (
+ "cppgambit_games",
+ {
+ "sources": glob.glob("src/games/*.cc") + glob.glob("src/games/agg/*.cc"),
+ "obj_deps": {
+ "": (
+ glob.glob("src/core/*.h") + glob.glob("src/core/*.imp") +
+ glob.glob("src/games/*.h") + glob.glob("src/games/*.imp") +
+ glob.glob("src/games/agg/*.h")
+ )
+ },
+ "include_dirs": cppgambit_include_dirs,
+ "cflags": cppgambit_cflags,
+ }
+)
+
+
+def solver_library_config(library_name: str, paths: list) -> tuple:
+ return (
+ library_name,
+ {
+ "sources": [fn for solver in paths for fn in glob.glob(f"src/solvers/{solver}/*.cc")],
+ "obj_deps": {
+ "": (
+ glob.glob("src/core/*.h") + glob.glob("src/games/*.h") +
+ glob.glob("src/games/agg/*.h") +
+ [fn for solver in paths for fn in glob.glob(f"src/solvers/{solver}/*.h")]
+ )
+ },
+ "include_dirs": cppgambit_include_dirs,
+ "cflags": cppgambit_cflags,
+ }
+ )
+
+
+cppgambit_bimatrix = solver_library_config("cppgambit_bimatrix",
+ ["linalg", "lp", "lcp", "enummixed"])
+cppgambit_liap = solver_library_config("cppgambit_liap", ["liap"])
+cppgambit_logit = solver_library_config("cppgambit_logit", ["logit"])
+cppgambit_gtracer = solver_library_config("cppgambit_gtracer", ["gtracer", "ipa", "gnm"])
+cppgambit_simpdiv = solver_library_config("cppgambit_simpdiv", ["simpdiv"])
+cppgambit_enumpoly = solver_library_config("cppgambit_enumpoly", ["nashsupport", "enumpoly"])
+
libgambit = setuptools.Extension(
"pygambit.gambit",
@@ -61,47 +99,10 @@
)
)
-
-def readme():
- with open("src/README.rst") as f:
- return f.read()
-
-
setuptools.setup(
- name="pygambit",
- version="16.2.1",
- description="The package for computation in game theory",
- long_description=readme(),
- classifiers=[
- "Development Status :: 5 - Production/Stable",
- "Intended Audience :: Science/Research",
- "License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)",
- "Programming Language :: Python :: 3.8",
- "Programming Language :: Python :: 3.9",
- "Programming Language :: Python :: 3.10",
- "Programming Language :: Python :: 3.11",
- "Programming Language :: Python :: 3.12",
- "Programming Language :: Python :: Implementation :: CPython",
- "Topic :: Scientific/Engineering :: Mathematics"
- ],
- keywords="game theory Nash equilibrium",
- license="GPL2+",
- author="Theodore Turocy",
- author_email="ted.turocy@gmail.com",
- url="http://www.gambit-project.org",
- project_urls={
- "Documentation": "https://gambitproject.readthedocs.io/",
- "Source": "https://github.com/gambitproject/gambit",
- "Tracker": "https://github.com/gambitproject/gambit/issues",
- },
- python_requires=">=3.8",
- install_requires=[
- "lxml", # used for reading/writing GTE files
- "numpy",
- "scipy",
- "deprecated",
- ],
- libraries=[cppgambit, lrslib],
+ libraries=[cppgambit_bimatrix, cppgambit_liap, cppgambit_logit, cppgambit_simpdiv,
+ cppgambit_gtracer, cppgambit_enumpoly,
+ cppgambit_games, cppgambit_core],
package_dir={"": "src"},
packages=["pygambit"],
ext_modules=Cython.Build.cythonize(libgambit,
diff --git a/src/core/array.h b/src/core/array.h
index e4d734fec..066ade8af 100644
--- a/src/core/array.h
+++ b/src/core/array.h
@@ -2,7 +2,7 @@
// This file is part of Gambit
// Copyright (c) 1994-2025, The Gambit Project (https://www.gambit-project.org)
//
-// FILE: src/libgambit/array.h
+// FILE: src/core/array.h
// A basic bounds-checked array type
//
// This program is free software; you can redistribute it and/or modify
@@ -20,60 +20,89 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
-#ifndef LIBGAMBIT_ARRAY_H
-#define LIBGAMBIT_ARRAY_H
+#ifndef GAMBIT_CORE_ARRAY_H
+#define GAMBIT_CORE_ARRAY_H
#include
#include
+#include "util.h"
+
namespace Gambit {
-/// A basic bounds-checked array
+/// \brief An extension of std::vector to have arbitrary offset index
+///
+/// This is a variation on std::vector, which allows for the index of the first
+/// element to be something other than zero.
template class Array {
protected:
- int mindex, maxdex;
- T *data;
+ int m_offset, m_length;
+ T *m_data;
- /// Private helper function that accomplishes the insertion of an object
int InsertAt(const T &t, int n)
{
- if (this->mindex > n || n > this->maxdex + 1) {
+ if (this->m_offset > n || n > this->last_index() + 1) {
throw IndexException();
}
- T *new_data = new T[++this->maxdex - this->mindex + 1] - this->mindex;
+ T *new_data = new T[++this->m_length] - this->m_offset;
int i;
- for (i = this->mindex; i <= n - 1; i++) {
- new_data[i] = this->data[i];
+ for (i = this->m_offset; i <= n - 1; i++) {
+ new_data[i] = this->m_data[i];
}
new_data[i++] = t;
- for (; i <= this->maxdex; i++) {
- new_data[i] = this->data[i - 1];
+ for (; i <= this->last_index(); i++) {
+ new_data[i] = this->m_data[i - 1];
}
- if (this->data) {
- delete[] (this->data + this->mindex);
+ if (this->m_data) {
+ delete[] (this->m_data + this->m_offset);
}
- this->data = new_data;
+ this->m_data = new_data;
return n;
}
+ T Remove(int n)
+ {
+ if (n < this->m_offset || n > this->last_index()) {
+ throw IndexException();
+ }
+
+ T ret(this->m_data[n]);
+ T *new_data = (--this->m_length > 0) ? new T[this->m_length] - this->m_offset : nullptr;
+
+ int i;
+ for (i = this->m_offset; i < n; i++) {
+ new_data[i] = this->m_data[i];
+ }
+ for (; i <= this->last_index(); i++) {
+ new_data[i] = this->m_data[i + 1];
+ }
+
+ delete[] (this->m_data + this->m_offset);
+ this->m_data = new_data;
+
+ return ret;
+ }
+
public:
class iterator {
+ friend class Array;
+
private:
Array *m_array;
int m_index;
public:
- using iterator_category = std::input_iterator_tag;
- using difference_type = typename std::vector::iterator::difference_type;
+ using iterator_category = std::bidirectional_iterator_tag;
+ using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = value_type *;
using reference = value_type &;
- iterator(Array *p_array, int p_index) : m_array(p_array), m_index(p_index) {}
+ iterator(Array *p_array = 0, int p_index = 0) : m_array(p_array), m_index(p_index) {}
reference operator*() { return (*m_array)[m_index]; }
pointer operator->() { return &(*m_array)[m_index]; }
iterator &operator++()
@@ -81,6 +110,17 @@ template class Array {
m_index++;
return *this;
}
+ iterator &operator--()
+ {
+ m_index--;
+ return *this;
+ }
+ iterator operator++(int)
+ {
+ auto ret = *this;
+ m_index++;
+ return ret;
+ }
bool operator==(const iterator &it) const
{
return (m_array == it.m_array) && (m_index == it.m_index);
@@ -89,13 +129,15 @@ template class Array {
};
class const_iterator {
+ friend class Array;
+
private:
const Array *m_array;
int m_index;
public:
- using iterator_category = std::input_iterator_tag;
- using difference_type = typename std::vector::iterator::difference_type;
+ using iterator_category = std::bidirectional_iterator_tag;
+ using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = value_type *;
using reference = value_type &;
@@ -108,6 +150,17 @@ template class Array {
m_index++;
return *this;
}
+ const_iterator &operator--()
+ {
+ m_index--;
+ return *this;
+ }
+ const_iterator operator++(int)
+ {
+ auto ret = *this;
+ m_index++;
+ return ret;
+ }
bool operator==(const const_iterator &it) const
{
return (m_array == it.m_array) && (m_index == it.m_index);
@@ -115,75 +168,59 @@ template class Array {
bool operator!=(const const_iterator &it) const { return !(*this == it); }
};
- /// @name Lifecycle
- //@{
- /// Constructs an array of length 'len', starting at '1'
explicit Array(unsigned int len = 0)
- : mindex(1), maxdex(len), data((len) ? new T[len] - 1 : nullptr)
+ : m_offset(1), m_length(len), m_data((len) ? new T[len] - 1 : nullptr)
{
}
- /// Constructs an array starting at lo and ending at hi
- Array(int lo, int hi) : mindex(lo), maxdex(hi)
+ Array(int lo, int hi) : m_offset(lo), m_length(hi - lo + 1)
{
- if (maxdex + 1 < mindex) {
+ if (m_length < 0) {
throw RangeException();
}
- data = (maxdex >= mindex) ? new T[maxdex - mindex + 1] - mindex : nullptr;
+ m_data = (m_length > 0) ? new T[m_length] - m_offset : nullptr;
}
- /// Copy the contents of another array
Array(const Array &a)
- : mindex(a.mindex), maxdex(a.maxdex),
- data((maxdex >= mindex) ? new T[maxdex - mindex + 1] - mindex : nullptr)
+ : m_offset(a.m_offset), m_length(a.m_length),
+ m_data((m_length > 0) ? new T[m_length] - m_offset : nullptr)
{
- for (int i = mindex; i <= maxdex; i++) {
- data[i] = a.data[i];
+ for (int i = m_offset; i <= last_index(); i++) {
+ m_data[i] = a.m_data[i];
}
}
- /// Destruct and deallocates the array
virtual ~Array()
{
- if (maxdex >= mindex) {
- delete[] (data + mindex);
+ if (m_length > 0) {
+ delete[] (m_data + m_offset);
}
}
- /// Copy the contents of another array
Array &operator=(const Array &a)
{
if (this != &a) {
- // We only reallocate if necessary. This should be somewhat faster
- // if many objects are of the same length. Furthermore, it is
- // _essential_ for the correctness of the PVector and DVector
- // assignment operator, since it assumes the value of data does
- // not change.
- if (!data || (data && (mindex != a.mindex || maxdex != a.maxdex))) {
- if (data) {
- delete[] (data + mindex);
+ // We only reallocate if necessary.
+ if (!m_data || (m_data && (m_offset != a.m_offset || m_length != a.m_length))) {
+ if (m_data) {
+ delete[] (m_data + m_offset);
}
- mindex = a.mindex;
- maxdex = a.maxdex;
- data = (maxdex >= mindex) ? new T[maxdex - mindex + 1] - mindex : nullptr;
+ m_offset = a.m_offset;
+ m_length = a.m_length;
+ m_data = (m_length > 0) ? new T[m_length] - m_offset : nullptr;
}
- for (int i = mindex; i <= maxdex; i++) {
- data[i] = a.data[i];
+ for (int i = m_offset; i <= last_index(); i++) {
+ m_data[i] = a.m_data[i];
}
}
return *this;
}
- //@}
-
- /// @name Operator overloading
- //@{
- /// Test the equality of two arrays
bool operator==(const Array &a) const
{
- if (mindex != a.mindex || maxdex != a.maxdex) {
+ if (m_offset != a.m_offset || m_length != a.m_length) {
return false;
}
- for (int i = mindex; i <= maxdex; i++) {
+ for (int i = m_offset; i <= last_index(); i++) {
if ((*this)[i] != a[i]) {
return false;
}
@@ -191,143 +228,60 @@ template class Array {
return true;
}
- /// Test the inequality of two arrays
bool operator!=(const Array &a) const { return !(*this == a); }
- //@}
-
- /// @name General data access
- //@{
- /// Return the length of the array
- int Length() const { return maxdex - mindex + 1; }
-
- /// Return the first index
- int First() const { return mindex; }
-
- /// Return the last index
- int Last() const { return maxdex; }
-
- /// Return a forward iterator starting at the beginning of the array
- iterator begin() { return iterator(this, mindex); }
- /// Return a forward iterator past the end of the array
- iterator end() { return iterator(this, maxdex + 1); }
- /// Return a const forward iterator starting at the beginning of the array
- const_iterator begin() const { return const_iterator(this, mindex); }
- /// Return a const forward iterator past the end of the array
- const_iterator end() const { return const_iterator(this, maxdex + 1); }
- /// Return a const forward iterator starting at the beginning of the array
- const_iterator cbegin() const { return const_iterator(this, mindex); }
- /// Return a const forward iterator past the end of the array
- const_iterator cend() const { return const_iterator(this, maxdex + 1); }
-
- /// Access the index'th entry in the array
+
const T &operator[](int index) const
{
- if (index < mindex || index > maxdex) {
+ if (index < m_offset || index > last_index()) {
throw IndexException();
}
- return data[index];
+ return m_data[index];
}
- /// Access the index'th entry in the array
T &operator[](int index)
{
- if (index < mindex || index > maxdex) {
+ if (index < m_offset || index > last_index()) {
throw IndexException();
}
- return data[index];
+ return m_data[index];
}
+ const T &front() const { return m_data[m_offset]; }
+ T &front() { return m_data[m_offset]; }
+ const T &back() const { return m_data[last_index()]; }
+ T &back() { return m_data[last_index()]; }
+
+ iterator begin() { return {this, m_offset}; }
+ const_iterator begin() const { return {this, m_offset}; }
+ iterator end() { return {this, m_offset + m_length}; }
+ const_iterator end() const { return {this, m_offset + m_length}; }
+ const_iterator cbegin() const { return {this, m_offset}; }
+ const_iterator cend() const { return {this, m_offset + m_length}; }
+
+ bool empty() const { return this->m_length == 0; }
+ size_t size() const { return m_length; }
+ int first_index() const { return m_offset; }
+ int last_index() const { return m_offset + m_length - 1; }
- /// Return the index at which a given element resides in the array.
- int Find(const T &t) const
- {
- int i;
- for (i = this->mindex; i <= this->maxdex && this->data[i] != t; i++)
- ;
- return (i <= this->maxdex) ? i : (mindex - 1);
- }
-
- /// Return true if the element is currently residing in the array
- bool Contains(const T &t) const { return Find(t) != mindex - 1; }
- //@}
-
- /// @name Modifying the contents of the array
- //@{
- /// \brief Insert a new element into the array at a given index.
- ///
- /// Insert a new element into the array at a given index. If the index is
- /// less than the lowest index, the element is inserted at the beginning;
- /// if the index is greater than the highest index, the element is appended.
- /// Returns the index at which the element actually is placed.
- int Insert(const T &t, int n)
- {
- return InsertAt(t, (n < this->mindex) ? this->mindex
- : ((n > this->maxdex + 1) ? this->maxdex + 1 : n));
- }
-
- /// \brief Remove an element from the array.
- ///
- /// Remove the element at a given index from the array. Returns the value
- /// of the element removed.
- T Remove(int n)
- {
- if (n < this->mindex || n > this->maxdex) {
- throw IndexException();
- }
-
- T ret(this->data[n]);
- T *new_data = (--this->maxdex >= this->mindex)
- ? new T[this->maxdex - this->mindex + 1] - this->mindex
- : nullptr;
-
- int i;
- for (i = this->mindex; i < n; i++) {
- new_data[i] = this->data[i];
- }
- for (; i <= this->maxdex; i++) {
- new_data[i] = this->data[i + 1];
- }
-
- delete[] (this->data + this->mindex);
- this->data = new_data;
-
- return ret;
- }
- //@}
-
- /// @name STL-style interface
- ///
- /// These operations are a partial implementation of operations on
- /// STL-style list containers. It is suggested that future code be
- /// written to use these, and existing code ported to use them as
- /// possible.
- ///@{
- /// Return whether the array container is empty (has size 0).
- bool empty() const { return (this->maxdex < this->mindex); }
- /// Return the number of elements in the array container.
- size_t size() const { return maxdex - mindex + 1; }
- /// Access first element.
- const T &front() const { return data[mindex]; }
- /// Access first element.
- T &front() { return data[mindex]; }
- /// Access last element.
- const T &back() const { return data[maxdex]; }
- /// Access last element.
- T &back() { return data[maxdex]; }
-
- /// Adds a new element at the end of the array container, after its
- /// current last element.
- void push_back(const T &val) { InsertAt(val, this->maxdex + 1); }
- /// Removes all elements from the array container (which are destroyed),
- /// leaving the container with a size of 0.
void clear()
{
- delete[] (this->data + this->mindex);
- this->data = 0;
- this->maxdex = this->mindex - 1;
+ if (m_length > 0) {
+ delete[] (m_data + m_offset);
+ }
+ m_data = nullptr;
+ m_length = 0;
}
- ///@}
+ void insert(const_iterator pos, const T &value) { InsertAt(value, pos.m_index); }
+ void erase(iterator pos) { Remove(pos.m_index); }
+ void push_back(const T &val) { InsertAt(val, this->last_index() + 1); }
+ void pop_back() { Remove(last_index()); }
};
+/// Convenience function to erase the element at `p_index`
+template void erase_atindex(Array &p_array, int p_index)
+{
+ p_array.erase(std::next(p_array.begin(), p_index - p_array.first_index()));
+}
+
} // end namespace Gambit
-#endif // LIBGAMBIT_ARRAY_H
+#endif // GAMBIT_CORE_ARRAY_H
diff --git a/src/core/pvector.cc b/src/core/core.h
similarity index 72%
rename from src/core/pvector.cc
rename to src/core/core.h
index 698d8e02d..726235445 100644
--- a/src/core/pvector.cc
+++ b/src/core/core.h
@@ -2,8 +2,8 @@
// This file is part of Gambit
// Copyright (c) 1994-2025, The Gambit Project (https://www.gambit-project.org)
//
-// FILE: src/libgambit/pvector.cc
-// Instantiation of partitioned vector types
+// FILE: src/core/core.h
+// Core (game theory-independent) declarations and utilities for Gambit
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -20,9 +20,15 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
-#include "gambit.h"
-#include "pvector.imp"
+#ifndef GAMBIT_CORE_CORE_H
+#define GAMBIT_CORE_CORE_H
-template class Gambit::PVector;
-template class Gambit::PVector;
-template class Gambit::PVector;
+#include "util.h"
+#include "array.h"
+#include "list.h"
+#include "recarray.h"
+#include "vector.h"
+#include "matrix.h"
+#include "rational.h"
+
+#endif // GAMBIT_CORE_CORE_H
diff --git a/src/core/dvector.cc b/src/core/dvector.cc
deleted file mode 100644
index 15d38be84..000000000
--- a/src/core/dvector.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-//
-// This file is part of Gambit
-// Copyright (c) 1994-2025, The Gambit Project (https://www.gambit-project.org)
-//
-// FILE: src/libgambit/dvector.cc
-// Instantiation of doubly partitioned vector types
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-//
-
-#include "gambit.h"
-#include "dvector.imp"
-
-template class Gambit::DVector;
-template class Gambit::DVector;
diff --git a/src/core/dvector.h b/src/core/dvector.h
deleted file mode 100644
index 9d206b76f..000000000
--- a/src/core/dvector.h
+++ /dev/null
@@ -1,69 +0,0 @@
-//
-// This file is part of Gambit
-// Copyright (c) 1994-2025, The Gambit Project (https://www.gambit-project.org)
-//
-// FILE: src/libgambit/dvector.h
-// Doubly-partitioned vector class
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-//
-
-#ifndef LIBGAMBIT_DVECTOR_H
-#define LIBGAMBIT_DVECTOR_H
-
-#include "pvector.h"
-
-namespace Gambit {
-
-template class DVector : public PVector {
-private:
- void setindex();
-
-protected:
- T ***dvptr;
- Array dvlen, dvidx;
-
-public:
- explicit DVector(const PVector &shape);
- DVector(const DVector &v);
- ~DVector() override;
-
- T &operator()(int a, int b, int c);
- const T &operator()(int a, int b, int c) const;
-
- DVector &operator=(const DVector &v)
- {
- if (this == &v) {
- return *this;
- }
- if (dvlen != v.dvlen || dvidx != v.dvidx) {
- throw DimensionException();
- }
- static_cast &>(*this) = static_cast &>(v);
- return *this;
- }
-
- DVector &operator=(const Vector &v)
- {
- static_cast &>(*this) = v;
- return *this;
- }
-
- DVector &operator=(const T &c);
-};
-
-} // end namespace Gambit
-
-#endif // LIBGAMBIT_DVECTOR_H
diff --git a/src/core/dvector.imp b/src/core/dvector.imp
deleted file mode 100644
index 209951a8c..000000000
--- a/src/core/dvector.imp
+++ /dev/null
@@ -1,115 +0,0 @@
-//
-// This file is part of Gambit
-// Copyright (c) 1994-2025, The Gambit Project (https://www.gambit-project.org)
-//
-// FILE: src/libgambit/dvector.imp
-// Implementation of doubly-partitioned vector class
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-//
-
-#include "dvector.h"
-
-namespace Gambit {
-
-//--------------------------------------------------------------------------
-// DVector: Private and protected member functions
-//--------------------------------------------------------------------------
-
-template void DVector::setindex()
-{
- int index = 1;
-
- for (int i = 1; i <= dvlen.Length(); i++) {
- dvptr[i] = this->svptr + index - 1;
- dvidx[i] = index;
- index += dvlen[i];
- }
-}
-
-//--------------------------------------------------------------------------
-// DVector: Constructors, destructor, and constructive operators
-//--------------------------------------------------------------------------
-
-template
-DVector::DVector(const PVector &shape)
- : PVector(static_cast &>(shape)), dvlen(shape.Lengths().Length()),
- dvidx(shape.Lengths().Length())
-{
- dvptr = new T **[dvlen.Length()];
- dvptr -= 1;
-
- for (int i = 1; i <= dvlen.Length(); i++) {
- dvlen[i] = shape.Lengths()[i];
- }
-
- setindex();
-}
-
-template
-DVector::DVector(const DVector &v) : PVector(v), dvlen(v.dvlen), dvidx(v.dvidx)
-{
- dvptr = new T **[dvlen.Length()];
- dvptr -= 1;
-
- setindex();
-}
-
-template DVector::~DVector()
-{
- if (dvptr) {
- delete[] (dvptr + 1);
- }
-}
-
-template DVector &DVector::operator=(const T &c)
-{
- PVector::operator=(c);
- return *this;
-}
-
-//--------------------------------------------------------------------------
-// DVector: Operator definitions
-//--------------------------------------------------------------------------
-
-template T &DVector::operator()(int a, int b, int c)
-{
- if (dvlen.First() > a || a > dvlen.Last()) {
- throw IndexException();
- }
- if (1 > b || b > dvlen[a]) {
- throw IndexException();
- }
- if (1 > c || c > this->svlen[dvidx[a] + b - 1]) {
- throw IndexException();
- }
- return dvptr[a][b][c];
-}
-
-template const T &DVector::operator()(int a, int b, int c) const
-{
- if (dvlen.First() > a || a > dvlen.Last()) {
- throw IndexException();
- }
- if (1 > b || b > dvlen[a]) {
- throw IndexException();
- }
- if (1 > c || c > this->svlen[dvidx[a] + b - 1]) {
- throw IndexException();
- }
- return dvptr[a][b][c];
-}
-
-} // end namespace Gambit
diff --git a/src/core/function.cc b/src/core/function.cc
index 2dcd72bf6..680bfa234 100644
--- a/src/core/function.cc
+++ b/src/core/function.cc
@@ -2,7 +2,7 @@
// This file is part of Gambit
// Copyright (c) 1994-2025, The Gambit Project (https://www.gambit-project.org)
//
-// FILE: src/libgambit/function.cc
+// FILE: src/core/function.cc
// Implementation of function and function minimization routines
//
// These routines derive from the N-dimensional function minimization
@@ -28,7 +28,7 @@
//
#include
-#include "gambit.h"
+
#include "function.h"
using namespace Gambit;
@@ -46,7 +46,7 @@ using namespace Gambit;
void FunctionOnSimplices::Project(Vector &x, const Array &lengths) const
{
int index = 1;
- for (int part = 1; part <= lengths.Length(); part++) {
+ for (size_t part = 1; part <= lengths.size(); part++) {
double avg = 0.0;
int j;
for (j = 1; j <= lengths[part]; j++, index++) {
@@ -70,7 +70,7 @@ void FunctionOnSimplices::Project(Vector &x, const Array &lengths)
void ConjugatePRMinimizer::AlphaXPlusY(double alpha, const Vector &x, Vector &y)
{
- for (int i = 1; i <= y.Length(); i++) {
+ for (size_t i = 1; i <= y.size(); i++) {
y[i] += alpha * x[i];
}
}
@@ -96,7 +96,7 @@ void ConjugatePRMinimizer::IntermediatePoint(const Function &fdf, const Vector &x
return; /* MAX ITERATIONS */
}
- double dw = w - u;
- double dv = v - u;
+ const double dw = w - u;
+ const double dv = v - u;
double du = 0.0;
- double e1 = ((fv - fu) * dw * dw + (fu - fw) * dv * dv);
- double e2 = 2.0 * ((fv - fu) * dw + (fu - fw) * dv);
+ const double e1 = ((fv - fu) * dw * dw + (fu - fw) * dv * dv);
+ const double e2 = 2.0 * ((fv - fu) * dw + (fu - fw) * dv);
if (e2 != 0.0) {
du = e1 / e2;
@@ -272,7 +272,7 @@ void ConjugatePRMinimizer::Set(const Function &fdf, const Vector &x, dou
p = gradient;
g0 = gradient;
- double gnorm = std::sqrt(gradient.NormSquared());
+ const double gnorm = std::sqrt(gradient.NormSquared());
pnorm = gnorm;
g0norm = gnorm;
}
@@ -282,9 +282,12 @@ void ConjugatePRMinimizer::Restart() { iter = 0; }
bool ConjugatePRMinimizer::Iterate(const Function &fdf, Vector &x, double &f,
Vector &gradient, Vector &dx)
{
- double fa = f, fb, fc;
+ const double fa = f;
+ double fb, fc;
double dir;
- double stepa = 0.0, stepb, stepc = step, tol = m_tol;
+ const double stepa = 0.0;
+ double stepb;
+ const double stepc = step, tol = m_tol;
double g1norm;
double pg;
@@ -330,7 +333,7 @@ bool ConjugatePRMinimizer::Iterate(const Function &fdf, Vector &x, doubl
x = x2;
/* Choose a new conjugate direction for the next step */
- iter = (iter + 1) % x.Length();
+ iter = (iter + 1) % x.size();
if (iter == 0) {
p = gradient;
diff --git a/src/core/function.h b/src/core/function.h
index 3762268ee..0a4e14c18 100644
--- a/src/core/function.h
+++ b/src/core/function.h
@@ -2,7 +2,7 @@
// This file is part of Gambit
// Copyright (c) 1994-2025, The Gambit Project (https://www.gambit-project.org)
//
-// FILE: src/libgambit/function.h
+// FILE: src/core/function.h
// Interface to function and function minimization routines
//
// This program is free software; you can redistribute it and/or modify
@@ -20,10 +20,10 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
-#ifndef LIBGAMBIT_FUNCTION_H
-#define LIBGAMBIT_FUNCTION_H
+#ifndef GAMBIT_CORE_FUNCTION_H
+#define GAMBIT_CORE_FUNCTION_H
-#include "core/vector.h"
+#include "vector.h"
namespace Gambit {
@@ -106,4 +106,4 @@ class ConjugatePRMinimizer : public FunctionMinimizer {
} // end namespace Gambit
-#endif // LIBGAMBIT_FUNCTION_H
+#endif // GAMBIT_CORE_FUNCTION_H
diff --git a/src/core/integer.cc b/src/core/integer.cc
index aad169b33..2ab3f9774 100644
--- a/src/core/integer.cc
+++ b/src/core/integer.cc
@@ -2,7 +2,7 @@
// This file is part of Gambit
// Copyright (c) 1994-2025, The Gambit Project (https://www.gambit-project.org)
//
-// FILE: src/libgambit/integer.cc
+// FILE: src/core/integer.cc
// Implementation of an arbitrary-length integer class
//
// The original copyright and license are included below.
@@ -35,14 +35,14 @@ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include
-
-#include "integer.h"
#include
#include
#include
#include
#include
-#include "gambit.h"
+
+#include "integer.h"
+#include "util.h"
namespace Gambit {
@@ -79,14 +79,14 @@ long lg(unsigned long x)
/* All routines assume SHORT_PER_LONG > 1 */
#define SHORT_PER_LONG ((unsigned)(((sizeof(long) + sizeof(short) - 1) / sizeof(short))))
-#define CHAR_PER_LONG ((unsigned)sizeof(long))
+// #define CHAR_PER_LONG ((unsigned)sizeof(long))
/*
minimum and maximum sizes for an IntegerRep
*/
#define MIN_INTREP_SIZE 16
-#define MAX_INTREP_SIZE I_MAXNUM
+// #define MAX_INTREP_SIZE I_MAXNUM
#ifndef MALLOC_MIN_OVERHEAD
#define MALLOC_MIN_OVERHEAD 4
@@ -176,7 +176,7 @@ static inline void nonnil(const IntegerRep *rep)
static IntegerRep *Inew(int newlen)
{
- unsigned int siz = sizeof(IntegerRep) + newlen * sizeof(short) + MALLOC_MIN_OVERHEAD;
+ const unsigned int siz = sizeof(IntegerRep) + newlen * sizeof(short) + MALLOC_MIN_OVERHEAD;
unsigned int allocsiz = MIN_INTREP_SIZE;
while (allocsiz < siz) {
allocsiz <<= 1; // find a power of 2
@@ -287,7 +287,7 @@ IntegerRep *Icopy(IntegerRep *old, const IntegerRep *src)
rep->sgn = I_POSITIVE;
}
else {
- int newlen = src->len;
+ const int newlen = src->len;
if (old == nullptr || newlen > old->sz) {
if (old != nullptr && !STATIC_IntegerRep(old)) {
delete old;
@@ -311,7 +311,7 @@ IntegerRep *Icopy(IntegerRep *old, const IntegerRep *src)
IntegerRep *Icopy_long(IntegerRep *old, long x)
{
- int newsgn = (x >= 0);
+ const int newsgn = (x >= 0);
IntegerRep *rep = Icopy_ulong(old, newsgn ? x : -x);
rep->sgn = newsgn;
return rep;
@@ -427,7 +427,7 @@ long Itolong(const IntegerRep *rep)
int Iislong(const IntegerRep *rep)
{
- unsigned int l = rep->len;
+ const unsigned int l = rep->len;
if (l < SHORT_PER_LONG) {
return 1;
}
@@ -455,7 +455,7 @@ int Iislong(const IntegerRep *rep)
double Itodouble(const IntegerRep *rep)
{
double d = 0.0;
- double bound = DBL_MAX / 2.0;
+ const double bound = DBL_MAX / 2.0;
for (int i = rep->len - 1; i >= 0; --i) {
auto a = (unsigned short)(I_RADIX >> 1);
while (a != 0) {
@@ -484,7 +484,7 @@ double Itodouble(const IntegerRep *rep)
int Iisdouble(const IntegerRep *rep)
{
double d = 0.0;
- double bound = DBL_MAX / 2.0;
+ const double bound = DBL_MAX / 2.0;
for (int i = rep->len - 1; i >= 0; --i) {
auto a = (unsigned short)(I_RADIX >> 1);
while (a != 0) {
@@ -507,7 +507,7 @@ double ratio(const Integer &num, const Integer &den)
{
Integer q, r;
divide(num, den, q, r);
- double d1 = q.as_double();
+ const double d1 = q.as_double();
if (d1 >= DBL_MAX || d1 <= -DBL_MAX || sign(r) == 0) {
return d1;
@@ -581,8 +581,8 @@ int ucompare(const IntegerRep *x, const IntegerRep *y)
int compare(const IntegerRep *x, long y)
{
- int xl = x->len;
- int xsgn = x->sgn;
+ const int xl = x->len;
+ const int xsgn = x->sgn;
if (y == 0) {
if (xl == 0) {
return 0;
@@ -595,7 +595,7 @@ int compare(const IntegerRep *x, long y)
}
}
else {
- int ysgn = y >= 0;
+ const int ysgn = y >= 0;
unsigned long uy = (ysgn) ? y : -y;
int diff = xsgn - ysgn;
if (diff == 0) {
@@ -622,7 +622,7 @@ int compare(const IntegerRep *x, long y)
int ucompare(const IntegerRep *x, long y)
{
- int xl = x->len;
+ const int xl = x->len;
if (y == 0) {
return xl;
}
@@ -652,14 +652,14 @@ IntegerRep *add(const IntegerRep *x, int negatex, const IntegerRep *y, int negat
nonnil(x);
nonnil(y);
- int xl = x->len;
- int yl = y->len;
+ const int xl = x->len;
+ const int yl = y->len;
- int xsgn = (negatex && xl != 0) ? !x->sgn : x->sgn;
- int ysgn = (negatey && yl != 0) ? !y->sgn : y->sgn;
+ const int xsgn = (negatex && xl != 0) ? !x->sgn : x->sgn;
+ const int ysgn = (negatey && yl != 0) ? !y->sgn : y->sgn;
- int xrsame = x == r;
- int yrsame = y == r;
+ const int xrsame = x == r;
+ const int yrsame = y == r;
if (yl == 0) {
r = Ialloc(r, x->s, xl, xsgn, xl);
@@ -713,7 +713,7 @@ IntegerRep *add(const IntegerRep *x, int negatex, const IntegerRep *y, int negat
}
}
else {
- int comp = ucompare(x, y);
+ const int comp = ucompare(x, y);
if (comp == 0) {
r = Icopy_zero(r);
}
@@ -768,11 +768,11 @@ IntegerRep *add(const IntegerRep *x, int negatex, const IntegerRep *y, int negat
IntegerRep *add(const IntegerRep *x, int negatex, long y, IntegerRep *r)
{
nonnil(x);
- int xl = x->len;
- int xsgn = (negatex && xl != 0) ? !x->sgn : x->sgn;
- int xrsame = x == r;
+ const int xl = x->len;
+ const int xsgn = (negatex && xl != 0) ? !x->sgn : x->sgn;
+ const int xrsame = x == r;
- int ysgn = (y >= 0);
+ const int ysgn = (y >= 0);
unsigned long uy = (ysgn) ? y : -y;
if (y == 0) {
@@ -794,7 +794,7 @@ IntegerRep *add(const IntegerRep *x, int negatex, long y, IntegerRep *r)
const unsigned short *topa = &(as[xl]);
unsigned long sum = 0;
while (as < topa && uy != 0) {
- unsigned long u = extract(uy);
+ const unsigned long u = extract(uy);
uy = down(uy);
sum += (unsigned long)(*as++) + u;
*rs++ = extract(sum);
@@ -880,13 +880,13 @@ IntegerRep *multiply(const IntegerRep *x, const IntegerRep *y, IntegerRep *r)
{
nonnil(x);
nonnil(y);
- int xl = x->len;
- int yl = y->len;
- int rl = xl + yl;
- int rsgn = x->sgn == y->sgn;
- int xrsame = x == r;
- int yrsame = y == r;
- int xysame = x == y;
+ const int xl = x->len;
+ const int yl = y->len;
+ const int rl = xl + yl;
+ const int rsgn = x->sgn == y->sgn;
+ const int xrsame = x == r;
+ const int yrsame = y == r;
+ const int xysame = x == y;
if (xl == 0 || yl == 0) {
r = Icopy_zero(r);
@@ -1026,7 +1026,7 @@ IntegerRep *multiply(const IntegerRep *x, const IntegerRep *y, IntegerRep *r)
IntegerRep *multiply(const IntegerRep *x, long y, IntegerRep *r)
{
nonnil(x);
- int xl = x->len;
+ const int xl = x->len;
if (xl == 0 || y == 0) {
r = Icopy_zero(r);
@@ -1035,8 +1035,8 @@ IntegerRep *multiply(const IntegerRep *x, long y, IntegerRep *r)
r = Icopy(r, x);
}
else {
- int ysgn = y >= 0;
- int rsgn = x->sgn == ysgn;
+ const int ysgn = y >= 0;
+ const int rsgn = x->sgn == ysgn;
unsigned long uy = (ysgn) ? y : -y;
unsigned short tmp[SHORT_PER_LONG];
int yl = 0;
@@ -1045,8 +1045,8 @@ IntegerRep *multiply(const IntegerRep *x, long y, IntegerRep *r)
uy = down(uy);
}
- int rl = xl + yl;
- int xrsame = x == r;
+ const int rl = xl + yl;
+ const int xrsame = x == r;
if (xrsame) {
r = Iresize(r, rl);
}
@@ -1115,8 +1115,8 @@ static void do_divide(unsigned short *rs, const unsigned short *ys, int yl, unsi
int ql)
{
const unsigned short *topy = &(ys[yl]);
- unsigned short d1 = ys[yl - 1];
- unsigned short d2 = ys[yl - 2];
+ const unsigned short d1 = ys[yl - 1];
+ const unsigned short d2 = ys[yl - 2];
int l = ql - 1;
int i = l + yl;
@@ -1127,7 +1127,7 @@ static void do_divide(unsigned short *rs, const unsigned short *ys, int yl, unsi
qhat = (unsigned short)I_MAXNUM;
}
else {
- unsigned long lr = up((unsigned long)rs[i]) | rs[i - 1];
+ const unsigned long lr = up((unsigned long)rs[i]) | rs[i - 1];
qhat = (unsigned short)(lr / d1);
}
@@ -1197,11 +1197,11 @@ static int unscale(const unsigned short *x, int xl, unsigned short y, unsigned s
unsigned long rem = 0;
while (qs >= botq) {
rem = up(rem) | *xs--;
- unsigned long u = rem / y;
+ const unsigned long u = rem / y;
*qs-- = extract(u);
rem -= u * y;
}
- int r = extract(rem);
+ const int r = extract(rem);
return r;
}
else // same loop, a bit faster if just need rem
@@ -1211,10 +1211,10 @@ static int unscale(const unsigned short *x, int xl, unsigned short y, unsigned s
unsigned long rem = 0;
while (xs >= botx) {
rem = up(rem) | *xs--;
- unsigned long u = rem / y;
+ const unsigned long u = rem / y;
rem -= u * y;
}
- int r = extract(rem);
+ const int r = extract(rem);
return r;
}
}
@@ -1223,17 +1223,17 @@ IntegerRep *div(const IntegerRep *x, const IntegerRep *y, IntegerRep *q)
{
nonnil(x);
nonnil(y);
- int xl = x->len;
- int yl = y->len;
+ const int xl = x->len;
+ const int yl = y->len;
if (yl == 0) {
throw Gambit::ZeroDivideException();
}
- int comp = ucompare(x, y);
- int xsgn = x->sgn;
- int ysgn = y->sgn;
+ const int comp = ucompare(x, y);
+ const int xsgn = x->sgn;
+ const int ysgn = y->sgn;
- int samesign = xsgn == ysgn;
+ const int samesign = xsgn == ysgn;
if (comp < 0) {
q = Icopy_zero(q);
@@ -1259,7 +1259,7 @@ IntegerRep *div(const IntegerRep *x, const IntegerRep *y, IntegerRep *q)
scpy(x->s, r->s, xl);
}
- int ql = xl - yl + 1;
+ const int ql = xl - yl + 1;
q = Icalloc(q, ql);
do_divide(r->s, yy->s, yl, q->s, ql);
@@ -1279,14 +1279,14 @@ IntegerRep *div(const IntegerRep *x, const IntegerRep *y, IntegerRep *q)
IntegerRep *div(const IntegerRep *x, long y, IntegerRep *q)
{
nonnil(x);
- int xl = x->len;
+ const int xl = x->len;
if (y == 0) {
throw Gambit::ZeroDivideException();
}
unsigned short ys[SHORT_PER_LONG];
unsigned long u;
- int ysgn = y >= 0;
+ const int ysgn = y >= 0;
if (ysgn) {
u = y;
}
@@ -1304,8 +1304,8 @@ IntegerRep *div(const IntegerRep *x, long y, IntegerRep *q)
comp = docmp(x->s, ys, xl);
}
- int xsgn = x->sgn;
- int samesign = xsgn == ysgn;
+ const int xsgn = x->sgn;
+ const int samesign = xsgn == ysgn;
if (comp < 0) {
q = Icopy_zero(q);
@@ -1332,7 +1332,7 @@ IntegerRep *div(const IntegerRep *x, long y, IntegerRep *q)
scpy(x->s, r->s, xl);
}
- int ql = xl - yl + 1;
+ const int ql = xl - yl + 1;
q = Icalloc(q, ql);
do_divide(r->s, ys, yl, q->s, ql);
@@ -1351,13 +1351,13 @@ void divide(const Integer &Ix, long y, Integer &Iq, long &rem)
const IntegerRep *x = Ix.rep;
nonnil(x);
IntegerRep *q = Iq.rep;
- int xl = x->len;
+ const int xl = x->len;
if (y == 0) {
throw Gambit::ZeroDivideException();
}
unsigned short ys[SHORT_PER_LONG];
unsigned long u;
- int ysgn = y >= 0;
+ const int ysgn = y >= 0;
if (ysgn) {
u = y;
}
@@ -1375,8 +1375,8 @@ void divide(const Integer &Ix, long y, Integer &Iq, long &rem)
comp = docmp(x->s, ys, xl);
}
- int xsgn = x->sgn;
- int samesign = xsgn == ysgn;
+ const int xsgn = x->sgn;
+ const int samesign = xsgn == ysgn;
if (comp < 0) {
rem = Itolong(x);
@@ -1405,7 +1405,7 @@ void divide(const Integer &Ix, long y, Integer &Iq, long &rem)
scpy(x->s, r->s, xl);
}
- int ql = xl - yl + 1;
+ const int ql = xl - yl + 1;
q = Icalloc(q, ql);
@@ -1439,16 +1439,16 @@ void divide(const Integer &Ix, const Integer &Iy, Integer &Iq, Integer &Ir)
IntegerRep *q = Iq.rep;
IntegerRep *r = Ir.rep;
- int xl = x->len;
- int yl = y->len;
+ const int xl = x->len;
+ const int yl = y->len;
if (yl == 0) {
throw Gambit::ZeroDivideException();
}
- int comp = ucompare(x, y);
- int xsgn = x->sgn;
- int ysgn = y->sgn;
+ const int comp = ucompare(x, y);
+ const int xsgn = x->sgn;
+ const int ysgn = y->sgn;
- int samesign = xsgn == ysgn;
+ const int samesign = xsgn == ysgn;
if (comp < 0) {
q = Icopy_zero(q);
@@ -1460,7 +1460,7 @@ void divide(const Integer &Ix, const Integer &Iy, Integer &Iq, Integer &Ir)
}
else if (yl == 1) {
q = Icopy(q, x);
- int rem = unscale(q->s, q->len, y->s[0], q->s);
+ const int rem = unscale(q->s, q->len, y->s[0], q->s);
r = Icopy_long(r, rem);
if (rem != 0) {
r->sgn = xsgn;
@@ -1479,7 +1479,7 @@ void divide(const Integer &Ix, const Integer &Iy, Integer &Iq, Integer &Ir)
scpy(x->s, r->s, xl);
}
- int ql = xl - yl + 1;
+ const int ql = xl - yl + 1;
q = Icalloc(q, ql);
do_divide(r->s, yy->s, yl, q->s, ql);
@@ -1503,15 +1503,15 @@ IntegerRep *mod(const IntegerRep *x, const IntegerRep *y, IntegerRep *r)
{
nonnil(x);
nonnil(y);
- int xl = x->len;
- int yl = y->len;
+ const int xl = x->len;
+ const int yl = y->len;
// if (yl == 0) (*lib_error_handler)("Integer", "attempted division by zero");
if (yl == 0) {
throw Gambit::ZeroDivideException();
}
- int comp = ucompare(x, y);
- int xsgn = x->sgn;
+ const int comp = ucompare(x, y);
+ const int xsgn = x->sgn;
if (comp < 0) {
r = Icopy(r, x);
@@ -1520,7 +1520,7 @@ IntegerRep *mod(const IntegerRep *x, const IntegerRep *y, IntegerRep *r)
r = Icopy_zero(r);
}
else if (yl == 1) {
- int rem = unscale(x->s, xl, y->s[0], nullptr);
+ const int rem = unscale(x->s, xl, y->s[0], nullptr);
r = Icopy_long(r, rem);
if (rem != 0) {
r->sgn = xsgn;
@@ -1557,13 +1557,13 @@ IntegerRep *mod(const IntegerRep *x, const IntegerRep *y, IntegerRep *r)
IntegerRep *mod(const IntegerRep *x, long y, IntegerRep *r)
{
nonnil(x);
- int xl = x->len;
+ const int xl = x->len;
if (y == 0) {
throw Gambit::ZeroDivideException();
}
unsigned short ys[SHORT_PER_LONG];
unsigned long u;
- int ysgn = y >= 0;
+ const int ysgn = y >= 0;
if (ysgn) {
u = y;
}
@@ -1581,7 +1581,7 @@ IntegerRep *mod(const IntegerRep *x, long y, IntegerRep *r)
comp = docmp(x->s, ys, xl);
}
- int xsgn = x->sgn;
+ const int xsgn = x->sgn;
if (comp < 0) {
r = Icopy(r, x);
@@ -1590,7 +1590,7 @@ IntegerRep *mod(const IntegerRep *x, long y, IntegerRep *r)
r = Icopy_zero(r);
}
else if (yl == 1) {
- int rem = unscale(x->s, xl, ys[0], nullptr);
+ const int rem = unscale(x->s, xl, ys[0], nullptr);
r = Icopy_long(r, rem);
if (rem != 0) {
r->sgn = xsgn;
@@ -1624,21 +1624,21 @@ IntegerRep *mod(const IntegerRep *x, long y, IntegerRep *r)
IntegerRep *lshift(const IntegerRep *x, long y, IntegerRep *r)
{
nonnil(x);
- int xl = x->len;
+ const int xl = x->len;
if (xl == 0 || y == 0) {
r = Icopy(r, x);
return r;
}
- int xrsame = x == r;
- int rsgn = x->sgn;
+ const int xrsame = x == r;
+ const int rsgn = x->sgn;
- long ay = (y < 0) ? -y : y;
- int bw = (int)(ay / I_SHIFT);
- int sw = (int)(ay % I_SHIFT);
+ const long ay = (y < 0) ? -y : y;
+ const int bw = (int)(ay / I_SHIFT);
+ const int sw = (int)(ay % I_SHIFT);
if (y > 0) {
- int rl = bw + xl + 1;
+ const int rl = bw + xl + 1;
if (xrsame) {
r = Iresize(r, rl);
}
@@ -1661,7 +1661,7 @@ IntegerRep *lshift(const IntegerRep *x, long y, IntegerRep *r)
}
}
else {
- int rl = xl - bw;
+ const int rl = xl - bw;
if (rl < 0) {
r = Icopy_zero(r);
}
@@ -1672,7 +1672,7 @@ IntegerRep *lshift(const IntegerRep *x, long y, IntegerRep *r)
else {
r = Icalloc(r, rl);
}
- int rw = I_SHIFT - sw;
+ const int rw = I_SHIFT - sw;
unsigned short *rs = r->s;
unsigned short *topr = &(rs[rl]);
const unsigned short *botx = (xrsame) ? rs : x->s;
@@ -1712,11 +1712,11 @@ IntegerRep *bitop(const IntegerRep *x, const IntegerRep *y, IntegerRep *r, char
{
nonnil(x);
nonnil(y);
- int xl = x->len;
- int yl = y->len;
- int xsgn = x->sgn;
- int xrsame = x == r;
- int yrsame = y == r;
+ const int xl = x->len;
+ const int yl = y->len;
+ const int xsgn = x->sgn;
+ const int xrsame = x == r;
+ const int yrsame = y == r;
if (xrsame || yrsame) {
r = Iresize(r, calc_len(xl, yl, 0));
}
@@ -1775,7 +1775,7 @@ IntegerRep *bitop(const IntegerRep *x, long y, IntegerRep *r, char op)
nonnil(x);
unsigned short tmp[SHORT_PER_LONG];
unsigned long u;
- int newsgn = (y >= 0);
+ const int newsgn = (y >= 0);
if (newsgn) {
u = y;
}
@@ -1789,10 +1789,10 @@ IntegerRep *bitop(const IntegerRep *x, long y, IntegerRep *r, char op)
u = down(u);
}
- int xl = x->len;
- int yl = l;
- int xsgn = x->sgn;
- int xrsame = x == r;
+ const int xl = x->len;
+ const int yl = l;
+ const int xsgn = x->sgn;
+ const int xrsame = x == r;
if (xrsame) {
r = Iresize(r, calc_len(xl, yl, 0));
}
@@ -1853,7 +1853,7 @@ IntegerRep *Compl(const IntegerRep *src, IntegerRep *r)
unsigned short *s = r->s;
unsigned short *top = &(s[r->len - 1]);
while (s < top) {
- unsigned short cmp = ~(*s);
+ const unsigned short cmp = ~(*s);
*s++ = cmp;
}
unsigned short a = *s;
@@ -1873,9 +1873,9 @@ IntegerRep *Compl(const IntegerRep *src, IntegerRep *r)
void(setbit)(Integer &x, long b)
{
if (b >= 0) {
- int bw = (int)((unsigned long)b / I_SHIFT);
- int sw = (int)((unsigned long)b % I_SHIFT);
- int xl = x.rep ? x.rep->len : 0;
+ const int bw = (int)((unsigned long)b / I_SHIFT);
+ const int sw = (int)((unsigned long)b % I_SHIFT);
+ const int xl = x.rep ? x.rep->len : 0;
if (xl <= bw) {
x.rep = Iresize(x.rep, calc_len(xl, bw + 1, 0));
}
@@ -1891,8 +1891,8 @@ void clearbit(Integer &x, long b)
x.rep = &ZeroRep;
}
else {
- int bw = (int)((unsigned long)b / I_SHIFT);
- int sw = (int)((unsigned long)b % I_SHIFT);
+ const int bw = (int)((unsigned long)b / I_SHIFT);
+ const int sw = (int)((unsigned long)b % I_SHIFT);
if (x.rep->len > bw) {
x.rep->s[bw] &= ~(1 << sw);
}
@@ -1904,8 +1904,8 @@ void clearbit(Integer &x, long b)
int testbit(const Integer &x, long b)
{
if (x.rep != nullptr && b >= 0) {
- int bw = (int)((unsigned long)b / I_SHIFT);
- int sw = (int)((unsigned long)b % I_SHIFT);
+ const int bw = (int)((unsigned long)b / I_SHIFT);
+ const int sw = (int)((unsigned long)b % I_SHIFT);
return (bw < x.rep->len && (x.rep->s[bw] & (1 << sw)) != 0);
}
else {
@@ -1920,8 +1920,8 @@ IntegerRep *gcd(const IntegerRep *x, const IntegerRep *y)
{
nonnil(x);
nonnil(y);
- int ul = x->len;
- int vl = y->len;
+ const int ul = x->len;
+ const int vl = y->len;
if (vl == 0) {
return Ialloc(nullptr, x->s, ul, I_POSITIVE, ul);
@@ -1936,7 +1936,7 @@ IntegerRep *gcd(const IntegerRep *x, const IntegerRep *y)
// find shift so that both not even
long k = 0;
- int l = (ul <= vl) ? ul : vl;
+ const int l = (ul <= vl) ? ul : vl;
int cont = 1, i;
for (i = 0; i < l && cont; ++i) {
unsigned long a = (i < ul) ? u->s[i] : 0;
@@ -1970,7 +1970,7 @@ IntegerRep *gcd(const IntegerRep *x, const IntegerRep *y)
while (t->len != 0) {
long s = 0; // shift t until odd
cont = 1;
- int tl = t->len;
+ const int tl = t->len;
for (i = 0; i < tl && cont; ++i) {
unsigned long a = t->s[i];
for (unsigned int j = 0; j < I_SHIFT; ++j) {
@@ -2013,7 +2013,7 @@ IntegerRep *gcd(const IntegerRep *x, const IntegerRep *y)
long lg(const IntegerRep *x)
{
nonnil(x);
- int xl = x->len;
+ const int xl = x->len;
if (xl == 0) {
return 0;
}
@@ -2039,7 +2039,7 @@ IntegerRep *power(const IntegerRep *x, long y, IntegerRep *r)
sgn = I_NEGATIVE;
}
- int xl = x->len;
+ const int xl = x->len;
if (y == 0 || (xl == 1 && x->s[0] == 1)) {
r = Icopy_one(r, sgn);
@@ -2051,7 +2051,7 @@ IntegerRep *power(const IntegerRep *x, long y, IntegerRep *r)
r = Icopy(r, x);
}
else {
- int maxsize = (int)(((lg(x) + 1) * y) / I_SHIFT + 2); // pre-allocate space
+ const int maxsize = (int)(((lg(x) + 1) * y) / I_SHIFT + 2); // pre-allocate space
IntegerRep *b = Ialloc(nullptr, x->s, xl, I_POSITIVE, maxsize);
b->len = xl;
r = Icalloc(r, maxsize);
@@ -2103,7 +2103,7 @@ IntegerRep *negate(const IntegerRep *src, IntegerRep *dest)
Integer sqrt(const Integer &x)
{
Integer r;
- int s = sign(x);
+ const int s = sign(x);
if (s < 0) {
x.error("Attempted square root of negative Integer");
}
@@ -2181,7 +2181,7 @@ Integer lcm(const Integer &x, const Integer &y)
IntegerRep *atoIntegerRep(const char *s, int base)
{
- int sl = strlen(s);
+ const int sl = strlen(s);
IntegerRep *r = Icalloc(nullptr, (int)(sl * (lg(base) + 1) / I_SHIFT + 1));
if (s != nullptr) {
char sgn;
@@ -2359,7 +2359,7 @@ std::istream &operator>>(std::istream &s, Integer &y)
}
else {
if (ch >= '0' && ch <= '9') {
- long digit = ch - '0';
+ const long digit = ch - '0';
y *= 10;
y += digit;
}
@@ -2380,8 +2380,8 @@ std::istream &operator>>(std::istream &s, Integer &y)
int Integer::OK() const
{
if (rep != nullptr) {
- int l = rep->len;
- int s = rep->sgn;
+ const int l = rep->len;
+ const int s = rep->sgn;
int v = l <= rep->sz || STATIC_IntegerRep(rep); // length within bounds
v &= s == 0 || s == 1; // legal sign
Icheck(rep); // and correctly adjusted
diff --git a/src/core/integer.h b/src/core/integer.h
index 51b28e33c..7a67d2d11 100644
--- a/src/core/integer.h
+++ b/src/core/integer.h
@@ -2,7 +2,7 @@
// This file is part of Gambit
// Copyright (c) 1994-2025, The Gambit Project (https://www.gambit-project.org)
//
-// FILE: src/libgambit/integer.h
+// FILE: src/core/integer.h
// Interface to an arbitrary-length integer class
//
// The original copyright and license are included below.
@@ -25,8 +25,8 @@ License along with this library; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#ifndef LIBGAMBIT_INTEGER_H
-#define LIBGAMBIT_INTEGER_H
+#ifndef GAMBIT_CORE_INTEGER_H
+#define GAMBIT_CORE_INTEGER_H
#include
@@ -249,4 +249,4 @@ extern Integer lcm(const Integer &x, const Integer &y); // least common mult
} // end namespace Gambit
-#endif // LIBGAMBIT_INTEGER_H
+#endif // GAMBIT_CORE_INTEGER_H
diff --git a/src/core/list.h b/src/core/list.h
index 53e675413..64d6718a4 100644
--- a/src/core/list.h
+++ b/src/core/list.h
@@ -2,8 +2,8 @@
// This file is part of Gambit
// Copyright (c) 1994-2025, The Gambit Project (https://www.gambit-project.org)
//
-// FILE: src/libgambit/list.h
-// A generic (doubly) linked-list container class
+// FILE: src/core/list.h
+// A generic linked-list container class
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -20,436 +20,113 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
-#ifndef LIBGAMBIT_LIST_H
-#define LIBGAMBIT_LIST_H
+#ifndef GAMBIT_CORE_LIST_H
+#define GAMBIT_CORE_LIST_H
+
+#include
namespace Gambit {
-/// A doubly-linked list container.
+/// @brief A linked-list container
+///
+/// This is a lightweight wrapper around std::list. It exists as a transitional
+/// class between Gambit's legacy linked-list implementation Gambit::List and
+/// STL's list.
///
-/// This implements a doubly-linked list. A special feature of this
-/// class is that it caches the last item accessed by indexing via
-/// operator[], meaning that, if accesses are done in sequential order,
-/// indexing time is constant.
+/// Gambit's List container supported the index operator [], which is not defined
+/// on STL lists. Therefore this class provides such an operator by wrapping
+/// an appropriate iterator-based operation. This is very inefficient! However,
+/// the [] operator and indexing is tightly embedded in the remaining uses of this
+/// class, so this operator provides a refactoring path while the remaining uses
+/// are rewritten.
///
-/// This index-cacheing feature was implemented very early in the development
-/// of Gambit, before STL-like concepts of iterators had been fully
-/// developed. An iterator approach is a better and more robust solution
-/// to iterating lists, both in terms of performance and encapsulation.
-/// Therefore, it is recommended to avoid operator[] and use the provided
-/// iterator classes in new code, and to upgrade existing code to the
-/// iterator idiom as practical.
+/// Note importantly that the index operator [] is **1-based** - that is, the first
+/// element of the list is 1, not 0.
template class List {
-protected:
- class Node {
- public:
- T m_data;
- Node *m_prev, *m_next;
-
- // CONSTRUCTOR
- Node(const T &p_data, Node *p_prev, Node *p_next);
- };
-
- int m_length;
- Node *m_head, *m_tail;
-
- int m_currentIndex;
- Node *m_currentNode;
-
- int InsertAt(const T &t, int where);
+private:
+ std::list m_list;
public:
- class iterator {
- private:
- List &m_list;
- Node *m_node;
+ using iterator = typename std::list::iterator;
+ using const_iterator = typename std::list::const_iterator;
+ using value_type = typename std::list::value_type;
+ using size_type = typename std::list::size_type;
- public:
- iterator(List &p_list, Node *p_node) : m_list(p_list), m_node(p_node) {}
- T &operator*() { return m_node->m_data; }
- iterator &operator++()
- {
- m_node = m_node->m_next;
- return *this;
- }
- bool operator==(const iterator &it) const { return (m_node == it.m_node); }
- bool operator!=(const iterator &it) const { return (m_node != it.m_node); }
- };
+ List() = default;
+ List(const List &) = default;
+ ~List() = default;
- class const_iterator {
- private:
- const List &m_list;
- Node *m_node;
+ List &operator=(const List &) = default;
- public:
- const_iterator(const List &p_list, Node *p_node) : m_list(p_list), m_node(p_node) {}
- const T &operator*() const { return m_node->m_data; }
- const_iterator &operator++()
- {
- m_node = m_node->m_next;
- return *this;
+ const T &operator[](size_type p_index) const
+ {
+ if (p_index < 1 || p_index > m_list.size()) {
+ throw IndexException();
}
- bool operator==(const const_iterator &it) const { return (m_node == it.m_node); }
- bool operator!=(const const_iterator &it) const { return (m_node != it.m_node); }
- };
-
- List();
- List(const List &);
- virtual ~List();
-
- List &operator=(const List &);
-
- bool operator==(const List &b) const;
- bool operator!=(const List &b) const;
-
- /// Return a forward iterator starting at the beginning of the list
- iterator begin() { return iterator(*this, m_head); }
- /// Return a forward iterator past the end of the list
- iterator end() { return iterator(*this, nullptr); }
- /// Return a const forward iterator starting at the beginning of the list
- const_iterator begin() const { return const_iterator(*this, m_head); }
- /// Return a const forward iterator past the end of the list
- const_iterator end() const { return const_iterator(*this, nullptr); }
- /// Return a const forward iterator starting at the beginning of the list
- const_iterator cbegin() const { return const_iterator(*this, m_head); }
- /// Return a const forward iterator past the end of the list
- const_iterator cend() const { return const_iterator(*this, nullptr); }
-
- const T &operator[](int) const;
- T &operator[](int);
-
- List operator+(const List &b) const;
- List &operator+=(const List &b);
-
- int Insert(const T &, int);
- T Remove(int);
+ return *std::next(m_list.cbegin(), p_index - 1);
+ }
- int Find(const T &) const;
- bool Contains(const T &t) const;
- int Length() const { return m_length; }
+ T &operator[](size_type p_index)
+ {
+ if (p_index < 1 || p_index > m_list.size()) {
+ throw IndexException();
+ }
+ return *std::next(m_list.begin(), p_index - 1);
+ }
/// @name STL-style interface
///
- /// These operations are a partial implementation of operations on
- /// STL-style list containers. It is suggested that future code be
- /// written to use these, and existing code ported to use them as
- /// possible.
+ /// These operations forward STL-type operations to the underlying list.
+ /// This does not provide all operations on std::list, only ones used in
+ /// existing code. Rather than adding new functions here, existing code
+ /// should be rewritten to use std::list directly.
///@{
/// Return whether the list container is empty (has size 0).
- bool empty() const { return (m_length == 0); }
+ bool empty() const { return m_list.empty(); }
/// Return the number of elements in the list container.
- size_t size() const { return m_length; }
+ size_type size() const { return m_list.size(); }
+ /// Adds a new element at the beginning of the list container
+ void push_front(const T &val) { m_list.push_front(val); }
/// Adds a new element at the end of the list container, after its
/// current last element.
- void push_back(const T &val);
+ void push_back(const T &val) { m_list.push_back(val); }
+ /// Removes the last element
+ void pop_back() { m_list.pop_back(); }
+ /// Inserts the value at the specified position
+ void insert(iterator pos, const T &value) { m_list.insert(pos, value); }
+ /// Erases the element at the specified position
+ void erase(iterator pos) { m_list.erase(pos); }
+
/// Removes all elements from the list container (which are destroyed),
/// leaving the container with a size of 0.
- void clear();
- /// Returns a reference to the first elemnet in the list container.
- T &front() { return m_head->m_data; }
+ void clear() { m_list.clear(); }
+ /// Returns a reference to the first element in the list container.
+ T &front() { return m_list.front(); }
/// Returns a reference to the first element in the list container.
- const T &front() const { return m_head->m_data; }
+ const T &front() const { return m_list.front(); }
/// Returns a reference to the last element in the list container.
- T &back() { return m_tail->m_data; }
+ T &back() { return m_list.back(); }
/// Returns a reference to the last element in the list container.
- const T &back() const { return m_tail->m_data; }
- ///@}
-};
-
-//--------------------------------------------------------------------------
-// Node: Member function implementations
-//--------------------------------------------------------------------------
-
-template
-List::Node::Node(const T &p_data, typename List::Node *p_prev,
- typename List::Node *p_next)
- : m_data(p_data), m_prev(p_prev), m_next(p_next)
-{
-}
-
-//--------------------------------------------------------------------------
-// List: Member function implementations
-//--------------------------------------------------------------------------
-
-template
-List::List()
- : m_length(0), m_head(nullptr), m_tail(nullptr), m_currentIndex(0), m_currentNode(nullptr)
-{
-}
-
-template List::List(const List &b) : m_length(b.m_length)
-{
- if (m_length) {
- Node *n = b.m_head;
- m_head = new Node(n->m_data, nullptr, nullptr);
- n = n->m_next;
- m_tail = m_head;
- while (n) {
- m_tail->m_next = new Node(n->m_data, m_tail, nullptr);
- n = n->m_next;
- m_tail = m_tail->m_next;
- }
- m_currentIndex = 1;
- m_currentNode = m_head;
- }
- else {
- m_head = m_tail = nullptr;
- m_currentIndex = 0;
- m_currentNode = nullptr;
- }
-}
-
-template List::~List()
-{
- Node *n = m_head;
- while (n) {
- Node *m_next = n->m_next;
- delete n;
- n = m_next;
- }
-}
-
-template int List::InsertAt(const T &t, int num)
-{
- if (num < 1 || num > m_length + 1) {
- throw IndexException();
- }
-
- if (!m_length) {
- m_head = m_tail = new Node(t, nullptr, nullptr);
- m_length = 1;
- m_currentIndex = 1;
- m_currentNode = m_head;
- return m_length;
- }
-
- Node *n;
- int i;
-
- if (num <= 1) {
- n = new Node(t, nullptr, m_head);
- m_head->m_prev = n;
- m_currentNode = m_head = n;
- m_currentIndex = 1;
- }
- else if (num >= m_length + 1) {
- n = new Node(t, m_tail, nullptr);
- m_tail->m_next = n;
- m_currentNode = m_tail = n;
- m_currentIndex = m_length + 1;
- }
- else {
- if (num < m_currentIndex) {
- for (i = m_currentIndex, n = m_currentNode; i > num; i--, n = n->m_prev)
- ;
- }
- else {
- for (i = m_currentIndex, n = m_currentNode; i < num; i++, n = n->m_next)
- ;
- }
- n = new Node(t, n->m_prev, n);
- m_currentNode = n->m_prev->m_next = n->m_next->m_prev = n;
- m_currentIndex = num;
- }
-
- m_length++;
- return num;
-}
-
-//--------------------- visible functions ------------------------
-
-template List &List::operator=(const List &b)
-{
- if (this != &b) {
- Node *n = m_head;
- while (n) {
- Node *m_next = n->m_next;
- delete n;
- n = m_next;
- }
-
- m_length = b.m_length;
- m_currentIndex = b.m_currentIndex;
- if (m_length) {
- Node *n = b.m_head;
- m_head = new Node(n->m_data, nullptr, nullptr);
- if (b.m_currentNode == n) {
- m_currentNode = m_head;
- }
- n = n->m_next;
- m_tail = m_head;
- while (n) {
- m_tail->m_next = new Node(n->m_data, m_tail, nullptr);
- if (b.m_currentNode == n) {
- m_currentNode = m_tail->m_next;
- }
- n = n->m_next;
- m_tail = m_tail->m_next;
- }
- }
- else {
- m_head = m_tail = nullptr;
- }
- }
- return *this;
-}
-
-template bool List::operator==(const List &b) const
-{
- if (m_length != b.m_length) {
- return false;
- }
- for (Node *m = m_head, *n = b.m_head; m; m = m->m_next, n = n->m_next) {
- if (m->m_data != n->m_data) {
- return false;
- }
- }
- return true;
-}
-
-template bool List::operator!=(const List &b) const { return !(*this == b); }
-
-template const T &List::operator[](int num) const
-{
- if (num < 1 || num > m_length) {
- throw IndexException();
- }
-
- int i;
- Node *n;
- if (num < m_currentIndex) {
- for (i = m_currentIndex, n = m_currentNode; i > num; i--, n = n->m_prev)
- ;
- }
- else {
- for (i = m_currentIndex, n = m_currentNode; i < num; i++, n = n->m_next)
- ;
- }
- return n->m_data;
-}
+ const T &back() const { return m_list.back(); }
-template T &List::operator[](int num)
-{
- if (num < 1 || num > m_length) {
- throw IndexException();
- }
- Node *n;
- int i;
- if (num < m_currentIndex) {
- for (i = m_currentIndex, n = m_currentNode; i > num; i--, n = n->m_prev)
- ;
- }
- else {
- for (i = m_currentIndex, n = m_currentNode; i < num; i++, n = n->m_next)
- ;
- }
- m_currentIndex = i;
- m_currentNode = n;
- return n->m_data;
-}
-
-template List List::operator+(const List &b) const
-{
- List result(*this);
- Node *n = b.m_head;
- while (n) {
- result.Append(n->data);
- n = n->m_next;
- }
- return result;
-}
-
-template List &List::operator+=(const List &b)
-{
- Node *n = b.m_head;
-
- while (n) {
- push_back(n->m_data);
- n = n->m_next;
- }
- return *this;
-}
-
-template int List::Insert(const T &t, int n)
-{
- return InsertAt(t, (n < 1) ? 1 : ((n > m_length + 1) ? m_length + 1 : n));
-}
-
-template T List::Remove(int num)
-{
- if (num < 1 || num > m_length) {
- throw IndexException();
- }
- Node *n;
- int i;
+ bool operator==(const List &b) const { return m_list == b.m_list; }
+ bool operator!=(const List &b) const { return m_list != b.m_list; }
- if (num < m_currentIndex) {
- for (i = m_currentIndex, n = m_currentNode; i > num; i--, n = n->m_prev)
- ;
- }
- else {
- for (i = m_currentIndex, n = m_currentNode; i < num; i++, n = n->m_next)
- ;
- }
-
- if (n->m_prev) {
- n->m_prev->m_next = n->m_next;
- }
- else {
- m_head = n->m_next;
- }
- if (n->m_next) {
- n->m_next->m_prev = n->m_prev;
- }
- else {
- m_tail = n->m_prev;
- }
-
- m_length--;
- m_currentIndex = i;
- m_currentNode = n->m_next;
- if (m_currentIndex > m_length) {
- m_currentIndex = m_length;
- m_currentNode = m_tail;
- }
- T ret = n->m_data;
- delete n;
- return ret;
-}
-
-template int List::Find(const T &t) const
-{
- if (m_length == 0) {
- return 0;
- }
- Node *n = m_head;
- for (int i = 1; n; i++, n = n->m_next) {
- if (n->m_data == t) {
- return i;
- }
- }
- return 0;
-}
-
-template bool List::Contains(const T &t) const { return (Find(t) != 0); }
-
-template void List::push_back(const T &val) { InsertAt(val, m_length + 1); }
-
-template void List::clear()
-{
- Node *n = m_head;
- while (n) {
- Node *m_next = n->m_next;
- delete n;
- n = m_next;
- }
- m_length = 0;
- m_head = nullptr;
- m_tail = nullptr;
- m_currentIndex = 0;
- m_currentNode = nullptr;
-}
+ /// Return a forward iterator starting at the beginning of the list
+ iterator begin() { return m_list.begin(); }
+ /// Return a forward iterator past the end of the list
+ iterator end() { return m_list.end(); }
+ /// Return a const forward iterator starting at the beginning of the list
+ const_iterator begin() const { return m_list.begin(); }
+ /// Return a const forward iterator past the end of the list
+ const_iterator end() const { return m_list.end(); }
+ /// Return a const forward iterator starting at the beginning of the list
+ const_iterator cbegin() const { return m_list.cbegin(); }
+ /// Return a const forward iterator past the end of the list
+ const_iterator cend() const { return m_list.cend(); }
+ ///@}
+};
} // namespace Gambit
-#endif // LIBGAMBIT_LIST_H
+#endif // GAMBIT_CORE_LIST_H
diff --git a/src/core/matrix.cc b/src/core/matrix.cc
index 54189fa35..29ec79b30 100644
--- a/src/core/matrix.cc
+++ b/src/core/matrix.cc
@@ -2,7 +2,7 @@
// This file is part of Gambit
// Copyright (c) 1994-2025, The Gambit Project (https://www.gambit-project.org)
//
-// FILE: src/libgambit/matrix.cc
+// FILE: src/core/matrix.cc
// Instantiation of common matrix types
//
// This program is free software; you can redistribute it and/or modify
@@ -20,7 +20,7 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
-#include "gambit.h"
+#include "core.h"
#include "matrix.imp"
namespace Gambit {
diff --git a/src/core/matrix.h b/src/core/matrix.h
index 241c51645..fecad1d55 100644
--- a/src/core/matrix.h
+++ b/src/core/matrix.h
@@ -2,7 +2,7 @@
// This file is part of Gambit
// Copyright (c) 1994-2025, The Gambit Project (https://www.gambit-project.org)
//
-// FILE: src/libgambit/matrix.h
+// FILE: src/core/matrix.h
// Interface to a matrix class
//
// This program is free software; you can redistribute it and/or modify
@@ -20,8 +20,8 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
-#ifndef LIBGAMBIT_MATRIX_H
-#define LIBGAMBIT_MATRIX_H
+#ifndef GAMBIT_CORE_MATRIX_H
+#define GAMBIT_CORE_MATRIX_H
#include "recarray.h"
#include "vector.h"
@@ -80,14 +80,10 @@ template class Matrix : public RectArray {
Matrix operator*(const Matrix &) const;
Vector operator*(const Vector &) const;
Matrix operator*(const T &) const;
- Matrix &operator*=(const Matrix &);
Matrix &operator*=(const T &);
Matrix operator/(const T &) const;
Matrix &operator/=(const T &);
-
- /// Kronecker product
- Matrix operator&(const Matrix &) const;
//@
/// @name Other operations
@@ -103,4 +99,4 @@ template Vector operator*(const Vector &, const Matrix &);
} // end namespace Gambit
-#endif // LIBGAMBIT_MATRIX_H
+#endif // GAMBIT_CORE_MATRIX_H
diff --git a/src/core/matrix.imp b/src/core/matrix.imp
index ace3be371..9922b5e43 100644
--- a/src/core/matrix.imp
+++ b/src/core/matrix.imp
@@ -2,7 +2,7 @@
// This file is part of Gambit
// Copyright (c) 1994-2025, The Gambit Project (https://www.gambit-project.org)
//
-// FILE: src/libgambit/matrix.imp
+// FILE: src/core/matrix.imp
// Implementation of matrix method functions
//
// This program is free software; you can redistribute it and/or modify
@@ -86,7 +86,7 @@ template Matrix Matrix::operator+(const Matrix &M) const
throw DimensionException();
}
- Matrix tmp(this->minrow, this->maxrow, this->mincol, this->maxcol);
+ const Matrix tmp(this->minrow, this->maxrow, this->mincol, this->maxcol);
for (int i = this->minrow; i <= this->maxrow; i++) {
T *src1 = this->data[i] + this->mincol;
T *src2 = M.data[i] + this->mincol;
@@ -106,7 +106,7 @@ template Matrix Matrix::operator-(const Matrix &M) const
throw DimensionException();
}
- Matrix tmp(this->minrow, this->maxrow, this->mincol, this->maxcol);
+ const Matrix tmp(this->minrow, this->maxrow, this->mincol, this->maxcol);
for (int i = this->minrow; i <= this->maxrow; i++) {
T *src1 = this->data[i] + this->mincol;
T *src2 = M.data[i] + this->mincol;
@@ -169,7 +169,7 @@ template void Matrix::CMultiply(const Vector &in, Vector &out
T sum = (T)0;
T *src1 = this->data[i] + this->mincol;
- T *src2 = in.data + this->mincol;
+ auto src2 = in.begin();
int j = this->maxcol - this->mincol + 1;
while (j--) {
sum += *(src1++) * *(src2++);
@@ -217,7 +217,7 @@ template void Matrix::RMultiply(const Vector &in, Vector &out
T k = in[i];
T *src = this->data[i] + this->mincol;
- T *dst = out.data + this->mincol;
+ auto dst = out.begin();
int j = this->maxcol - this->mincol + 1;
while (j--) {
*(dst++) += *(src++) * k;
@@ -240,7 +240,7 @@ template Vector operator*(const Vector &v, const Matrix &M)
template Matrix Matrix::operator*(const T &s) const
{
- Matrix tmp(this->minrow, this->maxrow, this->mincol, this->maxcol);
+ const Matrix tmp(this->minrow, this->maxrow, this->mincol, this->maxcol);
for (int i = this->minrow; i <= this->maxrow; i++) {
T *src = this->data[i] + this->mincol;
T *dst = tmp.data[i] + this->mincol;
@@ -253,26 +253,6 @@ template Matrix Matrix::operator*(const T &s) const
return tmp;
}
-// in-place multiplication by square matrix
-template Matrix &Matrix::operator*=(const Matrix &M)
-{
- if (this->mincol != M.minrow || this->maxcol != M.maxrow) {
- throw DimensionException();
- }
- if (M.minrow != M.mincol || M.maxrow != M.maxcol) {
- throw DimensionException();
- }
-
- Vector row(this->mincol, this->maxcol);
- Vector result(this->mincol, this->maxcol);
- for (int i = this->minrow; i <= this->maxrow; i++) {
- this->GetRow(i, row);
- M.RMultiply(row, result);
- this->SetRow(i, result);
- }
- return (*this);
-}
-
template Matrix &Matrix::operator*=(const T &s)
{
for (int i = this->minrow; i <= this->maxrow; i++) {
@@ -281,7 +261,6 @@ template Matrix &Matrix::operator*=(const T &s)
while (j--) {
*(dst++) *= s;
}
- // assert((dst - 1) == this->data[i] + this->maxcol);
}
return (*this);
}
@@ -292,7 +271,7 @@ template Matrix Matrix::operator/(const T &s) const
throw ZeroDivideException();
}
- Matrix tmp(this->minrow, this->maxrow, this->mincol, this->maxcol);
+ const Matrix tmp(this->minrow, this->maxrow, this->mincol, this->maxcol);
for (int i = this->minrow; i <= this->maxrow; i++) {
T *src = this->data[i] + this->mincol;
T *dst = tmp.data[i] + this->mincol;
@@ -300,7 +279,6 @@ template Matrix Matrix::operator/(const T &s) const
while (j--) {
*(dst++) = *(src++) / s;
}
- // assert((src - 1) == this->data[i] + this->maxcol);
}
return tmp;
}
@@ -317,35 +295,10 @@ template Matrix &Matrix::operator/=(const T &s)
while (j--) {
*(dst++) /= s;
}
- // assert((dst - 1) == this->data[i] + this->maxcol);
}
return (*this);
}
-//-------------------------------------------------------------------------
-// Matrix: Kronecker Product
-//-------------------------------------------------------------------------
-
-template Matrix Matrix::operator&(const Matrix &M) const
-{
- Matrix tmp(1, (this->maxrow - this->minrow + 1) * (M.maxrow - M.minrow + 1), 1,
- (this->maxcol - this->mincol + 1) * (M.maxcol - M.mincol + 1));
-
- for (int i = 0; i <= this->maxrow - this->minrow; i++) {
- for (int j = 1; j <= M.maxrow - M.minrow + 1; j++) {
- for (int k = 0; k <= this->maxcol - this->mincol; k++) {
- for (int l = 1; l <= M.maxcol - M.mincol + 1; l++) {
-
- tmp((M.maxrow - M.minrow + 1) * i + j, (M.maxcol - M.mincol + 1) * k + l) =
- (*this)(i + this->minrow, k + this->mincol) * M(j + M.minrow - 1, l + M.mincol - 1);
- }
- }
- }
- }
-
- return tmp;
-}
-
//-------------------------------------------------------------------------
// Matrix: Transpose
//-------------------------------------------------------------------------
diff --git a/src/core/pvector.h b/src/core/pvector.h
deleted file mode 100644
index 1b6233346..000000000
--- a/src/core/pvector.h
+++ /dev/null
@@ -1,85 +0,0 @@
-//
-// This file is part of Gambit
-// Copyright (c) 1994-2025, The Gambit Project (https://www.gambit-project.org)
-//
-// FILE: src/libgambit/pvector.h
-// Partitioned vector class
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-//
-
-#ifndef LIBGAMBIT_PVECTOR_H
-#define LIBGAMBIT_PVECTOR_H
-
-#include "vector.h"
-
-namespace Gambit {
-
-template class PVector : public Vector {
-private:
- int sum(const Array &V) const;
- void setindex();
-
-protected:
- T **svptr;
- Array svlen;
-
- int Check(const PVector &v) const;
-
-public:
- // constructors
-
- PVector();
- explicit PVector(const Array &sig);
- PVector(const Vector &val, const Array &sig);
- PVector(const PVector &v);
- ~PVector() override;
-
- // element access operators
- T &operator()(int a, int b);
- const T &operator()(int a, int b) const;
-
- // extract a subvector
- Vector GetRow(int row) const;
- void GetRow(int row, Vector &v) const;
- void SetRow(int row, const Vector &v);
- void CopyRow(int row, const PVector &v);
-
- // more operators
-
- PVector &operator=(const PVector &v);
- PVector &operator=(const Vector &v);
- PVector &operator=(T c);
-
- PVector