diff --git a/.ci/merge-fixes.sh b/.ci/merge-fixes.sh index 13350018221..9d36e40d72b 100755 --- a/.ci/merge-fixes.sh +++ b/.ci/merge-fixes.sh @@ -1,5 +1,8 @@ #!/bin/sh -# Apply open PRs labeled "blocker" from sagemath/sage as patches. +# Apply open PRs labeled "p: CI Fix" from sagemath/sage as patches. +# (policy set by vote in 2024-03, +# https://groups.google.com/g/sage-devel/c/OKwwUGyKveo/m/vpyCXYBqAAAJ) +# # This script is invoked by various workflows in .github/workflows # # The repository variable SAGE_CI_FIXES_FROM_REPOS can be set @@ -20,15 +23,15 @@ for REPO in ${SAGE_CI_FIXES_FROM_REPOSITORIES:-sagemath/sage}; do echo "Nothing to do for 'none' in SAGE_CI_FIXES_FROM_REPOSITORIES" ;; */*) - echo "Getting open PRs with 'blocker' status from https://github.com/$REPO/pulls?q=is%3Aopen+label%3A%22p%3A+blocker+%2F+1%22" + echo "Getting open PRs with 'p: CI Fix' label from https://github.com/$REPO/pulls?q=is%3Aopen+label%3A%22p%3A+CI+Fix%22" GH="gh -R $REPO" REPO_FILE="upstream/ci-fixes-${REPO%%/*}-${REPO##*/}" - PRs="$($GH pr list --label "p: blocker / 1" --json number --jq '.[].number' | tee $REPO_FILE)" + PRs="$($GH pr list --label "p: CI Fix" --json number --jq '.[].number' | tee $REPO_FILE)" date -u +"%Y-%m-%dT%H:%M:%SZ" > $REPO_FILE.date # Record the date, for future reference if [ -z "$PRs" ]; then - echo "Nothing to do: Found no open PRs with 'blocker' status in $REPO." + echo "Nothing to do: Found no open PRs with 'p: CI Fix' label in $REPO." else - echo "Found open PRs with 'blocker' status in $REPO: $(echo $PRs)" + echo "Found open PRs with 'p: CI Fix' label in $REPO: $(echo $PRs)" git tag -f test_base git commit -q -m "Uncommitted changes" --no-allow-empty -a for a in $PRs; do diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e9703d30bfa..5fa679ef209 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -373,6 +373,6 @@ jobs: - name: Upload coverage to codecov if: (success() || failure()) && steps.container.outcome == 'success' - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: directory: ./coverage-report diff --git a/.github/workflows/ci-conda.yml b/.github/workflows/ci-conda.yml index fd1c6442a79..8b8e8e88a16 100644 --- a/.github/workflows/ci-conda.yml +++ b/.github/workflows/ci-conda.yml @@ -61,7 +61,7 @@ jobs: ${{ runner.os }}-conda-${{ hashFiles('src/environment-3.11.yml') }} - name: Setup Conda environment - uses: conda-incubator/setup-miniconda@v2 + uses: conda-incubator/setup-miniconda@v3 with: python-version: ${{ matrix.python }} miniforge-version: latest diff --git a/.github/workflows/ci-linux-incremental.yml b/.github/workflows/ci-linux-incremental.yml index 4861ef1d1d0..88a31a5ce42 100644 --- a/.github/workflows/ci-linux-incremental.yml +++ b/.github/workflows/ci-linux-incremental.yml @@ -47,7 +47,7 @@ jobs: - uses: actions/checkout@v4 - name: Get all packages that have changed id: changed-packages - uses: tj-actions/changed-files@v42 + uses: tj-actions/changed-files@v44 with: files_yaml: | configures: diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index 8af0d7c2168..7e3f6b6ad62 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -37,16 +37,37 @@ jobs: sudo DEBIAN_FRONTEND=noninteractive apt-get update sudo DEBIAN_FRONTEND=noninteractive apt-get install $(build/bin/sage-get-system-packages debian _bootstrap) - name: make dist (--disable-download-from-upstream-url) + id: make_dist run: | ./bootstrap -D && ./configure --disable-download-from-upstream-url && make dist env: MAKE: make -j8 + - name: make download (--disable-download-from-upstream-url) + id: make_download + run: | + make -k download DOWNLOAD_PACKAGES=":all: --no-file huge" + env: + MAKE: make -j8 + - name: Reconfigure with --enable-download-from-upstream-url + if: (success() || failure()) && (steps.make_dist.outcome != 'success' || steps.make_download.outcome != 'success') + run: | + ./configure - name: make dist (--enable-download-from-upstream-url) - if: failure() + if: (success() || failure()) && steps.make_dist.outcome != 'success' run: | - ./configure && make dist + make dist env: MAKE: make -j8 + - name: make download (--enable-download-from-upstream-url) + if: (success() || failure()) && steps.make_download.outcome != 'success' + run: | + make -k download DOWNLOAD_PACKAGES=":all: --no-file huge --allow-upstream" + env: + MAKE: make -j8 + - name: Remove what cannot be distributed + if: success() || failure() + run: | + rm -f upstream/*do-not-distribute* - uses: actions/upload-artifact@v4 if: success() || failure() with: @@ -64,7 +85,7 @@ jobs: - uses: actions/download-artifact@v4 with: name: release_dist - - uses: softprops/action-gh-release@v1 + - uses: softprops/action-gh-release@v2 with: generate_release_notes: true files: | diff --git a/.github/workflows/doc-build-pdf.yml b/.github/workflows/doc-build-pdf.yml index 983455cacb0..d4cc6ebc6ca 100644 --- a/.github/workflows/doc-build-pdf.yml +++ b/.github/workflows/doc-build-pdf.yml @@ -40,7 +40,7 @@ jobs: - 5000:5000 steps: - name: Maximize build disk space - uses: easimon/maximize-build-space@v8 + uses: easimon/maximize-build-space@v10 with: # need space in /var for Docker images root-reserve-mb: 30000 diff --git a/.github/workflows/doc-build.yml b/.github/workflows/doc-build.yml index a9a1ddece13..2666a393f66 100644 --- a/.github/workflows/doc-build.yml +++ b/.github/workflows/doc-build.yml @@ -44,7 +44,7 @@ jobs: - 5000:5000 steps: - name: Maximize build disk space - uses: easimon/maximize-build-space@v8 + uses: easimon/maximize-build-space@v10 with: # need space in /var for Docker images root-reserve-mb: 30000 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 5d444594877..7c108f7eb2c 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,11 +1,11 @@ name: Lint -on: +on: push: branches: - master - develop - pull_request: + pull_request: merge_group: concurrency: @@ -29,7 +29,7 @@ jobs: SAGE_CI_FIXES_FROM_REPOSITORIES: ${{ vars.SAGE_CI_FIXES_FROM_REPOSITORIES }} - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.9 diff --git a/.gitignore b/.gitignore index 41494785183..7c6e7b55356 100644 --- a/.gitignore +++ b/.gitignore @@ -235,16 +235,16 @@ build/pkgs/wheel/version_requirements.txt /pkgs/sagemath-categories/pyproject.toml /pkgs/sagemath-environment/pyproject.toml /pkgs/sagemath-repl/pyproject.toml -/pkgs/sagemath-objects/requirements.txt -/pkgs/sagemath-bliss/requirements.txt -/pkgs/sagemath-coxeter3/requirements.txt -/pkgs/sagemath-mcqd/requirements.txt -/pkgs/sagemath-meataxe/requirements.txt -/pkgs/sagemath-sirocco/requirements.txt -/pkgs/sagemath-tdlib/requirements.txt -/pkgs/sagemath-categories/requirements.txt -/pkgs/sagemath-environment/requirements.txt -/pkgs/sagemath-repl/requirements.txt +/pkgs/sagemath-objects/requirements*.txt +/pkgs/sagemath-bliss/requirements*.txt +/pkgs/sagemath-coxeter3/requirements*.txt +/pkgs/sagemath-mcqd/requirements*.txt +/pkgs/sagemath-meataxe/requirements*.txt +/pkgs/sagemath-sirocco/requirements*.txt +/pkgs/sagemath-tdlib/requirements*.txt +/pkgs/sagemath-categories/requirements*.txt +/pkgs/sagemath-environment/requirements*.txt +/pkgs/sagemath-repl/requirements*.txt /pkgs/sagemath-categories/MANIFEST.in # same for old locations - before Issue #31577 diff --git a/CITATION.cff b/CITATION.cff index 3e6fd810876..ed836e58f83 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -4,8 +4,8 @@ title: SageMath abstract: SageMath is a free open-source mathematics software system. authors: - name: "The SageMath Developers" -version: 10.4.beta5 -doi: 10.5281/zenodo.593563 -date-released: 2024-05-02 +version: 10.4.beta6 +doi: 10.5281/zenodo.8042260 +date-released: 2024-05-12 repository-code: "https://github.com/sagemath/sage" url: "https://www.sagemath.org/" diff --git a/CITATION.cff.in b/CITATION.cff.in index bf7d5d3e58c..a3d4e1a35bd 100644 --- a/CITATION.cff.in +++ b/CITATION.cff.in @@ -5,7 +5,7 @@ abstract: SageMath is a free open-source mathematics software system. authors: - name: "The SageMath Developers" version: ${SAGE_VERSION} -doi: 10.5281/zenodo.593563 +doi: 10.5281/zenodo.8042260 date-released: ${SAGE_RELEASE_DATE} repository-code: "https://github.com/sagemath/sage" url: "https://www.sagemath.org/" diff --git a/Makefile b/Makefile index 4662dcd14b9..8c9a4504b49 100644 --- a/Makefile +++ b/Makefile @@ -79,10 +79,11 @@ reconfigure: fi # Preemptively download all source tarballs of normal packages. +DOWNLOAD_PACKAGES=:all: download: export SAGE_ROOT=$$(pwd) && \ export PATH=$$SAGE_ROOT/build/bin:$$PATH && \ - sage-package download :all: + sage-package download $(DOWNLOAD_PACKAGES) dist: build/make/Makefile ./sage --sdist diff --git a/VERSION.txt b/VERSION.txt index ad931d54bc1..356733d937f 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 10.4.beta5, Release Date: 2024-05-02 +SageMath version 10.4.beta6, Release Date: 2024-05-12 diff --git a/build/bin/sage-get-system-packages b/build/bin/sage-get-system-packages index 0172155023d..0a90232ed6e 100755 --- a/build/bin/sage-get-system-packages +++ b/build/bin/sage-get-system-packages @@ -50,6 +50,12 @@ case "$SYSTEM" in ;; esac +case "$SPKGS" in + *pkg:*|pypi/*|generic/*) + PATH="${SAGE_ROOT}/build/bin:$PATH" SPKGS=$(sage-package list $SPKGS) + ;; +esac + for PKG_BASE in $SPKGS; do if [ $FROM_PYPROJECT_TOML -eq 1 ]; then if [ -f "$SAGE_ROOT/src/pyproject.toml" ]; then diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index ca72b9d2a44..3222e604fa8 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=599182ff950764df31446f7bf8350499717dc76e -md5=06e0643f4948ad4b4c87d2530d026a1f -cksum=4286155814 +sha1=dca19f73642b76f1f96b860bb6603499f586380d +md5=cdfc81ecaa40045a7827959e9f52e226 +cksum=1337053183 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index fc1538054bc..8da0a664f61 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -e77b3df44ade2f51f750de90ad255f772a933776 +f5e78840b108412e1e26382910af993f874c6933 diff --git a/build/pkgs/cython/checksums.ini b/build/pkgs/cython/checksums.ini index e3c5756d5ea..6fcab0bf060 100644 --- a/build/pkgs/cython/checksums.ini +++ b/build/pkgs/cython/checksums.ini @@ -1,4 +1,5 @@ tarball=Cython-VERSION.tar.gz -sha1=48f0535ce0b05e0e4ae4daa6a597a2cdd76274f5 -sha256=fb299acf3a578573c190c858d49e0cf9d75f4bc49c3f24c5a63804997ef09213 +sha1=83d6428e3bb7869f44f92ed75d7dff867c2a38ce +md5=0110d7adac5ebb6ae65d8b71a72b664e +cksum=1043698601 upstream_url=https://pypi.io/packages/source/C/Cython/Cython-VERSION.tar.gz diff --git a/build/pkgs/cython/package-version.txt b/build/pkgs/cython/package-version.txt index 2451c27caf7..a909317fe5a 100644 --- a/build/pkgs/cython/package-version.txt +++ b/build/pkgs/cython/package-version.txt @@ -1 +1 @@ -3.0.7 +3.0.10 diff --git a/build/pkgs/deprecation/spkg-configure.m4 b/build/pkgs/deprecation/spkg-configure.m4 deleted file mode 100644 index 306d7c5cd1e..00000000000 --- a/build/pkgs/deprecation/spkg-configure.m4 +++ /dev/null @@ -1 +0,0 @@ -SAGE_SPKG_CONFIGURE([deprecation], [SAGE_PYTHON_PACKAGE_CHECK([deprecation])]) diff --git a/build/pkgs/fflas_ffpack/checksums.ini b/build/pkgs/fflas_ffpack/checksums.ini index 7a9d11351eb..1a5cdcee74b 100644 --- a/build/pkgs/fflas_ffpack/checksums.ini +++ b/build/pkgs/fflas_ffpack/checksums.ini @@ -1,3 +1,5 @@ tarball=fflas_ffpack-VERSION.tar.bz2 -sha1=c221513710b98e0e62153f424a9725c5be2ff62a -sha256=d4ecfc6289c7077185ed5e58fa77d07fdb034c1a74385366566226a4887c50c3 +sha1=7c5faa81abc2b88ec24cec373b5e44cbaa7844dd +md5=d8b7c113951a2a3f498a3aaadbe5620f +cksum=3321469120 +upstream_url=https://github.com/linbox-team/fflas-ffpack/releases/download/vVERSION/fflas_ffpack-VERSION.tar.bz2 \ No newline at end of file diff --git a/build/pkgs/fflas_ffpack/package-version.txt b/build/pkgs/fflas_ffpack/package-version.txt index e0af9dd584a..437459cd94c 100644 --- a/build/pkgs/fflas_ffpack/package-version.txt +++ b/build/pkgs/fflas_ffpack/package-version.txt @@ -1 +1 @@ -2.4.3.p0 +2.5.0 diff --git a/build/pkgs/fflas_ffpack/patches/0001-Do-not-use-variable-names-B0-B1-to-avoid-clash-with-.patch b/build/pkgs/fflas_ffpack/patches/0001-Do-not-use-variable-names-B0-B1-to-avoid-clash-with-.patch deleted file mode 100644 index 9351b03a941..00000000000 --- a/build/pkgs/fflas_ffpack/patches/0001-Do-not-use-variable-names-B0-B1-to-avoid-clash-with-.patch +++ /dev/null @@ -1,93 +0,0 @@ -From d72a7643b7f8a1dedd12eadf89690c07ff6eed6e Mon Sep 17 00:00:00 2001 -From: Matthias Koeppe -Date: Mon, 1 Mar 2021 09:23:50 -0800 -Subject: [PATCH] Do not use variable names B0, B1 to avoid clash with - sys/termio.h macros (again) - ---- - .../fflas/fflas_igemm/igemm_kernels.inl | 50 +++++++++---------- - 1 file changed, 25 insertions(+), 25 deletions(-) - -diff --git a/fflas-ffpack/fflas/fflas_igemm/igemm_kernels.inl b/fflas-ffpack/fflas/fflas_igemm/igemm_kernels.inl -index c69d32c6..0ca12110 100644 ---- a/fflas-ffpack/fflas/fflas_igemm/igemm_kernels.inl -+++ b/fflas-ffpack/fflas/fflas_igemm/igemm_kernels.inl -@@ -403,21 +403,21 @@ namespace FFLAS { namespace details { /* kernels */ - vect_t R0; - R0 = simd::set(r0[0], r1[0], r2[0], r3[0]); // could be done with a gather (marginally faster?) - for(k=0;k +Date: Fri, 17 Dec 2021 10:27:02 +0100 +Subject: [PATCH] Fix SimdChooser: on not x86_64 machines its value could be an + nonexistant struct + +--- + fflas-ffpack/fflas/fflas_simd.h | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/fflas-ffpack/fflas/fflas_simd.h b/fflas-ffpack/fflas/fflas_simd.h +index bf5d30211..84ced0fd8 100644 +--- a/fflas-ffpack/fflas/fflas_simd.h ++++ b/fflas-ffpack/fflas/fflas_simd.h +@@ -384,6 +384,20 @@ struct SimdChooser // integral number + #endif + }; + ++#ifndef __x86_64__ ++template <> ++struct SimdChooser ++{ ++ using value = NoSimd; ++}; ++ ++template <> ++struct SimdChooser ++{ ++ using value = NoSimd; ++}; ++#endif ++ + template using Simd = typename SimdChooser::value; + + // template struct SimdChooser { diff --git a/build/pkgs/fflas_ffpack/patches/fix-ksh-pkgconfig.patch b/build/pkgs/fflas_ffpack/patches/fix-ksh-pkgconfig.patch deleted file mode 100644 index fdaed2eebee..00000000000 --- a/build/pkgs/fflas_ffpack/patches/fix-ksh-pkgconfig.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 33a5ec4977f36ce3a24c9ee824d9dd053b8cea04 Mon Sep 17 00:00:00 2001 -From: Dima Pasechnik -Date: Fri, 8 May 2020 15:55:27 +0100 -Subject: [PATCH 1/1] remove 1st and last file in .pc file - -this causes problem if building with ksh, as they remain, causing a broken .pc file. ---- - fflas-ffpack.pc.in | 2 -- - 1 file changed, 2 deletions(-) - -diff --git a/fflas-ffpack.pc.in b/fflas-ffpack.pc.in -index a2618d6..e34a744 100644 ---- a/fflas-ffpack.pc.in -+++ b/fflas-ffpack.pc.in -@@ -1,4 +1,3 @@ --/------------------ fflas-ffpack.pc ------------------------ - prefix=@prefix@ - exec_prefix=@prefix@ - libdir=@prefix@/lib -@@ -11,4 +10,3 @@ Version: @VERSION@ - Requires: givaro >= 4.0.3 - Libs: @PARLIBS@ @PRECOMPILE_LIBS@ @BLAS_LIBS@ - Cflags: -I@includedir@ @BLAS_CFLAGS@ @PARFLAGS@ @PRECOMPILE_FLAGS@ @REQUIRED_FLAGS@ --\------------------------------------------------------- -\ No newline at end of file --- -2.26.2 - diff --git a/build/pkgs/fflas_ffpack/spkg-configure.m4 b/build/pkgs/fflas_ffpack/spkg-configure.m4 index 646cfb527e4..4e99de570e3 100644 --- a/build/pkgs/fflas_ffpack/spkg-configure.m4 +++ b/build/pkgs/fflas_ffpack/spkg-configure.m4 @@ -4,7 +4,7 @@ SAGE_SPKG_CONFIGURE([fflas_ffpack], [ # the system fflas-ffpack, too. Use pkg-config to find a # recentish version, if there is one. PKG_CHECK_MODULES([FFLAS_FFPACK], - [fflas-ffpack >= 2.4.0],dnl The version test is refined in linbox/spkg-configure.m4 + [fflas-ffpack >= 2.5.0],dnl The version test is refined in linbox/spkg-configure.m4 [sage_spkg_install_fflas_ffpack=no], [sage_spkg_install_fflas_ffpack=yes]) ]) diff --git a/build/pkgs/gcc/spkg-configure.m4 b/build/pkgs/gcc/spkg-configure.m4 index 8b5ff54fe8e..9b8c8dc44ce 100644 --- a/build/pkgs/gcc/spkg-configure.m4 +++ b/build/pkgs/gcc/spkg-configure.m4 @@ -165,8 +165,8 @@ SAGE_SPKG_CONFIGURE_BASE([gcc], [ # Install our own GCC if the system-provided one is older than gcc 8.4 SAGE_SHOULD_INSTALL_GCC([you have $CXX version $GXX_VERSION, which is quite old]) ], - [1[[4-9]].*], [ - # Install our own GCC if the system-provided one is newer than 13.x. + [1[[5-9]].*], [ + # Install our own GCC if the system-provided one is newer than 14.x. # See https://github.com/sagemath/sage/issues/29456 SAGE_SHOULD_INSTALL_GCC([$CXX is g++ version $GXX_VERSION, which is too recent for this version of Sage]) ]) diff --git a/build/pkgs/gfortran/spkg-configure.m4 b/build/pkgs/gfortran/spkg-configure.m4 index e6e396deaab..a8248a26aa5 100644 --- a/build/pkgs/gfortran/spkg-configure.m4 +++ b/build/pkgs/gfortran/spkg-configure.m4 @@ -86,8 +86,8 @@ SAGE_SPKG_CONFIGURE([gfortran], [ # Install our own gfortran if the system-provided one is older than gcc-4.8. SAGE_SHOULD_INSTALL_GFORTRAN([$FC is version $GFORTRAN_VERSION, which is quite old]) ], - [1[[4-9]].*], [ - # Install our own gfortran if the system-provided one is newer than 13.x. + [1[[5-9]].*], [ + # Install our own gfortran if the system-provided one is newer than 14.x. # See https://github.com/sagemath/sage/issues/29456, https://github.com/sagemath/sage/issues/31838 SAGE_MUST_INSTALL_GFORTRAN([$FC is version $GFORTRAN_VERSION, which is too recent for this version of Sage]) ]) diff --git a/build/pkgs/givaro/checksums.ini b/build/pkgs/givaro/checksums.ini index ac9d04b9d3a..8d20cfdafa5 100644 --- a/build/pkgs/givaro/checksums.ini +++ b/build/pkgs/givaro/checksums.ini @@ -1,3 +1,5 @@ tarball=givaro-VERSION.tar.gz -sha1=2e7af1537d6f8325578a54d5b8092c990028863d -sha256=628049899386e91da245aee6cd446350fbca87e94863bc0d815066c08150487f +sha1=73ef15ca34c6f1c9f61013d2bd7d4d547e3ace14 +md5=d03ca4ba1e4a44c20935cf2adfcb520b +cksum=3088182773 +upstream_url=https://github.com/linbox-team/givaro/releases/download/vVERSION/givaro-VERSION.tar.gz \ No newline at end of file diff --git a/build/pkgs/givaro/package-version.txt b/build/pkgs/givaro/package-version.txt index 627a3f43a64..6aba2b245a8 100644 --- a/build/pkgs/givaro/package-version.txt +++ b/build/pkgs/givaro/package-version.txt @@ -1 +1 @@ -4.1.1 +4.2.0 diff --git a/build/pkgs/givaro/patches/197.patch b/build/pkgs/givaro/patches/197.patch new file mode 100644 index 00000000000..bafaff3efbd --- /dev/null +++ b/build/pkgs/givaro/patches/197.patch @@ -0,0 +1,28 @@ +From ab3d332508c21daff41fb64a8658cdc7cc74fc47 Mon Sep 17 00:00:00 2001 +From: Cyril Bouvier +Date: Thu, 16 Dec 2021 17:12:25 +0100 +Subject: [PATCH] dom_power argument is now an uint64_t to avoid problem with + 32bit machine + +--- + src/kernel/system/givpower.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/kernel/system/givpower.h b/src/kernel/system/givpower.h +index eb784872..5644264d 100644 +--- a/src/kernel/system/givpower.h ++++ b/src/kernel/system/givpower.h +@@ -71,11 +71,11 @@ namespace Givaro { + + //! dom_power + template +- TT& dom_power(TT& res, const TT& n, long l, const D& F) ++ TT& dom_power(TT& res, const TT& n, uint64_t l, const D& F) + { + if (l == 0) return F.assign(res,F.one) ; + +- unsigned long p = (unsigned long) l ; ++ uint64_t p = l; + bool is_assg = false ; + + TT puiss; F.init(puiss); F.assign(puiss,n) ; diff --git a/build/pkgs/givaro/patches/226.patch b/build/pkgs/givaro/patches/226.patch new file mode 100644 index 00000000000..0459dc6f36c --- /dev/null +++ b/build/pkgs/givaro/patches/226.patch @@ -0,0 +1,30 @@ +From 20caba1b549fe46b483f120f8eec6ec4e9f4572d Mon Sep 17 00:00:00 2001 +From: "Benjamin A. Beasley" +Date: Thu, 25 Jan 2024 08:29:17 -0500 +Subject: [PATCH] Temporary GCC 14 workaround +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Fixes https://github.com/linbox-team/givaro/issues/226 “GCC 14: No match +for operator= for Givaro::ZRing” + +Recommended in +https://github.com/linbox-team/givaro/issues/226#issuecomment-1908853755 +--- + src/kernel/integer/random-integer.h | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/src/kernel/integer/random-integer.h b/src/kernel/integer/random-integer.h +index f9361d33..ea189a36 100644 +--- a/src/kernel/integer/random-integer.h ++++ b/src/kernel/integer/random-integer.h +@@ -87,7 +87,6 @@ namespace Givaro + if (this != &R) { + _bits = R._bits; + _integer = R._integer; +- const_cast(_ring)=R._ring; + } + return *this; + } +-- diff --git a/build/pkgs/givaro/patches/fix-ksh-pkgconfig.patch b/build/pkgs/givaro/patches/fix-ksh-pkgconfig.patch deleted file mode 100644 index 28fd95088c8..00000000000 --- a/build/pkgs/givaro/patches/fix-ksh-pkgconfig.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 91dcba743e15288abe69966a5f71704d9adcc57c Mon Sep 17 00:00:00 2001 -From: Dima Pasechnik -Date: Fri, 8 May 2020 10:22:57 +0100 -Subject: [PATCH 1/1] remove 1st and last lines in givaro.pc.in - ---- - givaro.pc.in | 2 -- - 1 file changed, 2 deletions(-) - -diff --git a/givaro.pc.in b/givaro.pc.in -index 285b854..af38bf3 100644 ---- a/givaro.pc.in -+++ b/givaro.pc.in -@@ -1,4 +1,3 @@ --/------------------ givaro.pc ------------------------ - prefix=@prefix@ - exec_prefix=@prefix@ - libdir=@prefix@/lib -@@ -11,4 +10,3 @@ Version: @VERSION@ - Requires: - Libs: -L@libdir@ -lgivaro @LIBS@ - Cflags: -I@includedir@ @REQUIRED_FLAGS@ --\------------------------------------------------------- -\ No newline at end of file --- -2.26.2 - diff --git a/build/pkgs/givaro/patches/givaro-26932_recintvsflint_longlong.patch b/build/pkgs/givaro/patches/givaro-26932_recintvsflint_longlong.patch deleted file mode 100644 index 3d7e100ad09..00000000000 --- a/build/pkgs/givaro/patches/givaro-26932_recintvsflint_longlong.patch +++ /dev/null @@ -1,2501 +0,0 @@ -diff --git a/src/kernel/recint/reclonglong.h b/src/kernel/recint/reclonglong.h -index cab889b..1b1e07e 100644 ---- a/src/kernel/recint/reclonglong.h -+++ b/src/kernel/recint/reclonglong.h -@@ -41,7 +41,6 @@ - */ - - /* longlong.h may already be included from another library (e.g. flint) */ --#ifndef add_ssaaaa - - #define __BITS4 (W_TYPE_SIZE / 4) - #define __ll_B ((UWtype) 1 << (W_TYPE_SIZE / 2)) -@@ -56,14 +55,14 @@ - - /* Define auxiliary asm macros. - -- 1) umul_ppmm(high_prod, low_prod, multiplier, multiplicand) multiplies two -+ 1) recint_umul_ppmm(high_prod, low_prod, multiplier, multiplicand) multiplies two - UWtype integers MULTIPLIER and MULTIPLICAND, and generates a two UWtype - word product in HIGH_PROD and LOW_PROD. - - 2) __umulsidi3(a,b) multiplies two UWtype integers A and B, and returns a -- UDWtype product. This is just a variant of umul_ppmm. -+ UDWtype product. This is just a variant of recint_umul_ppmm. - -- 3) udiv_qrnnd(quotient, remainder, high_numerator, low_numerator, -+ 3) recint_udiv_qrnnd(quotient, remainder, high_numerator, low_numerator, - denominator) divides a UDWtype, composed by the UWtype integers - HIGH_NUMERATOR and LOW_NUMERATOR, by DENOMINATOR and places the quotient - in QUOTIENT and the remainder in REMAINDER. HIGH_NUMERATOR must be less -@@ -72,24 +71,24 @@ - UDIV_NEEDS_NORMALIZATION is defined to 1. - - 4) sdiv_qrnnd(quotient, remainder, high_numerator, low_numerator, -- denominator). Like udiv_qrnnd but the numbers are signed. The quotient -+ denominator). Like recint_udiv_qrnnd but the numbers are signed. The quotient - is rounded towards 0. - -- 5) count_leading_zeros(count, x) counts the number of zero-bits from the -+ 5) recint_count_leading_zeros(count, x) counts the number of zero-bits from the - msb to the first non-zero bit in the UWtype X. This is the number of - steps X needs to be shifted left to set the msb. Undefined for X == 0, -- unless the symbol COUNT_LEADING_ZEROS_0 is defined to some value. -+ unless the symbol RECINT_COUNT_LEADING_ZEROS_0 is defined to some value. - -- 6) count_trailing_zeros(count, x) like count_leading_zeros, but counts -+ 6) recint_count_trailing_zeros(count, x) like recint_count_leading_zeros, but counts - from the least significant end. - -- 7) add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1, -+ 7) recint_add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1, - high_addend_2, low_addend_2) adds two UWtype integers, composed by - HIGH_ADDEND_1 and LOW_ADDEND_1, and HIGH_ADDEND_2 and LOW_ADDEND_2 - respectively. The result is placed in HIGH_SUM and LOW_SUM. Overflow - (i.e. carry out) is not stored anywhere, and is lost. - -- 8) sub_ddmmss(high_difference, low_difference, high_minuend, low_minuend, -+ 8) recint_sub_ddmmss(high_difference, low_difference, high_minuend, low_minuend, - high_subtrahend, low_subtrahend) subtracts two two-word UWtype integers, - composed by HIGH_MINUEND_1 and LOW_MINUEND_1, and HIGH_SUBTRAHEND_2 and - LOW_SUBTRAHEND_2 respectively. The result is placed in HIGH_DIFFERENCE -@@ -102,7 +101,7 @@ - - Notes: - -- For add_ssaaaa the two high and two low addends can both commute, but -+ For recint_add_ssaaaa the two high and two low addends can both commute, but - unfortunately gcc only supports one "%" commutative in each asm block. - This has always been so but is only documented in recent versions - (eg. pre-release 3.3). Having two or more "%"s can cause an internal -@@ -123,9 +122,9 @@ - for the CPUs below! */ - - --/* count_leading_zeros_gcc_clz is count_leading_zeros implemented with gcc -+/* recint_count_leading_zeros_gcc_clz is recint_count_leading_zeros implemented with gcc - 3.4 __builtin_clzl or __builtin_clzll, according to our limb size. -- Similarly count_trailing_zeros_gcc_ctz using __builtin_ctzl or -+ Similarly recint_count_trailing_zeros_gcc_ctz using __builtin_ctzl or - __builtin_ctzll. - - These builtins are only used when we check what code comes out, on some -@@ -137,1697 +136,39 @@ - usage. We keep an asm block for use on prior versions of gcc though. - - For reference, __builtin_ffs existed in gcc prior to __builtin_clz, but -- it's not used (for count_leading_zeros) because it generally gives extra -+ it's not used (for recint_count_leading_zeros) because it generally gives extra - code to ensure the result is 0 when the input is 0, which we don't need - or want. */ - - #ifdef _LONG_LONG_LIMB --#define count_leading_zeros_gcc_clz(count,x) \ -+#define recint_count_leading_zeros_gcc_clz(count,x) \ - do { \ - (count) = __builtin_clzll (x); \ - } while (0) - #else --#define count_leading_zeros_gcc_clz(count,x) \ -+#define recint_count_leading_zeros_gcc_clz(count,x) \ - do { \ - (count) = __builtin_clzl (x); \ - } while (0) - #endif - - #ifdef _LONG_LONG_LIMB --#define count_trailing_zeros_gcc_ctz(count,x) \ -+#define recint_count_trailing_zeros_gcc_ctz(count,x) \ - do { \ - (count) = __builtin_ctzll (x); \ - } while (0) - #else --#define count_trailing_zeros_gcc_ctz(count,x) \ -+#define recint_count_trailing_zeros_gcc_ctz(count,x) \ - do { \ - (count) = __builtin_ctzl (x); \ - } while (0) - #endif - - --/* FIXME: The macros using external routines like __MPN(count_leading_zeros) -- don't need to be under !NO_ASM */ --#if ! defined (NO_ASM) -- --#if defined (__alpha) && W_TYPE_SIZE == 64 --/* Most alpha-based machines, except Cray systems. */ --#if defined (__GNUC__) --#if __GMP_GNUC_PREREQ (3,3) --#define umul_ppmm(ph, pl, m0, m1) \ -- do { \ -- UDItype __m0 = (m0), __m1 = (m1); \ -- (ph) = __builtin_alpha_umulh (__m0, __m1); \ -- (pl) = __m0 * __m1; \ -- } while (0) --#else --#define umul_ppmm(ph, pl, m0, m1) \ -- do { \ -- UDItype __m0 = (m0), __m1 = (m1); \ -- __asm__ ("umulh %r1,%2,%0" \ -- : "=r" (ph) \ -- : "%rJ" (m0), "rI" (m1)); \ -- (pl) = __m0 * __m1; \ -- } while (0) --#endif --#define UMUL_TIME 18 --#else /* ! __GNUC__ */ --#include --#define umul_ppmm(ph, pl, m0, m1) \ -- do { \ -- UDItype __m0 = (m0), __m1 = (m1); \ -- (ph) = __UMULH (m0, m1); \ -- (pl) = __m0 * __m1; \ -- } while (0) --#endif --#ifndef LONGLONG_STANDALONE --#define udiv_qrnnd(q, r, n1, n0, d) \ -- do { UWtype __di; \ -- __di = __MPN(invert_limb) (d); \ -- udiv_qrnnd_preinv (q, r, n1, n0, d, __di); \ -- } while (0) --#define UDIV_PREINV_ALWAYS 1 --#define UDIV_NEEDS_NORMALIZATION 1 --#define UDIV_TIME 220 --#endif /* LONGLONG_STANDALONE */ -- --/* clz_tab is required in all configurations, since mpn/alpha/cntlz.asm -- always goes into libgmp.so, even when not actually used. */ --#define COUNT_LEADING_ZEROS_NEED_CLZ_TAB -- --#if defined (__GNUC__) && HAVE_HOST_CPU_alpha_CIX --#define count_leading_zeros(COUNT,X) \ -- __asm__("ctlz %1,%0" : "=r"(COUNT) : "r"(X)) --#define count_trailing_zeros(COUNT,X) \ -- __asm__("cttz %1,%0" : "=r"(COUNT) : "r"(X)) --#endif /* clz/ctz using cix */ -- --#if ! defined (count_leading_zeros) \ -- && defined (__GNUC__) && ! defined (LONGLONG_STANDALONE) -- /* ALPHA_CMPBGE_0 gives "cmpbge $31,src,dst", ie. test src bytes == 0. -- "$31" is written explicitly in the asm, since an "r" constraint won't -- select reg 31. There seems no need to worry about "r31" syntax for cray, -- since gcc itself (pre-release 3.4) emits just $31 in various places. */ --#define ALPHA_CMPBGE_0(dst, src) \ -- do { asm ("cmpbge $31, %1, %0" : "=r" (dst) : "r" (src)); } while (0) -- /* Zero bytes are turned into bits with cmpbge, a __clz_tab lookup counts -- them, locating the highest non-zero byte. A second __clz_tab lookup -- counts the leading zero bits in that byte, giving the result. */ --#define count_leading_zeros(count, x) \ -- do { \ -- UWtype __clz__b, __clz__c, __clz__x = (x); \ -- ALPHA_CMPBGE_0 (__clz__b, __clz__x); /* zero bytes */ \ -- __clz__b = __clz_tab [(__clz__b >> 1) ^ 0x7F]; /* 8 to 1 byte */ \ -- __clz__b = __clz__b * 8 - 7; /* 57 to 1 shift */ \ -- __clz__x >>= __clz__b; \ -- __clz__c = __clz_tab [__clz__x]; /* 8 to 1 bit */ \ -- __clz__b = 65 - __clz__b; \ -- (count) = __clz__b - __clz__c; \ -- } while (0) --#define COUNT_LEADING_ZEROS_NEED_CLZ_TAB --#endif /* clz using cmpbge */ -- --#if ! defined (count_leading_zeros) && ! defined (LONGLONG_STANDALONE) --#if HAVE_ATTRIBUTE_CONST --long __MPN(count_leading_zeros) (UDItype) __attribute__ ((const)); --#else --long __MPN(count_leading_zeros) (UDItype); --#endif --#define count_leading_zeros(count, x) \ -- ((count) = __MPN(count_leading_zeros) (x)) --#endif /* clz using mpn */ --#endif /* __alpha */ -- --#if defined (__AVR) && W_TYPE_SIZE == 8 --#define umul_ppmm(ph, pl, m0, m1) \ -- do { \ -- unsigned short __p = (unsigned short) (m0) * (m1); \ -- (ph) = __p >> 8; \ -- (pl) = __p; \ -- } while (0) --#endif /* AVR */ -- --#if defined (_CRAY) && W_TYPE_SIZE == 64 --#include --#define UDIV_PREINV_ALWAYS 1 --#define UDIV_NEEDS_NORMALIZATION 1 --#define UDIV_TIME 220 --long __MPN(count_leading_zeros) (UDItype); --#define count_leading_zeros(count, x) \ -- ((count) = _leadz ((UWtype) (x))) --#if defined (_CRAYIEEE) /* I.e., Cray T90/ieee, T3D, and T3E */ --#define umul_ppmm(ph, pl, m0, m1) \ -- do { \ -- UDItype __m0 = (m0), __m1 = (m1); \ -- (ph) = _int_mult_upper (m0, m1); \ -- (pl) = __m0 * __m1; \ -- } while (0) --#ifndef LONGLONG_STANDALONE --#define udiv_qrnnd(q, r, n1, n0, d) \ -- do { UWtype __di; \ -- __di = __MPN(invert_limb) (d); \ -- udiv_qrnnd_preinv (q, r, n1, n0, d, __di); \ -- } while (0) --#endif /* LONGLONG_STANDALONE */ --#endif /* _CRAYIEEE */ --#endif /* _CRAY */ -- --#if defined (__ia64) && W_TYPE_SIZE == 64 -- /* This form encourages gcc (pre-release 3.4 at least) to emit predicated -- "sub r=r,r" and "sub r=r,r,1", giving a 2 cycle latency. The generic -- code using "al>= _c; \ -- if (_x >= 1 << 4) \ -- _x >>= 4, _c += 4; \ -- if (_x >= 1 << 2) \ -- _x >>= 2, _c += 2; \ -- _c += _x >> 1; \ -- (count) = W_TYPE_SIZE - 1 - _c; \ -- } while (0) -- /* similar to what gcc does for __builtin_ffs, but 0 based rather than 1 -- based, and we don't need a special case for x==0 here */ --#define count_trailing_zeros(count, x) \ -- do { \ -- UWtype __ctz_x = (x); \ -- __asm__ ("popcnt %0 = %1" \ -- : "=r" (count) \ -- : "r" ((__ctz_x-1) & ~__ctz_x)); \ -- } while (0) --#endif --#if defined (__INTEL_COMPILER) --#include --#define umul_ppmm(ph, pl, m0, m1) \ -- do { \ -- UWtype _m0 = (m0), _m1 = (m1); \ -- ph = _m64_xmahu (_m0, _m1, 0); \ -- pl = _m0 * _m1; \ -- } while (0) --#endif --#ifndef LONGLONG_STANDALONE --#define udiv_qrnnd(q, r, n1, n0, d) \ -- do { UWtype __di; \ -- __di = __MPN(invert_limb) (d); \ -- udiv_qrnnd_preinv (q, r, n1, n0, d, __di); \ -- } while (0) --#define UDIV_PREINV_ALWAYS 1 --#define UDIV_NEEDS_NORMALIZATION 1 --#endif --#define UDIV_TIME 220 --#endif -- -- --#if defined (__GNUC__) -- -- /* We sometimes need to clobber "cc" with gcc2, but that would not be -- understood by gcc1. Use cpp to avoid major code duplication. */ --#if __GNUC__ < 2 --#define __CLOBBER_CC --#define __AND_CLOBBER_CC --#else /* __GNUC__ >= 2 */ --#define __CLOBBER_CC : "cc" --#define __AND_CLOBBER_CC , "cc" --#endif /* __GNUC__ < 2 */ -- --#if (defined (__a29k__) || defined (_AM29K)) && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("add %1,%4,%5\n\taddc %0,%2,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "r" (ah), "rI" (bh), "%r" (al), "rI" (bl)) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("sub %1,%4,%5\n\tsubc %0,%2,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "r" (ah), "rI" (bh), "r" (al), "rI" (bl)) --#define umul_ppmm(xh, xl, m0, m1) \ -- do { \ -- USItype __m0 = (m0), __m1 = (m1); \ -- __asm__ ("multiplu %0,%1,%2" \ -- : "=r" (xl) \ -- : "r" (__m0), "r" (__m1)); \ -- __asm__ ("multmu %0,%1,%2" \ -- : "=r" (xh) \ -- : "r" (__m0), "r" (__m1)); \ -- } while (0) --#define udiv_qrnnd(q, r, n1, n0, d) \ -- __asm__ ("dividu %0,%3,%4" \ -- : "=r" (q), "=q" (r) \ -- : "1" (n1), "r" (n0), "r" (d)) --#define count_leading_zeros(count, x) \ -- __asm__ ("clz %0,%1" \ -- : "=r" (count) \ -- : "r" (x)) --#define COUNT_LEADING_ZEROS_0 32 --#endif /* __a29k__ */ -- --#if defined (__arc__) --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("add.f\t%1, %4, %5\n\tadc\t%0, %2, %3" \ -- : "=r" (sh), \ -- "=&r" (sl) \ -- : "r" ((USItype) (ah)), \ -- "rIJ" ((USItype) (bh)), \ -- "%r" ((USItype) (al)), \ -- "rIJ" ((USItype) (bl))) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("sub.f\t%1, %4, %5\n\tsbc\t%0, %2, %3" \ -- : "=r" (sh), \ -- "=&r" (sl) \ -- : "r" ((USItype) (ah)), \ -- "rIJ" ((USItype) (bh)), \ -- "r" ((USItype) (al)), \ -- "rIJ" ((USItype) (bl))) --#endif -- --#if defined (__arm__) && !defined (__thumb__) && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("adds\t%1, %4, %5\n\tadc\t%0, %2, %3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "r" (ah), "rI" (bh), "%r" (al), "rI" (bl) __CLOBBER_CC) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- do { \ -- if (__builtin_constant_p (al)) \ -- { \ -- if (__builtin_constant_p (ah)) \ -- __asm__ ("rsbs\t%1, %5, %4\n\trsc\t%0, %3, %2" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rI" (ah), "r" (bh), "rI" (al), "r" (bl) __CLOBBER_CC); \ -- else \ -- __asm__ ("rsbs\t%1, %5, %4\n\tsbc\t%0, %2, %3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "r" (ah), "rI" (bh), "rI" (al), "r" (bl) __CLOBBER_CC); \ -- } \ -- else if (__builtin_constant_p (ah)) \ -- { \ -- if (__builtin_constant_p (bl)) \ -- __asm__ ("subs\t%1, %4, %5\n\trsc\t%0, %3, %2" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rI" (ah), "r" (bh), "r" (al), "rI" (bl) __CLOBBER_CC); \ -- else \ -- __asm__ ("rsbs\t%1, %5, %4\n\trsc\t%0, %3, %2" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rI" (ah), "r" (bh), "rI" (al), "r" (bl) __CLOBBER_CC); \ -- } \ -- else if (__builtin_constant_p (bl)) \ -- { \ -- if (__builtin_constant_p (bh)) \ -- __asm__ ("subs\t%1, %4, %5\n\tsbc\t%0, %2, %3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "r" (ah), "rI" (bh), "r" (al), "rI" (bl) __CLOBBER_CC); \ -- else \ -- __asm__ ("subs\t%1, %4, %5\n\trsc\t%0, %3, %2" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rI" (ah), "r" (bh), "r" (al), "rI" (bl) __CLOBBER_CC); \ -- } \ -- else /* only bh might be a constant */ \ -- __asm__ ("subs\t%1, %4, %5\n\tsbc\t%0, %2, %3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "r" (ah), "rI" (bh), "r" (al), "rI" (bl) __CLOBBER_CC); \ -- } while (0) --#if 1 || defined (__arm_m__) /* `M' series has widening multiply support */ --#define umul_ppmm(xh, xl, a, b) \ -- __asm__ ("umull %0,%1,%2,%3" : "=&r" (xl), "=&r" (xh) : "r" (a), "r" (b)) --#define UMUL_TIME 5 --#define smul_ppmm(xh, xl, a, b) \ -- __asm__ ("smull %0,%1,%2,%3" : "=&r" (xl), "=&r" (xh) : "r" (a), "r" (b)) --#ifndef LONGLONG_STANDALONE --#define udiv_qrnnd(q, r, n1, n0, d) \ -- do { UWtype __di; \ -- __di = __MPN(invert_limb) (d); \ -- udiv_qrnnd_preinv (q, r, n1, n0, d, __di); \ -- } while (0) --#define UDIV_PREINV_ALWAYS 1 --#define UDIV_NEEDS_NORMALIZATION 1 --#define UDIV_TIME 70 --#endif /* LONGLONG_STANDALONE */ --#else --#define umul_ppmm(xh, xl, a, b) \ -- __asm__ ("%@ Inlined umul_ppmm\n" \ -- " mov %|r0, %2, lsr #16\n" \ -- " mov %|r2, %3, lsr #16\n" \ -- " bic %|r1, %2, %|r0, lsl #16\n" \ -- " bic %|r2, %3, %|r2, lsl #16\n" \ -- " mul %1, %|r1, %|r2\n" \ -- " mul %|r2, %|r0, %|r2\n" \ -- " mul %|r1, %0, %|r1\n" \ -- " mul %0, %|r0, %0\n" \ -- " adds %|r1, %|r2, %|r1\n" \ -- " addcs %0, %0, #65536\n" \ -- " adds %1, %1, %|r1, lsl #16\n" \ -- " adc %0, %0, %|r1, lsr #16" \ -- : "=&r" (xh), "=r" (xl) \ -- : "r" (a), "r" (b) \ -- : "r0", "r1", "r2") --#define UMUL_TIME 20 --#ifndef LONGLONG_STANDALONE --#define udiv_qrnnd(q, r, n1, n0, d) \ -- do { UWtype __r; \ -- (q) = __MPN(udiv_qrnnd) (&__r, (n1), (n0), (d)); \ -- (r) = __r; \ -- } while (0) --extern UWtype __MPN(udiv_qrnnd) (UWtype *, UWtype, UWtype, UWtype); --#define UDIV_TIME 200 --#endif /* LONGLONG_STANDALONE */ --#endif -- /* This is a bizarre test, but GCC doesn't define useful common symbol. */ --#if defined (__ARM_ARCH_5__) || defined (__ARM_ARCH_5T__) || \ -- defined (__ARM_ARCH_5E__) || defined (__ARM_ARCH_5TE__)|| \ -- defined (__ARM_ARCH_6__) || defined (__ARM_ARCH_6J__) || \ -- defined (__ARM_ARCH_6K__) || defined (__ARM_ARCH_6Z__) || \ -- defined (__ARM_ARCH_6ZK__)|| defined (__ARM_ARCH_6T2__)|| \ -- defined (__ARM_ARCH_6M__) || defined (__ARM_ARCH_7__) || \ -- defined (__ARM_ARCH_7A__) || defined (__ARM_ARCH_7R__) || \ -- defined (__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__) --#define count_leading_zeros(count, x) \ -- __asm__ ("clz\t%0, %1" : "=r" (count) : "r" (x)) --#define COUNT_LEADING_ZEROS_0 32 --#endif --#endif /* __arm__ */ -- --#if defined (__aarch64__) && W_TYPE_SIZE == 64 -- /* FIXME: Extend the immediate range for the low word by using both -- ADDS and SUBS, since they set carry in the same way. */ --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("adds\t%1, %x4, %5\n\tadc\t%0, %x2, %x3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rZ" (ah), "rZ" (bh), "%r" (al), "rI" (bl) __CLOBBER_CC) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("subs\t%1, %x4, %5\n\tsbc\t%0, %x2, %x3" \ -- : "=r,r" (sh), "=&r,&r" (sl) \ -- : "rZ,rZ" (ah), "rZ,rZ" (bh), "r,Z" (al), "rI,r" (bl) __CLOBBER_CC) --#define umul_ppmm(ph, pl, m0, m1) \ -- do { \ -- UDItype __m0 = (m0), __m1 = (m1); \ -- __asm__ ("umulh\t%0, %1, %2" : "=r" (ph) : "r" (m0), "r" (m1)); \ -- (pl) = __m0 * __m1; \ -- } while (0) --#define count_leading_zeros(count, x) \ -- __asm__ ("clz\t%0, %1" : "=r" (count) : "r" (x)) --#define COUNT_LEADING_ZEROS_0 64 --#endif /* __aarch64__ */ -- --#if defined (__clipper__) && W_TYPE_SIZE == 32 --#define umul_ppmm(w1, w0, u, v) \ -- ({union {UDItype __ll; \ -- struct {USItype __l, __h;} __i; \ -- } __x; \ -- __asm__ ("mulwux %2,%0" \ -- : "=r" (__x.__ll) \ -- : "%0" ((USItype)(u)), "r" ((USItype)(v))); \ -- (w1) = __x.__i.__h; (w0) = __x.__i.__l;}) --#define smul_ppmm(w1, w0, u, v) \ -- ({union {DItype __ll; \ -- struct {SItype __l, __h;} __i; \ -- } __x; \ -- __asm__ ("mulwx %2,%0" \ -- : "=r" (__x.__ll) \ -- : "%0" ((SItype)(u)), "r" ((SItype)(v))); \ -- (w1) = __x.__i.__h; (w0) = __x.__i.__l;}) --#define __umulsidi3(u, v) \ -- ({UDItype __w; \ -- __asm__ ("mulwux %2,%0" \ -- : "=r" (__w) : "%0" ((USItype)(u)), "r" ((USItype)(v))); \ -- __w; }) --#endif /* __clipper__ */ -- --/* Fujitsu vector computers. */ --#if defined (__uxp__) && W_TYPE_SIZE == 32 --#define umul_ppmm(ph, pl, u, v) \ -- do { \ -- union {UDItype __ll; \ -- struct {USItype __h, __l;} __i; \ -- } __x; \ -- __asm__ ("mult.lu %1,%2,%0" : "=r" (__x.__ll) : "%r" (u), "rK" (v)); \ -- (ph) = __x.__i.__h; \ -- (pl) = __x.__i.__l; \ -- } while (0) --#define smul_ppmm(ph, pl, u, v) \ -- do { \ -- union {UDItype __ll; \ -- struct {USItype __h, __l;} __i; \ -- } __x; \ -- __asm__ ("mult.l %1,%2,%0" : "=r" (__x.__ll) : "%r" (u), "rK" (v)); \ -- (ph) = __x.__i.__h; \ -- (pl) = __x.__i.__l; \ -- } while (0) --#endif -- --#if defined (__gmicro__) && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("add.w %5,%1\n\taddx %3,%0" \ -- : "=g" (sh), "=&g" (sl) \ -- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \ -- "%1" ((USItype)(al)), "g" ((USItype)(bl))) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("sub.w %5,%1\n\tsubx %3,%0" \ -- : "=g" (sh), "=&g" (sl) \ -- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \ -- "1" ((USItype)(al)), "g" ((USItype)(bl))) --#define umul_ppmm(ph, pl, m0, m1) \ -- __asm__ ("mulx %3,%0,%1" \ -- : "=g" (ph), "=r" (pl) \ -- : "%0" ((USItype)(m0)), "g" ((USItype)(m1))) --#define udiv_qrnnd(q, r, nh, nl, d) \ -- __asm__ ("divx %4,%0,%1" \ -- : "=g" (q), "=r" (r) \ -- : "1" ((USItype)(nh)), "0" ((USItype)(nl)), "g" ((USItype)(d))) --#define count_leading_zeros(count, x) \ -- __asm__ ("bsch/1 %1,%0" \ -- : "=g" (count) : "g" ((USItype)(x)), "0" ((USItype)0)) --#endif -- --#if defined (__hppa) && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("add%I5 %5,%r4,%1\n\taddc %r2,%r3,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rM" (ah), "rM" (bh), "%rM" (al), "rI" (bl)) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("sub%I4 %4,%r5,%1\n\tsubb %r2,%r3,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rM" (ah), "rM" (bh), "rI" (al), "rM" (bl)) --#if defined (_PA_RISC1_1) --#define umul_ppmm(wh, wl, u, v) \ -- do { \ -- union {UDItype __ll; \ -- struct {USItype __h, __l;} __i; \ -- } __x; \ -- __asm__ ("xmpyu %1,%2,%0" : "=*f" (__x.__ll) : "*f" (u), "*f" (v)); \ -- (wh) = __x.__i.__h; \ -- (wl) = __x.__i.__l; \ -- } while (0) --#define UMUL_TIME 8 --#define UDIV_TIME 60 --#else --#define UMUL_TIME 40 --#define UDIV_TIME 80 --#endif --#define count_leading_zeros(count, x) \ -- do { \ -- USItype __tmp; \ -- __asm__ ( \ -- "ldi 1,%0\n" \ -- " extru,= %1,15,16,%%r0 ; Bits 31..16 zero?\n" \ -- " extru,tr %1,15,16,%1 ; No. Shift down, skip add.\n" \ -- " ldo 16(%0),%0 ; Yes. Perform add.\n" \ -- " extru,= %1,23,8,%%r0 ; Bits 15..8 zero?\n" \ -- " extru,tr %1,23,8,%1 ; No. Shift down, skip add.\n" \ -- " ldo 8(%0),%0 ; Yes. Perform add.\n" \ -- " extru,= %1,27,4,%%r0 ; Bits 7..4 zero?\n" \ -- " extru,tr %1,27,4,%1 ; No. Shift down, skip add.\n" \ -- " ldo 4(%0),%0 ; Yes. Perform add.\n" \ -- " extru,= %1,29,2,%%r0 ; Bits 3..2 zero?\n" \ -- " extru,tr %1,29,2,%1 ; No. Shift down, skip add.\n" \ -- " ldo 2(%0),%0 ; Yes. Perform add.\n" \ -- " extru %1,30,1,%1 ; Extract bit 1.\n" \ -- " sub %0,%1,%0 ; Subtract it.\n" \ -- : "=r" (count), "=r" (__tmp) : "1" (x)); \ -- } while (0) --#endif /* hppa */ -- -- /* These macros are for ABI=2.0w. In ABI=2.0n they can't be used, since GCC -- (3.2) puts longlong into two adjacent 32-bit registers. Presumably this -- is just a case of no direct support for 2.0n but treating it like 1.0. */ --#if defined (__hppa) && W_TYPE_SIZE == 64 && ! defined (_LONG_LONG_LIMB) --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("add%I5 %5,%r4,%1\n\tadd,dc %r2,%r3,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rM" (ah), "rM" (bh), "%rM" (al), "rI" (bl)) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("sub%I4 %4,%r5,%1\n\tsub,db %r2,%r3,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rM" (ah), "rM" (bh), "rI" (al), "rM" (bl)) --#endif /* hppa */ -- --#if (defined (__i370__) || defined (__s390__) || defined (__mvs__)) && W_TYPE_SIZE == 32 --#if defined (__zarch__) || defined (HAVE_HOST_CPU_s390_zarch) --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- do { \ -- /* if (__builtin_constant_p (bl)) \ -- __asm__ ("alfi\t%1,%o5\n\talcr\t%0,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" (ah), "r" (bh), "%1" (al), "n" (bl) __CLOBBER_CC); \ -- else \ -- */ __asm__ ("alr\t%1,%5\n\talcr\t%0,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" (ah), "r" (bh), "%1" (al), "r" (bl)__CLOBBER_CC); \ -- } while (0) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- do { \ -- /* if (__builtin_constant_p (bl)) \ -- __asm__ ("slfi\t%1,%o5\n\tslbr\t%0,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" (ah), "r" (bh), "1" (al), "n" (bl) __CLOBBER_CC); \ -- else \ -- */ __asm__ ("slr\t%1,%5\n\tslbr\t%0,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" (ah), "r" (bh), "1" (al), "r" (bl) __CLOBBER_CC); \ -- } while (0) --#if __GMP_GNUC_PREREQ (4,5) --#define umul_ppmm(xh, xl, m0, m1) \ -- do { \ -- union {UDItype __ll; \ -- struct {USItype __h, __l;} __i; \ -- } __x; \ -- __x.__ll = (UDItype) (m0) * (UDItype) (m1); \ -- (xh) = __x.__i.__h; (xl) = __x.__i.__l; \ -- } while (0) --#else --#if 0 -- /* FIXME: this fails if gcc knows about the 64-bit registers. Use only -- with a new enough processor pretending we have 32-bit registers. */ --#define umul_ppmm(xh, xl, m0, m1) \ -- do { \ -- union {UDItype __ll; \ -- struct {USItype __h, __l;} __i; \ -- } __x; \ -- __asm__ ("mlr\t%0,%2" \ -- : "=r" (__x.__ll) \ -- : "%0" (m0), "r" (m1)); \ -- (xh) = __x.__i.__h; (xl) = __x.__i.__l; \ -- } while (0) --#else --#define umul_ppmm(xh, xl, m0, m1) \ -- do { \ -- /* When we have 64-bit regs and gcc is aware of that, we cannot simply use -- DImode for the product, since that would be allocated to a single 64-bit -- register, whereas mlr uses the low 32-bits of an even-odd register pair. -- */ \ --register USItype __r0 __asm__ ("0"); \ --register USItype __r1 __asm__ ("1") = (m0); \ --__asm__ ("mlr\t%0,%3" \ --: "=r" (__r0), "=r" (__r1) \ -- : "r" (__r1), "r" (m1)); \ --(xh) = __r0; (xl) = __r1; \ --} while (0) --#endif /* if 0 */ --#endif --#if 0 --/* FIXME: this fails if gcc knows about the 64-bit registers. Use only -- with a new enough processor pretending we have 32-bit registers. */ --#define udiv_qrnnd(q, r, n1, n0, d) \ -- do { \ -- union {UDItype __ll; \ -- struct {USItype __h, __l;} __i; \ -- } __x; \ -- __x.__i.__h = n1; __x.__i.__l = n0; \ -- __asm__ ("dlr\t%0,%2" \ -- : "=r" (__x.__ll) \ -- : "0" (__x.__ll), "r" (d)); \ -- (q) = __x.__i.__l; (r) = __x.__i.__h; \ -- } while (0) --#else --#define udiv_qrnnd(q, r, n1, n0, d) \ -- do { \ -- register USItype __r0 __asm__ ("0") = (n1); \ -- register USItype __r1 __asm__ ("1") = (n0); \ -- __asm__ ("dlr\t%0,%4" \ -- : "=r" (__r0), "=r" (__r1) \ -- : "r" (__r0), "r" (__r1), "r" (d)); \ -- (q) = __r1; (r) = __r0; \ -- } while (0) --#endif /* if 0 */ --#else /* if __zarch__ */ --/* FIXME: this fails if gcc knows about the 64-bit registers. */ --#define smul_ppmm(xh, xl, m0, m1) \ -- do { \ -- union {DItype __ll; \ -- struct {USItype __h, __l;} __i; \ -- } __x; \ -- __asm__ ("mr\t%0,%2" \ -- : "=r" (__x.__ll) \ -- : "%0" (m0), "r" (m1)); \ -- (xh) = __x.__i.__h; (xl) = __x.__i.__l; \ -- } while (0) --/* FIXME: this fails if gcc knows about the 64-bit registers. */ --#define sdiv_qrnnd(q, r, n1, n0, d) \ -- do { \ -- union {DItype __ll; \ -- struct {USItype __h, __l;} __i; \ -- } __x; \ -- __x.__i.__h = n1; __x.__i.__l = n0; \ -- __asm__ ("dr\t%0,%2" \ -- : "=r" (__x.__ll) \ -- : "0" (__x.__ll), "r" (d)); \ -- (q) = __x.__i.__l; (r) = __x.__i.__h; \ -- } while (0) --#endif /* if __zarch__ */ --#endif -- --#if defined (__s390x__) && W_TYPE_SIZE == 64 --/* We need to cast operands with register constraints, otherwise their types -- will be assumed to be SImode by gcc. For these machines, such operations -- will insert a value into the low 32 bits, and leave the high 32 bits with -- garbage. */ --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- do { \ -- __asm__ ("algr\t%1,%5\n\talcgr\t%0,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((UDItype)(ah)), "r" ((UDItype)(bh)), \ -- "%1" ((UDItype)(al)), "r" ((UDItype)(bl)) __CLOBBER_CC); \ -- } while (0) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- do { \ -- __asm__ ("slgr\t%1,%5\n\tslbgr\t%0,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((UDItype)(ah)), "r" ((UDItype)(bh)), \ -- "1" ((UDItype)(al)), "r" ((UDItype)(bl)) __CLOBBER_CC); \ -- } while (0) --#define umul_ppmm(xh, xl, m0, m1) \ -- do { \ -- union {unsigned int __attribute__ ((mode(TI))) __ll; \ -- struct {UDItype __h, __l;} __i; \ -- } __x; \ -- __asm__ ("mlgr\t%0,%2" \ -- : "=r" (__x.__ll) \ -- : "%0" ((UDItype)(m0)), "r" ((UDItype)(m1))); \ -- (xh) = __x.__i.__h; (xl) = __x.__i.__l; \ -- } while (0) --#define udiv_qrnnd(q, r, n1, n0, d) \ -- do { \ -- union {unsigned int __attribute__ ((mode(TI))) __ll; \ -- struct {UDItype __h, __l;} __i; \ -- } __x; \ -- __x.__i.__h = n1; __x.__i.__l = n0; \ -- __asm__ ("dlgr\t%0,%2" \ -- : "=r" (__x.__ll) \ -- : "0" (__x.__ll), "r" ((UDItype)(d))); \ -- (q) = __x.__i.__l; (r) = __x.__i.__h; \ -- } while (0) --#if 0 /* FIXME: Enable for z10 (?) */ --#define count_leading_zeros(cnt, x) \ -- do { \ -- union {unsigned int __attribute__ ((mode(TI))) __ll; \ -- struct {UDItype __h, __l;} __i; \ -- } __clr_cnt; \ -- __asm__ ("flogr\t%0,%1" \ -- : "=r" (__clr_cnt.__ll) \ -- : "r" (x) __CLOBBER_CC); \ -- (cnt) = __clr_cnt.__i.__h; \ -- } while (0) --#endif --#endif -- --#if (defined (__i386__) || defined (__i486__)) && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("addl %5,%k1\n\tadcl %3,%k0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \ -- "%1" ((USItype)(al)), "g" ((USItype)(bl))) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("subl %5,%k1\n\tsbbl %3,%k0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \ -- "1" ((USItype)(al)), "g" ((USItype)(bl))) --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("mull %3" \ -- : "=a" (w0), "=d" (w1) \ -- : "%0" ((USItype)(u)), "rm" ((USItype)(v))) --#define udiv_qrnnd(q, r, n1, n0, dx) /* d renamed to dx avoiding "=d" */ \ -- __asm__ ("divl %4" /* stringification in K&R C */ \ -- : "=a" (q), "=d" (r) \ -- : "0" ((USItype)(n0)), "1" ((USItype)(n1)), "rm" ((USItype)(dx))) -- --#if HAVE_HOST_CPU_i586 || HAVE_HOST_CPU_pentium || HAVE_HOST_CPU_pentiummmx --/* Pentium bsrl takes between 10 and 72 cycles depending where the most -- significant 1 bit is, hence the use of the following alternatives. bsfl -- is slow too, between 18 and 42 depending where the least significant 1 -- bit is, so let the generic count_trailing_zeros below make use of the -- count_leading_zeros here too. */ -- --#if HAVE_HOST_CPU_pentiummmx && ! defined (LONGLONG_STANDALONE) --/* The following should be a fixed 14 or 15 cycles, but possibly plus an L1 -- cache miss reading from __clz_tab. For P55 it's favoured over the float -- below so as to avoid mixing MMX and x87, since the penalty for switching -- between the two is about 100 cycles. -- -- The asm block sets __shift to -3 if the high 24 bits are clear, -2 for -- 16, -1 for 8, or 0 otherwise. This could be written equivalently as -- follows, but as of gcc 2.95.2 it results in conditional jumps. -- -- __shift = -(__n < 0x1000000); -- __shift -= (__n < 0x10000); -- __shift -= (__n < 0x100); -- -- The middle two sbbl and cmpl's pair, and with luck something gcc -- generates might pair with the first cmpl and the last sbbl. The "32+1" -- constant could be folded into __clz_tab[], but it doesn't seem worth -- making a different table just for that. */ -- --#define count_leading_zeros(c,n) \ -- do { \ -- USItype __n = (n); \ -- USItype __shift; \ -- __asm__ ("cmpl $0x1000000, %1\n" \ -- "sbbl %0, %0\n" \ -- "cmpl $0x10000, %1\n" \ -- "sbbl $0, %0\n" \ -- "cmpl $0x100, %1\n" \ -- "sbbl $0, %0\n" \ -- : "=&r" (__shift) : "r" (__n)); \ -- __shift = __shift*8 + 24 + 1; \ -- (c) = 32 + 1 - __shift - __clz_tab[__n >> __shift]; \ -- } while (0) --#define COUNT_LEADING_ZEROS_NEED_CLZ_TAB --#define COUNT_LEADING_ZEROS_0 31 /* n==0 indistinguishable from n==1 */ -- --#else /* ! pentiummmx || LONGLONG_STANDALONE */ --/* The following should be a fixed 14 cycles or so. Some scheduling -- opportunities should be available between the float load/store too. This -- sort of code is used in gcc 3 for __builtin_ffs (with "n&-n") and is -- apparently suggested by the Intel optimizing manual (don't know exactly -- where). gcc 2.95 or up will be best for this, so the "double" is -- correctly aligned on the stack. */ --#define count_leading_zeros(c,n) \ -- do { \ -- union { \ -- double d; \ -- unsigned a[2]; \ -- } __u; \ -- __u.d = (UWtype) (n); \ -- (c) = 0x3FF + 31 - (__u.a[1] >> 20); \ -- } while (0) --#define COUNT_LEADING_ZEROS_0 (0x3FF + 31) --#endif /* pentiummx */ -- --#else /* ! pentium */ -- --#if __GMP_GNUC_PREREQ (3,4) /* using bsrl */ --#define count_leading_zeros(count,x) count_leading_zeros_gcc_clz(count,x) --#endif /* gcc clz */ -- --/* On P6, gcc prior to 3.0 generates a partial register stall for -- __cbtmp^31, due to using "xorb $31" instead of "xorl $31", the former -- being 1 code byte smaller. "31-__cbtmp" is a workaround, probably at the -- cost of one extra instruction. Do this for "i386" too, since that means -- generic x86. */ --#if ! defined (count_leading_zeros) && __GNUC__ < 3 \ -- && (HAVE_HOST_CPU_i386 \ -- || HAVE_HOST_CPU_i686 \ -- || HAVE_HOST_CPU_pentiumpro \ -- || HAVE_HOST_CPU_pentium2 \ -- || HAVE_HOST_CPU_pentium3) --#define count_leading_zeros(count, x) \ -- do { \ -- USItype __cbtmp; \ -- __asm__ ("bsrl %1,%0" : "=r" (__cbtmp) : "rm" ((USItype)(x))); \ -- (count) = 31 - __cbtmp; \ -- } while (0) --#endif /* gcc<3 asm bsrl */ -- --#ifndef count_leading_zeros --#define count_leading_zeros(count, x) \ -- do { \ -- USItype __cbtmp; \ -- __asm__ ("bsrl %1,%0" : "=r" (__cbtmp) : "rm" ((USItype)(x))); \ -- (count) = __cbtmp ^ 31; \ -- } while (0) --#endif /* asm bsrl */ -- --#if __GMP_GNUC_PREREQ (3,4) /* using bsfl */ --#define count_trailing_zeros(count,x) count_trailing_zeros_gcc_ctz(count,x) --#endif /* gcc ctz */ -- --#ifndef count_trailing_zeros --#define count_trailing_zeros(count, x) \ -- do { \ -- __asm__ ("bsfl %1,%k0" : "=r" (count) : "rm" ((USItype)(x))); \ -- } while (0) --#endif /* asm bsfl */ -- --#endif /* ! pentium */ -- --#ifndef UMUL_TIME --#define UMUL_TIME 10 --#endif --#ifndef UDIV_TIME --#define UDIV_TIME 40 --#endif --#endif /* 80x86 */ -- --#if defined (__amd64__) && W_TYPE_SIZE == 64 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("addq %5,%q1\n\tadcq %3,%q0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((UDItype)(ah)), "rme" ((UDItype)(bh)), \ -- "%1" ((UDItype)(al)), "rme" ((UDItype)(bl))) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("subq %5,%q1\n\tsbbq %3,%q0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((UDItype)(ah)), "rme" ((UDItype)(bh)), \ -- "1" ((UDItype)(al)), "rme" ((UDItype)(bl))) --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("mulq %3" \ -- : "=a" (w0), "=d" (w1) \ -- : "%0" ((UDItype)(u)), "rm" ((UDItype)(v))) --#define udiv_qrnnd(q, r, n1, n0, dx) /* d renamed to dx avoiding "=d" */ \ -- __asm__ ("divq %4" /* stringification in K&R C */ \ -- : "=a" (q), "=d" (r) \ -- : "0" ((UDItype)(n0)), "1" ((UDItype)(n1)), "rm" ((UDItype)(dx))) --/* bsrq destination must be a 64-bit register, hence UDItype for __cbtmp. */ --#define count_leading_zeros(count, x) \ -- do { \ -- UDItype __cbtmp; \ -- __asm__ ("bsrq %1,%0" : "=r" (__cbtmp) : "rm" ((UDItype)(x))); \ -- (count) = __cbtmp ^ 63; \ -- } while (0) --/* bsfq destination must be a 64-bit register, "%q0" forces this in case -- count is only an int. */ --#define count_trailing_zeros(count, x) \ -- do { \ -- __asm__ ("bsfq %1,%q0" : "=r" (count) : "rm" ((UDItype)(x))); \ -- } while (0) --#endif /* x86_64 */ -- --#if defined (__i860__) && W_TYPE_SIZE == 32 --#define rshift_rhlc(r,h,l,c) \ -- __asm__ ("shr %3,r0,r0\;shrd %1,%2,%0" \ -- "=r" (r) : "r" (h), "r" (l), "rn" (c)) --#endif /* i860 */ -- --#if defined (__i960__) && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("cmpo 1,0\;addc %5,%4,%1\;addc %3,%2,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "dI" (ah), "dI" (bh), "%dI" (al), "dI" (bl)) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("cmpo 0,0\;subc %5,%4,%1\;subc %3,%2,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "dI" (ah), "dI" (bh), "dI" (al), "dI" (bl)) --#define umul_ppmm(w1, w0, u, v) \ -- ({union {UDItype __ll; \ -- struct {USItype __l, __h;} __i; \ -- } __x; \ -- __asm__ ("emul %2,%1,%0" \ -- : "=d" (__x.__ll) : "%dI" (u), "dI" (v)); \ -- (w1) = __x.__i.__h; (w0) = __x.__i.__l;}) --#define __umulsidi3(u, v) \ -- ({UDItype __w; \ -- __asm__ ("emul %2,%1,%0" : "=d" (__w) : "%dI" (u), "dI" (v)); \ -- __w; }) --#define udiv_qrnnd(q, r, nh, nl, d) \ -- do { \ -- union {UDItype __ll; \ -- struct {USItype __l, __h;} __i; \ -- } __nn; \ -- __nn.__i.__h = (nh); __nn.__i.__l = (nl); \ -- __asm__ ("ediv %d,%n,%0" \ -- : "=d" (__rq.__ll) : "dI" (__nn.__ll), "dI" (d)); \ -- (r) = __rq.__i.__l; (q) = __rq.__i.__h; \ -- } while (0) --#define count_leading_zeros(count, x) \ -- do { \ -- USItype __cbtmp; \ -- __asm__ ("scanbit %1,%0" : "=r" (__cbtmp) : "r" (x)); \ -- (count) = __cbtmp ^ 31; \ -- } while (0) --#define COUNT_LEADING_ZEROS_0 (-32) /* sic */ --#if defined (__i960mx) /* what is the proper symbol to test??? */ --#define rshift_rhlc(r,h,l,c) \ -- do { \ -- union {UDItype __ll; \ -- struct {USItype __l, __h;} __i; \ -- } __nn; \ -- __nn.__i.__h = (h); __nn.__i.__l = (l); \ -- __asm__ ("shre %2,%1,%0" : "=d" (r) : "dI" (__nn.__ll), "dI" (c)); \ -- } --#endif /* i960mx */ --#endif /* i960 */ -- --#if (defined (__mc68000__) || defined (__mc68020__) || defined(mc68020) \ -- || defined (__m68k__) || defined (__mc5200__) || defined (__mc5206e__) \ -- || defined (__mc5307__)) && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("add%.l %5,%1\n\taddx%.l %3,%0" \ -- : "=d" (sh), "=&d" (sl) \ -- : "0" ((USItype)(ah)), "d" ((USItype)(bh)), \ -- "%1" ((USItype)(al)), "g" ((USItype)(bl))) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("sub%.l %5,%1\n\tsubx%.l %3,%0" \ -- : "=d" (sh), "=&d" (sl) \ -- : "0" ((USItype)(ah)), "d" ((USItype)(bh)), \ -- "1" ((USItype)(al)), "g" ((USItype)(bl))) -- /* The '020, '030, '040 and CPU32 have 32x32->64 and 64/32->32q-32r. */ --#if defined (__mc68020__) || defined(mc68020) \ -- || defined (__mc68030__) || defined (mc68030) \ -- || defined (__mc68040__) || defined (mc68040) \ -- || defined (__mcpu32__) || defined (mcpu32) \ -- || defined (__NeXT__) --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("mulu%.l %3,%1:%0" \ -- : "=d" (w0), "=d" (w1) \ -- : "%0" ((USItype)(u)), "dmi" ((USItype)(v))) --#define UMUL_TIME 45 --#define udiv_qrnnd(q, r, n1, n0, d) \ -- __asm__ ("divu%.l %4,%1:%0" \ -- : "=d" (q), "=d" (r) \ -- : "0" ((USItype)(n0)), "1" ((USItype)(n1)), "dmi" ((USItype)(d))) --#define UDIV_TIME 90 --#define sdiv_qrnnd(q, r, n1, n0, d) \ -- __asm__ ("divs%.l %4,%1:%0" \ -- : "=d" (q), "=d" (r) \ -- : "0" ((USItype)(n0)), "1" ((USItype)(n1)), "dmi" ((USItype)(d))) --#else /* for other 68k family members use 16x16->32 multiplication */ --#define umul_ppmm(xh, xl, a, b) \ -- do { USItype __umul_tmp1, __umul_tmp2; \ -- __asm__ ("| Inlined umul_ppmm\n" \ -- " move%.l %5,%3\n" \ -- " move%.l %2,%0\n" \ -- " move%.w %3,%1\n" \ -- " swap %3\n" \ -- " swap %0\n" \ -- " mulu%.w %2,%1\n" \ -- " mulu%.w %3,%0\n" \ -- " mulu%.w %2,%3\n" \ -- " swap %2\n" \ -- " mulu%.w %5,%2\n" \ -- " add%.l %3,%2\n" \ -- " jcc 1f\n" \ -- " add%.l %#0x10000,%0\n" \ -- "1: move%.l %2,%3\n" \ -- " clr%.w %2\n" \ -- " swap %2\n" \ -- " swap %3\n" \ -- " clr%.w %3\n" \ -- " add%.l %3,%1\n" \ -- " addx%.l %2,%0\n" \ -- " | End inlined umul_ppmm" \ -- : "=&d" (xh), "=&d" (xl), \ -- "=d" (__umul_tmp1), "=&d" (__umul_tmp2) \ -- : "%2" ((USItype)(a)), "d" ((USItype)(b))); \ -- } while (0) --#define UMUL_TIME 100 --#define UDIV_TIME 400 --#endif /* not mc68020 */ -- /* The '020, '030, '040 and '060 have bitfield insns. -- GCC 3.4 defines __mc68020__ when in CPU32 mode, check for __mcpu32__ to -- exclude bfffo on that chip (bitfield insns not available). */ --#if (defined (__mc68020__) || defined (mc68020) \ -- || defined (__mc68030__) || defined (mc68030) \ -- || defined (__mc68040__) || defined (mc68040) \ -- || defined (__mc68060__) || defined (mc68060) \ -- || defined (__NeXT__)) \ -- && ! defined (__mcpu32__) --#define count_leading_zeros(count, x) \ -- __asm__ ("bfffo %1{%b2:%b2},%0" \ -- : "=d" (count) \ -- : "od" ((USItype) (x)), "n" (0)) --#define COUNT_LEADING_ZEROS_0 32 --#endif --#endif /* mc68000 */ -- --#if defined (__m88000__) && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("addu.co %1,%r4,%r5\n\taddu.ci %0,%r2,%r3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rJ" (ah), "rJ" (bh), "%rJ" (al), "rJ" (bl)) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("subu.co %1,%r4,%r5\n\tsubu.ci %0,%r2,%r3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rJ" (ah), "rJ" (bh), "rJ" (al), "rJ" (bl)) --#define count_leading_zeros(count, x) \ -- do { \ -- USItype __cbtmp; \ -- __asm__ ("ff1 %0,%1" : "=r" (__cbtmp) : "r" (x)); \ -- (count) = __cbtmp ^ 31; \ -- } while (0) --#define COUNT_LEADING_ZEROS_0 63 /* sic */ --#if defined (__m88110__) --#define umul_ppmm(wh, wl, u, v) \ -- do { \ -- union {UDItype __ll; \ -- struct {USItype __h, __l;} __i; \ -- } __x; \ -- __asm__ ("mulu.d %0,%1,%2" : "=r" (__x.__ll) : "r" (u), "r" (v)); \ -- (wh) = __x.__i.__h; \ -- (wl) = __x.__i.__l; \ -- } while (0) --#define udiv_qrnnd(q, r, n1, n0, d) \ -- ({union {UDItype __ll; \ -- struct {USItype __h, __l;} __i; \ -- } __x, __q; \ -- __x.__i.__h = (n1); __x.__i.__l = (n0); \ -- __asm__ ("divu.d %0,%1,%2" \ -- : "=r" (__q.__ll) : "r" (__x.__ll), "r" (d)); \ -- (r) = (n0) - __q.__l * (d); (q) = __q.__l; }) --#define UMUL_TIME 5 --#define UDIV_TIME 25 --#else --#define UMUL_TIME 17 --#define UDIV_TIME 150 --#endif /* __m88110__ */ --#endif /* __m88000__ */ -- --#if defined (__mips) && W_TYPE_SIZE == 32 --#if __GMP_GNUC_PREREQ (4,4) --#define umul_ppmm(w1, w0, u, v) \ -- do { \ -- UDItype __ll = (UDItype)(u) * (v); \ -- w1 = __ll >> 32; \ -- w0 = __ll; \ -- } while (0) --#endif --#if !defined (umul_ppmm) && __GMP_GNUC_PREREQ (2,7) --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("multu %2,%3" : "=l" (w0), "=h" (w1) : "d" (u), "d" (v)) --#endif --#if !defined (umul_ppmm) --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("multu %2,%3\n\tmflo %0\n\tmfhi %1" \ -- : "=d" (w0), "=d" (w1) : "d" (u), "d" (v)) --#endif --#define UMUL_TIME 10 --#define UDIV_TIME 100 --#endif /* __mips */ -- --#if (defined (__mips) && __mips >= 3) && W_TYPE_SIZE == 64 --#if __GMP_GNUC_PREREQ (4,4) --#define umul_ppmm(w1, w0, u, v) \ -- do { \ -- typedef unsigned int __ll_UTItype __attribute__((mode(TI))); \ -- __ll_UTItype __ll = (__ll_UTItype)(u) * (v); \ -- w1 = __ll >> 64; \ -- w0 = __ll; \ -- } while (0) --#endif --#if !defined (umul_ppmm) && __GMP_GNUC_PREREQ (2,7) --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("dmultu %2,%3" : "=l" (w0), "=h" (w1) : "d" (u), "d" (v)) --#endif --#if !defined (umul_ppmm) --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("dmultu %2,%3\n\tmflo %0\n\tmfhi %1" \ -- : "=d" (w0), "=d" (w1) : "d" (u), "d" (v)) --#endif --#define UMUL_TIME 20 --#define UDIV_TIME 140 --#endif /* __mips */ -- --#if defined (__mmix__) && W_TYPE_SIZE == 64 --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("MULU %0,%2,%3" : "=r" (w0), "=z" (w1) : "r" (u), "r" (v)) --#endif -- --#if defined (__ns32000__) && W_TYPE_SIZE == 32 --#define umul_ppmm(w1, w0, u, v) \ -- ({union {UDItype __ll; \ -- struct {USItype __l, __h;} __i; \ -- } __x; \ -- __asm__ ("meid %2,%0" \ -- : "=g" (__x.__ll) \ -- : "%0" ((USItype)(u)), "g" ((USItype)(v))); \ -- (w1) = __x.__i.__h; (w0) = __x.__i.__l;}) --#define __umulsidi3(u, v) \ -- ({UDItype __w; \ -- __asm__ ("meid %2,%0" \ -- : "=g" (__w) \ -- : "%0" ((USItype)(u)), "g" ((USItype)(v))); \ -- __w; }) --#define udiv_qrnnd(q, r, n1, n0, d) \ -- ({union {UDItype __ll; \ -- struct {USItype __l, __h;} __i; \ -- } __x; \ -- __x.__i.__h = (n1); __x.__i.__l = (n0); \ -- __asm__ ("deid %2,%0" \ -- : "=g" (__x.__ll) \ -- : "0" (__x.__ll), "g" ((USItype)(d))); \ -- (r) = __x.__i.__l; (q) = __x.__i.__h; }) --#define count_trailing_zeros(count,x) \ -- do { \ -- __asm__ ("ffsd %2,%0" \ -- : "=r" (count) \ -- : "0" ((USItype) 0), "r" ((USItype) (x))); \ -- } while (0) --#endif /* __ns32000__ */ -- -- /* In the past we had a block of various #defines tested -- _ARCH_PPC - AIX -- _ARCH_PWR - AIX -- __powerpc__ - gcc -- __POWERPC__ - BEOS -- __ppc__ - Darwin -- PPC - old gcc, GNU/Linux, SysV -- The plain PPC test was not good for vxWorks, since PPC is defined on all -- CPUs there (eg. m68k too), as a constant one is expected to compare -- CPU_FAMILY against. -- -- At any rate, this was pretty unattractive and a bit fragile. The use of -- HAVE_HOST_CPU_FAMILY is designed to cut through it all and be sure of -- getting the desired effect. -- -- ENHANCE-ME: We should test _IBMR2 here when we add assembly support for -- the system vendor compilers. (Is that vendor compilers with inline asm, -- or what?) */ -- --#if (HAVE_HOST_CPU_FAMILY_power || HAVE_HOST_CPU_FAMILY_powerpc) \ -- && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- do { \ -- if (__builtin_constant_p (bh) && (bh) == 0) \ -- __asm__ ("add%I4c %1,%3,%4\n\taddze %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl)); \ -- else if (__builtin_constant_p (bh) && (bh) == ~(USItype) 0) \ -- __asm__ ("add%I4c %1,%3,%4\n\taddme %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl)); \ -- else \ -- __asm__ ("add%I5c %1,%4,%5\n\tadde %0,%2,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "r" (ah), "r" (bh), "%r" (al), "rI" (bl)); \ -- } while (0) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- do { \ -- if (__builtin_constant_p (ah) && (ah) == 0) \ -- __asm__ ("subf%I3c %1,%4,%3\n\tsubfze %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl)); \ -- else if (__builtin_constant_p (ah) && (ah) == ~(USItype) 0) \ -- __asm__ ("subf%I3c %1,%4,%3\n\tsubfme %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl)); \ -- else if (__builtin_constant_p (bh) && (bh) == 0) \ -- __asm__ ("subf%I3c %1,%4,%3\n\taddme %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl)); \ -- else if (__builtin_constant_p (bh) && (bh) == ~(USItype) 0) \ -- __asm__ ("subf%I3c %1,%4,%3\n\taddze %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl)); \ -- else \ -- __asm__ ("subf%I4c %1,%5,%4\n\tsubfe %0,%3,%2" \ -- : "=r" (sh), "=&r" (sl) \ -- : "r" (ah), "r" (bh), "rI" (al), "r" (bl)); \ -- } while (0) --#define count_leading_zeros(count, x) \ -- __asm__ ("cntlzw %0,%1" : "=r" (count) : "r" (x)) --#define COUNT_LEADING_ZEROS_0 32 --#if HAVE_HOST_CPU_FAMILY_powerpc --#if __GMP_GNUC_PREREQ (4,4) --#define umul_ppmm(w1, w0, u, v) \ -- do { \ -- UDItype __ll = (UDItype)(u) * (v); \ -- w1 = __ll >> 32; \ -- w0 = __ll; \ -- } while (0) --#endif --#if !defined (umul_ppmm) --#define umul_ppmm(ph, pl, m0, m1) \ -- do { \ -- USItype __m0 = (m0), __m1 = (m1); \ -- __asm__ ("mulhwu %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \ -- (pl) = __m0 * __m1; \ -- } while (0) --#endif --#define UMUL_TIME 15 --#define smul_ppmm(ph, pl, m0, m1) \ -- do { \ -- SItype __m0 = (m0), __m1 = (m1); \ -- __asm__ ("mulhw %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \ -- (pl) = __m0 * __m1; \ -- } while (0) --#define SMUL_TIME 14 --#define UDIV_TIME 120 --#else --#define UMUL_TIME 8 --#define smul_ppmm(xh, xl, m0, m1) \ -- __asm__ ("mul %0,%2,%3" : "=r" (xh), "=q" (xl) : "r" (m0), "r" (m1)) --#define SMUL_TIME 4 --#define sdiv_qrnnd(q, r, nh, nl, d) \ -- __asm__ ("div %0,%2,%4" : "=r" (q), "=q" (r) : "r" (nh), "1" (nl), "r" (d)) --#define UDIV_TIME 100 --#endif --#endif /* 32-bit POWER architecture variants. */ -- -- /* We should test _IBMR2 here when we add assembly support for the system -- vendor compilers. */ --#if HAVE_HOST_CPU_FAMILY_powerpc && W_TYPE_SIZE == 64 --#if !defined (_LONG_LONG_LIMB) -- /* _LONG_LONG_LIMB is ABI=mode32 where adde operates on 32-bit values. So -- use adde etc only when not _LONG_LONG_LIMB. */ --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- do { \ -- if (__builtin_constant_p (bh) && (bh) == 0) \ -- __asm__ ("add%I4c %1,%3,%4\n\taddze %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl)); \ -- else if (__builtin_constant_p (bh) && (bh) == ~(UDItype) 0) \ -- __asm__ ("add%I4c %1,%3,%4\n\taddme %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl)); \ -- else \ -- __asm__ ("add%I5c %1,%4,%5\n\tadde %0,%2,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "r" (ah), "r" (bh), "%r" (al), "rI" (bl)); \ -- } while (0) -- /* We use "*rI" for the constant operand here, since with just "I", gcc barfs. -- This might seem strange, but gcc folds away the dead code late. */ --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- do { \ -- if (__builtin_constant_p (bl) && bl > -0x8000 && bl <= 0x8000) { \ -- if (__builtin_constant_p (ah) && (ah) == 0) \ -- __asm__ ("addic %1,%3,%4\n\tsubfze %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "*rI" (-bl)); \ -- else if (__builtin_constant_p (ah) && (ah) == ~(UDItype) 0) \ -- __asm__ ("addic %1,%3,%4\n\tsubfme %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "*rI" (-bl)); \ -- else if (__builtin_constant_p (bh) && (bh) == 0) \ -- __asm__ ("addic %1,%3,%4\n\taddme %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "*rI" (-bl)); \ -- else if (__builtin_constant_p (bh) && (bh) == ~(UDItype) 0) \ -- __asm__ ("addic %1,%3,%4\n\taddze %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "*rI" (-bl)); \ -- else \ -- __asm__ ("addic %1,%4,%5\n\tsubfe %0,%3,%2" \ -- : "=r" (sh), "=&r" (sl) \ -- : "r" (ah), "r" (bh), "rI" (al), "*rI" (-bl)); \ -- } else { \ -- if (__builtin_constant_p (ah) && (ah) == 0) \ -- __asm__ ("subf%I3c %1,%4,%3\n\tsubfze %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl)); \ -- else if (__builtin_constant_p (ah) && (ah) == ~(UDItype) 0) \ -- __asm__ ("subf%I3c %1,%4,%3\n\tsubfme %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl)); \ -- else if (__builtin_constant_p (bh) && (bh) == 0) \ -- __asm__ ("subf%I3c %1,%4,%3\n\taddme %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl)); \ -- else if (__builtin_constant_p (bh) && (bh) == ~(UDItype) 0) \ -- __asm__ ("subf%I3c %1,%4,%3\n\taddze %0,%2" \ -- : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl)); \ -- else \ -- __asm__ ("subf%I4c %1,%5,%4\n\tsubfe %0,%3,%2" \ -- : "=r" (sh), "=&r" (sl) \ -- : "r" (ah), "r" (bh), "rI" (al), "r" (bl)); \ -- } \ -- } while (0) --#endif /* ! _LONG_LONG_LIMB */ --#define count_leading_zeros(count, x) \ -- __asm__ ("cntlzd %0,%1" : "=r" (count) : "r" (x)) --#define COUNT_LEADING_ZEROS_0 64 --#if 0 && __GMP_GNUC_PREREQ (4,4) /* Disable, this results in libcalls! */ --#define umul_ppmm(w1, w0, u, v) \ -- do { \ -- typedef unsigned int __ll_UTItype __attribute__((mode(TI))); \ -- __ll_UTItype __ll = (__ll_UTItype)(u) * (v); \ -- w1 = __ll >> 64; \ -- w0 = __ll; \ -- } while (0) --#endif --#if !defined (umul_ppmm) --#define umul_ppmm(ph, pl, m0, m1) \ -- do { \ -- UDItype __m0 = (m0), __m1 = (m1); \ -- __asm__ ("mulhdu %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \ -- (pl) = __m0 * __m1; \ -- } while (0) --#endif --#define UMUL_TIME 15 --#define smul_ppmm(ph, pl, m0, m1) \ -- do { \ -- DItype __m0 = (m0), __m1 = (m1); \ -- __asm__ ("mulhd %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \ -- (pl) = __m0 * __m1; \ -- } while (0) --#define SMUL_TIME 14 /* ??? */ --#define UDIV_TIME 120 /* ??? */ --#endif /* 64-bit PowerPC. */ -- --#if defined (__pyr__) && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("addw %5,%1\n\taddwc %3,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \ -- "%1" ((USItype)(al)), "g" ((USItype)(bl))) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("subw %5,%1\n\tsubwb %3,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \ -- "1" ((USItype)(al)), "g" ((USItype)(bl))) -- /* This insn works on Pyramids with AP, XP, or MI CPUs, but not with SP. */ --#define umul_ppmm(w1, w0, u, v) \ -- ({union {UDItype __ll; \ -- struct {USItype __h, __l;} __i; \ -- } __x; \ -- __asm__ ("movw %1,%R0\n\tuemul %2,%0" \ -- : "=&r" (__x.__ll) \ -- : "g" ((USItype) (u)), "g" ((USItype)(v))); \ -- (w1) = __x.__i.__h; (w0) = __x.__i.__l;}) --#endif /* __pyr__ */ -- --#if defined (__ibm032__) /* RT/ROMP */ && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("a %1,%5\n\tae %0,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((USItype)(ah)), "r" ((USItype)(bh)), \ -- "%1" ((USItype)(al)), "r" ((USItype)(bl))) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("s %1,%5\n\tse %0,%3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((USItype)(ah)), "r" ((USItype)(bh)), \ -- "1" ((USItype)(al)), "r" ((USItype)(bl))) --#define smul_ppmm(ph, pl, m0, m1) \ -- __asm__ ( \ -- "s r2,r2\n" \ -- " mts r10,%2\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " m r2,%3\n" \ -- " cas %0,r2,r0\n" \ -- " mfs r10,%1" \ -- : "=r" (ph), "=r" (pl) \ -- : "%r" ((USItype)(m0)), "r" ((USItype)(m1)) \ -- : "r2") --#define UMUL_TIME 20 --#define UDIV_TIME 200 --#define count_leading_zeros(count, x) \ -- do { \ -- if ((x) >= 0x10000) \ -- __asm__ ("clz %0,%1" \ -- : "=r" (count) : "r" ((USItype)(x) >> 16)); \ -- else \ -- { \ -- __asm__ ("clz %0,%1" \ -- : "=r" (count) : "r" ((USItype)(x))); \ -- (count) += 16; \ -- } \ -- } while (0) --#endif /* RT/ROMP */ -- --#if (defined (__SH2__) || defined (__SH3__) || defined (__SH4__)) && W_TYPE_SIZE == 32 --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("dmulu.l %2,%3\n\tsts macl,%1\n\tsts mach,%0" \ -- : "=r" (w1), "=r" (w0) : "r" (u), "r" (v) : "macl", "mach") --#define UMUL_TIME 5 --#endif -- --#if defined (__sparc__) && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("addcc %r4,%5,%1\n\taddx %r2,%3,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rJ" (ah), "rI" (bh),"%rJ" (al), "rI" (bl) \ -- __CLOBBER_CC) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("subcc %r4,%5,%1\n\tsubx %r2,%3,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rJ" (ah), "rI" (bh), "rJ" (al), "rI" (bl) \ -- __CLOBBER_CC) -- /* FIXME: When gcc -mcpu=v9 is used on solaris, gcc/config/sol2-sld-64.h -- doesn't define anything to indicate that to us, it only sets __sparcv8. */ --#if defined (__sparc_v9__) || defined (__sparcv9) -- /* Perhaps we should use floating-point operations here? */ --#if 0 -- /* Triggers a bug making mpz/tests/t-gcd.c fail. -- Perhaps we simply need explicitly zero-extend the inputs? */ --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("mulx %2,%3,%%g1; srl %%g1,0,%1; srlx %%g1,32,%0" : \ -- "=r" (w1), "=r" (w0) : "r" (u), "r" (v) : "g1") --#else -- /* Use v8 umul until above bug is fixed. */ --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("umul %2,%3,%1;rd %%y,%0" : "=r" (w1), "=r" (w0) : "r" (u), "r" (v)) --#endif -- /* Use a plain v8 divide for v9. */ --#define udiv_qrnnd(q, r, n1, n0, d) \ -- do { \ -- USItype __q; \ -- __asm__ ("mov %1,%%y;nop;nop;nop;udiv %2,%3,%0" \ -- : "=r" (__q) : "r" (n1), "r" (n0), "r" (d)); \ -- (r) = (n0) - __q * (d); \ -- (q) = __q; \ -- } while (0) --#else --#if defined (__sparc_v8__) /* gcc normal */ \ -- || defined (__sparcv8) /* gcc solaris */ \ -- || HAVE_HOST_CPU_supersparc -- /* Don't match immediate range because, 1) it is not often useful, -- 2) the 'I' flag thinks of the range as a 13 bit signed interval, -- while we want to match a 13 bit interval, sign extended to 32 bits, -- but INTERPRETED AS UNSIGNED. */ --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("umul %2,%3,%1;rd %%y,%0" : "=r" (w1), "=r" (w0) : "r" (u), "r" (v)) --#define UMUL_TIME 5 -- --#if HAVE_HOST_CPU_supersparc --#define UDIV_TIME 60 /* SuperSPARC timing */ --#else -- /* Don't use this on SuperSPARC because its udiv only handles 53 bit -- dividends and will trap to the kernel for the rest. */ --#define udiv_qrnnd(q, r, n1, n0, d) \ -- do { \ -- USItype __q; \ -- __asm__ ("mov %1,%%y;nop;nop;nop;udiv %2,%3,%0" \ -- : "=r" (__q) : "r" (n1), "r" (n0), "r" (d)); \ -- (r) = (n0) - __q * (d); \ -- (q) = __q; \ -- } while (0) --#define UDIV_TIME 25 --#endif /* HAVE_HOST_CPU_supersparc */ -- --#else /* ! __sparc_v8__ */ --#if defined (__sparclite__) -- /* This has hardware multiply but not divide. It also has two additional -- instructions scan (ffs from high bit) and divscc. */ --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("umul %2,%3,%1;rd %%y,%0" : "=r" (w1), "=r" (w0) : "r" (u), "r" (v)) --#define UMUL_TIME 5 --#define udiv_qrnnd(q, r, n1, n0, d) \ -- __asm__ ("! Inlined udiv_qrnnd\n" \ -- " wr %%g0,%2,%%y ! Not a delayed write for sparclite\n" \ -- " tst %%g0\n" \ -- " divscc %3,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%%g1\n" \ -- " divscc %%g1,%4,%0\n" \ -- " rd %%y,%1\n" \ -- " bl,a 1f\n" \ -- " add %1,%4,%1\n" \ -- "1: ! End of inline udiv_qrnnd" \ -- : "=r" (q), "=r" (r) : "r" (n1), "r" (n0), "rI" (d) \ -- : "%g1" __AND_CLOBBER_CC) --#define UDIV_TIME 37 --#define count_leading_zeros(count, x) \ -- __asm__ ("scan %1,1,%0" : "=r" (count) : "r" (x)) -- /* Early sparclites return 63 for an argument of 0, but they warn that future -- implementations might change this. Therefore, leave COUNT_LEADING_ZEROS_0 -- undefined. */ --#endif /* __sparclite__ */ --#endif /* __sparc_v8__ */ --#endif /* __sparc_v9__ */ -- /* Default to sparc v7 versions of umul_ppmm and udiv_qrnnd. */ --#ifndef umul_ppmm --#define umul_ppmm(w1, w0, u, v) \ -- __asm__ ("! Inlined umul_ppmm\n" \ -- " wr %%g0,%2,%%y ! SPARC has 0-3 delay insn after a wr\n" \ -- " sra %3,31,%%g2 ! Don't move this insn\n" \ -- " and %2,%%g2,%%g2 ! Don't move this insn\n" \ -- " andcc %%g0,0,%%g1 ! Don't move this insn\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,%3,%%g1\n" \ -- " mulscc %%g1,0,%%g1\n" \ -- " add %%g1,%%g2,%0\n" \ -- " rd %%y,%1" \ -- : "=r" (w1), "=r" (w0) : "%rI" (u), "r" (v) \ -- : "%g1", "%g2" __AND_CLOBBER_CC) --#define UMUL_TIME 39 /* 39 instructions */ --#endif --#ifndef udiv_qrnnd --#ifndef LONGLONG_STANDALONE --#define udiv_qrnnd(q, r, n1, n0, d) \ -- do { UWtype __r; \ -- (q) = __MPN(udiv_qrnnd) (&__r, (n1), (n0), (d)); \ -- (r) = __r; \ -- } while (0) -- extern UWtype __MPN(udiv_qrnnd) (UWtype *, UWtype, UWtype, UWtype); --#ifndef UDIV_TIME --#define UDIV_TIME 140 --#endif --#endif /* LONGLONG_STANDALONE */ --#endif /* udiv_qrnnd */ --#endif /* __sparc__ */ -- --#if defined (__sparc__) && W_TYPE_SIZE == 64 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ( \ -- "addcc %r4,%5,%1\n" \ -- " addccc %r6,%7,%%g0\n" \ -- " addc %r2,%3,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rJ" (ah), "rI" (bh), "%rJ" (al), "rI" (bl), \ -- "%rJ" ((al) >> 32), "rI" ((bl) >> 32) \ -- __CLOBBER_CC) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ( \ -- "subcc %r4,%5,%1\n" \ -- " subccc %r6,%7,%%g0\n" \ -- " subc %r2,%3,%0" \ -- : "=r" (sh), "=&r" (sl) \ -- : "rJ" (ah), "rI" (bh), "rJ" (al), "rI" (bl), \ -- "rJ" ((al) >> 32), "rI" ((bl) >> 32) \ -- __CLOBBER_CC) --#endif -- --#if (defined (__vax) || defined (__vax__)) && W_TYPE_SIZE == 32 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("addl2 %5,%1\n\tadwc %3,%0" \ -- : "=g" (sh), "=&g" (sl) \ -- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \ -- "%1" ((USItype)(al)), "g" ((USItype)(bl))) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("subl2 %5,%1\n\tsbwc %3,%0" \ -- : "=g" (sh), "=&g" (sl) \ -- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \ -- "1" ((USItype)(al)), "g" ((USItype)(bl))) --#define smul_ppmm(xh, xl, m0, m1) \ -- do { \ -- union {UDItype __ll; \ -- struct {USItype __l, __h;} __i; \ -- } __x; \ -- USItype __m0 = (m0), __m1 = (m1); \ -- __asm__ ("emul %1,%2,$0,%0" \ -- : "=g" (__x.__ll) : "g" (__m0), "g" (__m1)); \ -- (xh) = __x.__i.__h; (xl) = __x.__i.__l; \ -- } while (0) --#define sdiv_qrnnd(q, r, n1, n0, d) \ -- do { \ -- union {DItype __ll; \ -- struct {SItype __l, __h;} __i; \ -- } __x; \ -- __x.__i.__h = n1; __x.__i.__l = n0; \ -- __asm__ ("ediv %3,%2,%0,%1" \ -- : "=g" (q), "=g" (r) : "g" (__x.__ll), "g" (d)); \ -- } while (0) --#if 0 -- /* FIXME: This instruction appears to be unimplemented on some systems (vax -- 8800 maybe). */ --#define count_trailing_zeros(count,x) \ -- do { \ -- __asm__ ("ffs 0, 31, %1, %0" \ -- : "=g" (count) \ -- : "g" ((USItype) (x))); \ -- } while (0) --#endif --#endif /* vax */ -- --#if defined (__z8000__) && W_TYPE_SIZE == 16 --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -- __asm__ ("add %H1,%H5\n\tadc %H0,%H3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((unsigned int)(ah)), "r" ((unsigned int)(bh)), \ -- "%1" ((unsigned int)(al)), "rQR" ((unsigned int)(bl))) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -- __asm__ ("sub %H1,%H5\n\tsbc %H0,%H3" \ -- : "=r" (sh), "=&r" (sl) \ -- : "0" ((unsigned int)(ah)), "r" ((unsigned int)(bh)), \ -- "1" ((unsigned int)(al)), "rQR" ((unsigned int)(bl))) --#define umul_ppmm(xh, xl, m0, m1) \ -- do { \ -- union {long int __ll; \ -- struct {unsigned int __h, __l;} __i; \ -- } __x; \ -- unsigned int __m0 = (m0), __m1 = (m1); \ -- __asm__ ("mult %S0,%H3" \ -- : "=r" (__x.__i.__h), "=r" (__x.__i.__l) \ -- : "%1" (m0), "rQR" (m1)); \ -- (xh) = __x.__i.__h; (xl) = __x.__i.__l; \ -- (xh) += ((((signed int) __m0 >> 15) & __m1) \ -- + (((signed int) __m1 >> 15) & __m0)); \ -- } while (0) --#endif /* __z8000__ */ -- --#endif /* __GNUC__ */ -- --#endif /* NO_ASM */ -- - - /* FIXME: "sidi" here is highly doubtful, should sometimes be "diti". */ --#if !defined (umul_ppmm) && defined (__umulsidi3) --#define umul_ppmm(ph, pl, m0, m1) \ -+#if !defined (recint_umul_ppmm) && defined (__umulsidi3) -+#define recint_umul_ppmm(ph, pl, m0, m1) \ - { \ - UDWtype __ll = __umulsidi3 (m0, m1); \ - ph = (UWtype) (__ll >> W_TYPE_SIZE); \ -@@ -1838,75 +179,75 @@ __asm__ ("mlr\t%0,%3" \ - #if !defined (__umulsidi3) - #define __umulsidi3(u, v) \ - ({UWtype __hi, __lo; \ -- umul_ppmm (__hi, __lo, u, v); \ -+ recint_umul_ppmm (__hi, __lo, u, v); \ - ((UDWtype) __hi << W_TYPE_SIZE) | __lo; }) - #endif - - -- /* Use mpn_umul_ppmm or mpn_udiv_qrnnd functions, if they exist. The "_r" -+ /* Use mpn_recint_umul_ppmm or mpn_recint_udiv_qrnnd functions, if they exist. The "_r" - forms have "reversed" arguments, meaning the pointer is last, which - sometimes allows better parameter passing, in particular on 64-bit - hppa. */ - --#define mpn_umul_ppmm __MPN(umul_ppmm) --extern UWtype mpn_umul_ppmm (UWtype *, UWtype, UWtype); -+#define mpn_recint_umul_ppmm __MPN(recint_umul_ppmm) -+extern UWtype mpn_recint_umul_ppmm (UWtype *, UWtype, UWtype); - --#if ! defined (umul_ppmm) && HAVE_NATIVE_mpn_umul_ppmm \ -+#if ! defined (recint_umul_ppmm) && HAVE_NATIVE_mpn_recint_umul_ppmm \ - && ! defined (LONGLONG_STANDALONE) --#define umul_ppmm(wh, wl, u, v) \ -+#define recint_umul_ppmm(wh, wl, u, v) \ - do { \ -- UWtype __umul_ppmm__p0; \ -- (wh) = mpn_umul_ppmm (&__umul_ppmm__p0, (UWtype) (u), (UWtype) (v)); \ -- (wl) = __umul_ppmm__p0; \ -+ UWtype __recint_umul_ppmm__p0; \ -+ (wh) = mpn_recint_umul_ppmm (&__recint_umul_ppmm__p0, (UWtype) (u), (UWtype) (v)); \ -+ (wl) = __recint_umul_ppmm__p0; \ - } while (0) - #endif - --#define mpn_umul_ppmm_r __MPN(umul_ppmm_r) --extern UWtype mpn_umul_ppmm_r (UWtype, UWtype, UWtype *); -+#define mpn_recint_umul_ppmm_r __MPN(recint_umul_ppmm_r) -+extern UWtype mpn_recint_umul_ppmm_r (UWtype, UWtype, UWtype *); - --#if ! defined (umul_ppmm) && HAVE_NATIVE_mpn_umul_ppmm_r \ -+#if ! defined (recint_umul_ppmm) && HAVE_NATIVE_mpn_recint_umul_ppmm_r \ - && ! defined (LONGLONG_STANDALONE) --#define umul_ppmm(wh, wl, u, v) \ -+#define recint_umul_ppmm(wh, wl, u, v) \ - do { \ -- UWtype __umul_ppmm__p0; \ -- (wh) = mpn_umul_ppmm_r ((UWtype) (u), (UWtype) (v), &__umul_ppmm__p0); \ -- (wl) = __umul_ppmm__p0; \ -+ UWtype __recint_umul_ppmm__p0; \ -+ (wh) = mpn_recint_umul_ppmm_r ((UWtype) (u), (UWtype) (v), &__recint_umul_ppmm__p0); \ -+ (wl) = __recint_umul_ppmm__p0; \ - } while (0) - #endif - --#define mpn_udiv_qrnnd __MPN(udiv_qrnnd) --extern UWtype mpn_udiv_qrnnd (UWtype *, UWtype, UWtype, UWtype); -+#define mpn_recint_udiv_qrnnd __MPN(recint_udiv_qrnnd) -+extern UWtype mpn_recint_udiv_qrnnd (UWtype *, UWtype, UWtype, UWtype); - --#if ! defined (udiv_qrnnd) && HAVE_NATIVE_mpn_udiv_qrnnd \ -+#if ! defined (recint_udiv_qrnnd) && HAVE_NATIVE_mpn_recint_udiv_qrnnd \ - && ! defined (LONGLONG_STANDALONE) --#define udiv_qrnnd(q, r, n1, n0, d) \ -+#define recint_udiv_qrnnd(q, r, n1, n0, d) \ - do { \ -- UWtype __udiv_qrnnd__r; \ -- (q) = mpn_udiv_qrnnd (&__udiv_qrnnd__r, \ -+ UWtype __recint_udiv_qrnnd__r; \ -+ (q) = mpn_recint_udiv_qrnnd (&__recint_udiv_qrnnd__r, \ - (UWtype) (n1), (UWtype) (n0), (UWtype) d); \ -- (r) = __udiv_qrnnd__r; \ -+ (r) = __recint_udiv_qrnnd__r; \ - } while (0) - #endif - --#define mpn_udiv_qrnnd_r __MPN(udiv_qrnnd_r) --extern UWtype mpn_udiv_qrnnd_r (UWtype, UWtype, UWtype, UWtype *); -+#define mpn_recint_udiv_qrnnd_r __MPN(recint_udiv_qrnnd_r) -+extern UWtype mpn_recint_udiv_qrnnd_r (UWtype, UWtype, UWtype, UWtype *); - --#if ! defined (udiv_qrnnd) && HAVE_NATIVE_mpn_udiv_qrnnd_r \ -+#if ! defined (recint_udiv_qrnnd) && HAVE_NATIVE_mpn_recint_udiv_qrnnd_r \ - && ! defined (LONGLONG_STANDALONE) --#define udiv_qrnnd(q, r, n1, n0, d) \ -+#define recint_udiv_qrnnd(q, r, n1, n0, d) \ - do { \ -- UWtype __udiv_qrnnd__r; \ -- (q) = mpn_udiv_qrnnd_r ((UWtype) (n1), (UWtype) (n0), (UWtype) d, \ -- &__udiv_qrnnd__r); \ -- (r) = __udiv_qrnnd__r; \ -+ UWtype __recint_udiv_qrnnd__r; \ -+ (q) = mpn_recint_udiv_qrnnd_r ((UWtype) (n1), (UWtype) (n0), (UWtype) d, \ -+ &__recint_udiv_qrnnd__r); \ -+ (r) = __recint_udiv_qrnnd__r; \ - } while (0) - #endif - - - /* If this machine has no inline assembler, use C macros. */ - --#if !defined (add_ssaaaa) --#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ -+#if !defined (recint_add_ssaaaa) -+#define recint_add_ssaaaa(sh, sl, ah, al, bh, bl) \ - do { \ - UWtype __x; \ - __x = (al) + (bl); \ -@@ -1915,8 +256,8 @@ extern UWtype mpn_udiv_qrnnd_r (UWtype, UWtype, UWtype, UWtype *); - } while (0) - #endif - --#if !defined (sub_ddmmss) --#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ -+#if !defined (recint_sub_ddmmss) -+#define recint_sub_ddmmss(sh, sl, ah, al, bh, bl) \ - do { \ - UWtype __x; \ - __x = (al) - (bl); \ -@@ -1925,20 +266,20 @@ extern UWtype mpn_udiv_qrnnd_r (UWtype, UWtype, UWtype, UWtype *); - } while (0) - #endif - -- /* If we lack umul_ppmm but have smul_ppmm, define umul_ppmm in terms of -- smul_ppmm. */ --#if !defined (umul_ppmm) && defined (smul_ppmm) --#define umul_ppmm(w1, w0, u, v) \ -+ /* If we lack recint_umul_ppmm but have recint_smul_ppmm, define recint_umul_ppmm in terms of -+ recint_smul_ppmm. */ -+#if !defined (recint_umul_ppmm) && defined (recint_smul_ppmm) -+#define recint_umul_ppmm(w1, w0, u, v) \ - do { \ - UWtype __w1; \ - UWtype __xm0 = (u), __xm1 = (v); \ -- smul_ppmm (__w1, w0, __xm0, __xm1); \ -+ recint_smul_ppmm (__w1, w0, __xm0, __xm1); \ - (w1) = __w1 + (-(__xm0 >> (W_TYPE_SIZE - 1)) & __xm1) \ - + (-(__xm1 >> (W_TYPE_SIZE - 1)) & __xm0); \ - } while (0) - #endif - -- /* If we still don't have umul_ppmm, define it using plain C. -+ /* If we still don't have recint_umul_ppmm, define it using plain C. - - For reference, when this code is used for squaring (ie. u and v identical - expressions), gcc recognises __x1 and __x2 are the same and generates 3 -@@ -1948,8 +289,8 @@ extern UWtype mpn_udiv_qrnnd_r (UWtype, UWtype, UWtype, UWtype *); - performance problems than a couple of extra instructions on the diagonal - of sqr_basecase. */ - --#if !defined (umul_ppmm) --#define umul_ppmm(w1, w0, u, v) \ -+#if !defined (recint_umul_ppmm) -+#define recint_umul_ppmm(w1, w0, u, v) \ - do { \ - UWtype __x0, __x1, __x2, __x3; \ - UHWtype __ul, __vl, __uh, __vh; \ -@@ -1975,21 +316,21 @@ extern UWtype mpn_udiv_qrnnd_r (UWtype, UWtype, UWtype, UWtype *); - } while (0) - #endif - -- /* If we don't have smul_ppmm, define it using umul_ppmm (which surely will -+ /* If we don't have recint_smul_ppmm, define it using recint_umul_ppmm (which surely will - exist in one form or another. */ --#if !defined (smul_ppmm) --#define smul_ppmm(w1, w0, u, v) \ -+#if !defined (recint_smul_ppmm) -+#define recint_smul_ppmm(w1, w0, u, v) \ - do { \ - UWtype __w1; \ - UWtype __xm0 = (u), __xm1 = (v); \ -- umul_ppmm (__w1, w0, __xm0, __xm1); \ -+ recint_umul_ppmm (__w1, w0, __xm0, __xm1); \ - (w1) = __w1 - (-(__xm0 >> (W_TYPE_SIZE - 1)) & __xm1) \ - - (-(__xm1 >> (W_TYPE_SIZE - 1)) & __xm0); \ - } while (0) - #endif - - /* Define this unconditionally, so it can be used for debugging. */ --#define __udiv_qrnnd_c(q, r, n1, n0, d) \ -+#define __recint_udiv_qrnnd_c(q, r, n1, n0, d) \ - do { \ - UWtype __d1, __d0, __q1, __q0, __r1, __r0, __m; \ - \ -@@ -2026,26 +367,27 @@ extern UWtype mpn_udiv_qrnnd_r (UWtype, UWtype, UWtype, UWtype *); - (r) = __r0; \ - } while (0) - -- /* If the processor has no udiv_qrnnd but sdiv_qrnnd, go through -+ /* If the processor has no recint_udiv_qrnnd but sdiv_qrnnd, go through - __udiv_w_sdiv (defined in libgcc or elsewhere). */ --#if !defined (udiv_qrnnd) && defined (sdiv_qrnnd) --#define udiv_qrnnd(q, r, nh, nl, d) \ -- do { \ -- UWtype __r; \ -- (q) = __MPN(udiv_w_sdiv) (&__r, nh, nl, d); \ -- (r) = __r; \ -- } while (0) --UWtype __MPN(udiv_w_sdiv) (UWtype *, UWtype, UWtype, UWtype); --#endif -- -- /* If udiv_qrnnd was not defined for this processor, use __udiv_qrnnd_c. */ --#if !defined (udiv_qrnnd) -+/* -+#if !defined (recint_udiv_qrnnd) && defined (sdiv_qrnnd) -+ #define recint_udiv_qrnnd(q, r, nh, nl, d) \ -+ do { \ -+ UWtype __r; \ -+ (q) = __MPN(udiv_w_sdiv) (&__r, nh, nl, d); \ -+ (r) = __r; \ -+ } while (0) -+ UWtype __MPN(udiv_w_sdiv) (UWtype *, UWtype, UWtype, UWtype); -+ #endif -+*/ -+ /* If recint_udiv_qrnnd was not defined for this processor, use __recint_udiv_qrnnd_c. */ -+#if !defined (recint_udiv_qrnnd) - #define UDIV_NEEDS_NORMALIZATION 1 --#define udiv_qrnnd __udiv_qrnnd_c -+#define recint_udiv_qrnnd __recint_udiv_qrnnd_c - #endif - --#if !defined (count_leading_zeros) --#define count_leading_zeros(count, x) \ -+#if !defined (recint_count_leading_zeros) -+#define recint_count_leading_zeros(count, x) \ - do { \ - UWtype __xr = (x); \ - UWtype __a; \ -@@ -2068,35 +410,35 @@ UWtype __MPN(udiv_w_sdiv) (UWtype *, UWtype, UWtype, UWtype); - (count) = W_TYPE_SIZE + 1 - __a - __clz_tab[__xr >> __a]; \ - } while (0) - /* This version gives a well-defined value for zero. */ --#define COUNT_LEADING_ZEROS_0 (W_TYPE_SIZE - 1) --#define COUNT_LEADING_ZEROS_NEED_CLZ_TAB --#define COUNT_LEADING_ZEROS_SLOW -+#define RECINT_COUNT_LEADING_ZEROS_0 (W_TYPE_SIZE - 1) -+#define RECINT_COUNT_LEADING_ZEROS_NEED_CLZ_TAB -+#define RECINT_COUNT_LEADING_ZEROS_SLOW - #endif - - /* clz_tab needed by mpn/x86/pentium/mod_1.asm in a fat binary */ - #if HAVE_HOST_CPU_FAMILY_x86 && WANT_FAT_BINARY --#define COUNT_LEADING_ZEROS_NEED_CLZ_TAB -+#define RECINT_COUNT_LEADING_ZEROS_NEED_CLZ_TAB - #endif - --#ifdef COUNT_LEADING_ZEROS_NEED_CLZ_TAB -+#ifdef RECINT_COUNT_LEADING_ZEROS_NEED_CLZ_TAB - extern const unsigned char __clz_tab[129]; - #endif - --#if !defined (count_trailing_zeros) --#if !defined (COUNT_LEADING_ZEROS_SLOW) -- /* Define count_trailing_zeros using an asm count_leading_zeros. */ --#define count_trailing_zeros(count, x) \ -+#if !defined (recint_count_trailing_zeros) -+#if !defined (RECINT_COUNT_LEADING_ZEROS_SLOW) -+ /* Define recint_count_trailing_zeros using an asm recint_count_leading_zeros. */ -+#define recint_count_trailing_zeros(count, x) \ - do { \ - UWtype __ctz_x = (x); \ - UWtype __ctz_c; \ -- count_leading_zeros (__ctz_c, __ctz_x & -__ctz_x); \ -+ recint_count_leading_zeros (__ctz_c, __ctz_x & -__ctz_x); \ - (count) = W_TYPE_SIZE - 1 - __ctz_c; \ - } while (0) - #else -- /* Define count_trailing_zeros in plain C, assuming small counts are common. -- We use clz_tab without ado, since the C count_leading_zeros above will have -+ /* Define recint_count_trailing_zeros in plain C, assuming small counts are common. -+ We use clz_tab without ado, since the C recint_count_leading_zeros above will have - pulled it in. */ --#define count_trailing_zeros(count, x) \ -+#define recint_count_trailing_zeros(count, x) \ - do { \ - UWtype __ctz_x = (x); \ - int __ctz_c; \ -@@ -2122,7 +464,7 @@ extern const unsigned char __clz_tab[129]; - #define UDIV_NEEDS_NORMALIZATION 0 - #endif - -- /* Whether udiv_qrnnd is actually implemented with udiv_qrnnd_preinv, and -+ /* Whether recint_udiv_qrnnd is actually implemented with recint_udiv_qrnnd_preinv, and - that hence the latter should always be used. */ - #ifndef UDIV_PREINV_ALWAYS - #define UDIV_PREINV_ALWAYS 0 -@@ -2138,4 +480,3 @@ extern const unsigned char __clz_tab[129]; - #endif - /* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - // vim:sts=4:sw=4:ts=4:et:sr:cino=>s,f0,{0,g0,(0,\:0,t0,+0,=s --#endif -diff --git a/src/kernel/recint/ruadd.h b/src/kernel/recint/ruadd.h -index 01d4073..deed001 100644 ---- a/src/kernel/recint/ruadd.h -+++ b/src/kernel/recint/ruadd.h -@@ -190,7 +190,7 @@ namespace RecInt - template<> - inline void add(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c) { - auto bp(b); -- add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); - r = (a < bp); - } - #endif -@@ -219,7 +219,7 @@ namespace RecInt - template<> - inline void add(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) { - auto bp(b); -- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); - r = (a < bp); - } - #endif -@@ -245,7 +245,7 @@ namespace RecInt - #else - template<> - inline void add(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c) { -- add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); - } - #endif - template<> -@@ -268,7 +268,7 @@ namespace RecInt - #else - template<> - inline void add(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) { -- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); - } - #endif - template<> -@@ -288,7 +288,7 @@ namespace RecInt - // TODO Use __RECINT_USE_FAST_128 here too - template - inline __RECINT_IS_ARITH(T, void) add(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const T& c) { -- add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, (UWtype)(c)); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, (UWtype)(c)); - r = (a < c); - } - template -@@ -306,7 +306,7 @@ namespace RecInt - } - template - inline __RECINT_IS_ARITH(T, void) add(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const T& b) { -- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, (UWtype)(b)); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, (UWtype)(b)); - r = (a < b); - } - template -@@ -345,7 +345,7 @@ namespace RecInt - #else - template<> - inline void add_1(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) { -- add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1); - r = (a == 0); - } - #endif -@@ -371,7 +371,7 @@ namespace RecInt - #else - template<> - inline void add_1(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a) { -- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); - r = (a == 0); - } - #endif -@@ -396,7 +396,7 @@ namespace RecInt - #else - template<> - inline void add_1(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) { -- add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1); - } - #endif - template<> -@@ -419,7 +419,7 @@ namespace RecInt - #else - template<> - inline void add_1(ruint<__RECINT_LIMB_SIZE+1>& a) { -- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); - } - #endif - template<> -@@ -452,9 +452,9 @@ namespace RecInt - template<> - inline void add_wc(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c, const bool& cy) { - auto bp(b); -- add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); - if (cy) { -- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); - r = (a <= bp); - } else { - r = (a < bp); -@@ -496,9 +496,9 @@ namespace RecInt - template<> - inline void add_wc(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const bool& cy) { - auto bp(b); -- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); - if (cy) { -- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); - r = (a <= bp); - } else { - r = (a < bp); -@@ -533,8 +533,8 @@ namespace RecInt - #else - template<> - inline void add_wc(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c, const bool& cy) { -- add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); -- if (cy) add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); -+ if (cy) recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); - } - #endif - template<> -@@ -557,8 +557,8 @@ namespace RecInt - #else - template<> - inline void add_wc(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const bool& cy) { -- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); -- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, cy); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); -+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, cy); - } - #endif - template<> -diff --git a/src/kernel/recint/ruaddmul.h b/src/kernel/recint/ruaddmul.h -index e3c8154..365340e 100644 ---- a/src/kernel/recint/ruaddmul.h -+++ b/src/kernel/recint/ruaddmul.h -@@ -115,8 +115,8 @@ namespace RecInt - template <> - inline void laddmul(bool& r, ruint<__RECINT_LIMB_SIZE>& ah, ruint<__RECINT_LIMB_SIZE>& al, const ruint<__RECINT_LIMB_SIZE>& b, const ruint<__RECINT_LIMB_SIZE>& c, const ruint<__RECINT_LIMB_SIZE>& d) { - auto dp(d.Value); -- umul_ppmm(ah.Value, al.Value, b.Value, c.Value); -- add_ssaaaa(ah.Value, al.Value, ah.Value, al.Value, 0, dp); -+ recint_umul_ppmm(ah.Value, al.Value, b.Value, c.Value); -+ recint_add_ssaaaa(ah.Value, al.Value, ah.Value, al.Value, 0, dp); - r = ((ah.Value == 0) && (al.Value < dp)); - } - -@@ -156,8 +156,8 @@ namespace RecInt - template <> - inline void laddmul(ruint<__RECINT_LIMB_SIZE>& ah, ruint<__RECINT_LIMB_SIZE>& al, const ruint<__RECINT_LIMB_SIZE>& b, const ruint<__RECINT_LIMB_SIZE>& c, const ruint<__RECINT_LIMB_SIZE>& d) { - auto dp(d.Value); -- umul_ppmm(ah.Value, al.Value, b.Value, c.Value); -- add_ssaaaa(ah.Value, al.Value, ah.Value, al.Value, 0, dp); -+ recint_umul_ppmm(ah.Value, al.Value, b.Value, c.Value); -+ recint_add_ssaaaa(ah.Value, al.Value, ah.Value, al.Value, 0, dp); - } - - // a = b*c + d (r stores the carry) -@@ -202,8 +202,8 @@ namespace RecInt - auto dph(d.High.Value); - auto dpl(d.Low.Value); - -- umul_ppmm(ah.Value, al.Value, b.Value, c.Value); -- add_ssaaaa(ah.Value, al.Value, ah.Value, al.Value, dph, dpl); -+ recint_umul_ppmm(ah.Value, al.Value, b.Value, c.Value); -+ recint_add_ssaaaa(ah.Value, al.Value, ah.Value, al.Value, dph, dpl); - r = ((ah.Value < dph) || ((ah.Value == dph) && (al.Value < dpl))); - } - -diff --git a/src/kernel/recint/rudiv.h b/src/kernel/recint/rudiv.h -index 83d2129..6151e22 100644 ---- a/src/kernel/recint/rudiv.h -+++ b/src/kernel/recint/rudiv.h -@@ -211,7 +211,7 @@ namespace RecInt - bool ret = false; - - if (a2.Value < b1.Value) { -- udiv_qrnnd(q.Value, c, a2.Value, a1.Value, b1.Value); -+ recint_udiv_qrnnd(q.Value, c, a2.Value, a1.Value, b1.Value); - } else { - q.Value = __RECINT_MINUSONE; - c = a1.Value + b1.Value; -@@ -219,8 +219,8 @@ namespace RecInt - ret = true; - } - -- umul_ppmm(d1, d0, q.Value, b0.Value); -- sub_ddmmss(r1.Value, r0.Value, c, a0.Value, d1, d0); -+ recint_umul_ppmm(d1, d0, q.Value, b0.Value); -+ recint_sub_ddmmss(r1.Value, r0.Value, c, a0.Value, d1, d0); - - if (!ret && ((d1 > c) || ((d1 == c) && (d0 > a0.Value)))) { - q.Value--; -@@ -253,7 +253,7 @@ namespace RecInt - inline void div_2_1(ruint<__RECINT_LIMB_SIZE>& q, ruint<__RECINT_LIMB_SIZE>& r, - const ruint<__RECINT_LIMB_SIZE>& ah, const ruint<__RECINT_LIMB_SIZE>& al, - const ruint<__RECINT_LIMB_SIZE>& b) { -- udiv_qrnnd(q.Value, r.Value, ah.Value, al.Value, b.Value); -+ recint_udiv_qrnnd(q.Value, r.Value, ah.Value, al.Value, b.Value); - } - - // computes (q, r) such that a = q*b + r (0 <= r < b) -diff --git a/src/kernel/recint/ruint.h b/src/kernel/recint/ruint.h -index 1fc930b..e95d254 100644 ---- a/src/kernel/recint/ruint.h -+++ b/src/kernel/recint/ruint.h -@@ -80,19 +80,7 @@ knowledge of the CeCILL-B license and that you accept its terms. - /* GMP conversion system */ - #include "ruconvert.h" - --// Cleaning up MACROS defined in reclonglong.h (to avoid collision with FLINT's longlong.h) --#undef add_ssaaaa --#undef count_leading_zeros --#undef count_trailing_zeros --#undef smul_ppmm --#undef sub_ddmmss --#undef umul_ppmm --#undef udiv_qrnnd --#undef __BITS4 --#undef __ll_B --#undef __ll_lowpart --#undef __ll_highpart --#endif -+#endif // RUINT_H - - /* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - // vim:sts=4:sw=4:ts=4:et:sr:cino=>s,f0,{0,g0,(0,\:0,t0,+0,=s -diff --git a/src/kernel/recint/rumul.h b/src/kernel/recint/rumul.h -index 10edcf8..d7d33db 100644 ---- a/src/kernel/recint/rumul.h -+++ b/src/kernel/recint/rumul.h -@@ -174,7 +174,7 @@ namespace RecInt - - template<> - inline void lmul_naive(ruint<__RECINT_LIMB_SIZE>& ah, ruint<__RECINT_LIMB_SIZE>& al, const ruint<__RECINT_LIMB_SIZE>& b, const ruint<__RECINT_LIMB_SIZE>& c) { -- umul_ppmm(ah.Value, al.Value, b.Value, c.Value); -+ recint_umul_ppmm(ah.Value, al.Value, b.Value, c.Value); - } - template - inline void lmul_naive(ruint& a, const ruint& b, const ruint& c) { -@@ -243,7 +243,7 @@ namespace RecInt - } - template - inline __RECINT_IS_ARITH(T, void) lmul(limb& ret, ruint<__RECINT_LIMB_SIZE>& a, const ruint<__RECINT_LIMB_SIZE>& b, const T& c) { -- umul_ppmm(ret, a.Value, b.Value, limb(c)); -+ recint_umul_ppmm(ret, a.Value, b.Value, limb(c)); - } - - // a = b*c -@@ -352,7 +352,7 @@ namespace RecInt - } - template<> - inline ruint<__RECINT_LIMB_SIZE+1>& lsquare(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE>& b) { -- umul_ppmm(a.High.Value, a.Low.Value, b.Value, b.Value); -+ recint_umul_ppmm(a.High.Value, a.Low.Value, b.Value, b.Value); - return a; - } - -diff --git a/src/kernel/recint/rusub.h b/src/kernel/recint/rusub.h -index 74a2e39..0b91088 100644 ---- a/src/kernel/recint/rusub.h -+++ b/src/kernel/recint/rusub.h -@@ -187,7 +187,7 @@ namespace RecInt - template<> - inline void sub(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c) { - r = (b < c); -- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); - } - #endif - template<> -@@ -213,7 +213,7 @@ namespace RecInt - template<> - inline void sub(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) { - r = (a < b); -- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); - } - #endif - template<> -@@ -239,7 +239,7 @@ namespace RecInt - #else - template<> - inline ruint<__RECINT_LIMB_SIZE+1>& sub(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c) { -- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); - return a; - } - #endif -@@ -266,7 +266,7 @@ namespace RecInt - #else - template<> - inline ruint<__RECINT_LIMB_SIZE+1>& sub(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) { -- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); - return a; - - } -@@ -290,7 +290,7 @@ namespace RecInt - template - inline __RECINT_IS_ARITH(T, void) sub(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const T& c) { - r = (b < c); -- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, limb(c)); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, limb(c)); - } - template - inline __RECINT_IS_ARITH(T, void) sub(bool& r, ruint<__RECINT_LIMB_SIZE>& a, const ruint<__RECINT_LIMB_SIZE>& b, const T& c) { -@@ -308,7 +308,7 @@ namespace RecInt - template - inline __RECINT_IS_ARITH(T, void) sub(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const T& b) { - r = (a < b); -- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, limb(b)); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, limb(b)); - } - template - inline __RECINT_IS_ARITH(T, void) sub(bool& r, ruint<__RECINT_LIMB_SIZE>& a, const T& b) { -@@ -325,7 +325,7 @@ namespace RecInt - } - template - inline __RECINT_IS_ARITH(T, void) sub(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const T& c) { -- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, limb(c)); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, limb(c)); - } - template - inline __RECINT_IS_ARITH(T, void) sub(ruint<__RECINT_LIMB_SIZE>& a, const ruint<__RECINT_LIMB_SIZE>& b, const T& c) { -@@ -341,7 +341,7 @@ namespace RecInt - } - template - inline __RECINT_IS_ARITH(T, void) sub(ruint<__RECINT_LIMB_SIZE+1>& a, const T& b) { -- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, limb(b)); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, limb(b)); - } - template - inline __RECINT_IS_ARITH(T, void) sub(ruint<__RECINT_LIMB_SIZE>& a, const T& b) { -@@ -367,7 +367,7 @@ namespace RecInt - template<> - inline void sub_1(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) { - r = (b == 0); -- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1); - } - #endif - template<> -@@ -393,7 +393,7 @@ namespace RecInt - template<> - inline void sub_1(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a) { - r = (a == 0); -- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); - } - #endif - template<> -@@ -417,7 +417,7 @@ namespace RecInt - #else - template<> - inline void sub_1(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) { -- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1); - } - #endif - template<> -@@ -440,7 +440,7 @@ namespace RecInt - #else - template<> - inline void sub_1(ruint<__RECINT_LIMB_SIZE+1>& a) { -- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); - } - #endif - template<> -@@ -469,8 +469,8 @@ namespace RecInt - inline void sub_wc(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c, const bool& cy) { - if (cy) r = (b <= c); - else r = (b < c); -- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); -- if (cy) sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); -+ if (cy) recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); - } - #endif - template<> -@@ -499,8 +499,8 @@ namespace RecInt - inline void sub_wc(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const bool& cy) { - if (cy) r = (a <= b); - else r = (a < b); -- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); -- if (cy) sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); -+ if (cy) recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1); - } - #endif - template<> -@@ -525,8 +525,8 @@ namespace RecInt - #else - template<> - inline void sub_wc(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c, const bool& cy) { -- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); -- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, cy); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, cy); - } - #endif - template<> -@@ -549,8 +549,8 @@ namespace RecInt - #else - template<> - inline void sub_wc(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const bool& cy) { -- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, cy); -- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, cy); -+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value); - } - #endif - template<> diff --git a/build/pkgs/givaro/spkg-configure.m4 b/build/pkgs/givaro/spkg-configure.m4 index 36cfab37081..18eb1d30195 100644 --- a/build/pkgs/givaro/spkg-configure.m4 +++ b/build/pkgs/givaro/spkg-configure.m4 @@ -1,6 +1,6 @@ SAGE_SPKG_CONFIGURE([givaro], [ PKG_CHECK_MODULES([GIVARO], - [givaro >= 4.1.1],dnl The version test is refined in linbox/spkg-configure.m4 + [givaro >= 4.2.0],dnl The version test is refined in linbox/spkg-configure.m4 [sage_spkg_install_givaro=no], [sage_spkg_install_givaro=yes]) ]) diff --git a/build/pkgs/linbox/checksums.ini b/build/pkgs/linbox/checksums.ini index 43b3c64d61e..397bf97190f 100644 --- a/build/pkgs/linbox/checksums.ini +++ b/build/pkgs/linbox/checksums.ini @@ -1,3 +1,5 @@ tarball=linbox-VERSION.tar.gz -sha1=9268e21b5aecbbfc45204b25195b786f80b769bc -sha256=a58a188307b07c57964e844bceb99321d3043a8a4a1fccc082a54928bb9a0057 +sha1=24e8bdbd16fe3dedce0dd343398999a4aed7c02c +md5=1e1b95f12f015815a0194eac0cb611d0 +cksum=253115750 +upstream_url=https://github.com/linbox-team/linbox/releases/download/vVERSION/linbox-VERSION.tar.gz \ No newline at end of file diff --git a/build/pkgs/linbox/package-version.txt b/build/pkgs/linbox/package-version.txt index 8ecda4f1393..bd8bf882d06 100644 --- a/build/pkgs/linbox/package-version.txt +++ b/build/pkgs/linbox/package-version.txt @@ -1 +1 @@ -1.6.3.p1 +1.7.0 diff --git a/build/pkgs/linbox/patches/292.patch b/build/pkgs/linbox/patches/292.patch new file mode 100644 index 00000000000..d3333cc23d0 --- /dev/null +++ b/build/pkgs/linbox/patches/292.patch @@ -0,0 +1,24 @@ +From 49b9cccd0286980c1c1811c3b03df883ef0164df Mon Sep 17 00:00:00 2001 +From: Doug Torrance +Date: Tue, 14 Dec 2021 16:22:33 -0500 +Subject: [PATCH] Only register uint128_t as a TypeName when it's available. + +Otherwise, test-fft will fail when it isn't. +--- + tests/test-fft.C | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/tests/test-fft.C b/tests/test-fft.C +index d19184c2d..5811ebc5d 100644 +--- a/tests/test-fft.C ++++ b/tests/test-fft.C +@@ -55,7 +55,9 @@ REGISTER_TYPE_NAME(double); + REGISTER_TYPE_NAME(uint16_t); + REGISTER_TYPE_NAME(uint32_t); + REGISTER_TYPE_NAME(uint64_t); ++#ifdef __FFLASFFPACK_HAVE_INT128 + REGISTER_TYPE_NAME(uint128_t); ++#endif + REGISTER_TYPE_NAME(Modular); + REGISTER_TYPE_NAME(ModularExtended); + diff --git a/build/pkgs/linbox/patches/294.patch b/build/pkgs/linbox/patches/294.patch new file mode 100644 index 00000000000..7c645f165d3 --- /dev/null +++ b/build/pkgs/linbox/patches/294.patch @@ -0,0 +1,38 @@ +From f81a1f4a5e0835b7a0f3bb88a0fcbbaa32174cfa Mon Sep 17 00:00:00 2001 +From: Cyril Bouvier +Date: Wed, 15 Dec 2021 16:00:39 +0100 +Subject: [PATCH] Only register uint128_t as a TypeName when it's available + +--- + benchmarks/benchmark-fft.C | 2 ++ + benchmarks/benchmark-polynomial-matrix-mul-fft.C | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/benchmarks/benchmark-fft.C b/benchmarks/benchmark-fft.C +index 39b86c9d9..59a8be57c 100644 +--- a/benchmarks/benchmark-fft.C ++++ b/benchmarks/benchmark-fft.C +@@ -54,7 +54,9 @@ REGISTER_TYPE_NAME(double); + REGISTER_TYPE_NAME(uint16_t); + REGISTER_TYPE_NAME(uint32_t); + REGISTER_TYPE_NAME(uint64_t); ++#ifdef __FFLASFFPACK_HAVE_INT128 + REGISTER_TYPE_NAME(uint128_t); ++#endif + REGISTER_TYPE_NAME(Modular); + REGISTER_TYPE_NAME(ModularExtended); + +diff --git a/benchmarks/benchmark-polynomial-matrix-mul-fft.C b/benchmarks/benchmark-polynomial-matrix-mul-fft.C +index e9b184bcf..7bf17f33e 100644 +--- a/benchmarks/benchmark-polynomial-matrix-mul-fft.C ++++ b/benchmarks/benchmark-polynomial-matrix-mul-fft.C +@@ -65,7 +65,9 @@ REGISTER_TYPE_NAME(double); + REGISTER_TYPE_NAME(uint16_t); + REGISTER_TYPE_NAME(uint32_t); + REGISTER_TYPE_NAME(uint64_t); ++#ifdef __FFLASFFPACK_HAVE_INT128 + REGISTER_TYPE_NAME(uint128_t); ++#endif + REGISTER_TYPE_NAME(Modular); + REGISTER_TYPE_NAME(ModularExtended); + diff --git a/build/pkgs/linbox/patches/310-backport.patch b/build/pkgs/linbox/patches/310-backport.patch new file mode 100644 index 00000000000..c0c44bc1ec7 --- /dev/null +++ b/build/pkgs/linbox/patches/310-backport.patch @@ -0,0 +1,69 @@ +From b8f2d4ccdc0af4418d14f72caf6c4d01969092a3 Mon Sep 17 00:00:00 2001 +From: Jean-Guillaume Dumas +Date: Fri, 26 Jan 2024 16:31:56 +0100 +Subject: [PATCH] const_cast missing faster empty init + +--- + linbox/algorithms/gauss/gauss-nullspace.inl | 10 +- + .../matrix/sparsematrix/sparse-ell-matrix.h | 8 +- + .../matrix/sparsematrix/sparse-ellr-matrix.h | 18 +-- + linbox/ring/ntl/ntl-lzz_p.h | 11 +- + linbox/ring/ntl/ntl-lzz_pe.h | 143 +++++++++--------- + linbox/ring/ntl/ntl-zz_px.h | 6 + + 6 files changed, 104 insertions(+), 92 deletions(-) + +diff --git a/linbox/matrix/sparsematrix/sparse-ell-matrix.h b/linbox/matrix/sparsematrix/sparse-ell-matrix.h +index 59006d6c5f..2604f47b81 100644 +--- a/linbox/matrix/sparsematrix/sparse-ell-matrix.h ++++ b/linbox/matrix/sparsematrix/sparse-ell-matrix.h +@@ -1210,10 +1210,10 @@ namespace LinBox + _colid_beg = iter._colid_beg ; + _colid_it = iter._colid_it ; + _data_it = iter._data_it ; +- _data_beg = iter._data_beg ; +- _data_end = iter._data_end ; +- _field = iter._field ; +- _ld = iter._ld ; ++ const_cast(_data_beg) = iter._data_beg ; ++ const_cast(_data_end) = iter._data_end ; ++ const_cast(_field) = iter._field ; ++ const_cast(ld) = iter._ld ; + _row = iter._row ; + + return *this; +diff --git a/linbox/matrix/sparsematrix/sparse-ellr-matrix.h b/linbox/matrix/sparsematrix/sparse-ellr-matrix.h +index 498a5525db..a60943868b 100644 +--- a/linbox/matrix/sparsematrix/sparse-ellr-matrix.h ++++ b/linbox/matrix/sparsematrix/sparse-ellr-matrix.h +@@ -1102,11 +1102,11 @@ namespace LinBox + _Iterator &operator = (const _Iterator &iter) + { + _data_it = iter._data_it ; +- _data_beg = iter._data_beg ; +- _data_end = iter._data_end ; +- _field = iter._field ; +- _rowid = iter._rowid; +- _ld = iter._ld ; ++ const_cast(_data_beg) = iter._data_beg ; ++ const_cast(_data_end)= iter._data_end ; ++ const_cast(_field) = iter._field ; ++ const_cast&>(_rowid) = iter._rowid; ++ const_cast(ld) = iter._ld ; + _row = iter._row ; + + return *this; +@@ -1252,10 +1252,10 @@ namespace LinBox + _colid_beg = iter._colid_beg ; + _colid_it = iter._colid_it ; + _data_it = iter._data_it ; +- _data_beg = iter._data_beg ; +- _data_end = iter._data_end ; +- _field = iter._field ; +- _ld = iter._ld ; ++ const_cast(_data_beg) = iter._data_beg ; ++ const_cast(_data_end) = iter._data_end ; ++ const_cast(_field) = iter._field ; ++ const_cast(ld)= iter._ld ; + _row = iter._row ; + + return *this; diff --git a/build/pkgs/linbox/patches/fix-ksh-pkgconfig.patch b/build/pkgs/linbox/patches/fix-ksh-pkgconfig.patch deleted file mode 100644 index e0cb575b1a1..00000000000 --- a/build/pkgs/linbox/patches/fix-ksh-pkgconfig.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 52c78df67a08de074991a93b57946b7bd5ea7196 Mon Sep 17 00:00:00 2001 -From: Dima Pasechnik -Date: Fri, 8 May 2020 15:53:25 +0100 -Subject: [PATCH 1/1] remove redundant 1st and last lines - -they remain if the script is run under ksh, leading to broken .pc file ---- - linbox.pc.in | 3 +-- - 1 file changed, 1 insertion(+), 2 deletions(-) - -diff --git a/linbox.pc.in b/linbox.pc.in -index f54285e..eb6835b 100644 ---- a/linbox.pc.in -+++ b/linbox.pc.in -@@ -1,4 +1,3 @@ --/------------------ linbox.pc ------------------------ - prefix=@prefix@ - exec_prefix=@prefix@ - libdir=@libdir@ -@@ -11,4 +10,4 @@ Version: @VERSION@ - Requires: fflas-ffpack >= 2.4.0, givaro >= 4.1.0 - Libs: -L${libdir} -llinbox @LINBOXSAGE_LIBS@ @NTL_LIBS@ @MPFR_LIBS@ @FPLLL_LIBS@ @IML_LIBS@ @FLINT_LIBS@ @OCL_LIBS@ - Cflags: @DEFAULT_CFLAGS@ -DDISABLE_COMMENTATOR -I${includedir} @NTL_CFLAGS@ @MPFR_CFLAGS@ @FPLLL_CFLAGS@ @IML_CFLAGS@ @FLINT_CFLAGS@ --\------------------------------------------------------- -+ --- -2.26.2 - diff --git a/build/pkgs/linbox/patches/linbox-pr-256.patch b/build/pkgs/linbox/patches/linbox-pr-256.patch deleted file mode 100644 index 9ca03791d30..00000000000 --- a/build/pkgs/linbox/patches/linbox-pr-256.patch +++ /dev/null @@ -1,47 +0,0 @@ -Extracted from the following patch: - -From 27811e0021c73de26bf2cff5105c763e163ca8b7 Mon Sep 17 00:00:00 2001 -From: Jean-Guillaume Dumas -Date: Fri, 26 Jun 2020 09:54:38 +0200 -Subject: [PATCH 1/3] iterators may have different types - ---- - linbox/vector/blas-subvector.h | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -From 4ff828e20053ab2ef9adc4ce6931d159fd513cef Mon Sep 17 00:00:00 2001 -From: Jean-Guillaume Dumas -Date: Fri, 26 Jun 2020 09:54:57 +0200 -Subject: [PATCH 2/3] incompatible const & with modifiers - ---- - examples/Makefile.am | 3 +- - examples/ratdet.C | 96 ++++++++++++++++++++++++++++++++ - linbox/algorithms/det-rational.h | 4 +- - 3 files changed, 100 insertions(+), 3 deletions(-) - create mode 100644 examples/ratdet.C - -diff --git a/linbox/algorithms/det-rational.h b/linbox/algorithms/det-rational.h -index 327b4710f..1943876c1 100644 ---- a/linbox/algorithms/det-rational.h -+++ b/linbox/algorithms/det-rational.h -@@ -79,8 +79,8 @@ namespace LinBox - struct MyRationalModularDet { - const Blackbox &A; - const MyMethod &M; -- const Integer &mul;//multiplicative prec; -- const Integer ÷ -+ Integer mul;//multiplicative prec; -+ Integer div; - - MyRationalModularDet(const Blackbox& b, const MyMethod& n, - const Integer & p1, const Integer & p2) : - -From 567f727aa42de6678433591d731b275ea55fa8ad Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Cl=C3=A9ment=20Pernet?= -Date: Fri, 26 Jun 2020 17:11:37 +0200 -Subject: [PATCH 3/3] untabify + auto-indent - ---- - examples/ratdet.C | 66 +++++++++++++++++++++++------------------------ - 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/build/pkgs/linbox/patches/remove-linboxsage-libs-from-pc.patch b/build/pkgs/linbox/patches/remove-linboxsage-libs-from-pc.patch deleted file mode 100644 index c93915fb1b0..00000000000 --- a/build/pkgs/linbox/patches/remove-linboxsage-libs-from-pc.patch +++ /dev/null @@ -1,23 +0,0 @@ -Backported from: - -From 426eb97ba762c7663884f57ead0909f2aa3cd6a5 Mon Sep 17 00:00:00 2001 -From: Cyril Bouvier -Date: Thu, 17 Jan 2019 16:32:19 +0100 -Subject: [PATCH] Remove @LINBOXSAGE_LIBS@ from linbox.pc.in - ---- - linbox.pc.in | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/linbox.pc.in b/linbox.pc.in -index 278f127e4..c6b8091eb 100644 ---- a/linbox.pc.in -+++ b/linbox.pc.in -@@ -9,6 +9,6 @@ Description: Exact Linear Algebra library - URL: http://github.com/linbox-team/linbox - Version: @VERSION@ - Requires: fflas-ffpack >= 2.4.0, givaro >= 4.1.0 --Libs: -L${libdir} -llinbox @LINBOXSAGE_LIBS@ @NTL_LIBS@ @MPFR_LIBS@ @FPLLL_LIBS@ @IML_LIBS@ @FLINT_LIBS@ @OCL_LIBS@ -+Libs: -L${libdir} -llinbox @NTL_LIBS@ @MPFR_LIBS@ @FPLLL_LIBS@ @IML_LIBS@ @FLINT_LIBS@ @OCL_LIBS@ - Cflags: @DEFAULT_CFLAGS@ -DDISABLE_COMMENTATOR -I${includedir} @NTL_CFLAGS@ @MPFR_CFLAGS@ @FPLLL_CFLAGS@ @IML_CFLAGS@ @FLINT_CFLAGS@ - \------------------------------------------------------- diff --git a/build/pkgs/linbox/spkg-configure.m4 b/build/pkgs/linbox/spkg-configure.m4 index 8c5b85ef7fd..49cc871a85c 100644 --- a/build/pkgs/linbox/spkg-configure.m4 +++ b/build/pkgs/linbox/spkg-configure.m4 @@ -1,10 +1,10 @@ SAGE_SPKG_CONFIGURE([linbox], [ SAGE_SPKG_DEPCHECK([fflas_ffpack flint fplll givaro gmp iml m4ri m4rie mpfr ntl], [ PKG_CHECK_MODULES([LINBOX],dnl Check for a set of matching old versions - [linbox >= 1.6.3 linbox <= 1.6.4 fflas-ffpack >= 2.4.0 fflas-ffpack < 2.5.0 givaro >= 4.1.1 givaro < 4.2.0], + [linbox >= 1.7.0 linbox < 1.8.0 fflas-ffpack >= 2.5.0 fflas-ffpack < 2.6.0 givaro >= 4.2.0 givaro < 4.3.0], [sage_spkg_install_linbox=no], [PKG_CHECK_MODULES([LINBOX],dnl Check for a set of matching new versions - [linbox >= 1.7.0 linbox <= 1.7.0 fflas-ffpack >= 2.5.0 givaro >= 4.2.0 givaro < 4.3.0], + [linbox >= 1.8.0 linbox <= 1.9.0 fflas-ffpack >= 2.6.0 givaro >= 4.3.0 givaro < 4.4.0], [sage_spkg_install_linbox=no], [sage_spkg_install_linbox=yes])]) ]) diff --git a/build/pkgs/linbox/spkg-install.in b/build/pkgs/linbox/spkg-install.in index 8d415e81fd8..7acc5547e9f 100644 --- a/build/pkgs/linbox/spkg-install.in +++ b/build/pkgs/linbox/spkg-install.in @@ -15,7 +15,7 @@ export CPPFLAGS="$CPPFLAGS -DDISABLE_COMMENTATOR" # If SAGE_FAT_BINARY is set, disable dependency that be discovered on the building system. if [ "$SAGE_FAT_BINARY" = yes ]; then - LINBOX_CONFIGURE="--disable-sse --disable-sse2 --disable-sse3 --disable-ssse3 --disable-sse41 --disable-sse42 --disable-fma --disable-fma4 --disable-avx --disable-avx2 --without-ocl $LINBOX_CONFIGURE" + LINBOX_CONFIGURE="--disable-sse3 --disable-ssse3 --disable-sse41 --disable-sse42 --disable-fma --disable-fma4 --disable-avx --disable-avx2 --without-ocl $LINBOX_CONFIGURE" fi # Disable fplll as version 5.x is not supported by linbox <= 1.5.0. diff --git a/build/pkgs/networkx/version_requirements.txt b/build/pkgs/networkx/version_requirements.txt index 5a7e22a60db..2ff86e7c737 100644 --- a/build/pkgs/networkx/version_requirements.txt +++ b/build/pkgs/networkx/version_requirements.txt @@ -1 +1 @@ -networkx >=2.4, <3.3 +networkx >=2.4 diff --git a/build/pkgs/pcre/distros/alpine.txt b/build/pkgs/pcre/distros/alpine.txt deleted file mode 100644 index a16e4119734..00000000000 --- a/build/pkgs/pcre/distros/alpine.txt +++ /dev/null @@ -1 +0,0 @@ -pcre-dev diff --git a/build/pkgs/pycygwin/spkg-configure.m4 b/build/pkgs/pycygwin/spkg-configure.m4 deleted file mode 100644 index 7876a693d7b..00000000000 --- a/build/pkgs/pycygwin/spkg-configure.m4 +++ /dev/null @@ -1 +0,0 @@ -SAGE_SPKG_CONFIGURE([pycygwin], [SAGE_PYTHON_PACKAGE_CHECK([pycygwin])]) diff --git a/build/pkgs/sage_conf/version_requirements.txt b/build/pkgs/sage_conf/version_requirements.txt index 8a94176716b..858e8da5968 100644 --- a/build/pkgs/sage_conf/version_requirements.txt +++ b/build/pkgs/sage_conf/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-conf ~= 10.4b5 +sage-conf ~= 10.4b6 diff --git a/build/pkgs/sage_docbuild/version_requirements.txt b/build/pkgs/sage_docbuild/version_requirements.txt index 066d0bbc7d8..0005ac13ace 100644 --- a/build/pkgs/sage_docbuild/version_requirements.txt +++ b/build/pkgs/sage_docbuild/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-docbuild ~= 10.4b5 +sage-docbuild ~= 10.4b6 diff --git a/build/pkgs/sage_setup/version_requirements.txt b/build/pkgs/sage_setup/version_requirements.txt index 743b8a3afb1..4efa9ed072c 100644 --- a/build/pkgs/sage_setup/version_requirements.txt +++ b/build/pkgs/sage_setup/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-setup ~= 10.4b5 +sage-setup ~= 10.4b6 diff --git a/build/pkgs/sage_sws2rst/version_requirements.txt b/build/pkgs/sage_sws2rst/version_requirements.txt index 191508c073f..a44b4f816f4 100644 --- a/build/pkgs/sage_sws2rst/version_requirements.txt +++ b/build/pkgs/sage_sws2rst/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-sws2rst ~= 10.4b5 +sage-sws2rst ~= 10.4b6 diff --git a/build/pkgs/sagelib/version_requirements.txt b/build/pkgs/sagelib/version_requirements.txt index 8e5e0643152..43ee6e618ef 100644 --- a/build/pkgs/sagelib/version_requirements.txt +++ b/build/pkgs/sagelib/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-standard ~= 10.4b5 +sagemath-standard ~= 10.4b6 diff --git a/build/pkgs/sagemath_bliss/version_requirements.txt b/build/pkgs/sagemath_bliss/version_requirements.txt index aa21a391eb1..11ff86c39e6 100644 --- a/build/pkgs/sagemath_bliss/version_requirements.txt +++ b/build/pkgs/sagemath_bliss/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-bliss ~= 10.4b5 +sagemath-bliss ~= 10.4b6 diff --git a/build/pkgs/sagemath_categories/version_requirements.txt b/build/pkgs/sagemath_categories/version_requirements.txt index 59a93ac5c5c..4bf63679c82 100644 --- a/build/pkgs/sagemath_categories/version_requirements.txt +++ b/build/pkgs/sagemath_categories/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-categories ~= 10.4b5 +sagemath-categories ~= 10.4b6 diff --git a/build/pkgs/sagemath_coxeter3/version_requirements.txt b/build/pkgs/sagemath_coxeter3/version_requirements.txt index 5bd13da7dc7..4d8933d8f37 100644 --- a/build/pkgs/sagemath_coxeter3/version_requirements.txt +++ b/build/pkgs/sagemath_coxeter3/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-coxeter3 ~= 10.4b5 +sagemath-coxeter3 ~= 10.4b6 diff --git a/build/pkgs/sagemath_environment/version_requirements.txt b/build/pkgs/sagemath_environment/version_requirements.txt index d608b96ded2..c97a53a9437 100644 --- a/build/pkgs/sagemath_environment/version_requirements.txt +++ b/build/pkgs/sagemath_environment/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-environment ~= 10.4b5 +sagemath-environment ~= 10.4b6 diff --git a/build/pkgs/sagemath_mcqd/version_requirements.txt b/build/pkgs/sagemath_mcqd/version_requirements.txt index 7701f84d6e6..697eed8cf48 100644 --- a/build/pkgs/sagemath_mcqd/version_requirements.txt +++ b/build/pkgs/sagemath_mcqd/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-mcqd ~= 10.4b5 +sagemath-mcqd ~= 10.4b6 diff --git a/build/pkgs/sagemath_meataxe/version_requirements.txt b/build/pkgs/sagemath_meataxe/version_requirements.txt index c8a11e39149..8e7db0b81a8 100644 --- a/build/pkgs/sagemath_meataxe/version_requirements.txt +++ b/build/pkgs/sagemath_meataxe/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-meataxe ~= 10.4b5 +sagemath-meataxe ~= 10.4b6 diff --git a/build/pkgs/sagemath_objects/version_requirements.txt b/build/pkgs/sagemath_objects/version_requirements.txt index 542ae36504a..92bb9707da1 100644 --- a/build/pkgs/sagemath_objects/version_requirements.txt +++ b/build/pkgs/sagemath_objects/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-objects ~= 10.4b5 +sagemath-objects ~= 10.4b6 diff --git a/build/pkgs/sagemath_repl/version_requirements.txt b/build/pkgs/sagemath_repl/version_requirements.txt index 6ea51fe6efe..2b2a5e66ef5 100644 --- a/build/pkgs/sagemath_repl/version_requirements.txt +++ b/build/pkgs/sagemath_repl/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-repl ~= 10.4b5 +sagemath-repl ~= 10.4b6 diff --git a/build/pkgs/sagemath_sirocco/version_requirements.txt b/build/pkgs/sagemath_sirocco/version_requirements.txt index ca0ac089f26..106893032ab 100644 --- a/build/pkgs/sagemath_sirocco/version_requirements.txt +++ b/build/pkgs/sagemath_sirocco/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-sirocco ~= 10.4b5 +sagemath-sirocco ~= 10.4b6 diff --git a/build/pkgs/sagemath_tdlib/version_requirements.txt b/build/pkgs/sagemath_tdlib/version_requirements.txt index a99c2c26a9d..71dc621822a 100644 --- a/build/pkgs/sagemath_tdlib/version_requirements.txt +++ b/build/pkgs/sagemath_tdlib/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-tdlib ~= 10.4b5 +sagemath-tdlib ~= 10.4b6 diff --git a/build/pkgs/setuptools_scm_git_archive/distros/alpine.txt b/build/pkgs/setuptools_scm_git_archive/distros/alpine.txt deleted file mode 100644 index c08bc88e16c..00000000000 --- a/build/pkgs/setuptools_scm_git_archive/distros/alpine.txt +++ /dev/null @@ -1 +0,0 @@ -py3-setuptools-scm-git-archive diff --git a/build/pkgs/setuptools_scm_git_archive/distros/arch.txt b/build/pkgs/setuptools_scm_git_archive/distros/arch.txt deleted file mode 100644 index bc2a39f97a3..00000000000 --- a/build/pkgs/setuptools_scm_git_archive/distros/arch.txt +++ /dev/null @@ -1 +0,0 @@ -python-setuptools-scm-git-archive diff --git a/build/pkgs/setuptools_scm_git_archive/distros/debian.txt b/build/pkgs/setuptools_scm_git_archive/distros/debian.txt deleted file mode 100644 index 538474ff946..00000000000 --- a/build/pkgs/setuptools_scm_git_archive/distros/debian.txt +++ /dev/null @@ -1 +0,0 @@ -setuptools-scm-git-archive diff --git a/build/pkgs/setuptools_scm_git_archive/distros/fedora.txt b/build/pkgs/setuptools_scm_git_archive/distros/fedora.txt deleted file mode 100644 index ada37357769..00000000000 --- a/build/pkgs/setuptools_scm_git_archive/distros/fedora.txt +++ /dev/null @@ -1 +0,0 @@ -python-setuptools_scm_git_archive diff --git a/build/pkgs/setuptools_scm_git_archive/distros/freebsd.txt b/build/pkgs/setuptools_scm_git_archive/distros/freebsd.txt deleted file mode 100644 index 2ace76a2af0..00000000000 --- a/build/pkgs/setuptools_scm_git_archive/distros/freebsd.txt +++ /dev/null @@ -1 +0,0 @@ -devel/py-setuptools_scm_git_archive diff --git a/build/pkgs/setuptools_scm_git_archive/distros/gentoo.txt b/build/pkgs/setuptools_scm_git_archive/distros/gentoo.txt deleted file mode 100644 index fb7388e3dd7..00000000000 --- a/build/pkgs/setuptools_scm_git_archive/distros/gentoo.txt +++ /dev/null @@ -1 +0,0 @@ -dev-python/setuptools_scm_git_archive diff --git a/build/pkgs/setuptools_scm_git_archive/spkg-configure.m4 b/build/pkgs/setuptools_scm_git_archive/spkg-configure.m4 deleted file mode 100644 index 0da3db40d22..00000000000 --- a/build/pkgs/setuptools_scm_git_archive/spkg-configure.m4 +++ /dev/null @@ -1 +0,0 @@ -SAGE_SPKG_CONFIGURE([setuptools_scm_git_archive], [SAGE_PYTHON_PACKAGE_CHECK([setuptools_scm_git_archive])]) diff --git a/build/sage_bootstrap/app.py b/build/sage_bootstrap/app.py index 44d62c2cca1..018e5a2d5b5 100644 --- a/build/sage_bootstrap/app.py +++ b/build/sage_bootstrap/app.py @@ -10,7 +10,10 @@ # **************************************************************************** -# Copyright (C) 2016 Volker Braun +# Copyright (C) 2016 Volker Braun +# 2020-2024 Matthias Koeppe +# 2022 Thierry Monteil +# 2024 Marc Culler # # 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 @@ -21,6 +24,7 @@ import os +import re import logging log = logging.getLogger() @@ -36,6 +40,10 @@ from sage_bootstrap.env import SAGE_DISTFILES +# Approximation of https://peps.python.org/pep-0508/#names dependency specification +dep_re = re.compile('^ *([-A-Z0-9._]+)', re.IGNORECASE) + + class Application(object): def config(self): @@ -88,7 +96,7 @@ def properties(self, *package_classes, **kwds): source_maxima='normal' trees_maxima='SAGE_LOCAL' """ - props = kwds.pop('props', ['path', 'version_with_patchlevel', 'type', 'source', 'trees']) + props = kwds.pop('props', ['path', 'version_with_patchlevel', 'type', 'source', 'trees', 'purl']) format = kwds.pop('format', 'plain') log.debug('Looking up properties') pc = PackageClass(*package_classes) @@ -256,6 +264,9 @@ def update_latest(self, package_name, commit=False): Update a package to the latest version. This modifies the Sage sources. """ pkg = Package(package_name) + if pkg.source not in ['normal', 'wheel']: + log.debug('update_latest can only update normal and wheel packages; %s is a %s package' % (pkg, pkg.source)) + return dist_name = pkg.distribution_name if dist_name is None: log.debug('%s does not have Python distribution info in version_requirements.txt' % pkg) @@ -304,11 +315,14 @@ def download(self, package_name, allow_upstream=False): package.tarball.download(allow_upstream=allow_upstream) print(package.tarball.upstream_fqn) - def download_cls(self, package_name_or_class, allow_upstream=False, on_error='stop'): + def download_cls(self, *package_classes, **kwds): """ Download a package or a class of packages """ - pc = PackageClass(package_name_or_class, has_files=['checksums.ini']) + allow_upstream = kwds.pop('allow_upstream', False) + on_error = kwds.pop('on_error', 'stop') + has_files = list(kwds.pop('has_files', [])) + pc = PackageClass(*package_classes, has_files=has_files + ['checksums.ini'], **kwds) def download_with_args(package): try: @@ -380,7 +394,8 @@ def fix_checksum(self, package_name): update.fix_checksum() def create(self, package_name, version=None, tarball=None, pkg_type=None, upstream_url=None, - description=None, license=None, upstream_contact=None, pypi=False, source=None): + description=None, license=None, upstream_contact=None, pypi=False, source=None, + dependencies=None): """ Create a package @@ -392,7 +407,12 @@ def create(self, package_name, version=None, tarball=None, pkg_type=None, upstre $ sage --package create jupyterlab_markup --pypi --source wheel --type optional """ - if '-' in package_name: + if package_name.startswith('pypi/'): + package_name = 'pkg:' + package_name + if package_name.startswith('pkg:pypi/'): + pypi = True + package_name = package_name[len('pkg:pypi/'):].lower().replace('-', '_').replace('.', '_') + elif '-' in package_name: raise ValueError('package names must not contain dashes, use underscore instead') if pypi: if source is None: @@ -420,6 +440,24 @@ def create(self, package_name, version=None, tarball=None, pkg_type=None, upstre raise ValueError('Only platform-independent wheels can be used for wheel packages, got {0}'.format(tarball)) if not version: version = pypi_version.version + if dependencies is None: + requires_dist = pypi_version.requires_dist + if requires_dist: + dependencies = [] + for item in requires_dist: + if "extra ==" in item: + continue + try: + dep = dep_re.match(item).groups()[0].strip() + except Exception: + continue + dep = 'pkg:pypi/' + dep + try: + dep = Package(dep).name + except ValueError: + self.create(dep, pkg_type=pkg_type) + dep = Package(dep).name + dependencies.append(dep) upstream_url = 'https://pypi.io/packages/{2}/{0:1.1}/{0}/{1}'.format(package_name, tarball, pypi_version.python_version) if not description: description = pypi_version.summary @@ -444,7 +482,8 @@ def create(self, package_name, version=None, tarball=None, pkg_type=None, upstre if description or license or upstream_contact: creator.set_description(description, license, upstream_contact) if pypi or source == 'pip': - creator.set_python_data_and_scripts(pypi_package_name=pypi_version.name, source=source) + creator.set_python_data_and_scripts(pypi_package_name=pypi_version.name, source=source, + dependencies=dependencies) if tarball: creator.set_tarball(tarball, upstream_url) if upstream_url and version: diff --git a/build/sage_bootstrap/cmdline.py b/build/sage_bootstrap/cmdline.py index 3ed185a9185..ff11315aa51 100644 --- a/build/sage_bootstrap/cmdline.py +++ b/build/sage_bootstrap/cmdline.py @@ -12,7 +12,9 @@ """ # **************************************************************************** -# Copyright (C) 2016 Volker Braun +# Copyright (C) 2015-2016 Volker Braun +# 2020-2024 Matthias Koeppe +# 2022 Thierry Monteil # # 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 @@ -270,18 +272,19 @@ def make_parser(): parser_config = subparsers.add_parser( 'config', epilog=epilog_config, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Print the configuration') + help='print the configuration') parser_list = subparsers.add_parser( 'list', epilog=epilog_list, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Print a list of packages known to Sage') + help='print a list of packages known to Sage') parser_list.add_argument( - 'package_class', metavar='[package_name|:package_type:]', + 'package_class', metavar='[PACKAGE_NAME|pkg:pypi/DISTRIBUTION-NAME|:PACKAGE_TYPE:]', type=str, default=[':all-or-nothing:'], nargs='*', - help=('package name or designator for all packages of a given type ' + help=('package name, pkg:pypi/ followed by a distribution name, ' + 'or designator for all packages of a given type ' '(one of :all:, :standard:, :optional:, and :experimental:); ' - 'default: :all: (or nothing when --include-dependencies or --exclude-dependencies is given')) + 'default: :all: (or nothing when --include-dependencies or --exclude-dependencies is given)')) parser_list.add_argument( '--has-file', action='append', default=[], metavar='FILENAME', dest='has_files', help=('only include packages that have this file in their metadata directory ' @@ -303,11 +306,12 @@ def make_parser(): parser_properties = subparsers.add_parser( 'properties', epilog=epilog_properties, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Print properties of given packages') + help='print properties of given packages') parser_properties.add_argument( - 'package_class', metavar='[package_name|:package_type:]', + 'package_class', metavar='[PACKAGE_NAME|pkg:pypi/DISTRIBUTION-NAME|:PACKAGE_TYPE:]', type=str, nargs='+', - help=('package name or designator for all packages of a given type ' + help=('package name, pkg:pypi/ followed by a distribution name, ' + 'or designator for all packages of a given type ' '(one of :all:, :standard:, :optional:, and :experimental:)')) parser_properties.add_argument( '--format', type=str, default='plain', @@ -316,7 +320,7 @@ def make_parser(): parser_dependencies = subparsers.add_parser( 'dependencies', epilog=epilog_dependencies, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Print the list of packages that are dependencies of given packages') + help='print the list of packages that are dependencies of given packages') parser_dependencies.add_argument( 'package_class', metavar='[package_name|:package_type:]', type=str, nargs='+', @@ -341,121 +345,136 @@ def make_parser(): parser_name = subparsers.add_parser( 'name', epilog=epilog_name, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Find the package name given a tarball filename') - parser_name.add_argument('tarball_filename', type=str, help='Tarball filename') + help='find the package name given a tarball filename') + parser_name.add_argument('tarball_filename', type=str, help='tarball filename') parser_tarball = subparsers.add_parser( 'tarball', epilog=epilog_tarball, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Find the tarball filename given a package name') - parser_tarball.add_argument('package_name', type=str, help='Package name') + help='find the tarball filename given a package name') + parser_tarball.add_argument('package_name', type=str, help='package name') parser_apropos = subparsers.add_parser( 'apropos', epilog=epilog_apropos, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Find up to 5 package names that are close to the given name') + help='find up to 5 package names that are close to the given name') parser_apropos.add_argument( 'incorrect_name', type=str, - help='Fuzzy name to search for') + help='fuzzy name to search for') parser_update = subparsers.add_parser( 'update', epilog=epilog_update, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Update a package. This modifies the Sage sources.') + help='update a package, modifying the Sage sources') parser_update.add_argument( - 'package_name', type=str, help='Package name') + 'package_name', type=str, help='package name') parser_update.add_argument( - 'new_version', type=str, help='New version') + 'new_version', type=str, help='new version') parser_update.add_argument( - '--url', type=str, default=None, help='Download URL') + '--url', type=str, default=None, help='download URL') parser_update.add_argument( '--commit', action="store_true", - help='Whether to run "git commit"') + help='whether to run "git commit"') parser_update_latest = subparsers.add_parser( 'update-latest', epilog=epilog_update_latest, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Update a package to the latest version. This modifies the Sage sources.') + help='update a package to the latest version, modifying the Sage sources') parser_update_latest.add_argument( - 'package_name', type=str, help='Package name (:all: for all packages)') + 'package_name', type=str, help='package name (:all: for all packages)') parser_update_latest.add_argument( '--commit', action="store_true", - help='Whether to run "git commit"') + help='whether to run "git commit"') parser_download = subparsers.add_parser( 'download', epilog=epilog_download, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Download tarball') + help='download tarball') parser_download.add_argument( - 'package_name', type=str, help='Package name or :type:') + 'package_class', metavar='[package_name|:package_type:]', + type=str, nargs='+', + help=('package name or designator for all packages of a given type ' + '(one of :all:, :standard:, :optional:, and :experimental:)')) + parser_download.add_argument( + '--has-file', action='append', default=[], metavar='FILENAME', dest='has_files', + help=('only include packages that have this file in their metadata directory ' + '(examples: SPKG.rst, spkg-configure.m4, distros/debian.txt, spkg-install|spkg-install.in)')) + parser_download.add_argument( + '--no-file', action='append', default=[], metavar='FILENAME', dest='no_files', + help=('only include packages that do not have this file in their metadata directory ' + '(examples: huge, patches, huge|has_nonfree_dependencies)')) + parser_download.add_argument( + '--exclude', nargs='*', action='append', default=[], metavar='PACKAGE_NAME', + help='exclude package from list') parser_download.add_argument( '--allow-upstream', action="store_true", - help='Whether to fall back to downloading from the upstream URL') + help='whether to fall back to downloading from the upstream URL') parser_download.add_argument( '--on-error', choices=['stop', 'warn'], default='stop', - help='What to do if the tarball cannot be downloaded') + help='what to do if the tarball cannot be downloaded') parser_download.add_argument( '--no-check-certificate', action='store_true', - help='Do not check SSL certificates for https connections') + help='do not check SSL certificates for https connections') parser_upload = subparsers.add_parser( 'upload', epilog=epilog_upload, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Upload tarball to Sage mirrors') + help='upload tarball to Sage mirrors') parser_upload.add_argument( - 'package_name', type=str, help='Package name or :type:') + 'package_name', type=str, help='package name or :type:') parser_fix_checksum = subparsers.add_parser( 'fix-checksum', epilog=epilog_fix_checksum, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Fix the checksum of normal packages.') + help='fix the checksum of normal packages') parser_fix_checksum.add_argument( - 'package_class', metavar='[package_name|:package_type:]', + 'package_class', metavar='[PACKAGE_NAME|pkg:pypi/DISTRIBUTION-NAME|:PACKAGE_TYPE:]', type=str, default=[':all:'], nargs='*', - help=('package name or designator for all packages of a given type ' - '(one of :all:, :standard:, :optional:, and :experimental:); ' - 'default: :all:')) + help=('package name, pkg:pypi/ followed by a distribution name, ' + 'or designator for all packages of a given type ' + '(one of :all:, :standard:, :optional:, and :experimental:; default: :all:)')) parser_create = subparsers.add_parser( 'create', epilog=epilog_create, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Create or overwrite package.') + help='create or overwrite a package') parser_create.add_argument( 'package_name', default=None, type=str, - help='Package name.') + help='package name') parser_create.add_argument( - '--source', type=str, default=None, help='Package source (one of normal, wheel, script, pip); default depends on provided arguments') + '--source', type=str, default=None, help='package source (one of normal, wheel, script, pip); default depends on provided arguments') parser_create.add_argument( - '--version', type=str, default=None, help='Package version') + '--version', type=str, default=None, help='package version') parser_create.add_argument( - '--tarball', type=str, default=None, help='Tarball filename pattern, e.g. Foo-VERSION.tar.bz2') + '--tarball', type=str, default=None, help='tarball filename pattern, e.g. Foo-VERSION.tar.bz2') parser_create.add_argument( - '--type', type=str, default=None, help='Package type') + '--type', type=str, default=None, help='package type') parser_create.add_argument( - '--url', type=str, default=None, help='Download URL pattern, e.g. http://example.org/Foo-VERSION.tar.bz2') + '--url', type=str, default=None, help='download URL pattern, e.g. http://example.org/Foo-VERSION.tar.bz2') parser_create.add_argument( - '--description', type=str, default=None, help='Short description of the package (for SPKG.rst)') + '--description', type=str, default=None, help='short description of the package (for SPKG.rst)') parser_create.add_argument( - '--license', type=str, default=None, help='License of the package (for SPKG.rst)') + '--license', type=str, default=None, help='license of the package (for SPKG.rst)') parser_create.add_argument( - '--upstream-contact', type=str, default=None, help='Upstream contact (for SPKG.rst)') + '--upstream-contact', type=str, default=None, help='upstream contact (for SPKG.rst)') parser_create.add_argument( '--pypi', action="store_true", - help='Create a package for a Python package available on PyPI') + help='create a package for a Python package available on PyPI') parser_clean = subparsers.add_parser( 'clean', epilog=epilog_clean, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Remove outdated source tarballs from the upstream/ directory') + help='remove outdated source tarballs from the upstream/ directory') parser_metrics = subparsers.add_parser( 'metrics', epilog=epilog_metrics, formatter_class=argparse.RawDescriptionHelpFormatter, - help='Print metrics of given packages') + help='print metrics of given packages') parser_metrics.add_argument( - 'package_class', metavar='[package_name|:package_type:]', + 'package_class', metavar='[PACKAGE_NAME|pkg:pypi/DISTRIBUTION-NAME|:PACKAGE_TYPE:]', type=str, nargs='*', default=[':all:'], - help=('package name or designator for all packages of a given type ' + help=('package name, pkg:pypi/ followed by a distribution name, ' + 'or designator for all packages of a given type ' '(one of :all:, :standard:, :optional:, and :experimental:; default: :all:)')) return parser @@ -517,7 +536,9 @@ def run(): ssl._create_default_https_context = ssl._create_unverified_context except ImportError: pass - app.download_cls(args.package_name, + app.download_cls(*args.package_class, + has_files=args.has_files, no_files=args.no_files, + exclude=args.exclude, allow_upstream=args.allow_upstream, on_error=args.on_error) elif args.subcommand == 'create': diff --git a/build/sage_bootstrap/creator.py b/build/sage_bootstrap/creator.py index a738d772215..e16002f12f9 100644 --- a/build/sage_bootstrap/creator.py +++ b/build/sage_bootstrap/creator.py @@ -4,7 +4,8 @@ """ # **************************************************************************** -# Copyright (C) 2016 Volker Braun +# Copyright (C) 2015-2016 Volker Braun +# 2020-2024 Matthias Koeppe # # 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 @@ -105,7 +106,7 @@ def _remove_files(self, files): except OSError: pass - def set_python_data_and_scripts(self, pypi_package_name=None, source='normal'): + def set_python_data_and_scripts(self, pypi_package_name=None, source='normal', dependencies=None): """ Write the file ``dependencies`` and other files for Python packages. @@ -121,7 +122,15 @@ def set_python_data_and_scripts(self, pypi_package_name=None, source='normal'): if pypi_package_name is None: pypi_package_name = self.package_name with open(os.path.join(self.path, 'dependencies'), 'w+') as f: - f.write(' | $(PYTHON_TOOLCHAIN) $(PYTHON)\n\n') + if dependencies: + dependencies = ' '.join(dependencies) + else: + dependencies = '' + if source == 'wheel': + dependencies_order_only = 'pip $(PYTHON)' + else: + dependencies_order_only = '$(PYTHON_TOOLCHAIN) $(PYTHON)' + f.write(dependencies + ' | ' + dependencies_order_only + '\n\n') f.write('----------\nAll lines of this file are ignored except the first.\n') if source == 'normal': with open(os.path.join(self.path, 'spkg-install.in'), 'w+') as f: diff --git a/build/sage_bootstrap/download/cmdline.py b/build/sage_bootstrap/download/cmdline.py index a6afb4f92be..594cc773b38 100644 --- a/build/sage_bootstrap/download/cmdline.py +++ b/build/sage_bootstrap/download/cmdline.py @@ -6,7 +6,9 @@ """ # **************************************************************************** -# Copyright (C) 2016 Volker Braun +# Copyright (C) 2015-2016 Volker Braun +# 2015 Jeroen Demeyer +# 2020 Matthias Koeppe # # 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 diff --git a/build/sage_bootstrap/download/mirror_list.py b/build/sage_bootstrap/download/mirror_list.py index 4cab19f5d64..f464d87ffbd 100644 --- a/build/sage_bootstrap/download/mirror_list.py +++ b/build/sage_bootstrap/download/mirror_list.py @@ -4,7 +4,9 @@ """ #***************************************************************************** -# Copyright (C) 2015 Volker Braun +# Copyright (C) 2014-2016 Volker Braun +# 2015 Jeroen Demeyer +# 2023 Matthias Koeppe # # 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 diff --git a/build/sage_bootstrap/expand_class.py b/build/sage_bootstrap/expand_class.py index c7fdb308bd3..c5bab8a313e 100644 --- a/build/sage_bootstrap/expand_class.py +++ b/build/sage_bootstrap/expand_class.py @@ -4,7 +4,8 @@ """ # **************************************************************************** -# Copyright (C) 2016 Volker Braun +# Copyright (C) 2016 Volker Braun +# 2020-2024 Matthias Koeppe # # 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 @@ -52,12 +53,21 @@ def included_in_filter(pkg): self._init_optional(predicate=included_in_filter) elif package_name_or_class == ':experimental:': self._init_experimental(predicate=included_in_filter) + elif any(package_name_or_class.startswith(prefix) + for prefix in ["pkg:", "pypi/", "generic"]): + self.__names.add(Package(package_name_or_class).name) else: if ':' in package_name_or_class: - raise ValueError('a colon may only appear in designators of package types, ' + raise ValueError('a colon may only appear in a PURL such as ' + 'pkg:pypi/DISTRIBUTION-NAME ' + 'and in designators of package types, ' 'which must be one of ' ':all:, :standard:, :optional:, or :experimental:' 'got {}'.format(package_name_or_class)) + if '-' in package_name_or_class: + raise ValueError('dashes may only appear in a PURL such as ' + 'pkg:pypi/DISTRIBUTION-NAME; ' + 'SPKG names use underscores') self.__names.add(package_name_or_class) def include_recursive_dependencies(names, package_name): diff --git a/build/sage_bootstrap/package.py b/build/sage_bootstrap/package.py index 014b4f0d443..bc607ebb6e6 100644 --- a/build/sage_bootstrap/package.py +++ b/build/sage_bootstrap/package.py @@ -4,7 +4,9 @@ """ # **************************************************************************** -# Copyright (C) 2015 Volker Braun +# Copyright (C) 2015-2016 Volker Braun +# 2018 Jeroen Demeyer +# 2020-2024 Matthias Koeppe # # 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 @@ -25,6 +27,33 @@ class Package(object): + def __new__(cls, package_name): + if package_name.startswith("pypi/") or package_name.startswith("generic/"): + package_name = "pkg:" + package_name + if package_name.startswith("pkg:"): + package_name = package_name.replace('_', '-') + if package_name.startswith("pkg:generic/"): # fast path + try: + pkg = cls(package_name[len("pkg:generic/"):].replace('-', '_')) + if pkg.purl == package_name: + return pkg # assume unique + except Exception: + pass + elif package_name.startswith("pkg:pypi/"): # fast path + try: + pkg = cls(package_name[len("pkg:pypi/"):].replace('-', '_')) + if pkg.purl == package_name: + return pkg # assume unique + except Exception: + pass + for pkg in cls.all(): + if pkg.purl == package_name: + return pkg # assume unique + raise ValueError('no package for PURL {0}'.format(package_name)) + self = object.__new__(cls) + self.__init__(package_name) + return self + def __init__(self, package_name): """ Sage Package @@ -41,12 +70,22 @@ def __init__(self, package_name): -- ``package_name`` -- string. Name of the package. The Sage convention is that all package names are lower case. """ + if any(package_name.startswith(prefix) + for prefix in ["pkg:", "pypi/", "generic"]): + # Already initialized + return + if package_name != package_name.lower(): + raise ValueError('package names should be lowercase, got {0}'.format(package_name)) + if '-' in package_name: + raise ValueError('package names use underscores, not dashes, got {0}'.format(package_name)) + self.__name = package_name self.__tarball = None self._init_checksum() self._init_version() self._init_type() self._init_version_requirements() + self._init_requirements() self._init_dependencies() self._init_trees() @@ -308,7 +347,7 @@ def source(self): """ Return the package source type """ - if self.has_file('requirements.txt'): + if self.__requirements is not None: return 'pip' if self.tarball_filename: if self.tarball_filename.endswith('.whl'): @@ -331,15 +370,39 @@ def trees(self): return self.__trees if self.__version_requirements is not None: return 'SAGE_VENV' - if self.has_file('requirements.txt'): + if self.__requirements is not None: return 'SAGE_VENV' return 'SAGE_LOCAL' + @property + def purl(self): + """ + Return a PURL (Package URL) for the package + + OUTPUT: + + A string in the format ``SCHEME:TYPE/NAMESPACE/NAME``, + i.e., without components for version, qualifiers, and subpath. + See https://github.com/package-url/purl-spec/blob/master/PURL-SPECIFICATION.rst#package-url-specification-v10x + for details + """ + dist = self.distribution_name + if dist: + return 'pkg:pypi/' + dist.lower().replace('_', '-') + return 'pkg:generic/' + self.name.replace('_', '-') + @property def distribution_name(self): """ Return the Python distribution name or ``None`` for non-Python packages """ + if self.__requirements is not None: + for line in self.__requirements.split('\n'): + line = line.strip() + if line.startswith('#'): + continue + for part in line.split(): + return part if self.__version_requirements is None: return None for line in self.__version_requirements.split('\n'): @@ -405,7 +468,7 @@ def all(cls): continue try: yield cls(subdir) - except BaseException: + except Exception: log.error('Failed to open %s', subdir) raise @@ -501,6 +564,13 @@ def _init_version_requirements(self): except IOError: self.__version_requirements = None + def _init_requirements(self): + try: + with open(os.path.join(self.path, 'requirements.txt')) as f: + self.__requirements = f.read().strip() + except IOError: + self.__requirements = None + def _init_dependencies(self): try: with open(os.path.join(self.path, 'dependencies')) as f: diff --git a/build/sage_bootstrap/pypi.py b/build/sage_bootstrap/pypi.py index e3ca4e560c7..9427e9c8808 100644 --- a/build/sage_bootstrap/pypi.py +++ b/build/sage_bootstrap/pypi.py @@ -5,7 +5,8 @@ # **************************************************************************** -# Copyright (C) 2016 Volker Braun +# Copyright (C) 2016 Volker Braun +# 2020-2023 Matthias Koeppe # # 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 @@ -106,6 +107,13 @@ def summary(self): """ return self.json['info']['summary'] + @property + def requires_dist(self): + """ + Return the dependencies + """ + return self.json['info']['requires_dist'] + def update(self, package=None): if package is None: package = Package(self.name) diff --git a/build/sage_bootstrap/tarball.py b/build/sage_bootstrap/tarball.py index 07bdff45dd5..54af50dd8f8 100644 --- a/build/sage_bootstrap/tarball.py +++ b/build/sage_bootstrap/tarball.py @@ -4,7 +4,9 @@ """ # **************************************************************************** -# Copyright (C) 2015 Volker Braun +# Copyright (C) 2014-2015 Volker Braun +# 2017 Jeroen Demeyer +# 2020 Matthias Koeppe # # 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 diff --git a/build/sage_bootstrap/uninstall.py b/build/sage_bootstrap/uninstall.py index 08ce337386d..25496246ac7 100644 --- a/build/sage_bootstrap/uninstall.py +++ b/build/sage_bootstrap/uninstall.py @@ -24,7 +24,9 @@ are also removed. """ # **************************************************************************** -# Copyright (C) 2017 Erik M. Bray +# Copyright (C) 2017-2018 Erik M. Bray +# 2019 Jeroen Demeyer +# 2021-2022 Matthias Koeppe # # 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 diff --git a/pkgs/sage-conf/VERSION.txt b/pkgs/sage-conf/VERSION.txt index ef206d78947..54ab6979ef2 100644 --- a/pkgs/sage-conf/VERSION.txt +++ b/pkgs/sage-conf/VERSION.txt @@ -1 +1 @@ -10.4.beta5 +10.4.beta6 diff --git a/pkgs/sage-conf_conda/VERSION.txt b/pkgs/sage-conf_conda/VERSION.txt index ef206d78947..54ab6979ef2 100644 --- a/pkgs/sage-conf_conda/VERSION.txt +++ b/pkgs/sage-conf_conda/VERSION.txt @@ -1 +1 @@ -10.4.beta5 +10.4.beta6 diff --git a/pkgs/sage-conf_pypi/VERSION.txt b/pkgs/sage-conf_pypi/VERSION.txt index ef206d78947..54ab6979ef2 100644 --- a/pkgs/sage-conf_pypi/VERSION.txt +++ b/pkgs/sage-conf_pypi/VERSION.txt @@ -1 +1 @@ -10.4.beta5 +10.4.beta6 diff --git a/pkgs/sage-docbuild/VERSION.txt b/pkgs/sage-docbuild/VERSION.txt index ef206d78947..54ab6979ef2 100644 --- a/pkgs/sage-docbuild/VERSION.txt +++ b/pkgs/sage-docbuild/VERSION.txt @@ -1 +1 @@ -10.4.beta5 +10.4.beta6 diff --git a/pkgs/sage-setup/VERSION.txt b/pkgs/sage-setup/VERSION.txt index ef206d78947..54ab6979ef2 100644 --- a/pkgs/sage-setup/VERSION.txt +++ b/pkgs/sage-setup/VERSION.txt @@ -1 +1 @@ -10.4.beta5 +10.4.beta6 diff --git a/pkgs/sage-sws2rst/VERSION.txt b/pkgs/sage-sws2rst/VERSION.txt index ef206d78947..54ab6979ef2 100644 --- a/pkgs/sage-sws2rst/VERSION.txt +++ b/pkgs/sage-sws2rst/VERSION.txt @@ -1 +1 @@ -10.4.beta5 +10.4.beta6 diff --git a/pkgs/sagemath-bliss/VERSION.txt b/pkgs/sagemath-bliss/VERSION.txt index ef206d78947..54ab6979ef2 100644 --- a/pkgs/sagemath-bliss/VERSION.txt +++ b/pkgs/sagemath-bliss/VERSION.txt @@ -1 +1 @@ -10.4.beta5 +10.4.beta6 diff --git a/pkgs/sagemath-categories/VERSION.txt b/pkgs/sagemath-categories/VERSION.txt index ef206d78947..54ab6979ef2 100644 --- a/pkgs/sagemath-categories/VERSION.txt +++ b/pkgs/sagemath-categories/VERSION.txt @@ -1 +1 @@ -10.4.beta5 +10.4.beta6 diff --git a/pkgs/sagemath-categories/known-test-failures.json b/pkgs/sagemath-categories/known-test-failures.json index ab27f65ca8d..80705635b7e 100644 --- a/pkgs/sagemath-categories/known-test-failures.json +++ b/pkgs/sagemath-categories/known-test-failures.json @@ -597,6 +597,10 @@ "failed": true, "ntests": 126 }, + "sage.categories.noetherian_rings": { + "failed": true, + "ntests": 19 + }, "sage.categories.number_fields": { "failed": true, "ntests": 41 @@ -1428,4 +1432,4 @@ "sage.typeset.unicode_characters": { "ntests": 27 } -} \ No newline at end of file +} diff --git a/pkgs/sagemath-coxeter3/VERSION.txt b/pkgs/sagemath-coxeter3/VERSION.txt index ef206d78947..54ab6979ef2 100644 --- a/pkgs/sagemath-coxeter3/VERSION.txt +++ b/pkgs/sagemath-coxeter3/VERSION.txt @@ -1 +1 @@ -10.4.beta5 +10.4.beta6 diff --git a/pkgs/sagemath-environment/VERSION.txt b/pkgs/sagemath-environment/VERSION.txt index ef206d78947..54ab6979ef2 100644 --- a/pkgs/sagemath-environment/VERSION.txt +++ b/pkgs/sagemath-environment/VERSION.txt @@ -1 +1 @@ -10.4.beta5 +10.4.beta6 diff --git a/pkgs/sagemath-mcqd/VERSION.txt b/pkgs/sagemath-mcqd/VERSION.txt index ef206d78947..54ab6979ef2 100644 --- a/pkgs/sagemath-mcqd/VERSION.txt +++ b/pkgs/sagemath-mcqd/VERSION.txt @@ -1 +1 @@ -10.4.beta5 +10.4.beta6 diff --git a/pkgs/sagemath-meataxe/VERSION.txt b/pkgs/sagemath-meataxe/VERSION.txt index ef206d78947..54ab6979ef2 100644 --- a/pkgs/sagemath-meataxe/VERSION.txt +++ b/pkgs/sagemath-meataxe/VERSION.txt @@ -1 +1 @@ -10.4.beta5 +10.4.beta6 diff --git a/pkgs/sagemath-objects/VERSION.txt b/pkgs/sagemath-objects/VERSION.txt index ef206d78947..54ab6979ef2 100644 --- a/pkgs/sagemath-objects/VERSION.txt +++ b/pkgs/sagemath-objects/VERSION.txt @@ -1 +1 @@ -10.4.beta5 +10.4.beta6 diff --git a/pkgs/sagemath-repl/VERSION.txt b/pkgs/sagemath-repl/VERSION.txt index ef206d78947..54ab6979ef2 100644 --- a/pkgs/sagemath-repl/VERSION.txt +++ b/pkgs/sagemath-repl/VERSION.txt @@ -1 +1 @@ -10.4.beta5 +10.4.beta6 diff --git a/pkgs/sagemath-sirocco/VERSION.txt b/pkgs/sagemath-sirocco/VERSION.txt index ef206d78947..54ab6979ef2 100644 --- a/pkgs/sagemath-sirocco/VERSION.txt +++ b/pkgs/sagemath-sirocco/VERSION.txt @@ -1 +1 @@ -10.4.beta5 +10.4.beta6 diff --git a/pkgs/sagemath-tdlib/VERSION.txt b/pkgs/sagemath-tdlib/VERSION.txt index ef206d78947..54ab6979ef2 100644 --- a/pkgs/sagemath-tdlib/VERSION.txt +++ b/pkgs/sagemath-tdlib/VERSION.txt @@ -1 +1 @@ -10.4.beta5 +10.4.beta6 diff --git a/src/VERSION.txt b/src/VERSION.txt index ef206d78947..54ab6979ef2 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -10.4.beta5 +10.4.beta6 diff --git a/src/bin/sage-notebook b/src/bin/sage-notebook index 8f7687adcc5..bd898b2ae54 100755 --- a/src/bin/sage-notebook +++ b/src/bin/sage-notebook @@ -7,6 +7,8 @@ import ast import argparse import logging import textwrap +from contextlib import contextmanager + logging.basicConfig() logger = logging.getLogger() @@ -19,7 +21,6 @@ _system_jupyter_url = "https://doc.sagemath.org/html/en/installation/launching.h class NotebookJupyter(): def print_banner(self): - banner() print('Please wait while the Sage Jupyter Notebook server starts...') @classmethod @@ -50,7 +51,6 @@ class NotebookJupyter(): class NotebookJupyterlab(): def print_banner(self): - banner() print('Please wait while the Jupyterlab server starts...') @classmethod @@ -77,7 +77,6 @@ class NotebookJupyterlab(): class SageNBExport(NotebookJupyter): def print_banner(self): - banner() print('Please wait while the SageNB export server starts...') @classmethod @@ -131,7 +130,6 @@ EXAMPLES: """ - notebook_launcher = { 'default': NotebookJupyter, # change this to change the default 'ipython': NotebookJupyter, @@ -140,7 +138,6 @@ notebook_launcher = { 'export': SageNBExport, } - notebook_names = ', '.join(notebook_launcher.keys()) @@ -187,6 +184,32 @@ def trac_23428_browser_workaround(): os.environ['BROWSER'] = 'open' +@contextmanager +def sage_doc_server(): + from functools import partial + from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer + from threading import Thread + from sage.env import SAGE_DOC, SAGE_DOC_LOCAL_PORT as port + + server = ThreadingHTTPServer(('127.0.0.1', int(port)), + partial(SimpleHTTPRequestHandler, directory=SAGE_DOC)) + + if port == '0': + port = str(server.server_address[1]) + os.environ['SAGE_DOC_LOCAL_PORT'] = port + + server_thread = Thread(target=server.serve_forever, name="sage_doc_server") + server_thread.start() + print(f'Sage doc server started running at http://127.0.0.1:{port}') + + try: + yield + finally: + server.shutdown() + server_thread.join() + print(f'Sage doc server stopped runnning at http://127.0.0.1:{port}') + + if __name__ == '__main__': parser = make_parser() args, unknown = parser.parse_known_args(sys.argv[1:]) @@ -227,4 +250,19 @@ if __name__ == '__main__': launcher.print_help() sys.exit(0) - launcher(unknown) + banner() + + # Start a Sage doc server if the Sage documentation is available locally. + # See the corresponding code in src/sage/repl/ipython_kernel/kernel.py. + + from sage.env import SAGE_DOC_SERVER_URL + from sage.features.sagemath import sagemath_doc_html + + if SAGE_DOC_SERVER_URL: + print(f'Sage doc server is running at {SAGE_DOC_SERVER_URL}') + launcher(unknown) + elif sagemath_doc_html().is_present(): + with sage_doc_server(): + launcher(unknown) + else: + launcher(unknown) diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 90e01157b13..bab5675baae 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -4,6 +4,6 @@ # which stops "setup.py develop" from rewriting it as a Python file. : # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='10.4.beta5' -SAGE_RELEASE_DATE='2024-05-02' -SAGE_VERSION_BANNER='SageMath version 10.4.beta5, Release Date: 2024-05-02' +SAGE_VERSION='10.4.beta6' +SAGE_RELEASE_DATE='2024-05-12' +SAGE_VERSION_BANNER='SageMath version 10.4.beta6, Release Date: 2024-05-12' diff --git a/src/doc/en/developer/packaging.rst b/src/doc/en/developer/packaging.rst index 83720282c35..6a778c3a71a 100644 --- a/src/doc/en/developer/packaging.rst +++ b/src/doc/en/developer/packaging.rst @@ -14,7 +14,7 @@ The installation of packages is done through a bash script located in :sage_root:`build/bin/sage-spkg`. This script is typically invoked by giving the command:: - [alice@localhost sage]$ sage -i ... + [alice@localhost sage]$ ./sage -i ... options can be: @@ -1049,10 +1049,10 @@ Creating packages Assuming that you have downloaded ``$SAGE_ROOT/upstream/FoO-1.3.tar.gz``, you can use:: - [alice@localhost sage]$ sage --package create foo \ - --version 1.3 \ - --tarball FoO-VERSION.tar.gz \ - --type experimental + [alice@localhost sage]$ ./sage --package create foo \ + --version 1.3 \ + --tarball FoO-VERSION.tar.gz \ + --type experimental to create ``$SAGE_ROOT/build/pkgs/foo/package-version.txt``, ``checksums.ini``, and ``type`` in one step. @@ -1061,36 +1061,47 @@ You can skip the manual downloading of the upstream tarball by using the additional argument ``--upstream-url``. This command will also set the ``upstream_url`` field in ``checksums.ini`` described above. -For Python packages available from PyPI, you can use:: +For Python packages available from PyPI, use a PURL (Package URL, +see `PEP 725 `_):: - [alice@localhost sage]$ sage --package create scikit_spatial --pypi \ - --type optional + [alice@localhost sage]$ ./sage --package create pkg:pypi/scikit-spatial \ + --type optional -This automatically downloads the most recent version from PyPI and also -obtains most of the necessary information by querying PyPI. In particular, -the ``SPKG.rst`` file is created as a copy of the package's README file. +An equivalent command uses the SPKG name of the new package:: + [alice@localhost sage]$ ./sage --package create scikit_spatial --pypi \ + --type optional -The ``dependencies`` file may need editing (watch out for warnings regarding -``--no-deps`` that Sage issues during installation of the package!). +Either of these two commands automatically downloads the most recent version +from PyPI and also obtains most of the necessary information by querying PyPI. +In particular, the ``SPKG.rst`` file is created as a copy of the package's +README file. + +By default, when the package is available as a platform-independent +wheel, the ``sage --package`` creates a ``wheel`` package. In this case, +the ``dependencies`` file is automatically generated from the information +on PyPI, but may still need some manual editing. + +For ``normal`` and ``pip`` packages, the ``dependencies`` file is initialized +to the bare minimum and will need manual editing. (Watch out for warnings +regarding ``--no-deps`` that Sage issues during installation of the package!) Also you may want to set lower and upper bounds for acceptable package versions in the file ``version_requirements.txt``. (Make sure that the version in ``package-version.txt`` falls within this acceptable version range!) -By default, when the package is available as a platform-independent -wheel, the ``sage --package`` creates a wheel package. To create a normal package -instead (for example, when the package requires patching), you can use:: +To create a ``normal`` package instead of a ``wheel`` package (for example, when the +package requires patching), you can use:: - [alice@localhost sage]$ sage --package create scikit_spatial --pypi \ - --source normal \ - --type optional + [alice@localhost sage]$ ./sage --package create pkg:pypi/scikit-spatial \ + --source normal \ + --type optional -To create a pip package rather than a normal or wheel package, you can use:: +To create a ``pip`` package rather than a ``normal`` or ``wheel`` package, you can use:: - [alice@localhost sage]$ sage --package create scikit_spatial --pypi \ - --source pip \ - --type optional + [alice@localhost sage]$ ./sage --package create pkg:pypi/scikit-spatial \ + --source pip \ + --type optional When the package already exists, ``sage --package create`` overwrites it. @@ -1101,14 +1112,14 @@ Updating packages to a new version A package that has the ``upstream_url`` information can be updated by simply typing:: - [alice@localhost sage]$ sage --package update numpy 3.14.59 + [alice@localhost sage]$ ./sage --package update openblas 0.3.79 which will automatically download the archive and update the -information in ``build/pkgs/numpy/``. +information in ``build/pkgs/openblas/``. For Python packages available from PyPI, there is another shortcut:: - [alice@localhost sage]$ sage --package update-latest matplotlib + [alice@localhost sage]$ ./sage --package update-latest pkg:pypi/matplotlib Updating matplotlib: 3.3.0 -> 3.3.1 Downloading tarball to ...matplotlib-3.3.1.tar.bz2 [...............................................................] @@ -1122,10 +1133,10 @@ version range! If you pass the switch ``--commit``, the script will run ``git commit`` for you. -If you prefer to make update a package ``foo`` by making manual +If you prefer to update a package ``foo`` by making manual changes to the files in ``build/pkgs/foo``, you will need to run:: - [alice@localhost sage]$ sage --package fix-checksum foo + [alice@localhost sage]$ ./sage --package fix-checksum foo which will modify the ``checksums.ini`` file with the correct checksums. @@ -1138,7 +1149,7 @@ The command ``sage --package metrics`` computes machine-readable aggregated metrics for all packages in the Sage distribution or a given list of packages:: - [alice@localhost sage]$ sage --package metrics + [alice@localhost sage]$ ./sage --package metrics has_file_distros_arch_txt=181 has_file_distros_conda_txt=289 has_file_distros_debian_txt=172 @@ -1212,20 +1223,20 @@ Sage (``FoO-1.3.tar.gz`` in the example of section Now you can install the package using:: - [alice@localhost sage]$ sage -i package_name + [alice@localhost sage]$ ./sage -i package_name or:: - [alice@localhost sage]$ sage -f package_name + [alice@localhost sage]$ ./sage -f package_name to force a reinstallation. If your package contains a ``spkg-check`` script (see :ref:`section-spkg-check`) it can be run with:: - [alice@localhost sage]$ sage -i -c package_name + [alice@localhost sage]$ ./sage -i -c package_name or:: - [alice@localhost sage]$ sage -f -c package_name + [alice@localhost sage]$ ./sage -f -c package_name If all went fine, open a PR with the code under :sage_root:`build/pkgs`. diff --git a/src/doc/en/faq/faq-general.rst b/src/doc/en/faq/faq-general.rst index 354df395e99..f5457ddca0a 100644 --- a/src/doc/en/faq/faq-general.rst +++ b/src/doc/en/faq/faq-general.rst @@ -334,8 +334,10 @@ Here is a BibTeX entry for Sage: } Adjust version/year as needed. You might also like to use DOI for Sage, -as the note entry in the above record, or directly as DOI record. - +as the ``note`` entry in the above record, or directly as DOI record. +The DOI :doi:`10.5281/zenodo.8042260` represents all versions of Sage; +clicking on it will show the DOI for the latest Sage version +(at the time of writing, 10.3, see :doi:`10.5281/zenodo.10841614`). If you happen to use the Sage interface to PARI, GAP or Singular, you should definitely reference them as well. Likewise, if you use @@ -403,4 +405,4 @@ What are DOI records for Sage? e.g. see `record for Sage 9.5 `_. The corresponding :doi:`10.5281/zenodo.6259615`. -There is also DOI for the latest version, :doi:`10.5281/zenodo.593563`. +There is also DOI for the latest version, :doi:`10.5281/zenodo.8042260`. diff --git a/src/doc/en/reference/discrete_geometry/index.rst b/src/doc/en/reference/discrete_geometry/index.rst index c64c6d9cd8a..9dde6700341 100644 --- a/src/doc/en/reference/discrete_geometry/index.rst +++ b/src/doc/en/reference/discrete_geometry/index.rst @@ -80,6 +80,7 @@ Toric geometry sage/geometry/toric_lattice sage/geometry/cone sage/geometry/cone_catalog + sage/geometry/cone_critical_angles sage/geometry/fan sage/geometry/fan_morphism sage/geometry/point_collection diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 894b680b2a9..44c22814471 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -5120,6 +5120,13 @@ REFERENCES: Electronic Journal of Linear Algebra, 34:444-458, 2018, :doi:`10.13001/1081-3810.3782`. +.. [Or2020] \M. Orlitzky. When a maximal angle among cones is nonobtuse. + Computational and Applied Mathematics 39(2), 2020. + :doi:`10.1007/s40314-020-1115-y`. + +.. [Or2024] \M. Orlitzky. Continuity of the conic hull. + Journal of Convex Analysis 31(1):255-264, 2024. + .. [ORS2013] Peter Ozsvath, Jacob Rasmussen, Zoltan Szabo, *Odd Khovanov homology*, Algebraic & Geometric Topology 13 (2013) 1465–1488, @@ -6167,6 +6174,9 @@ REFERENCES: :doi: `10.1017/fms.2022.38`. :arxiv:`2106.11141`. +.. [Ryom2015] Steen Ryom-Hansen. *Projective modules for the symmetric group and + Young's seminormal form*. J. Algebra **439** (2015) pp. 515-541. + .. [SV1970] \H. Schneider and M. Vidyasagar. Cross-positive matrices. SIAM Journal on Numerical Analysis, 7:508-519, 1970. diff --git a/src/doc/en/thematic_tutorials/coercion_and_categories.rst b/src/doc/en/thematic_tutorials/coercion_and_categories.rst index afa83bfa754..0c6be456348 100644 --- a/src/doc/en/thematic_tutorials/coercion_and_categories.rst +++ b/src/doc/en/thematic_tutorials/coercion_and_categories.rst @@ -116,7 +116,6 @@ This base class provides a lot more methods than a general parent:: '_coerce_impl', '_default_category', '_gens', - '_ideal_class_', '_ideal_monoid', '_latex_names', '_list', @@ -127,8 +126,6 @@ This base class provides a lot more methods than a general parent:: '_zero_ideal', 'algebraic_closure', 'base_extend', - 'class_group', - 'content', 'derivation', 'derivation_module', 'divides', @@ -136,7 +133,6 @@ This base class provides a lot more methods than a general parent:: 'extension', 'fraction_field', 'frobenius_endomorphism', - 'gcd', 'gen', 'gens', 'ideal', @@ -144,7 +140,6 @@ This base class provides a lot more methods than a general parent:: 'integral_closure', 'is_commutative', 'is_field', - 'is_integral_domain', 'is_integrally_closed', 'is_noetherian', 'is_prime_field', @@ -858,7 +853,9 @@ The four axioms requested for coercions rational field is a homomorphism of euclidean domains:: sage: QQ.coerce_map_from(ZZ).category_for() - Join of Category of euclidean domains and Category of infinite sets + Join of Category of euclidean domains + and Category of noetherian rings + and Category of infinite sets and Category of metric spaces .. end of output diff --git a/src/doc/en/tutorial/tour_coercion.rst b/src/doc/en/tutorial/tour_coercion.rst index 9a3cb8e8ee7..f5c17d619d9 100644 --- a/src/doc/en/tutorial/tour_coercion.rst +++ b/src/doc/en/tutorial/tour_coercion.rst @@ -118,6 +118,7 @@ implemented in Sage as well: sage: ZZ.category() Join of Category of Dedekind domains and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces sage: ZZ.category().is_subcategory(Rings()) diff --git a/src/doc/fr/tutorial/tour_coercion.rst b/src/doc/fr/tutorial/tour_coercion.rst index 41ec9264ae6..e19487c0cbd 100644 --- a/src/doc/fr/tutorial/tour_coercion.rst +++ b/src/doc/fr/tutorial/tour_coercion.rst @@ -119,6 +119,7 @@ par ailleurs les catégories en tant que telles : sage: ZZ.category() Join of Category of Dedekind domains and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces sage: ZZ.category().is_subcategory(Rings()) diff --git a/src/doc/ja/tutorial/tour_coercion.rst b/src/doc/ja/tutorial/tour_coercion.rst index 6aaf7aa2911..f13b48c782b 100644 --- a/src/doc/ja/tutorial/tour_coercion.rst +++ b/src/doc/ja/tutorial/tour_coercion.rst @@ -101,6 +101,7 @@ Sageのクラス階層と圏の階層構造にはそれなりに類似が見ら sage: ZZ.category() Join of Category of Dedekind domains and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces sage: ZZ.category().is_subcategory(Rings()) diff --git a/src/doc/pt/tutorial/tour_coercion.rst b/src/doc/pt/tutorial/tour_coercion.rst index b5eeaa85a9f..94efa8cec15 100644 --- a/src/doc/pt/tutorial/tour_coercion.rst +++ b/src/doc/pt/tutorial/tour_coercion.rst @@ -125,6 +125,7 @@ categorias matemáticas também são implementadas no Sage: sage: ZZ.category() Join of Category of Dedekind domains and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces sage: ZZ.category().is_subcategory(Rings()) diff --git a/src/pyproject.toml b/src/pyproject.toml index 781af00a8d7..f70304534d9 100644 --- a/src/pyproject.toml +++ b/src/pyproject.toml @@ -11,12 +11,9 @@ requires = [ # Exclude 3.0.3 because of https://github.com/cython/cython/issues/5748 'cython >=3.0, != 3.0.3, <4.0', 'gmpy2 ~=2.1.b999', - 'jupyter_core >=4.6.3', 'memory_allocator', 'numpy >=1.19', 'pkgconfig', - # pplpy 0.8.4 and earlier do not declare dependencies correctly (see https://github.com/sagemath/sage/issues/30922) - 'pplpy >=0.8.6', ] build-backend = "setuptools.build_meta" diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index f87d52c94ce..05a686f95d0 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -514,6 +514,7 @@ def __init__(self, Q, names, category=None): sage: Cl.category() Category of finite dimensional super algebras with basis over (Dedekind domains and euclidean domains + and noetherian rings and infinite enumerated sets and metric spaces) sage: TestSuite(Cl).run() @@ -1093,6 +1094,7 @@ def lift_module_morphism(self, m, names=None): sage: phi.category_for() Category of finite dimensional super algebras with basis over (Dedekind domains and euclidean domains + and noetherian rings and infinite enumerated sets and metric spaces) sage: phi.matrix() [ 1 0 0 0 7 -3 -7 0] @@ -1177,6 +1179,7 @@ def lift_isometry(self, m, names=None): sage: phi.category_for() Category of finite dimensional super algebras with basis over (Dedekind domains and euclidean domains + and noetherian rings and infinite enumerated sets and metric spaces) sage: phi.matrix() [ 1 0 0 0 1 2 5 0] diff --git a/src/sage/algebras/free_algebra.py b/src/sage/algebras/free_algebra.py index e84a6b03f24..9564869df42 100644 --- a/src/sage/algebras/free_algebra.py +++ b/src/sage/algebras/free_algebra.py @@ -376,6 +376,10 @@ def is_FreeAlgebra(x) -> bool: sage: from sage.algebras.free_algebra import is_FreeAlgebra sage: is_FreeAlgebra(5) + doctest:warning... + DeprecationWarning: the function is_FreeAlgebra is deprecated; + use 'isinstance(..., (FreeAlgebra_generic, FreeAlgebra_letterplace))' instead + See https://github.com/sagemath/sage/issues/37896 for details. False sage: is_FreeAlgebra(ZZ) False @@ -387,6 +391,8 @@ def is_FreeAlgebra(x) -> bool: ....: degrees=list(range(1,11)))) True """ + from sage.misc.superseded import deprecation + deprecation(37896, "the function is_FreeAlgebra is deprecated; use 'isinstance(..., (FreeAlgebra_generic, FreeAlgebra_letterplace))' instead") return isinstance(x, (FreeAlgebra_generic, FreeAlgebra_letterplace)) @@ -742,7 +748,7 @@ def _coerce_map_from_(self, R): return True # free algebras in the same variable over any base that coerces in: - if is_FreeAlgebra(R): + if isinstance(R, (FreeAlgebra_generic, FreeAlgebra_letterplace)): if R.variable_names() == self.variable_names(): return self.base_ring().has_coerce_map_from(R.base_ring()) if isinstance(R, PBWBasisOfFreeAlgebra): diff --git a/src/sage/algebras/free_algebra_quotient.py b/src/sage/algebras/free_algebra_quotient.py index 94be310a0b1..df19a18e3e8 100644 --- a/src/sage/algebras/free_algebra_quotient.py +++ b/src/sage/algebras/free_algebra_quotient.py @@ -60,13 +60,16 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.algebras.free_algebra import is_FreeAlgebra +from sage.algebras.free_algebra import FreeAlgebra_generic from sage.algebras.free_algebra_quotient_element import FreeAlgebraQuotientElement from sage.categories.algebras import Algebras +from sage.misc.lazy_import import lazy_import from sage.modules.free_module import FreeModule from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent +lazy_import('sage.algebras.letterplace.free_algebra_letterplace', 'FreeAlgebra_letterplace') + class FreeAlgebraQuotient(UniqueRepresentation, Parent): @staticmethod @@ -153,7 +156,7 @@ def __init__(self, A, mons, mats, names): sage: TestSuite(H2).run() """ - if not is_FreeAlgebra(A): + if not isinstance(A, (FreeAlgebra_generic, FreeAlgebra_letterplace)): raise TypeError("argument A must be a free algebra") R = A.base_ring() n = A.ngens() diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 4077d52de00..5d9ca10e9f0 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -307,10 +307,16 @@ def is_QuaternionAlgebra(A): EXAMPLES:: sage: sage.algebras.quatalg.quaternion_algebra.is_QuaternionAlgebra(QuaternionAlgebra(QQ,-1,-1)) + doctest:warning... + DeprecationWarning: the function is_QuaternionAlgebra is deprecated; + use 'isinstance(..., QuaternionAlgebra_abstract)' instead + See https://github.com/sagemath/sage/issues/37896 for details. True sage: sage.algebras.quatalg.quaternion_algebra.is_QuaternionAlgebra(ZZ) False """ + from sage.misc.superseded import deprecation + deprecation(37896, "the function is_QuaternionAlgebra is deprecated; use 'isinstance(..., QuaternionAlgebra_abstract)' instead") return isinstance(A, QuaternionAlgebra_abstract) @@ -515,7 +521,7 @@ def is_integral_domain(self, proof=True) -> bool: def is_noetherian(self) -> bool: """ - Return ``True`` always, since any quaternion algebra is a noetherian + Return ``True`` always, since any quaternion algebra is a Noetherian ring (because it is a finitely generated module over a field). EXAMPLES:: diff --git a/src/sage/algebras/steenrod/steenrod_algebra.py b/src/sage/algebras/steenrod/steenrod_algebra.py index 3f9153cf962..f3f5447245f 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra.py +++ b/src/sage/algebras/steenrod/steenrod_algebra.py @@ -3054,7 +3054,7 @@ def is_integral_domain(self, proof=True): def is_noetherian(self): """ - This algebra is noetherian if and only if it is finite. + This algebra is Noetherian if and only if it is finite. EXAMPLES:: diff --git a/src/sage/categories/bimodules.py b/src/sage/categories/bimodules.py index 4e92f890cd0..f45f42c52dc 100644 --- a/src/sage/categories/bimodules.py +++ b/src/sage/categories/bimodules.py @@ -77,6 +77,7 @@ def _make_named_class_key(self, name): and Category of metric spaces, Join of Category of Dedekind domains and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces) @@ -85,6 +86,7 @@ def _make_named_class_key(self, name): (Category of fields, Join of Category of Dedekind domains and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces) diff --git a/src/sage/categories/category.py b/src/sage/categories/category.py index b71331c5ab5..ce316e50138 100644 --- a/src/sage/categories/category.py +++ b/src/sage/categories/category.py @@ -1228,7 +1228,7 @@ def structure(self): sage: structure(Rings()) (Category of unital magmas, Category of additive unital additive magmas) sage: structure(Fields()) - (Category of euclidean domains,) + (Category of euclidean domains, Category of noetherian rings) sage: structure(Algebras(QQ)) (Category of unital magmas, Category of right modules over Rational Field, @@ -2840,6 +2840,7 @@ def _make_named_class_key(self, name): sage: Algebras(ZZ)._make_named_class_key("parent_class") Join of Category of Dedekind domains and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces @@ -2852,6 +2853,7 @@ def _make_named_class_key(self, name): and Category of metric spaces, Join of Category of Dedekind domains and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces) @@ -2979,6 +2981,7 @@ def _make_named_class_key(self, name): sage: Modules(ZZ)._make_named_class_key('element_class') Join of Category of Dedekind domains and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces sage: Modules(QQ)._make_named_class_key('parent_class') diff --git a/src/sage/categories/category_types.py b/src/sage/categories/category_types.py index d80f5aa7bac..46736d03620 100644 --- a/src/sage/categories/category_types.py +++ b/src/sage/categories/category_types.py @@ -226,7 +226,8 @@ def _make_named_class_key(self, name): sage: Modules(ZZ)._make_named_class_key('element_class') Join of Category of Dedekind domains - and Category of euclidean domains + and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces sage: Modules(QQ)._make_named_class_key('parent_class') diff --git a/src/sage/categories/commutative_rings.py b/src/sage/categories/commutative_rings.py index 4e63eb8b594..38ae0d22ada 100644 --- a/src/sage/categories/commutative_rings.py +++ b/src/sage/categories/commutative_rings.py @@ -65,6 +65,39 @@ def is_commutative(self) -> bool: """ return True + def _ideal_class_(self, n=0): + r""" + Return a callable object that can be used to create ideals in this + commutative ring. + + This class can depend on `n`, the number of generators of the ideal. + The default input of `n=0` indicates an unspecified number of generators, + in which case a class that works for any number of generators is returned. + + EXAMPLES:: + + sage: ZZ._ideal_class_() + + sage: RR._ideal_class_() + + sage: R. = GF(5)[] + sage: R._ideal_class_(1) + + sage: S = R.quo(x^3 - y^2) + sage: S._ideal_class_(1) + + sage: S._ideal_class_(2) + + sage: T. = S[] # needs sage.libs.singular + sage: T._ideal_class_(5) # needs sage.libs.singular + + sage: T._ideal_class_(1) # needs sage.libs.singular + + """ + # One might need more than just n + from sage.rings.ideal import Ideal_generic, Ideal_principal + return Ideal_principal if n == 1 else Ideal_generic + def _test_divides(self, **options): r""" Run generic tests on the method :meth:`divides`. @@ -248,6 +281,18 @@ class Finite(CategoryWithAxiom): ....: GF(5)]) in Rings().Commutative().Finite() True """ + def extra_super_categories(self): + r""" + Let Sage know that finite commutative rings are Noetherian. + + EXAMPLES:: + + sage: CommutativeRings().Finite().extra_super_categories() + [Category of noetherian rings] + """ + from sage.categories.noetherian_rings import NoetherianRings + return [NoetherianRings()] + class ParentMethods: def cyclotomic_cosets(self, q, cosets=None): r""" @@ -367,7 +412,7 @@ def cyclotomic_cosets(self, q, cosets=None): try: ~q except ZeroDivisionError: - raise ValueError("%s is not invertible in %s" % (q,self)) + raise ValueError("%s is not invertible in %s" % (q, self)) if cosets is None: rest = set(self) @@ -378,7 +423,7 @@ def cyclotomic_cosets(self, q, cosets=None): while rest: x0 = rest.pop() o = [x0] - x = q*x0 + x = q * x0 while x != x0: o.append(x) rest.discard(x) diff --git a/src/sage/categories/fields.py b/src/sage/categories/fields.py index 7407632a93f..e0b03fa0901 100644 --- a/src/sage/categories/fields.py +++ b/src/sage/categories/fields.py @@ -18,6 +18,7 @@ from sage.categories.category_singleton import Category_contains_method_by_parent_class from sage.categories.euclidean_domains import EuclideanDomains from sage.categories.division_rings import DivisionRings +from sage.categories.noetherian_rings import NoetherianRings from sage.structure.element import coerce_binop @@ -33,7 +34,9 @@ class Fields(CategoryWithAxiom): sage: K Category of fields sage: Fields().super_categories() - [Category of euclidean domains, Category of division rings] + [Category of euclidean domains, + Category of division rings, + Category of noetherian rings] sage: K(IntegerRing()) Rational Field @@ -54,10 +57,9 @@ def extra_super_categories(self): EXAMPLES:: sage: Fields().extra_super_categories() - [Category of euclidean domains] - + [Category of euclidean domains, Category of noetherian rings] """ - return [EuclideanDomains()] + return [EuclideanDomains(), NoetherianRings()] def __contains__(self, x): """ @@ -165,7 +167,9 @@ def _call_(self, x): sage: K Category of fields sage: Fields().super_categories() - [Category of euclidean domains, Category of division rings] + [Category of euclidean domains, + Category of division rings, + Category of noetherian rings] sage: K(IntegerRing()) # indirect doctest Rational Field diff --git a/src/sage/categories/group_algebras.py b/src/sage/categories/group_algebras.py index c5cceb53633..6732a49b150 100644 --- a/src/sage/categories/group_algebras.py +++ b/src/sage/categories/group_algebras.py @@ -358,7 +358,7 @@ def is_integral_domain(self, proof=True): return ans # I haven't written is_noetherian(), because I don't know when group - # algebras are noetherian, and I haven't written is_prime_field(), because + # algebras are Noetherian, and I haven't written is_prime_field(), because # I don't know if that means "is canonically isomorphic to a prime field" # or "is identical to a prime field". diff --git a/src/sage/categories/homset.py b/src/sage/categories/homset.py index deb489a9042..29f4c7df69a 100644 --- a/src/sage/categories/homset.py +++ b/src/sage/categories/homset.py @@ -1245,7 +1245,7 @@ def reversed(self): the principal ideal domain Integer Ring to Ambient free module of rank 3 over the principal ideal domain Integer Ring in Category of finite dimensional modules with basis over (Dedekind - domains and euclidean domains + domains and euclidean domains and noetherian rings and infinite enumerated sets and metric spaces) sage: type(H) @@ -1254,7 +1254,7 @@ def reversed(self): the principal ideal domain Integer Ring to Ambient free module of rank 2 over the principal ideal domain Integer Ring in Category of finite dimensional modules with basis over (Dedekind - domains and euclidean domains + domains and euclidean domains and noetherian rings and infinite enumerated sets and metric spaces) sage: type(H.reversed()) diff --git a/src/sage/categories/integral_domains.py b/src/sage/categories/integral_domains.py index b6e6f59a196..d29b2b159f0 100644 --- a/src/sage/categories/integral_domains.py +++ b/src/sage/categories/integral_domains.py @@ -103,6 +103,8 @@ def is_integral_domain(self, proof=True): EXAMPLES:: + sage: ZZ.is_integral_domain() + True sage: QQ.is_integral_domain() True sage: Parent(QQ, category=IntegralDomains()).is_integral_domain() @@ -113,6 +115,9 @@ def is_integral_domain(self, proof=True): True sage: L.is_integral_domain(proof=True) # needs sage.combinat True + + sage: ZZ['x'].is_integral_domain() + True """ return True diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index c5b1aa2afb5..952ca09fbde 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -885,7 +885,8 @@ def __init_extra__(self): sage: M.category() # needs sage.modules Category of Cartesian products of modules with basis over (Dedekind domains and euclidean domains - and infinite enumerated sets and metric spaces) + and noetherian rings + and infinite enumerated sets and metric spaces) sage: M.base_ring() # needs sage.modules Integer Ring diff --git a/src/sage/categories/morphism.pyx b/src/sage/categories/morphism.pyx index d4b412dd126..57e00943eda 100644 --- a/src/sage/categories/morphism.pyx +++ b/src/sage/categories/morphism.pyx @@ -179,9 +179,12 @@ cdef class Morphism(Map): sage: f.category() Category of endsets of unital magmas and right modules over (Dedekind domains and euclidean domains + and noetherian rings + and infinite enumerated sets and metric spaces) + and left modules over + (Dedekind domains and euclidean domains + and noetherian rings and infinite enumerated sets and metric spaces) - and left modules over (Dedekind domains and euclidean domains - and infinite enumerated sets and metric spaces) sage: # needs sage.rings.number_field sage: K = CyclotomicField(12) diff --git a/src/sage/categories/noetherian_rings.py b/src/sage/categories/noetherian_rings.py new file mode 100644 index 00000000000..bdffb796243 --- /dev/null +++ b/src/sage/categories/noetherian_rings.py @@ -0,0 +1,86 @@ +r""" +Noetherian rings + +EXAMPLES:: + + sage: from sage.categories.noetherian_rings import NoetherianRings + sage: GF(4, "a") in NoetherianRings() # needs sage.rings.finite_rings + True + sage: QQ in NoetherianRings() + True + sage: ZZ in NoetherianRings() + True + sage: IntegerModRing(4) in NoetherianRings() + True + sage: IntegerModRing(5) in NoetherianRings() + True +""" +# **************************************************************************** +# Copyright (C) 2008 Teresa Gomez-Diaz (CNRS) +# 2012 Nicolas M. Thiery +# +# Distributed under the terms of the GNU General Public License (GPL) +# https://www.gnu.org/licenses/ +# ***************************************************************************** + +from sage.categories.category import Category +from sage.categories.commutative_rings import CommutativeRings + + +class NoetherianRings(Category): + """ + The category of Noetherian rings + + A Noetherian ring is a commutative ring in which + every ideal is finitely generated. + + See :wikipedia:`Noetherian ring` + + EXAMPLES:: + + sage: from sage.categories.noetherian_rings import NoetherianRings + sage: C = NoetherianRings(); C + Category of noetherian rings + sage: sorted(C.super_categories(), key=str) + [Category of commutative rings] + + TESTS:: + + sage: TestSuite(C).run() + """ + def super_categories(self): + """ + EXAMPLES:: + + sage: from sage.categories.noetherian_rings import NoetherianRings + sage: NoetherianRings().super_categories() + [Category of commutative rings] + """ + return [CommutativeRings()] + + class ParentMethods: + def is_noetherian(self, proof=True): + r""" + Return ``True``, since this in an object of the category + of Noetherian rings. + + EXAMPLES:: + + sage: ZZ.is_noetherian() + True + sage: QQ.is_noetherian() + True + sage: ZZ['x'].is_noetherian() + True + sage: R. = PolynomialRing(QQ) + sage: R.is_noetherian() + True + + sage: L. = LazyLaurentSeriesRing(QQ) # needs sage.combinat + sage: L.is_noetherian() # needs sage.combinat + True + """ + return True + + class ElementMethods: + pass diff --git a/src/sage/categories/primer.py b/src/sage/categories/primer.py index c7339b712e3..aa47130b205 100644 --- a/src/sage/categories/primer.py +++ b/src/sage/categories/primer.py @@ -352,18 +352,20 @@ sage: ZZ.category() Join of Category of Dedekind domains and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces sage: ZZ.categories() [Join of Category of Dedekind domains and Category of euclidean domains + and Category of noetherian rings and Category of infinite enumerated sets and Category of metric spaces, Category of Dedekind domains, Category of euclidean domains, Category of principal ideal domains, Category of unique factorization domains, Category of gcd domains, - Category of integral domains, Category of domains, + Category of integral domains, Category of domains, ... Category of commutative rings, Category of rings, ... Category of magmas and additive magmas, ... Category of monoids, Category of semigroups, diff --git a/src/sage/categories/principal_ideal_domains.py b/src/sage/categories/principal_ideal_domains.py index f020cfb383d..6118e06b1c7 100644 --- a/src/sage/categories/principal_ideal_domains.py +++ b/src/sage/categories/principal_ideal_domains.py @@ -1,16 +1,17 @@ r""" Principal ideal domains """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008 Teresa Gomez-Diaz (CNRS) # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#****************************************************************************** +# https://www.gnu.org/licenses/ +# ***************************************************************************** from sage.categories.category_singleton import Category_singleton from sage.categories.unique_factorization_domains import UniqueFactorizationDomains + class PrincipalIdealDomains(Category_singleton): """ The category of (constructive) principal ideal domains @@ -94,33 +95,175 @@ def _test_gcd_vs_xgcd(self, **options): # there are some strange things in Sage doctests... so it is better # to cut the list in order to avoid lists of size 531441. elts = elts[:10] - pairs = [(x,y) for x in elts for y in elts] + pairs = [(x, y) for x in elts for y in elts] try: - xgcds = [x.xgcd(y) for x,y in pairs] - except (AttributeError,NotImplementedError): + xgcds = [x.xgcd(y) for x, y in pairs] + except (AttributeError, NotImplementedError): return has_gcd = True try: - gcds = [x.gcd(y) for x,y in pairs] - except (AttributeError,NotImplementedError): + gcds = [x.gcd(y) for x, y in pairs] + except (AttributeError, NotImplementedError): has_gcd = False tester.assertTrue(has_gcd, "The ring {} provides a xgcd but no gcd".format(self)) - for (x,y),gcd,xgcd in zip(pairs,gcds,xgcds): + for (x, y), gcd, xgcd in zip(pairs, gcds, xgcds): tester.assertTrue(gcd.parent() == self, "The parent of the gcd is {} for element of {}".format( gcd.parent(), self)) tester.assertTrue(xgcd[0].parent() == self and - xgcd[1].parent() == self and xgcd[2].parent() == self, - "The parent of output in xgcd is different from " - "the parent of input for elements in {}".format(self)) + xgcd[1].parent() == self == xgcd[2].parent(), + "The parent of output in xgcd is different from " + "the parent of input for elements in {}".format(self)) tester.assertTrue(gcd == xgcd[0], "The methods gcd and xgcd disagree on {}:\n" " gcd({},{}) = {}\n" - " xgcd({},{}) = {}\n".format(self,x,y,gcd,x,y,xgcd)) + " xgcd({},{}) = {}\n".format(self, x, y, gcd, x, y, xgcd)) + + def is_noetherian(self) -> bool: + """ + Every principal ideal domain is Noetherian, so we return ``True``. + + EXAMPLES:: + + sage: Zp(5).is_noetherian() # needs sage.rings.padics + True + """ + return True + + def class_group(self): + """ + Return the trivial group, since the class group of a PID is trivial. + + EXAMPLES:: + + sage: QQ.class_group() # needs sage.groups + Trivial Abelian group + """ + from sage.groups.abelian_gps.abelian_group import AbelianGroup + return AbelianGroup([]) + + def gcd(self, x, y, coerce=True): + r""" + Return the greatest common divisor of ``x`` and ``y``, as elements + of ``self``. + + EXAMPLES: + + The integers are a principal ideal domain and hence a GCD domain:: + + sage: ZZ.gcd(42, 48) + 6 + sage: 42.factor(); 48.factor() + 2 * 3 * 7 + 2^4 * 3 + sage: ZZ.gcd(2^4*7^2*11, 2^3*11*13) + 88 + sage: 88.factor() + 2^3 * 11 + + In a field, any nonzero element is a GCD of any nonempty set + of nonzero elements. In previous versions, Sage used to return + 1 in the case of the rational field. However, since :issue:`10771`, + the rational field is considered as the + *fraction field* of the integer ring. For the fraction field + of an integral domain that provides both GCD and LCM, it is + possible to pick a GCD that is compatible with the GCD of the + base ring:: + + sage: QQ.gcd(ZZ(42), ZZ(48)); type(QQ.gcd(ZZ(42), ZZ(48))) + 6 + + sage: QQ.gcd(1/2, 1/3) + 1/6 + + Polynomial rings over fields are GCD domains as well. Here is a simple + example over the ring of polynomials over the rationals as well as + over an extension ring. Note that ``gcd`` requires x and y to be + coercible:: + + sage: # needs sage.rings.number_field + sage: R. = PolynomialRing(QQ) + sage: S. = NumberField(x^2 - 2, 'a') + sage: f = (x - a)*(x + a); g = (x - a)*(x^2 - 2) + sage: print(f); print(g) + x^2 - 2 + x^3 - a*x^2 - 2*x + 2*a + sage: f in R + True + sage: g in R + False + sage: R.gcd(f, g) + Traceback (most recent call last): + ... + TypeError: Unable to coerce 2*a to a rational + sage: R.base_extend(S).gcd(f,g) + x^2 - 2 + sage: R.base_extend(S).gcd(f, (x - a)*(x^2 - 3)) + x - a + """ + if coerce: + x = self(x) + y = self(y) + return x.gcd(y) + + def content(self, x, y, coerce=True): + r""" + Return the content of `x` and `y`. + + This is the unique element `c` of + ``self`` such that `x/c` and `y/c` + are coprime and integral. + + EXAMPLES:: + + sage: QQ.content(ZZ(42), ZZ(48)); type(QQ.content(ZZ(42), ZZ(48))) + 6 + + sage: QQ.content(1/2, 1/3) + 1/6 + sage: factor(1/2); factor(1/3); factor(1/6) + 2^-1 + 3^-1 + 2^-1 * 3^-1 + sage: a = (2*3)/(7*11); b = (13*17)/(19*23) + sage: factor(a); factor(b); factor(QQ.content(a,b)) + 2 * 3 * 7^-1 * 11^-1 + 13 * 17 * 19^-1 * 23^-1 + 7^-1 * 11^-1 * 19^-1 * 23^-1 + + Note the changes to the second entry:: + + sage: c = (2*3)/(7*11); d = (13*17)/(7*19*23) + sage: factor(c); factor(d); factor(QQ.content(c,d)) + 2 * 3 * 7^-1 * 11^-1 + 7^-1 * 13 * 17 * 19^-1 * 23^-1 + 7^-1 * 11^-1 * 19^-1 * 23^-1 + sage: e = (2*3)/(7*11); f = (13*17)/(7^3*19*23) + sage: factor(e); factor(f); factor(QQ.content(e,f)) + 2 * 3 * 7^-1 * 11^-1 + 7^-3 * 13 * 17 * 19^-1 * 23^-1 + 7^-3 * 11^-1 * 19^-1 * 23^-1 + """ + if coerce: + x = self(x) + y = self(y) + return x.content(y) + + def _ideal_class_(self, n=0): + """ + Ideals in PIDs have their own special class. + + EXAMPLES:: + + sage: ZZ._ideal_class_() + + """ + from sage.rings.ideal import Ideal_pid + return Ideal_pid class ElementMethods: pass diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index e8eee82998d..61f0f841720 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -35,7 +35,7 @@ lazy_import('sage.categories.commutative_rings', 'CommutativeRings') lazy_import('sage.categories.groups', 'Groups') lazy_import('sage.categories.objects', 'Objects') -lazy_import('sage.categories.rings', 'Rings', at_startup=True) +lazy_import('sage.categories.rings', 'Rings') # TODO, think through the rankings, and override pushout where necessary. diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index 9685fb80dd4..02e81ff1f63 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -339,6 +339,101 @@ def is_commutative(self) -> bool: """ return False + def is_integral_domain(self, proof=True) -> bool: + """ + Return ``True`` if this ring is an integral domain. + + INPUT: + + - ``proof`` -- (default: ``True``) Determines what to do in unknown + cases + + ALGORITHM: + + If the parameter ``proof`` is set to ``True``, the returned value is + correct but the method might throw an error. Otherwise, if it is set + to ``False``, the method returns ``True`` if it can establish that ``self`` + is an integral domain and ``False`` otherwise. + + EXAMPLES:: + + sage: QQ.is_integral_domain() + True + sage: ZZ.is_integral_domain() + True + sage: ZZ['x,y,z'].is_integral_domain() + True + sage: Integers(8).is_integral_domain() + False + sage: Zp(7).is_integral_domain() # needs sage.rings.padics + True + sage: Qp(7).is_integral_domain() # needs sage.rings.padics + True + sage: R. = QQ[] + sage: S. = R.quo((b^3)) # needs sage.libs.singular + sage: S.is_integral_domain() # needs sage.libs.singular + False + sage: R = ZZ.quotient(ZZ.ideal(10)); R.is_integral_domain() + False + + This illustrates the use of the ``proof`` parameter:: + + sage: R. = ZZ[] + sage: S. = R.quo((b^3)) # needs sage.libs.singular + sage: S.is_integral_domain(proof=True) # needs sage.libs.singular + Traceback (most recent call last): + ... + NotImplementedError + sage: S.is_integral_domain(proof=False) # needs sage.libs.singular + False + + TESTS: + + Make sure :issue:`10481` is fixed:: + + sage: x = polygen(ZZ, 'x') + sage: R. = ZZ['x'].quo(x^2) # needs sage.libs.pari + sage: R.fraction_field() # needs sage.libs.pari + Traceback (most recent call last): + ... + TypeError: self must be an integral domain. + sage: R.is_integral_domain() # needs sage.libs.pari + False + + Forward the proof flag to ``is_field``, see :issue:`22910`:: + + sage: # needs sage.libs.singular + sage: R1. = GF(5)[] + sage: F1 = R1.quotient_ring(x^2 + x + 1) + sage: R2. = F1[] + sage: F2 = R2.quotient_ring(x^2 + x + 1) + sage: F2.is_integral_domain(False) + False + """ + if self.is_field(proof): + return True + + if self.is_zero(): + return False + + if proof: + raise NotImplementedError + + return False + + def is_noetherian(self): + """ + Return ``True`` if this ring is Noetherian. + + EXAMPLES:: + + sage: QQ.is_noetherian() + True + sage: ZZ.is_noetherian() + True + """ + return False + def is_zero(self) -> bool: """ Return ``True`` if this is the zero ring. @@ -579,6 +674,26 @@ def ideal_monoid(self): from sage.rings.noncommutative_ideals import IdealMonoid_nc return IdealMonoid_nc(self) + def _ideal_class_(self, n=0): + r""" + Return a callable object that can be used to create ideals in this + ring. + + EXAMPLES:: + + sage: MS = MatrixSpace(QQ, 2, 2) # needs sage.modules + sage: MS._ideal_class_() # needs sage.modules + + + Since :issue:`7797`, non-commutative rings have ideals as well:: + + sage: A = SteenrodAlgebra(2) # needs sage.combinat sage.modules + sage: A._ideal_class_() # needs sage.combinat sage.modules + + """ + from sage.rings.noncommutative_ideals import Ideal_nc + return Ideal_nc + def characteristic(self): """ Return the characteristic of this ring. @@ -734,62 +849,6 @@ def ideal(self, *args, **kwds): gens = gens[0] return C(self, gens, **kwds) - def _ideal_class_(self, n=0): - """ - Return the class that is used to implement ideals of this ring. - - .. NOTE:: - - We copy the code from :class:`~sage.rings.ring.Ring`. This is - necessary because not all rings inherit from that class, such - as matrix algebras. - - INPUT: - - - ``n`` (optional integer, default 0): The number of generators - of the ideal to be created. - - OUTPUT: - - The class that is used to implement ideals of this ring with - ``n`` generators. - - .. NOTE:: - - Often principal ideals (``n==1``) are implemented via - a different class. - - EXAMPLES:: - - sage: MS = MatrixSpace(QQ, 2, 2) # needs sage.modules - sage: MS._ideal_class_() # needs sage.modules - - - We do not know of a commutative ring in Sage that does not inherit - from the base class of rings. So, we need to cheat in the next - example:: - - sage: super(Ring,QQ)._ideal_class_.__module__ - 'sage.categories.rings' - sage: super(Ring,QQ)._ideal_class_() - - sage: super(Ring,QQ)._ideal_class_(1) - - sage: super(Ring,QQ)._ideal_class_(2) - - """ - from sage.rings.noncommutative_ideals import Ideal_nc - try: - if not self.is_commutative(): - return Ideal_nc - except (NotImplementedError, AttributeError): - return Ideal_nc - from sage.rings.ideal import Ideal_generic, Ideal_principal - if n == 1: - return Ideal_principal - return Ideal_generic - - ## # Quotient rings def quotient(self, I, names=None, **kwds): """ diff --git a/src/sage/combinat/cluster_algebra_quiver/quiver.py b/src/sage/combinat/cluster_algebra_quiver/quiver.py index 3fc3499ef67..37103b623f4 100644 --- a/src/sage/combinat/cluster_algebra_quiver/quiver.py +++ b/src/sage/combinat/cluster_algebra_quiver/quiver.py @@ -787,9 +787,8 @@ def qmu_save(self, filename=None): string.append('1') string.append('//Matrix') string.append(str(m) + ' ' + str(m)) - for i in range(m): - string.append(' '.join(str(M[i, j]) - for j in range(m))) + string.extend(' '.join(str(M[i, j]) for j in range(m)) + for i in range(m)) string.append('//Points') for i in range(m): diff --git a/src/sage/combinat/nu_dyck_word.py b/src/sage/combinat/nu_dyck_word.py index ddc54a03612..bdc2c0f6e3a 100644 --- a/src/sage/combinat/nu_dyck_word.py +++ b/src/sage/combinat/nu_dyck_word.py @@ -829,7 +829,7 @@ def _latex_(self): if latex_options['show_grid']: grid = [((0, 0), (self.width(), self.height()))] for v1, v2 in grid: - res += " \\draw[dotted] %s grid %s;" % (str(v1), str(v2)) + res += f" \\draw[dotted] {v1} grid {v2};" res += "\n" # Add points if wanted @@ -838,8 +838,8 @@ def _latex_(self): radius = 0.15 + .03 * latex_options['line width'] for v in self.points(): res += " \\draw[line width=2," - res += "color=%s,fill=%s]" % (pt_color, pt_color) - res += "%s circle (%s);" % (str(v), str(radius)) + res += f"color={pt_color},fill={pt_color}]" + res += f"{v} circle ({radius});" res += "\n" # Add nu if wanted @@ -853,7 +853,7 @@ def _latex_(self): res += ";\n" # setup Path - res += " \\draw[rounded corners=1, color=%s, line width=%s]" % ( + res += " \\draw[rounded corners=1, color={}, line width={}]".format( latex_options['color'], str(latex_options['line width']) ) @@ -1210,46 +1210,46 @@ class options(GlobalOptions): """ NAME = 'NuDyckWords' module = 'sage.combinat.nu_dyck_path' - display = dict(default="list", - description='Specifies how nu Dyck words should be printed', - values=dict(list='displayed as a list', - lattice='displayed on the lattice defined by ``diagram_style``'), - case_sensitive=False) - ascii_art = dict(default="pretty_output", - description='Specifies how the ascii art of nu Dyck words should be printed', - values=dict(pretty_output="Using pretty printing"), - alias=dict(pretty_print="pretty_output",), - case_sensitive=False) - diagram_style = dict(default="grid", - values=dict( - grid='printing as paths on a grid using N and E steps',), - alias={'N-E': 'grid'}, - case_sensitive=False) - latex_tikz_scale = dict(default=1, - description='The default value for the tikz scale when latexed', - checker=lambda x: True) # More trouble than it's worth to check - latex_line_width_scalar = dict(default=2, - description='The default value for the line width as a ' - 'multiple of the tikz scale when latexed', - checker=lambda x: True) # More trouble than it's worth to check - latex_color = dict(default="black", - description='The default value for the color when latexed', - checker=lambda x: isinstance(x, str)) - latex_show_points = dict(default=False, - description='The default value for showing points', - checker=lambda x: isinstance(x, bool)) - latex_points_color = dict(default='black', - description='The default value for path color.', - checker=lambda x: isinstance(x, str)) - latex_show_grid = dict(default=True, - description='The default value for showing grid', - checker=lambda x: isinstance(x, bool)) - latex_show_nu = dict(default=True, - description='The default value for showing nu', - checker=lambda x: isinstance(x, bool)) - latex_nu_options = dict(default='rounded corners=1, color=red, line width=1', - description='The default value for options for nu path', - checker=lambda x: isinstance(x, str)) + display = {'default': "list", + 'description': 'Specifies how nu Dyck words should be printed', + 'values': {'list': 'displayed as a list', + 'lattice': 'displayed on the lattice defined by ``diagram_style``'}, + 'case_sensitive': False} + ascii_art = {'default': "pretty_output", + 'description': 'Specifies how the ascii art of nu Dyck words should be printed', + 'values': {'pretty_output': "Using pretty printing"}, + 'alias': {'pretty_print': "pretty_output"}, + 'case_sensitive': False} + diagram_style = {'default': "grid", + 'values': { + 'grid': 'printing as paths on a grid using N and E steps'}, + 'alias': {'N-E': 'grid'}, + 'case_sensitive': False} + latex_tikz_scale = {'default': 1, + 'description': 'The default value for the tikz scale when latexed', + 'checker': lambda x: True} # More trouble than it's worth to check + latex_line_width_scalar = {'default': 2, + 'description': 'The default value for the line width as a ' + 'multiple of the tikz scale when latexed', + 'checker': lambda x: True} # More trouble than it's worth to check + latex_color = {'default': "black", + 'description': 'The default value for the color when latexed', + 'checker': lambda x: isinstance(x, str)} + latex_show_points = {'default': False, + 'description': 'The default value for showing points', + 'checker': lambda x: isinstance(x, bool)} + latex_points_color = {'default': 'black', + 'description': 'The default value for path color.', + 'checker': lambda x: isinstance(x, str)} + latex_show_grid = {'default': True, + 'description': 'The default value for showing grid', + 'checker': lambda x: isinstance(x, bool)} + latex_show_nu = {'default': True, + 'description': 'The default value for showing nu', + 'checker': lambda x: isinstance(x, bool)} + latex_nu_options = {'default': 'rounded corners=1, color=red, line width=1', + 'description': 'The default value for options for nu path', + 'checker': lambda x: isinstance(x, str)} def _element_constructor_(self, word): """ diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index da521bd7822..539d63a867b 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -713,8 +713,8 @@ def _repr_exp_low(self): if not self._list: return '-' exp = self.to_exp() - return '%s' % ', '.join('%s%s' % (m+1, '' if e == 1 else '^%s' % e) - for (m,e) in enumerate(exp) if e > 0) + return '%s' % ', '.join('{}{}'.format(m + 1, '' if e == 1 else '^%s' % e) + for (m,e) in enumerate(exp) if e > 0) def _repr_exp_high(self): """ @@ -733,7 +733,7 @@ def _repr_exp_high(self): return '-' exp = self.to_exp()[::-1] # reversed list of exponents M = max(self) - return ', '.join('%s%s' % (M - m, '' if e == 1 else '^%s' % e) + return ', '.join('{}{}'.format(M - m, '' if e == 1 else '^%s' % e) for m, e in enumerate(exp) if e > 0) def _repr_compact_low(self): @@ -751,8 +751,8 @@ def _repr_compact_low(self): if not self._list: return '-' exp = self.to_exp() - return '%s' % ','.join('%s%s' % (m+1, '' if e == 1 else '^%s' % e) - for (m,e) in enumerate(exp) if e > 0) + return '%s' % ','.join('{}{}'.format(m + 1, '' if e == 1 else '^%s' % e) + for (m,e) in enumerate(exp) if e > 0) def _repr_compact_high(self): """ @@ -770,8 +770,8 @@ def _repr_compact_high(self): return '-' exp = self.to_exp()[::-1] # reversed list of exponents M = max(self) - return '%s' % ','.join('%s%s' % (M-m, '' if e == 1 else '^%s' % e) - for (m,e) in enumerate(exp) if e > 0) + return '%s' % ','.join('{}{}'.format(M - m, '' if e == 1 else '^%s' % e) + for (m,e) in enumerate(exp) if e > 0) def _repr_diagram(self): r""" @@ -945,8 +945,8 @@ def _latex_exp_low(self): if not self._list: return "{\\emptyset}" exp = self.to_exp() - return '%s' % ','.join('%s%s' % (m+1, '' if e == 1 else '^{%s}' % e) - for (m,e) in enumerate(exp) if e > 0) + return '%s' % ','.join('{}{}'.format(m + 1, '' if e == 1 else '^{%s}' % e) + for (m,e) in enumerate(exp) if e > 0) def _latex_exp_high(self): r""" @@ -963,8 +963,8 @@ def _latex_exp_high(self): return "{\\emptyset}" exp = self.to_exp()[::-1] # reversed list of exponents M = max(self) - return '%s' % ','.join('%s%s' % (M-m, '' if e == 1 else '^{%s}' % e) - for (m,e) in enumerate(exp) if e > 0) + return '%s' % ','.join('{}{}'.format(M - m, '' if e == 1 else '^{%s}' % e) + for (m,e) in enumerate(exp) if e > 0) def ferrers_diagram(self): r""" @@ -2339,11 +2339,7 @@ def cells(self): sage: Partition([3,2]).cells() [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1)] """ - res = [] - for i in range(len(self)): - for j in range(self[i]): - res.append( (i,j) ) - return res + return [(i, j) for i, si in enumerate(self) for j in range(si)] def generalized_pochhammer_symbol(self, a, alpha): r""" @@ -2925,7 +2921,7 @@ def top_garnir_tableau(self,e,cell): """ (row,col) = cell if row+1 >= len(self) or col >= self[row+1]: - raise ValueError('(%s,%s)=(row+1,col) must be inside the diagram' % (row+1,col)) + raise ValueError(f'({row+1},{col})=(row+1,col) must be inside the diagram') g = self.garnir_tableau(cell) # start with the Garnir tableau and modify @@ -2945,6 +2941,110 @@ def top_garnir_tableau(self,e,cell): t[row+1][col-b+1:col+1] = [m+a+col-b+1+i for i in range(b)] return tableau.StandardTableau(t) + def ladder_tableau(self, e, ladder_lengths=False): + r""" + Return the ladder tableau of shape ``self``. + + The `e`-*ladder tableau* is the standard Young tableau obtained + by reading the *ladders*, the set of cells `(i, j)` that differ + from `(i+e-1, j-1)`, of the partition `\lambda` from left-to-right. + + INPUT: + + - ``e`` -- a nonnegative integer; ``0`` is considered as `\infty` + (analogous to the characteristic of a ring) + - ``ladder_sizes`` -- (default: ``False``) if ``True``, also return + the sizes of the ladders + + .. SEEALSO:: + + :meth:`ladders` + + EXAMPLES:: + + sage: la = Partition([6, 5, 3, 1]) + sage: ascii_art(la.ladder_tableau(3)) + 1 2 3 5 7 10 + 4 6 8 11 13 + 9 12 14 + 15 + sage: la.ladder_tableau(3, ladder_lengths=True)[1] + [1, 1, 2, 2, 3, 3, 3] + + sage: ascii_art(la.ladder_tableau(0)) + 1 2 3 4 5 6 + 7 8 9 10 11 + 12 13 14 + 15 + sage: all(ll == 1 for ll in la.ladder_tableau(0, ladder_lengths=True)[1]) + True + """ + Tlad = [[None] * val for val in self] + counter = 0 + start = 0 + n = sum(self) + sizes = [] + e = e - 1 if e > 0 else n # change to the slope + while counter < n: + cur = start + size = 0 + for i, val in enumerate(self): + if cur < 0: + break + if cur < val: + counter += 1 + Tlad[i][cur] = counter + size += 1 + cur -= e + if ladder_lengths and size: + sizes.append(size) + start += 1 + ret = tableau.StandardTableaux(self)(Tlad) + if ladder_lengths: + return (ret, sizes) + return ret + + def ladders(self, e): + r""" + Return a dictionary containing the ladders in the diagram of ``self``. + + For `e > 0`, a node `(i, j)` in a partition belongs to the `l`-th + `e`-ladder if `l = (e - 1) r + c`. + + INPUT: + + - ``e`` -- a nonnegative integer; if ``0``, then we + set ``e = self.size() + 1`` + + EXAMPLES:: + + sage: Partition([3, 2]).ladders(3) + {0: [(0, 0)], 1: [(0, 1)], 2: [(0, 2), (1, 0)], 3: [(1, 1)]} + + When ``e`` is ``0``, the cells are in bijection with the ladders, + but the index of the ladder depends on the size of the partition:: + + sage: Partition([3, 2]).ladders(0) + {0: [(0, 0)], 1: [(0, 1)], 2: [(0, 2)], 5: [(1, 0)], 6: [(1, 1)]} + sage: Partition([3, 2, 1]).ladders(0) + {0: [(0, 0)], 1: [(0, 1)], 2: [(0, 2)], 6: [(1, 0)], 7: [(1, 1)], + 12: [(2, 0)]} + sage: Partition([3, 1, 1]).ladders(0) + {0: [(0, 0)], 1: [(0, 1)], 2: [(0, 2)], 5: [(1, 0)], 10: [(2, 0)]} + sage: Partition([1, 1, 1]).ladders(0) + {0: [(0, 0)], 3: [(1, 0)], 6: [(2, 0)]} + """ + if e == 0: + e = sum(self) + 1 + ladders = {} + for row, val in enumerate(self): + for col in range(val): + ell = col + row * (e - 1) + if ell not in ladders: + ladders[ell] = [] + ladders[ell].append((row, col)) + return ladders + @cached_method def young_subgroup(self): r""" @@ -3336,16 +3436,16 @@ def attacking_pairs(self): attacking_pairs = [] for i, r in enumerate(self): for j in range(r): - #c is in position (i,j) - #Find the d that satisfy condition 1 - for k in range(j+1, r): - attacking_pairs.append( ((i,j),(i,k)) ) + # c is in position (i,j) + # Find the d that satisfy condition 1 + attacking_pairs.extend(((i, j), (i, k)) + for k in range(j + 1, r)) - #Find the d that satisfy condition 2 + # Find the d that satisfy condition 2 if i == 0: continue - for k in range(j): - attacking_pairs.append( ((i,j),(i-1,k)) ) + attacking_pairs.extend(((i, j), (i - 1, k)) + for k in range(j)) return attacking_pairs @@ -4017,7 +4117,7 @@ def is_restricted(self, e, multicharge=(0,)): return (not self or ( self[-1] < e and all(self[r]-self[r+1] < e for r in range(len(self)-1)) )) - def is_regular(self, e, multicharge=(0,)): + def is_regular(self, e, multicharge=(0,)) -> bool: """ Return ``True`` is this is an ``e``-regular partition. @@ -4049,9 +4149,9 @@ def conjugacy_class_size(self): sage: Partition([2,1,1]).conjugacy_class_size() 6 """ - return factorial(sum(self))/self.centralizer_size() + return factorial(sum(self)) / self.centralizer_size() - def corners(self): + def corners(self) -> list: r""" Return a list of the corners of the partition ``self``. @@ -4224,9 +4324,8 @@ def rim(self): p = self res = [] prevLen = 1 - for i in range(len(p)-1, -1, -1): - for c in range(prevLen-1, p[i]): - res.append((i,c)) + for i in range(len(p) - 1, -1, -1): + res.extend((i, c) for c in range(prevLen - 1, p[i])) prevLen = p[i] return res @@ -4263,9 +4362,8 @@ def outer_rim(self): p = self res = [] prevLen = 0 - for i in range(len(p)-1, -1, -1): - for c in range(prevLen, p[i]+1): - res.append((i+1,c)) + for i in range(len(p) - 1, -1, -1): + res.extend((i + 1, c) for c in range(prevLen, p[i] + 1)) prevLen = p[i] res.append((0, prevLen)) return res @@ -4316,7 +4414,7 @@ def zero_one_sequence(self): sage: all(Partitions().from_zero_one(mu.zero_one_sequence()) == mu for n in range(10) for mu in Partitions(n)) True """ - tmp = set(self[i] - i for i in range(len(self))) + tmp = {si - i for i, si in enumerate(self)} return [Integer(i not in tmp) for i in range(-len(self) + 1, self.get_part(0) + 1)] @@ -4958,7 +5056,7 @@ def horizontal_border_strip_cells(self, k): [(3, 0), (3, 1)]] """ if k == 0: - return list() + return [] L = self._list shelf = [k] # the number of boxes which will fit in a row @@ -5099,11 +5197,8 @@ def atom(self): sage: Partition([3,2,1]).atom() [[[1, 2, 3, 6], [4, 5]], [[1, 2, 3], [4, 5], [6]]] """ - res = [] - for tab in tableau.StandardTableaux_size(self.size()): - if tab.atom() == self: - res.append(tab) - return res + return [tab for tab in tableau.StandardTableaux_size(self.size()) + if tab.atom() == self] def k_atom(self, k): r""" @@ -6059,7 +6154,7 @@ def __classcall_private__(cls, n=None, **kwargs): return RestrictedPartitions_n(n, kwargs['restricted']) # FIXME: should inherit from IntegerListLex, and implement repr, or _name as a lazy attribute - kwargs['name'] = "Partitions of the integer %s satisfying constraints %s" % (n, ", ".join( ["%s=%s" % (key, kwargs[key]) for key in sorted(kwargs)] )) + kwargs['name'] = "Partitions of the integer {} satisfying constraints {}".format(n, ", ".join( ["{}={}".format(key, kwargs[key]) for key in sorted(kwargs)] )) # min_part is at least 1, and it is 1 by default kwargs['min_part'] = max(1, kwargs.get('min_part', 1)) @@ -6195,34 +6290,34 @@ class options(GlobalOptions): """ NAME = 'Partitions' module = 'sage.combinat.partition' - display = dict(default="list", - description='Specifies how partitions should be printed', - values=dict(list='displayed as a list', - exp_low='in exponential form (lowest first)', - exp_high='in exponential form (highest first)', - diagram='as a Ferrers diagram', - compact_low='compact form of ``exp_low``', - compact_high='compact form of ``exp_high``'), - alias=dict(exp="exp_low", compact="compact_low", array="diagram", - ferrers_diagram="diagram", young_diagram="diagram"), - case_sensitive=False) - latex = dict(default="young_diagram", - description='Specifies how partitions should be latexed', - values=dict(diagram='latex as a Ferrers diagram', - young_diagram='latex as a Young diagram', - list='latex as a list', - exp_high='latex as a list in exponential notation (highest first)', - exp_low='as a list latex in exponential notation (lowest first)'), - alias=dict(exp="exp_low", array="diagram", ferrers_diagram="diagram"), - case_sensitive=False) - diagram_str = dict(default="*", - description='The character used for the cells when printing Ferrers diagrams', - checker=lambda char: isinstance(char,str)) - latex_diagram_str = dict(default="\\ast", - description='The character used for the cells when latexing Ferrers diagrams', - checker=lambda char: isinstance(char,str)) - convention = dict(link_to=(tableau.Tableaux.options,'convention')) - notation = dict(alt_name='convention') + display = {'default': "list", + 'description': 'Specifies how partitions should be printed', + 'values': {'list': 'displayed as a list', + 'exp_low': 'in exponential form (lowest first)', + 'exp_high': 'in exponential form (highest first)', + 'diagram': 'as a Ferrers diagram', + 'compact_low': 'compact form of ``exp_low``', + 'compact_high': 'compact form of ``exp_high``'}, + 'alias': {'exp': "exp_low", 'compact': "compact_low", 'array': "diagram", + 'ferrers_diagram': "diagram", 'young_diagram': "diagram"}, + 'case_sensitive': False} + latex = {'default': "young_diagram", + 'description': 'Specifies how partitions should be latexed', + 'values': {'diagram': 'latex as a Ferrers diagram', + 'young_diagram': 'latex as a Young diagram', + 'list': 'latex as a list', + 'exp_high': 'latex as a list in exponential notation (highest first)', + 'exp_low': 'as a list latex in exponential notation (lowest first)'}, + 'alias': {'exp': "exp_low", 'array': "diagram", 'ferrers_diagram': "diagram"}, + 'case_sensitive': False} + diagram_str = {'default': "*", + 'description': 'The character used for the cells when printing Ferrers diagrams', + 'checker': lambda char: isinstance(char,str)} + latex_diagram_str = {'default': "\\ast", + 'description': 'The character used for the cells when latexing Ferrers diagrams', + 'checker': lambda char: isinstance(char,str)} + convention = {'link_to': (tableau.Tableaux.options,'convention')} + notation = {'alt_name': 'convention'} def __reversed__(self): """ @@ -6282,7 +6377,7 @@ def _element_constructor_(self, lst): # trailing zeros are removed in Partition.__init__ return self.element_class(self, lst) - raise ValueError('%s is not an element of %s' % (lst, self)) + raise ValueError(f'{lst} is not an element of {self}') def __contains__(self, x): """ @@ -7122,7 +7217,7 @@ def _repr_(self): sage: Partitions(5, length=2) # indirect doctest Partitions of the integer 5 of length 2 """ - return "Partitions of the integer {} of length {}".format(self.n, self.k) + return f"Partitions of the integer {self.n} of length {self.k}" def _an_element_(self): """ @@ -7344,7 +7439,7 @@ def _repr_(self): sage: Partitions(5, parts_in=[1,2,3]) # indirect doctest Partitions of the integer 5 with parts in [1, 2, 3] """ - return "Partitions of the integer %s with parts in %s" % (self.n, self.parts) + return f"Partitions of the integer {self.n} with parts in {self.parts}" def cardinality(self): r""" @@ -7877,7 +7972,7 @@ def _repr_(self): sage: PartitionsInBox(2,2) # indirect doctest Integer partitions which fit in a 2 x 2 box """ - return "Integer partitions which fit in a %s x %s box" % (self.h, self.w) + return f"Integer partitions which fit in a {self.h} x {self.w} box" def __contains__(self, x): """ @@ -8161,7 +8256,7 @@ def _repr_(self): sage: RegularPartitions_all(3) 3-Regular Partitions """ - return "{}-Regular Partitions".format(self._ell) + return f"{self._ell}-Regular Partitions" def __iter__(self): """ @@ -8251,7 +8346,7 @@ def _repr_(self): sage: RegularPartitions_truncated(4, 3) 4-Regular Partitions with max length 3 """ - return "{}-Regular Partitions with max length {}".format(self._ell, self._max_len) + return f"{self._ell}-Regular Partitions with max length {self._max_len}" def __iter__(self): """ @@ -8369,7 +8464,7 @@ def _repr_(self): sage: RegularPartitions_bounded(4, 3) 4-Regular 3-Bounded Partitions """ - return "{}-Regular {}-Bounded Partitions".format(self._ell, self.k) + return f"{self._ell}-Regular {self.k}-Bounded Partitions" def __iter__(self): """ @@ -8432,7 +8527,7 @@ def _repr_(self): sage: RegularPartitions_n(3, 5) 5-Regular Partitions of the integer 3 """ - return "{}-Regular Partitions of the integer {}".format(self._ell, self.n) + return f"{self._ell}-Regular Partitions of the integer {self.n}" def __contains__(self, x): """ @@ -8724,7 +8819,7 @@ def _repr_(self): sage: PartitionsGreatestLE(10, 2) # indirect doctest Partitions of 10 having parts less than or equal to 2 """ - return "Partitions of %s having parts less than or equal to %s" % (self.n, self.k) + return f"Partitions of {self.n} having parts less than or equal to {self.k}" def cardinality(self): """ @@ -8814,7 +8909,7 @@ def _repr_(self): sage: PartitionsGreatestEQ(10, 2) # indirect doctest Partitions of 10 having greatest part equal to 2 """ - return "Partitions of %s having greatest part equal to %s" % (self.n, self.k) + return f"Partitions of {self.n} having greatest part equal to {self.k}" def cardinality(self): """ @@ -8996,7 +9091,7 @@ def _repr_(self): sage: RestrictedPartitions_all(3) 3-Restricted Partitions """ - return "{}-Restricted Partitions".format(self._ell) + return f"{self._ell}-Restricted Partitions" def __iter__(self): """ @@ -9051,7 +9146,7 @@ def _repr_(self): sage: RestrictedPartitions_n(3, 5) 5-Restricted Partitions of the integer 3 """ - return "{}-Restricted Partitions of the integer {}".format(self._ell, self.n) + return f"{self._ell}-Restricted Partitions of the integer {self.n}" def __contains__(self, x): """ diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 3919d9866da..0ec9451a9a9 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -239,7 +239,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** from __future__ import annotations -from typing import Iterator +from collections.abc import Iterator import itertools import operator @@ -680,9 +680,9 @@ def _latex_(self): redword = self.reduced_word() if not redword: return self.parent().options.latex_empty_str - return " ".join("%s_{%s}" % (let, i) for i in redword) + return " ".join(f"{let}_{{{i}}}" for i in redword) if display == "twoline": - return "\\begin{pmatrix} %s \\\\ %s \\end{pmatrix}" % ( + return r"\begin{{pmatrix}} {} \\ {} \end{{pmatrix}}".format( " & ".join("%s" % i for i in range(1, len(self._list)+1)), " & ".join("%s" % i for i in self._list)) if display == "list": @@ -1406,7 +1406,7 @@ def __call__(self, i): """ if isinstance(i, (int, Integer)) and 1 <= i <= len(self): return self[i - 1] - raise TypeError("i (= %s) must be between 1 and %s" % (i, len(self))) + raise TypeError(f"i (= {i}) must be between 1 and {len(self)}") ######## # Rank # @@ -2780,7 +2780,7 @@ def destandardize(self, weight, ordered_alphabet=None): for a in weight: partial.append(partial[-1]+a) if not set(ides).issubset(set(partial)): - raise ValueError("Standardization with weight {} is not possible!".format(weight)) + raise ValueError(f"Standardization with weight {weight} is not possible!") if ordered_alphabet is None: ordered_alphabet = list(range(1,len(weight)+1)) else: @@ -4221,7 +4221,7 @@ def right_permutohedron_interval_iterator(self, other): [2, 1, 5, 4, 3], [2, 5, 1, 4, 3], [2, 5, 4, 1, 3]] """ if len(self) != len(other): - raise ValueError("len({}) and len({}) must be equal".format(self, other)) + raise ValueError(f"len({self}) and len({other}) must be equal") if not self.permutohedron_lequal(other): raise ValueError("{} must be lower or equal than {} for the right permutohedron order".format(self, other)) d = DiGraph() @@ -6079,7 +6079,7 @@ def _repr_(self) -> str: sage: Permutations(3,2) Permutations of {1,...,3} of length 2 """ - return "Permutations of {1,...,%s} of length %s" % (self.n, self._k) + return f"Permutations of {{1,...,{self.n}}} of length {self._k}" def __iter__(self) -> Iterator[Permutation]: """ @@ -6710,7 +6710,7 @@ def _repr_(self): sage: Permutations([1,2,2], 2) Permutations of the multi-set [1, 2, 2] of length 2 """ - return "Permutations of the multi-set %s of length %s" % (list(self.mset), self._k) + return f"Permutations of the multi-set {list(self.mset)} of length {self._k}" def cardinality(self): """ @@ -6797,8 +6797,7 @@ def _repr_(self): sage: repr(Permutations([1,2,4],2)) 'Permutations of the set [1, 2, 4] of length 2' """ - return "Permutations of the set %s of length %s" % (list(self._set), - self._k) + return f"Permutations of the set {list(self._set)} of length {self._k}" def __iter__(self): """ @@ -6914,7 +6913,7 @@ def _repr_(self): sage: Arrangements([1,2,2],2) Arrangements of the multi-set [1, 2, 2] of length 2 """ - return "Arrangements of the multi-set %s of length %s" % (list(self.mset), self._k) + return f"Arrangements of the multi-set {list(self.mset)} of length {self._k}" class Arrangements_setk(Arrangements, Permutations_setk): @@ -6929,8 +6928,7 @@ def _repr_(self): sage: Arrangements([1,2,3],2) Arrangements of the set [1, 2, 3] of length 2 """ - return "Arrangements of the set %s of length %s" % (list(self._set), - self._k) + return f"Arrangements of the set {list(self._set)} of length {self._k}" ############################################################### # Standard permutations @@ -7420,8 +7418,8 @@ def element_in_conjugacy_classes(self, nu): from sage.combinat.partition import Partition nu = Partition(nu) if nu.size() > self.n: - raise ValueError("the size of the partition (=%s) should be at most" - " the size of the permutations (=%s)" % (nu.size(), self.n)) + raise ValueError("the size of the partition (={}) should be at most" + " the size of the permutations (={})".format(nu.size(), self.n)) l = [] i = 0 for nui in nu: @@ -8307,7 +8305,7 @@ def _repr_(self): sage: Permutations(descents=([1,0,4,8],12)) Standard permutations of 12 with descents [0, 1, 4, 8] """ - return "Standard permutations of %s with descents %s" % (self.n, list(self._d)) + return f"Standard permutations of {self.n} with descents {list(self._d)}" def cardinality(self): """ @@ -9239,8 +9237,7 @@ def _repr_(self): sage: CyclicPermutationsOfPartition([[1,2,3,4],[5,6,7]]) Cyclic permutations of partition [[1, 2, 3, 4], [5, 6, 7]] """ - return "Cyclic permutations of partition {}".format( - [list(_) for _ in self.partition]) + return f"Cyclic permutations of partition {[list(_) for _ in self.partition]}" def __iter__(self, distinct=False): """ @@ -9498,7 +9495,7 @@ def _repr_(self): sage: Permutations(3, avoiding=[[2, 1, 3],[1,2,3]]) Standard permutations of 3 avoiding [[2, 1, 3], [1, 2, 3]] """ - return "Standard permutations of %s avoiding %s" % (self.n, list(self._a)) + return f"Standard permutations of {self.n} avoiding {list(self._a)}" def __iter__(self): """ diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index d2d9d6385e5..aab1cf6d2bb 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -246,6 +246,10 @@ def is_SymmetricFunctionAlgebra(x): sage: from sage.combinat.sf.sfa import is_SymmetricFunctionAlgebra sage: is_SymmetricFunctionAlgebra(5) + doctest:warning... + DeprecationWarning: the function is_SymmetricFunctionAlgebra is deprecated; + use 'isinstance(..., SymmetricFunctionAlgebra_generic)' instead + See https://github.com/sagemath/sage/issues/37896 for details. False sage: is_SymmetricFunctionAlgebra(ZZ) False @@ -258,6 +262,8 @@ def is_SymmetricFunctionAlgebra(x): sage: is_SymmetricFunctionAlgebra(SymmetricFunctions(FractionField(QQ['q','t'])).macdonald().P()) True """ + from sage.misc.superseded import deprecation + deprecation(37896, "the function is_SymmetricFunctionAlgebra is deprecated; use 'isinstance(..., SymmetricFunctionAlgebra_generic)' instead") return isinstance(x, SymmetricFunctionAlgebra_generic) diff --git a/src/sage/combinat/symmetric_group_algebra.py b/src/sage/combinat/symmetric_group_algebra.py index 3b7673a5425..f79413e2be3 100644 --- a/src/sage/combinat/symmetric_group_algebra.py +++ b/src/sage/combinat/symmetric_group_algebra.py @@ -1244,6 +1244,100 @@ def _blocks_dictionary(self): blocks[c] = [la] return blocks + def ladder_idemponent(self, la): + r""" + Return the ladder idempontent of ``self``. + + Let `F` be a field of characteristic `p`. The *ladder idempotent* + of shape `\lambda` is the idempotent of `F[S_n]` defined as follows. + Let `T` be the :meth:`ladder tableau + ` of shape `\lambda`. + Let `[T]` be the set of standard tableaux whose residue sequence + is the same as for `T`. Let `\alpha` be the sizes of the ladders + of `\lambda`. Then the ladder idempontent is constructed as + + .. MATH:: + + \widetilde{e}_{\lambda} := \frac{1}{\alpha!} + \left( \sum_{\sigma \in S_{\alpha}} \sigma \right) + \left( \overline{\sum_{U \in [T]} E_U} \right), + + where `E_{UU}` is the :meth:`seminormal_basis` element over `\QQ` + and we project the sum to `F`, `S_{\alpha}` is the Young subgroup + corresponding to `\alpha`, and `\alpha! = \alpha_1! \cdots \alpha_k!`. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(GF(3), 4) + sage: for la in Partitions(SGA.n): + ....: idem = SGA.ladder_idemponent(la) + ....: print(la) + ....: print(idem) + ....: assert idem^2 == idem + [4] + 2*[1, 2, 3, 4] + 2*[1, 2, 4, 3] + 2*[2, 1, 3, 4] + 2*[2, 1, 4, 3] + + 2*[3, 4, 1, 2] + 2*[3, 4, 2, 1] + 2*[4, 3, 1, 2] + 2*[4, 3, 2, 1] + [3, 1] + 2*[1, 2, 3, 4] + 2*[1, 2, 4, 3] + 2*[2, 1, 3, 4] + 2*[2, 1, 4, 3] + + [3, 4, 1, 2] + [3, 4, 2, 1] + [4, 3, 1, 2] + [4, 3, 2, 1] + [2, 2] + 2*[1, 2, 3, 4] + 2*[1, 2, 4, 3] + 2*[2, 1, 3, 4] + 2*[2, 1, 4, 3] + + 2*[3, 4, 1, 2] + 2*[3, 4, 2, 1] + 2*[4, 3, 1, 2] + 2*[4, 3, 2, 1] + [2, 1, 1] + 2*[1, 2, 3, 4] + [1, 2, 4, 3] + 2*[1, 3, 2, 4] + [1, 3, 4, 2] + + [1, 4, 2, 3] + 2*[1, 4, 3, 2] + 2*[2, 1, 3, 4] + [2, 1, 4, 3] + + 2*[2, 3, 1, 4] + [2, 3, 4, 1] + [2, 4, 1, 3] + 2*[2, 4, 3, 1] + + 2*[3, 1, 2, 4] + [3, 1, 4, 2] + 2*[3, 2, 1, 4] + [3, 2, 4, 1] + + [4, 1, 2, 3] + 2*[4, 1, 3, 2] + [4, 2, 1, 3] + 2*[4, 2, 3, 1] + [1, 1, 1, 1] + 2*[1, 2, 3, 4] + [1, 2, 4, 3] + [2, 1, 3, 4] + 2*[2, 1, 4, 3] + + 2*[3, 4, 1, 2] + [3, 4, 2, 1] + [4, 3, 1, 2] + 2*[4, 3, 2, 1] + + When `p = 0`, these idempotents will generate all of the simple + modules (which are the :meth:`Specht modules ` + and also projective modules):: + + sage: SGA = SymmetricGroupAlgebra(QQ, 5) + sage: for la in Partitions(SGA.n): + ....: idem = SGA.ladder_idemponent(la) + ....: assert idem^2 == idem + ....: print(la, SGA.principal_ideal(idem).dimension()) + [5] 1 + [4, 1] 4 + [3, 2] 5 + [3, 1, 1] 6 + [2, 2, 1] 5 + [2, 1, 1, 1] 4 + [1, 1, 1, 1, 1] 1 + sage: [StandardTableaux(la).cardinality() for la in Partitions(SGA.n)] + [1, 4, 5, 6, 5, 4, 1] + + REFERENCES: + + - [Ryom2015]_ + """ + R = self.base_ring() + p = R.characteristic() + n = self.n + if not p: + p = n + 1 + la = _Partitions(la) + if sum(la) != n: + raise ValueError(f"{la} is not a partition of {n}") + Tlad, alpha = la.ladder_tableau(p, ladder_lengths=True) + if not all(val < p for val in alpha): + raise ValueError(f"{la} is not {p}-ladder restricted") + Tclass = Tlad.residue_sequence(p).standard_tableaux() + Elad = sum(epsilon_ik(T, T) for T in Tclass) + Elad = self.element_class(self, {sigma: R(c) for sigma, c in Elad._monomial_coefficients.items()}) + from sage.groups.perm_gps.permgroup_named import SymmetricGroup + YG = SymmetricGroup(n).young_subgroup(alpha) + coeff = ~R.prod(factorial(val) for val in alpha) + G = self.group() + eprod = self.element_class(self, {G(list(elt.tuple())): coeff + for elt in YG}) + return Elad * eprod + @cached_method def algebra_generators(self): r""" diff --git a/src/sage/combinat/words/finite_word.py b/src/sage/combinat/words/finite_word.py index 1bdd38e92c7..1b5dcb3b03e 100644 --- a/src/sage/combinat/words/finite_word.py +++ b/src/sage/combinat/words/finite_word.py @@ -293,7 +293,7 @@ def _repr_(self): if word_options['old_repr']: if word_options['truncate'] and \ self.length() > word_options['truncate_length']: - return "Finite word of length %s over %s" % (self.length(), str(self.parent().alphabet())[17:]) + return "Finite word of length {} over {}".format(self.length(), str(self.parent().alphabet())[17:]) return word_options['identifier'] + self.string_rep() def coerce(self, other): @@ -331,7 +331,7 @@ def coerce(self, other): self = other.parent()(self) self.parent()._check(self, length=None) except Exception: - raise TypeError("no coercion rule between %r and %r" % (self.parent(), other.parent())) + raise TypeError("no coercion rule between {!r} and {!r}".format(self.parent(), other.parent())) return self, other def __hash__(self): @@ -1261,7 +1261,7 @@ def number_of_factors(self, n=None, algorithm='suffix tree'): elif algorithm == 'naive': return ZZ(len(self.factor_set(n, algorithm='naive'))) else: - raise ValueError('Unknown algorithm (={})'.format(algorithm)) + raise ValueError(f'Unknown algorithm (={algorithm})') def factor_iterator(self, n=None): r""" @@ -1437,7 +1437,7 @@ def factor_set(self, n=None, algorithm='suffix tree'): S.add(self[i:i+n]) return Set(S) else: - raise ValueError('Unknown algorithm (={})'.format(algorithm)) + raise ValueError(f'Unknown algorithm (={algorithm})') def topological_entropy(self, n): r""" @@ -2067,8 +2067,7 @@ def _conjugates_list(self): [word: a] """ S = [self] - for i in range(1, self.length()): - S.append(self.conjugate(i)) + S.extend(self.conjugate(i) for i in range(1, self.length())) return S def conjugates_iterator(self): @@ -2853,9 +2852,9 @@ def length_maximal_palindrome(self, j, m=None, f=None): # Initialize the next (left) position to check i = (jj - m - 1) / 2 if not i.is_integer(): - raise ValueError("(2*j-m-1)/2(={}) must be an integer, i.e., " - "2*j(={}) and m(={}) can't " - "have the same parity".format(i, jj, m)) + raise ValueError(f"(2*j-m-1)/2(={i}) must be an integer, i.e., " + f"2*j(={jj}) and m(={m}) can't " + "have the same parity") i = Integer(i) # Compute @@ -2970,8 +2969,7 @@ def lps_lengths(self, f=None): for j in range(1, 2 * len(self) + 1): Nj = j + LPC[j] if Nj > Nk: - for i in range(Nk + 2 - (Nk % 2), Nj + 1, 2): - LPS.append(i - j) + LPS.extend(i - j for i in range(Nk + 2 - (Nk % 2), Nj + 1, 2)) Nk = Nj return LPS @@ -3858,8 +3856,7 @@ def subword_complementaries(self, other): if len(m) == 1: temp.append([j]+m[0]) if len(m) > 1: - for sw in m: - temp.append([j]+sw) + temp.extend([j] + sw for sw in m) Mpos[i][j] = temp # Create the list of positions for occurrences of `self` as a subword @@ -3875,7 +3872,7 @@ def subword_complementaries(self, other): comp_words.append(Word([other[i] for i in comp_pos])) return comp_words - def is_lyndon(self): + def is_lyndon(self) -> bool: r""" Return ``True`` if ``self`` is a Lyndon word, and ``False`` otherwise. diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 9424a3370b7..46207beab1c 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -883,18 +883,15 @@ def _latex_(self): A = self.domain().alphabet() latex_layout = self.latex_layout() if latex_layout == 'oneliner': - L = [r"%s \mapsto %s" % (a, self.image(a)) for a in A] - return LatexExpr(r','.join(L)) - elif latex_layout == 'array': + lines = (fr"{a} \mapsto {self.image(a)}" for a in A) + return LatexExpr(r','.join(lines)) + if latex_layout == 'array': s = r"\begin{array}{l}" + '\n' - lines = [] - for a in A: - lines.append(r"%s \mapsto %s" % (a, self.image(a))) + lines = (fr"{a} \mapsto {self.image(a)}" for a in A) s += '\\\\\n'.join(lines) s += '\n' + r"\end{array}" return LatexExpr(s) - else: - raise ValueError('unknown latex_layout(=%s)' % latex_layout) + raise ValueError('unknown latex_layout(=%s)' % latex_layout) def __mul__(self, other): r""" @@ -1693,8 +1690,7 @@ def is_prolongable(self, letter): TypeError: codomain of self must be an instance of Words """ if letter not in self.domain().alphabet(): - raise TypeError("letter (=%s) is not in the domain alphabet (=%s)" - % (letter, self.domain().alphabet())) + raise TypeError("letter (={}) is not in the domain alphabet (={})".format(letter, self.domain().alphabet())) image = self.image(letter) return not image.is_empty() and letter == image[0] @@ -1893,11 +1889,9 @@ def fixed_points(self): [] """ - L = [] - for letter in self.domain().alphabet(): - if self.is_prolongable(letter=letter): - L.append(self.fixed_point(letter=letter)) - return L + return [self.fixed_point(letter=letter) + for letter in self.domain().alphabet() + if self.is_prolongable(letter=letter)] def periodic_point(self, letter): r""" diff --git a/src/sage/combinat/words/paths.py b/src/sage/combinat/words/paths.py index f5506152d44..21709124f64 100644 --- a/src/sage/combinat/words/paths.py +++ b/src/sage/combinat/words/paths.py @@ -1489,11 +1489,11 @@ def is_tangent(self): class FiniteWordPath_2d(FiniteWordPath_all): - def plot(self, pathoptions=dict(rgbcolor='red', thickness=3), - fill=True, filloptions=dict(rgbcolor='red', alpha=0.2), - startpoint=True, startoptions=dict(rgbcolor='red', pointsize=100), - endarrow=True, arrowoptions=dict(rgbcolor='red', arrowsize=20, width=3), - gridlines=False, gridoptions=dict()): + def plot(self, pathoptions={"rgbcolor": 'red', "thickness": 3}, + fill=True, filloptions={"rgbcolor": 'red', "alpha": 0.2}, + startpoint=True, startoptions={"rgbcolor": 'red', "pointsize": 100}, + endarrow=True, arrowoptions={"rgbcolor": 'red', "arrowsize": 20, "width": 3}, + gridlines=False, gridoptions={}): r""" Return a 2d Graphics illustrating the path. @@ -1698,7 +1698,7 @@ def animate(self): return animate(images, **kwds) - def plot_directive_vector(self, options=dict(rgbcolor='blue')): + def plot_directive_vector(self, options={"rgbcolor": 'blue'}): r""" Return an arrow 2d graphics that goes from the start of the path to the end. @@ -2003,8 +2003,8 @@ def ymax(self): class FiniteWordPath_3d(FiniteWordPath_all): - def plot(self, pathoptions=dict(rgbcolor='red', arrow_head=True, thickness=3), - startpoint=True, startoptions=dict(rgbcolor='red', size=10)): + def plot(self, pathoptions={"rgbcolor": 'red', "arrow_head": True, "thickness": 3}, + startpoint=True, startoptions={"rgbcolor": 'red', "size": 10}): r""" INPUT: diff --git a/src/sage/combinat/words/suffix_trees.py b/src/sage/combinat/words/suffix_trees.py index 8c56ba3d4b3..c2bda4caf96 100644 --- a/src/sage/combinat/words/suffix_trees.py +++ b/src/sage/combinat/words/suffix_trees.py @@ -1740,7 +1740,7 @@ def treat_node(current_node, parent): D = self.transition_function_dictionary() string_depth = {0: 0} n = len(self.word()) - labeling = dict() + labeling = {} treat_node(0, None) return labeling @@ -1827,7 +1827,7 @@ def treat_node(current_node, i, j): walk_chain(current_node, child, l, square_start) prelabeling = self._partial_labeling() - labeling = dict() + labeling = {} D = self.transition_function_dictionary() treat_node(0, 0, 0) return labeling diff --git a/src/sage/combinat/yang_baxter_graph.py b/src/sage/combinat/yang_baxter_graph.py index f2fdb7a5a47..6b7684e077a 100644 --- a/src/sage/combinat/yang_baxter_graph.py +++ b/src/sage/combinat/yang_baxter_graph.py @@ -166,7 +166,7 @@ def _successors(self, u): if v != u: yield (v, op) - def __repr__(self): + def __repr__(self) -> str: r""" EXAMPLES:: @@ -176,7 +176,7 @@ def __repr__(self): sage: Y.__repr__() 'Yang-Baxter graph with root vertex (1, 2, 3)' """ - return "Yang-Baxter graph with root vertex %s" % (self._root,) + return f"Yang-Baxter graph with root vertex {self._root}" @lazy_attribute def _digraph(self): @@ -216,7 +216,7 @@ def __hash__(self): # used in containers but are mutable. return hash(self._digraph.copy(immutable=True)) - def __eq__(self, other): + def __eq__(self, other) -> bool: r""" EXAMPLES:: @@ -238,7 +238,7 @@ def __eq__(self, other): """ return type(self) is type(other) and self._digraph == other._digraph - def __ne__(self, other): + def __ne__(self, other) -> bool: r""" Test non-equality. @@ -369,7 +369,7 @@ def root(self): """ return self._root - def successors(self, v): + def successors(self, v) -> list: r""" Return the successors of the vertex ``v``. @@ -405,7 +405,7 @@ def plot(self, *args, **kwds): kwds["vertex_labels"] = True return self._digraph.plot(*args, **kwds) - def vertices(self, sort=False): + def vertices(self, sort=False) -> list: r""" Return the vertices of ``self``. @@ -439,7 +439,7 @@ def edges(self): """ return self._digraph.edges(sort=True) - def vertex_relabelling_dict(self, v, relabel_operator): + def vertex_relabelling_dict(self, v, relabel_operator) -> dict: r""" Return a dictionary pairing vertices ``u`` of ``self`` with the object obtained from ``v`` by applying the @@ -590,7 +590,7 @@ def __init__(self, partition): for i in range(sum(partition) - 1)] super().__init__(root, operators) - def __repr__(self): + def __repr__(self) -> str: r""" EXAMPLES:: @@ -598,7 +598,7 @@ def __repr__(self): sage: Y.__repr__() # needs sage.combinat 'Yang-Baxter graph of [3, 2], with top vertex (1, 0, 2, 1, 0)' """ - return "Yang-Baxter graph of %s, with top vertex %s" % (self._partition, self._root) + return f"Yang-Baxter graph of {self._partition}, with top vertex {self._root}" def __copy__(self): r""" @@ -695,7 +695,7 @@ def _swap_operator(self, operator, u): """ return operator(u) - def vertex_relabelling_dict(self, v): + def vertex_relabelling_dict(self, v) -> dict: r""" Return a dictionary pairing vertices ``u`` of ``self`` with the object obtained from ``v`` by applying transpositions corresponding to the @@ -792,7 +792,7 @@ def __hash__(self): """ return hash(self._position) - def __eq__(self, other): + def __eq__(self, other) -> bool: r""" Compare two swap operators. @@ -811,7 +811,7 @@ def __eq__(self, other): return False return self._position == other._position - def __ne__(self, other): + def __ne__(self, other) -> bool: """ Check whether ``self`` is not equal to ``other``. @@ -826,7 +826,7 @@ def __ne__(self, other): """ return not (self == other) - def __repr__(self): + def __repr__(self) -> str: r""" Representation string. @@ -840,7 +840,7 @@ def __repr__(self): pos = self._position return f"Swap positions {pos} and {pos + 1}" - def __str__(self): + def __str__(self) -> str: r""" A short str representation (used, for example, in labelling edges of graphs). @@ -889,7 +889,7 @@ def position(self): class SwapIncreasingOperator(SwapOperator): - def __repr__(self): + def __repr__(self) -> str: r""" Representation string. diff --git a/src/sage/databases/conway.py b/src/sage/databases/conway.py index b1f76292ce8..54912a2471c 100644 --- a/src/sage/databases/conway.py +++ b/src/sage/databases/conway.py @@ -138,11 +138,16 @@ def __len__(self): """ Return the number of polynomials in this database. - TESTS:: + TESTS: + + The database currently contains `35357` polynomials, but due to + :issue:`35357` it will be extended by Conway polynomials of + degrees `1`, `2` and `3` for primes between `65537` and `110000`, + thus leading to a new total of `47090` entries:: sage: c = ConwayPolynomials() - sage: len(c) - 35357 + sage: len(c) in [35357, 47090] + True """ try: return self._len diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index e4635803dc2..ba78f9bc2a6 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -1577,8 +1577,9 @@ def run(self): self.log("Features detected for doctesting: " + ','.join(available_software.seen())) if self.options.hidden_features: - features_hidden = [f.name for f in self.options.hidden_features if f.unhide()] - self.log("Features that have been hidden: " + ','.join(features_hidden)) + for f in self.options.hidden_features: + f.unhide() + self.log("Features that have been hidden: " + ','.join(available_software.hidden())) self.cleanup() return self.reporter.error_status diff --git a/src/sage/doctest/external.py b/src/sage/doctest/external.py index 85eefc27119..4628db45f5c 100644 --- a/src/sage/doctest/external.py +++ b/src/sage/doctest/external.py @@ -429,6 +429,7 @@ def __init__(self): self._features = sorted(features, key=lambda feature: feature.name) self._indices = {feature.name: idx for idx, feature in enumerate(self._features)} self._seen = Array('i', len(self._features)) # initialized to zeroes + self._hidden = Array('i', len(self._features)) # initialized to zeroes def __contains__(self, item): """ @@ -444,19 +445,26 @@ def __contains__(self, item): idx = self._indices[item] except KeyError: return False - if not self._seen[idx]: - if not self._allow_external and self._features[idx] in self._external_features: - self._seen[idx] = -1 # not available - elif self._features[idx].is_present(): - self._seen[idx] = 1 # available + feature = self._features[idx] + if feature.is_hidden(): + if not self._hidden[idx]: + self._hidden[idx] = 1 + available = False # a hidden feature is considered to be not available + else: + if not self._allow_external and feature in self._external_features: + # an external feature is considered to be not available + # if this is not allowed + available = False + elif feature.is_present(): + available = True else: - self._seen[idx] = -1 # not available - if self._seen[idx] == 1: + available = False + if available: + if not self._seen[idx]: + self._seen[idx] = 1 return True - elif self._seen[idx] == -1: - return False else: - raise AssertionError("Invalid value for self.seen") + return False def issuperset(self, other): """ @@ -495,5 +503,29 @@ def seen(self): for feature, seen in zip(self._features, self._seen) if seen > 0] + def hidden(self): + """ + Return the list of detected hidden external software. + + EXAMPLES:: + + sage: # needs conway_polynomials database_cremona_mini_ellcurve database_ellcurves database_graphs + sage: from sage.doctest.external import available_software + sage: from sage.features.databases import all_features + sage: for f in all_features(): + ....: f.hide() + ....: if f._spkg_type() == 'standard': + ....: test = f.name in available_software + ....: f.unhide() + sage: sorted(available_software.hidden()) + [...'conway_polynomials',... + 'database_cremona_mini_ellcurve',... + 'database_ellcurves',... + 'database_graphs'...] + """ + return [feature.name + for feature, hidden in zip(self._features, self._hidden) + if hidden > 0] + available_software = AvailableSoftware() diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py index 353eb1ea041..4b42ad87b1f 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py @@ -6056,8 +6056,7 @@ def reduced_form(self, **kwds): sage: f.reduced_form(prec=50, smallest_coeffs=False) # this needs 2 periodic Traceback (most recent call last): ... - ValueError: accuracy of Newton's root not within tolerance(0.000066... > 1e-06), - increase precision + ValueError: accuracy of Newton's root not within tolerance(0.00006... > 1e-06), increase precision sage: f.reduced_form(smallest_coeffs=False) ( Dynamical System of Projective Space of dimension 1 over Rational Field @@ -6107,7 +6106,7 @@ def reduced_form(self, **kwds): sage: f.reduced_form(prec=30, smallest_coeffs=False) Traceback (most recent call last): ... - ValueError: accuracy of Newton's root not within tolerance(0.00008... > 1e-06), increase precision + ValueError: accuracy of Newton's root not within tolerance(0.00009... > 1e-06), increase precision sage: f.reduced_form(smallest_coeffs=False) ( Dynamical System of Projective Space of dimension 1 over Rational Field diff --git a/src/sage/env.py b/src/sage/env.py index bb228e7f041..3d336aa18ea 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -187,6 +187,11 @@ def var(key: str, *fallbacks: Optional[str], force: bool = False) -> Optional[st SAGE_PKGS = var("SAGE_PKGS", join(SAGE_ROOT, "build", "pkgs")) SAGE_ROOT_GIT = var("SAGE_ROOT_GIT", join(SAGE_ROOT, ".git")) +# Sage doc server (local server with PORT if URL is not given) +SAGE_DOC_SERVER_URL = var("SAGE_DOC_SERVER_URL") +# The default port is 0 so that the system will assign a random unused port > 1024 +SAGE_DOC_LOCAL_PORT = var("SAGE_DOC_LOCAL_PORT", "0") + # ~/.sage DOT_SAGE = var("DOT_SAGE", join(os.environ.get("HOME"), ".sage")) SAGE_STARTUP_FILE = var("SAGE_STARTUP_FILE", join(DOT_SAGE, "init.sage")) diff --git a/src/sage/features/__init__.py b/src/sage/features/__init__.py index ebeb55527a8..86a777ee915 100644 --- a/src/sage/features/__init__.py +++ b/src/sage/features/__init__.py @@ -158,12 +158,6 @@ def __init__(self, name, spkg=None, url=None, description=None, type='optional') self._hidden = False self._type = type - # For multiprocessing of doctests, the data self._num_hidings should be - # shared among subprocesses. Thus we use the Value class from the - # multiprocessing module (cf. self._seen of class AvailableSoftware) - from multiprocessing import Value - self._num_hidings = Value('i', 0, lock=False) - try: from sage.misc.package import spkg_type except ImportError: # may have been surgically removed in a downstream distribution @@ -220,10 +214,6 @@ def is_present(self): self._cache_is_present = res if self._hidden: - if self._num_hidings.value > 0: - self._num_hidings.value += 1 - elif self._cache_is_present: - self._num_hidings.value = 1 return FeatureTestResult(self, False, reason="Feature `{name}` is hidden.".format(name=self.name)) return self._cache_is_present @@ -354,7 +344,6 @@ def is_standard(self): sage: from sage.features.databases import DatabaseCremona sage: DatabaseCremona().is_standard() False - """ if self.name.startswith('sage.'): return True @@ -369,7 +358,6 @@ def is_optional(self): sage: from sage.features.databases import DatabaseCremona sage: DatabaseCremona().is_optional() True - """ return self._spkg_type() == 'optional' @@ -394,9 +382,7 @@ def hide(self): Use method `unhide` to make it available again. sage: Benzene().unhide() # optional - benzene, needs sage.graphs - 1 sage: len(list(graphs.fusenes(2))) # optional - benzene, needs sage.graphs - 1 """ self._hidden = True @@ -404,8 +390,6 @@ def unhide(self): r""" Revert what :meth:`hide` did. - OUTPUT: The number of events a present feature has been hidden. - EXAMPLES: sage: from sage.features.sagemath import sage__plot @@ -413,15 +397,28 @@ def unhide(self): sage: sage__plot().is_present() FeatureTestResult('sage.plot', False) sage: sage__plot().unhide() # needs sage.plot - 1 sage: sage__plot().is_present() # needs sage.plot FeatureTestResult('sage.plot', True) """ - num_hidings = self._num_hidings.value - self._num_hidings.value = 0 self._hidden = False - return int(num_hidings) + def is_hidden(self): + r""" + Return whether ``self`` is present but currently hidden. + + EXAMPLES: + + sage: from sage.features.sagemath import sage__plot + sage: sage__plot().hide() + sage: sage__plot().is_hidden() # needs sage.plot + True + sage: sage__plot().unhide() + sage: sage__plot().is_hidden() + False + """ + if self._hidden and self._is_present(): + return True + return False class FeatureNotPresentError(RuntimeError): r""" diff --git a/src/sage/features/join_feature.py b/src/sage/features/join_feature.py index 24c6583c123..81a5b7fb6e9 100644 --- a/src/sage/features/join_feature.py +++ b/src/sage/features/join_feature.py @@ -154,8 +154,6 @@ def unhide(self): r""" Revert what :meth:`hide` did. - OUTPUT: The number of events a present feature has been hidden. - EXAMPLES:: sage: from sage.features.sagemath import sage__groups @@ -167,14 +165,11 @@ def unhide(self): FeatureTestResult('sage.groups.perm_gps.permgroup', False) sage: f.unhide() - 4 sage: f.is_present() # optional sage.groups FeatureTestResult('sage.groups', True) sage: f._features[0].is_present() # optional sage.groups FeatureTestResult('sage.groups.perm_gps.permgroup', True) """ - num_hidings = 0 for f in self._features: - num_hidings += f.unhide() - num_hidings += super().unhide() - return num_hidings + f.unhide() + super().unhide() diff --git a/src/sage/functions/piecewise.py b/src/sage/functions/piecewise.py index 6e50aef052c..677f5a06bd4 100644 --- a/src/sage/functions/piecewise.py +++ b/src/sage/functions/piecewise.py @@ -213,17 +213,16 @@ def _subs_(self, subs_map, options, parameters, x): piecewise(x|-->-x^sin(y) on (-2, 0), x|-->x - sin(y) on [0, 2]; x) """ point = subs_map.apply_to(x, 0) - if point == x: + if ((point.is_numeric() or point.is_constant()) and (point.is_real())): + if hasattr(point, 'pyobject'): + # unwrap any numeric values + point = point.pyobject() + elif point == x: # this comparison may be very slow (see #37925) # substitution only in auxiliary variables new_params = [] for domain, func in parameters: new_params.append((domain, subs_map.apply_to(func, 0))) return piecewise(new_params, var=x) - if ((point.is_numeric() or point.is_constant()) - and (point.is_real())): - if hasattr(point, 'pyobject'): - # unwrap any numeric values - point = point.pyobject() else: raise ValueError('substituting the piecewise variable must result in real number') diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 6dabca389f9..0d9689b4d2e 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -6197,6 +6197,201 @@ def Z_operators_gens(self): """ return [ -cp for cp in self.cross_positive_operators_gens() ] + def max_angle(self, other=None, exact=True, epsilon=0): + r""" + Return the maximal angle between ``self`` and ``other``. + + The maximal angle between two closed convex cones is the + unique largest angle formed by any two unit-norm vectors in + those cones. In pathological cases, this computation can fail. + + If it fails when ``exact`` is ``True`` and if each of the + cones :meth:`is_strictly_convex`, then a second attempt will + be made using inexact arithmetic. (This sometimes avoids the + problem noted in [Or2024]_). If the computation fails when the + cones are not strictly convex or when ``exact`` is ``False``, + a :class:`ValueError` is raised. + + INPUT: + + - ``other`` -- (default: ``None``) a rational, polyhedral + convex cone + + - ``exact`` -- (default: ``True``) whether or not to use exact + rational arithmetic instead of floating point computations; + beware that ``True`` is not guaranteed to avoid floating + point computations if the algorithm runs into trouble in + rational arithmetic + + - ``epsilon`` -- (default: ``0``) the tolerance to use when + making comparisons + + .. WARNING:: + + Using inexact arithmetic (``exact=False``) is faster, but + this computation is only known to be stable when both of + the cones are strictly convex (or when one of them is the + entire space, but the maximal angle is obviously `\pi` in + that case). + + OUTPUT: + + A triple `\left( \theta_{\max}, u, v \right)` + containing: + + - the maximal angle `\theta_{\max}` between ``self`` and + ``other`` + + - a vector `u` in ``self`` that achieves the maximal angle + + - a vector `v` in ``other`` that achieves the maximal angle + + If ``other`` is ``None`` (the default), then the maximal angle + within this cone (between this cone and itself) is returned. + + If an eigenspace of dimension greater than one is encountered + and if the corresponding angle cannot be ruled out as a + maximum, the behavior of this function depends on + ``exact``: + + - If ``exact`` is ``True`` and if both ``self`` and ``other`` + are strictly convex, then the algorithm will fall back to + inexact arithmetic. In that case, the returned angle and + vectors will be over :class:`sage.rings.real_double.RDF`. + + - If ``exact`` is ``False`` or if either cone is not strictly + convex, then a :class:`ValueError` is raised to indicate + that we have failed; i.e. we cannot say with certainty what + the maximal angle is. + + REFERENCES: + + - [IS2005]_ + - [SS2016]_ + - [Or2020]_ + - [Or2024]_ + + ALGORITHM: + + Algorithm 3 in [Or2020]_ is used. If a potentially-maximal + angle corresponds to an eigenspace of dimension two or more, + we sometimes fall back to inexact arithmetic which has the + effect of perturbing the cones. That this will not affect the + answer too much is one conclusion of [Or2024]_. + + EXAMPLES: + + The maximal angle in a single ray is zero:: + + sage: K = random_cone(min_rays=1, max_rays=1, max_ambient_dim=5) + sage: K.max_angle()[0] + 0 + + The maximal angle in the nonnegative octant is `\pi/2`:: + + sage: K = cones.nonnegative_orthant(3) + sage: K.max_angle()[0] + 1/2*pi + + The maximal angle between the nonnegative quintant and the + Schur cone of dimension 5 is about `0.8524 \pi`. The same + result can be obtained faster using inexact arithmetic, but + only confidently so because we already know the answer:: + + sage: # long time + sage: P = cones.nonnegative_orthant(5) + sage: Q = cones.schur(5) + sage: actual = P.max_angle(Q)[0] + sage: expected = 0.8524*pi + sage: bool( (actual - expected).abs() < 0.0001 ) + True + sage: actual = P.max_angle(Q,exact=False)[0] + sage: bool( (actual - expected).abs() < 0.0001 ) + True + + The maximal angle within the Schur cone is known explicitly + via Gourion and Seeger's Proposition 2 [GS2010]_:: + + sage: n = 3 + sage: K = cones.schur(n) + sage: bool(K.max_angle()[0] == ((n-1)/n)*pi) + True + + Sage can't prove that the actual and expected results are + equal in the next two cases without a little nudge in the + right direction, and, moreover, it's crashy about it:: + + sage: n = 4 + sage: K = cones.schur(n) + sage: actual = K.max_angle()[0].simplify()._sympy_()._sage_() + sage: expected = ((n-1)/n)*pi + sage: bool( actual == expected ) + True + sage: n = 5 + sage: K = cones.schur(n) + sage: actual = K.max_angle()[0].simplify()._sympy_()._sage_() + sage: expected = ((n-1)/n)*pi + sage: bool( actual == expected ) + True + + When there's a unit norm vector in ``self`` whose negation is + in ``other``, they form a maximal angle of `\pi`:: + + sage: P = Cone([(5,1), (1,-1)]) + sage: Q = Cone([(-1,0), (-1,0)]) + sage: P.max_angle(Q)[0] + pi + + TESTS: + + When ``self`` and ``-other`` have nontrivial intersection, we + expect the maximal angle to be `\pi`:: + + sage: # long time + sage: from sage.geometry.cone_critical_angles import ( + ....: _random_admissible_cone ) + sage: n = ZZ.random_element(1,3) + sage: P = _random_admissible_cone(ambient_dim=n) + sage: Q = _random_admissible_cone(ambient_dim=n) + sage: expected = P.intersection(-Q).is_trivial() + sage: actual = bool(P.max_angle(Q)[0] != pi) + sage: actual == expected + True + + In particular, this should happen when either cone is the full + space:: + + sage: from sage.geometry.cone_critical_angles import ( + ....: _random_admissible_cone ) + sage: n = ZZ.random_element(1,5) + sage: P = _random_admissible_cone(ambient_dim=n) + sage: Q = cones.trivial(n, P.dual().lattice()).dual() + sage: Q.is_full_space() + True + sage: P.max_angle(Q)[0] + pi + sage: Q.max_angle(P)[0] + pi + """ + # We do the argument checking here, in the public cone method, + # because the error message should say something like "this + # cone" and not reference the argument of a function the user + # has never heard of in some internal module. + if self.is_trivial(): + raise ValueError("cone should not be trivial") + + if other is None: + other = self + else: + if (other.lattice_dim() != self.lattice_dim()): + raise ValueError("lattice dimensions of self and other " + "must agree") + if other.is_trivial(): + raise ValueError("other cone cannot be trivial") + + from sage.geometry.cone_critical_angles import max_angle + return max_angle(self, other, exact, epsilon) + def random_cone(lattice=None, min_ambient_dim=0, max_ambient_dim=None, min_rays=0, max_rays=None, strictly_convex=None, solid=None): diff --git a/src/sage/geometry/cone_critical_angles.py b/src/sage/geometry/cone_critical_angles.py new file mode 100644 index 00000000000..3e39102426a --- /dev/null +++ b/src/sage/geometry/cone_critical_angles.py @@ -0,0 +1,1024 @@ +r""" +Find maximal angles between polyhedral convex cones + +.. WARNING:: + + This module is considered internal and its contents are subject to + change at any time without (deprecation) warning. The stable + interface is + :meth:`sage.geometry.cone.ConvexRationalPolyhedralCone.max_angle`. + +Finding the maximal (or equivalently, the minimal) angle between two +polyhedral convex cones is a hard nonconvex optimization problem. The +problem for a single cone was introduced in [IS2005]_, and was later +extended in [SS2016]_ to two cones as a generalization of the +principal angle between two vector subspaces. + +Seeger and Sossa proposed an algorithm in [SS2016]_ to find maximal +angles, and [Or2020]_ elaborates on that algorithm. It is this latest +improvement that is implemented (more or less) by this module. The +fact that perturbations of pointed cones may not change the answer too +much [Or2024]_ is taken into consideration to avoid pathological +cases. + +This module is internal to SageMath; the interface presented to users +consists of a public method, +:meth:`sage.geometry.cone.ConvexRationalPolyhedralCone.max_angle` for +polyhedral convex cones. Even though all of the functions in this +module are internal, some are more internal than others. There are a +few functions that are used only in doctests, and not by any code that +an end-user would run. Breaking somewhat with tradition, only those +methods have been prefixed with an underscore. +""" + +from sage.functions.trig import arccos, cos +from sage.matrix.constructor import matrix +from sage.misc.misc import powerset +from sage.rings.integer_ring import ZZ +from sage.rings.qqbar import AA +from sage.rings.rational_field import QQ +from sage.rings.real_double import RDF +from sage.symbolic.constants import pi + +def _normalize_gevp_solution(gevp_solution): + r""" + Normalize the results of :func:`solve_gevp_nonzero` and + :func:`_solve_gevp_naive`. + + Those two functions return solutions (pairs of vectors) to an + eigenvalue problem, but eigenvectors are only unique up to a + scalar multiple. This function normalizes those results so that + every eigenvector has a leading entry of positive one. This allows + us to identity equivalent solutions to those problems. + + INPUT: + + A quartet ``gevp_solution`` whose components are, in order: + + - ``eigenvalue`` -- ignored + + - ``xi`` -- first component of the `( \xi, \eta )` eigenvector + + - ``eta`` -- second component of the `( \xi, \eta )` eigenvector + + - ``multiplicity`` -- ignored + + OUTPUT: + + If `c` is the first nonzero component of the concatenated `( \xi, + \eta )` vector, then a quartet whose components are, in order: + + - ``eigenvalue`` -- the unmodified ``eigenvalue`` argument + + - ``xi*(1/c)`` -- the `\xi` component normalized so that the first + nonzero component of the concatenated vector + `( \xi, \eta )` is positive one + + - ``eta*(1/c)`` -- the `\eta` component normalized so that the + first nonzero component of the concatenated vector + `( \xi, \eta )` is positive one + + - ``multiplicity`` -- the unmodified ``multiplicity`` argument + + If there is no such `c` (that is, if both `\xi` and `\eta` are + zero), then the entire input quartet is returned unmodified. + + EXAMPLES:: + + sage: from sage.geometry.cone_critical_angles import ( + ....: _normalize_gevp_solution) + sage: s1 = (-1, vector(QQ,[0,-2]), vector(QQ,[1]), 1) + sage: _normalize_gevp_solution(s1) + (-1, (0, 1), (-1/2), 1) + sage: s2 = (1, vector(QQ,[0,0]), vector(QQ,[0,0,-1]), 2) + sage: _normalize_gevp_solution(s2) + (1, (0, 0), (0, 0, 1), 2) + """ + eigenvalue, xi, eta, multiplicity = gevp_solution + from itertools import chain + + # We'll use this default of zero as the scaling factor if we don't + # find a better one; that is, if xi = eta = 0 -- in which case the + # additional multiplication by zero is a no-op. + scale = 0 + for c in chain(xi, eta): + if c != 0: + scale = ~c + break + + xi *= scale + xi.set_immutable() + eta *= scale + eta.set_immutable() + return (eigenvalue, xi, eta, multiplicity) + + +def _random_admissible_cone(ambient_dim): + r""" + Generate a random cone in a lattice of dimension + ``ambient_dim`` that isn't trivial. + + This is a convenience method used to simplify some test cases. The + number of rays that the cone possesses is limited to two more than + ``ambient_dim``; so, for example, you will not get more than five + rays in a three-dimensional space. This limits the amount of time + spent in any one test case. In contrast with the definition in + [Or2020]_ we consider the full ambient space to be admissible (it + doesn't hurt anything, and [Or2024]_ was forced to allow it). + + INPUT: + + - ``ambient_dim`` -- a positive integer representing the dimension + of the ambient lattice in which the returned cone lives + + OUTPUT: + + A "random" nontrivial closed convex cone in a lattice of dimension + ``ambient_dim``. + + A :class:`ValueError` is raised if ``ambient_dim`` is not + positive. + + EXAMPLES: + + The result has all of the desired properties:: + + sage: from sage.geometry.cone_critical_angles import ( + ....: _random_admissible_cone ) + sage: K = _random_admissible_cone(5) + sage: K.lattice_dim() + 5 + sage: K.is_trivial() + False + + Unless the ``ambient_dim`` argument is nonsense:: + + sage: from sage.geometry.cone_critical_angles import ( + ....: _random_admissible_cone ) + sage: K = _random_admissible_cone(0) + Traceback (most recent call last): + ... + ValueError: there are no nontrivial cones in dimension 0 + """ + if ambient_dim < 1 or ambient_dim not in ZZ: + # The random_cone() method already crashes if we ask the + # impossible of it, but having this here emits a more sensible + # error message. + raise ValueError("there are no nontrivial cones in dimension %d" + % ambient_dim) + + args = { 'min_ambient_dim': ambient_dim, + 'max_ambient_dim': ambient_dim, + 'min_rays': 1, + 'max_rays': ambient_dim+2 } + + from sage.geometry.cone import random_cone + return random_cone(**args) + + return K + + +def gevp_licis(G): + r""" + Return all nonempty subsets of indices for the columns of + ``G`` that correspond to linearly independent sets (of columns of + ``G``). + + Mnemonic: linearly independent column-index subsets (LICIS). + + The returned lists are all sorted in the same (the natural) order; + and are returned as lists so that they may be used to index into + the rows/columns of matrices. + + INPUT: + + - ``G`` -- the matrix whose linearly independent column index sets + we want + + OUTPUT: + + A generator that returns sorted lists of natural numbers. Each + generated list ``I`` is a set of indices corresponding to columns + of ``G`` that, when considered as a set, is linearly independent. + + EXAMPLES: + + The linearly independent subsets of the matrix corresponding to a + line (with two generators pointing in opposite directions) are the + one-element subsets, since the only two-element subset isn't + linearly independent:: + + sage: from sage.geometry.cone_critical_angles import gevp_licis + sage: K = Cone([(1,0),(-1,0)]) + sage: G = matrix.column(K.rays()) + sage: list(gevp_licis(G)) + [[0], [1]] + + The matrix for the trivial cone has no linearly independent + subsets, since we require them to be nonempty:: + + sage: from sage.geometry.cone_critical_angles import gevp_licis + sage: trivial_cone = cones.trivial(0) + sage: trivial_cone.is_trivial() + True + sage: list(gevp_licis(matrix.column(trivial_cone.rays()))) + [] + + All rays in the nonnegative orthant of `R^{n}` are + linearly independent, so we should get back `2^{n} - 1` subsets + after accounting for the absence of the empty set:: + + sage: from sage.geometry.cone_critical_angles import gevp_licis + sage: K = cones.nonnegative_orthant(3) + sage: G = matrix.column(K.rays()) + sage: len(list(gevp_licis(G))) == 2^(K.nrays()) - 1 + True + + TESTS: + + All sets corresponding to the returned indices should be linearly + independent:: + + sage: from sage.geometry.cone_critical_angles import gevp_licis + sage: K = random_cone(max_rays=8) + sage: G = matrix.column(K.rays()) + sage: all( len(s) == K.rays(s).dimension() for s in gevp_licis(G) ) + True + """ + from sage.matroids.linear_matroid import LinearMatroid + + # There's a fast implementation of this for matroids, but we need + # to drop the empty set from its output and convert the rest to + # lists that are all sorted in the same order. + return map(sorted, filter(bool, LinearMatroid(G).independent_sets())) + + +def _solve_gevp_naive(GG, HH, M, I, J): + r""" + Solve the generalized eigenvalue problem in Theorem 3 + [Or2020]_ in a very naive way, by (slowly) inverting the matrices + and finding the eigenvalues in the product space. + + This is used only for testing, to ensure that the smart way of + solving the generalized eigenvalue problem via + :func:`solve_gevp_zero` and :func:`solve_gevp_nonzero` returns the + same answers as the dumb way. + + This returns a generator, like :func:`solve_gevp_zero` and + :func:`solve_gevp_nonzero`. + + INPUT: + + See the arguments for :func:`solve_gevp_nonzero`. + + ALGORITHM: + + We construct the two matrices `A` and `B` in Theorem 3 [Or2020]_ + in block form, and then use the naive "inverse" method on `B` to + move it to the left and obtain `M = B^{-1}A`. We then compute the + right eigenvectors of the whole big matrix `M`. + + EXAMPLES: + + A simple usage example, that also appears as Example 3 in + [Or2020]_:: + + sage: from sage.geometry.cone_critical_angles import _solve_gevp_naive + sage: K = cones.nonnegative_orthant(2) + sage: G = matrix.column(K.rays()) + sage: GG = G.transpose() * G + sage: I = [0] + sage: J = [1] + sage: list(_solve_gevp_naive(GG,GG,GG,I,J)) + [(0, (1), (0), 2), (0, (0), (1), 2)] + + Check Example 4 [Or2020]_ symbolically to ensure that we get + eigenspaces of dimension `n=2` corresponding to the eigenvalues + `\cos\theta = -1` and `\cos\theta = 1`:: + + sage: from sage.geometry.cone_critical_angles import _solve_gevp_naive + sage: g11,g12,g21,g22 = SR.var('g11,g12,g21,g22', domain='real') + sage: h11,h12,h21,h22 = SR.var('h11,h12,h21,h22', domain='real') + sage: gs = [[g11,g12], [g21,g22]] + sage: hs = [[h11,h12], [h21,h22]] + sage: G = matrix.column(gs) + sage: H = matrix.column(hs) + sage: GG = G.transpose() * G + sage: HH = H.transpose() * H + sage: M = G.transpose() * H + sage: I = [0, 1] + sage: J = [0, 1] + sage: all( v in [-1,1] and m == 2 + ....: for (v,_,_,m) in _solve_gevp_naive(GG,HH,M,I,J) ) + True + """ + A = matrix.block([ + [ZZ.zero(), M[I,J]], + [M.transpose()[J,I], ZZ.zero()] + ]) + B = matrix.block([ + [GG[I,I], ZZ.zero()], + [ZZ.zero(), HH[J,J]] + ]) + M = B.inverse() * A + + # We'll format the result to match the solve_gevp_nonzero() return value. + for (evalue, evectors, multiplicity) in M.eigenvectors_right(): + for z in evectors: + xi = z[0:len(I)] + xi.set_immutable() + eta = z[len(I):] + eta.set_immutable() + yield (evalue, xi, eta, multiplicity) + + +def solve_gevp_zero(M, I, J): + r""" + Solve the generalized eigenvalue problem in Theorem 3 + [Or2020]_ for a zero eigenvalue using Propositions 3 and 4 + [Or2020]_. + + INPUT: + + - ``M`` -- the matrix whose `(i,j)`-th entry is the inner product + of `g_{i}` and `h_{j}` as in Proposition 6 [Or2020]_ + + - ``I`` -- a linearly independent column-index set for the matrix + `G` that appears in Theorem 3 [Or2020]_ + + - ``J`` -- a linearly independent column-index set for the matrix + `H` that appears in Theorem 3 [Or2020]_ + + OUTPUT: + + A generator of ``(eigenvalue, xi, eta, multiplicity)`` quartets + where + + - ``eigenvalue`` is zero (the eigenvalue of the system) + + - ``xi`` is the first (length ``len(I)``) component of an + eigenvector associated with ``eigenvalue`` + + - ``eta`` is the second (length ``len(J)``) component of an + eigenvector associated with ``eigenvalue`` + + - ``multiplicity`` is the dimension of the eigenspace associated + with ``eigenvalue`` + + ALGORITHM: + + Proposition 4 in [Or2020]_ is used. + + EXAMPLES: + + This particular configuration results in the zero matrix in the + eigenvalue problem, so the only solutions correspond to the + eigenvalue zero:: + + sage: from sage.geometry.cone_critical_angles import solve_gevp_zero + sage: K = cones.nonnegative_orthant(2) + sage: G = matrix.column(K.rays()) + sage: GG = G.transpose() * G + sage: I = [0] + sage: J = [1] + sage: list(solve_gevp_zero(GG, I, J)) + [(0, (1), (0), 2), (0, (0), (1), 2)] + """ + # A Cartesian product would be more appropriate here, but Sage + # isn't smart enough to figure out a basis for the product. So, + # we use the direct sum and then chop it up. + M_IJ = M[I,J] + xi_space = M_IJ.left_kernel() + eta_space = M_IJ.right_kernel() + + fake_cartprod = xi_space.direct_sum(eta_space) + multiplicity = fake_cartprod.dimension() + + for z in fake_cartprod.basis(): + z1 = z[0:len(I)] + z1.set_immutable() + z2 = z[len(I):] + z2.set_immutable() + + # The base ring of M will either be RDF or AA, which is enough + # to contain any eigenvalues that will arise... meaning that + # if we use the corresponding "zero" here, it will match the + # field of the eigenvalues returned by the nonzero function. + yield (M.base_ring().zero(), z1, z2, multiplicity) + + +def solve_gevp_nonzero(GG, HH, M, I, J): + r""" + Solve the generalized eigenvalue problem in Theorem 3 + [Or2020]_ for a nonzero eigenvalue using Propositions 3 and 5 + [Or2020]_. + + INPUT: + + - ``GG`` -- the matrix whose `(i,j)`-th entry is the inner product + of `g_{i}` and `g_{j}`, which are in turn the `i`-th and `j`-th + columns of the matrix `G` in Theorem 3 [Or2020]_ + + - ``HH`` -- the matrix whose `(i,j)`-th entry is the inner product + of `h_{i}` and `h_{j}`, which are in turn the `i`-th and `j`-th + columns of the matrix `H` in Theorem 3 [Or2020]_ + + - ``M`` -- the matrix whose `(i,j)`-th entry is the inner product + of `g_{i}` and `h_{j}` as in Proposition 6 in [Or2020]_ + + - ``I`` -- a linearly independent column-index set for the matrix + `G` that appears in Theorem 3 [Or2020]_ + + - ``J`` -- a linearly independent column-index set for the matrix + `H` that appears in Theorem 3 [Or2020]_ + + OUTPUT: + + A generator of ``(eigenvalue, xi, eta, multiplicity)`` quartets + where + + - ``eigenvalue`` is a real eigenvalue of the system + + - ``xi`` is the first (length ``len(I)``) component of an + eigenvector associated with ``eigenvalue`` + + - ``eta`` is the second (length ``len(J)``) component of an + eigenvector associated with ``eigenvalue`` + + - ``multiplicity`` is the dimension of the eigenspace associated + with ``eigenvalue`` + + Note that we do not return a basis for each eigenspace along with + its eigenvalue. For the application we have in mind, an eigenspace + of dimension greater than one (so, ``multiplicity > 1``) is an + error. As such, our return value is optimized for convenience in + the non-error case, where there is only one eigenvector (spanning + a one-dimensional eigenspace) associated with each eigenvalue. + + ALGORITHM: + + According to Proposition 5 [Or2020]_, the solutions corresponding + to non-zero eigenvalues can be found by solving a smaller + eigenvalue problem in only the variable `\xi`. So, we do that, and + then solve for `\eta` in terms of `\xi` as described in the + proposition. + + EXAMPLES: + + When the zero solutions are included, this function returns the + same solutions as the naive method on the Schur cone in three + dimensions:: + + sage: from itertools import chain + sage: from sage.geometry.cone_critical_angles import ( + ....: _normalize_gevp_solution, + ....: _solve_gevp_naive, + ....: gevp_licis, + ....: solve_gevp_nonzero, + ....: solve_gevp_zero) + sage: K = cones.schur(3) + sage: gs = [g.change_ring(AA).normalized() for g in K] + sage: G = matrix.column(gs) + sage: GG = G.transpose() * G + sage: G_index_sets = list(gevp_licis(G)) + sage: all( + ....: set( + ....: _normalize_gevp_solution(s) + ....: for s in + ....: chain( + ....: solve_gevp_zero(GG, I, J), + ....: solve_gevp_nonzero(GG, GG, GG, I, J) + ....: ) + ....: ) + ....: == + ....: set( + ....: _normalize_gevp_solution(s) + ....: for s in + ....: _solve_gevp_naive(GG,GG,GG,I,J) + ....: ) + ....: for I in G_index_sets + ....: for J in G_index_sets + ....: ) + True + + TESTS: + + This function should return the same solutions (with zero included, + of course) as the naive implementation even for random cones:: + + sage: # long time + sage: from itertools import chain + sage: from sage.geometry.cone_critical_angles import ( + ....: _normalize_gevp_solution, + ....: _random_admissible_cone, + ....: _solve_gevp_naive, + ....: gevp_licis, + ....: solve_gevp_nonzero, + ....: solve_gevp_zero) + sage: n = ZZ.random_element(1,3) + sage: P = _random_admissible_cone(ambient_dim=n) + sage: Q = _random_admissible_cone(ambient_dim=n) + sage: gs = [g.change_ring(AA).normalized() for g in P] + sage: G = matrix.column(gs) + sage: GG = G.transpose() * G + sage: hs = [h.change_ring(AA).normalized() for h in Q] + sage: H = matrix.column(hs) + sage: HH = H.transpose() * H + sage: M = G.transpose() * H + sage: G_index_sets = list(gevp_licis(G)) + sage: H_index_sets = list(gevp_licis(H)) + sage: all( + ....: set( + ....: _normalize_gevp_solution(s) + ....: for s in + ....: chain( + ....: solve_gevp_zero(M, I, J), + ....: solve_gevp_nonzero(GG, HH, M, I, J) + ....: ) + ....: ) + ....: == + ....: set( + ....: _normalize_gevp_solution(s) + ....: for s in + ....: _solve_gevp_naive(GG, HH, M, I, J) + ....: ) + ....: for I in G_index_sets + ....: for J in H_index_sets + ....: ) + True + + According to Proposition 7 [Or2020]_, the only eigenvalues that + arise when either ``G`` or ``H`` is invertible are `-1`, `0`, and + `1`:: + + sage: # long time + sage: from sage.geometry.cone_critical_angles import ( + ....: _random_admissible_cone, + ....: gevp_licis, + ....: solve_gevp_nonzero) + sage: n = ZZ.random_element(1,3) + sage: P = _random_admissible_cone(ambient_dim=n) + sage: Q = _random_admissible_cone(ambient_dim=n) + sage: gs = [g.change_ring(AA).normalized() for g in P] + sage: hs = [h.change_ring(AA).normalized() for h in Q] + sage: G = matrix.column(gs) + sage: GG = G.transpose() * G + sage: H = matrix.column(hs) + sage: HH = H.transpose() * H + sage: M = G.transpose() * H + sage: from itertools import product + sage: all( + ....: (v in [-1,0,1] + ....: for (v,_,_,_) in solve_gevp_nonzero(GG, HH, M, I, J)) + ....: for (I,J) in product(gevp_licis(G),gevp_licis(H)) + ....: if len(I) == n or len(J) == n ) + True + """ + if len(J) < len(I): + # We can always opt to solve the smaller problem. Reading the + # first three assignments below, you should be able to + # convince yourself that switching GG <-> HH, I <-> J, and + # transposing M does in fact switch from the "xi problem" to + # the "eta problem." + yield from ((l, xi, eta, m) + for (l, eta, xi, m) + in solve_gevp_nonzero(HH, GG, M.transpose(), J, I)) + else: + M_IJ = M[I,J] + G_I_pinv_H_J = GG[I,I].inverse_positive_definite() * M_IJ + H_J_pinv_G_I = HH[J,J].inverse_positive_definite() * M_IJ.transpose() + L = (G_I_pinv_H_J * H_J_pinv_G_I) + + for (sigma, xis, m) in L.eigenvectors_right(): + if sigma > 0: + # Avoid recomputing these for each xi in xis + sigma_sqrt = sigma.sqrt() + inv_sqrt = ~sigma_sqrt + pm_sqrt_inv_pairs = [ + (-sigma_sqrt, -inv_sqrt), + (sigma_sqrt, inv_sqrt) + ] + + for xi in xis: + for l, li in pm_sqrt_inv_pairs: + eta = li * H_J_pinv_G_I*xi + eta.set_immutable() + yield (l, xi, eta, m) + + +def compute_gevp_M(gs, hs): + r""" + Compute the matrix `M` whose `(i,j)`-th entry is the inner + product of ``gs[i]`` and ``hs[j]``. + + This is the "generalized Gram matrix" appearing in Proposition 6 + in [Or2020]_. For efficiency, we also return the minimal pair, + whose inner product is minimal among the entries of `M`. This + allows our consumer to bail out immediately (knowing the optimal + pair!) if it turns out that the maximal angle is acute; i.e. if + the smallest entry of `M` is nonnegative. + + INPUT: + + - ``gs`` -- a linearly independent list of unit-norm generators + for the cone `P` + + - ``hs`` -- a linearly independent list of unit-norm generators + for the cone `Q` + + OUTPUT: + + A tuple containing four elements, in order: + + - The matrix `M` described in Proposition 6 + + - The minimal entry in the matrix `M` + + - A vector in ``gs`` that achieves that minimal inner product + along with the next element of the tuple + + - A vector in ``hs`` that achieves the minimal inner product + along with the previous element in the tuple + + EXAMPLES:: + + sage: from sage.geometry.cone_critical_angles import compute_gevp_M + sage: P = Cone([ (1,2,0), (3,4,0) ]) + sage: Q = Cone([ (-1,4,1), (5,-2,-1), (-1,-1,5) ]) + sage: gs = [g.change_ring(QQ) for g in P] + sage: hs = [h.change_ring(QQ) for h in Q] + sage: M = compute_gevp_M(gs, hs)[0] + sage: all( M[i][j] == gs[i].inner_product(hs[j]) + ....: for i in range(P.nrays()) + ....: for j in range(Q.nrays()) ) + True + + TESTS: + + The products `(G_{I})^{T}H_{J}` correspond to + submatrices of the "generalized Gram matrix" `M` in Proposition + 6. Note that SageMath does (row,column) indexing but [Or2020]_ + does (column,row) indexing:: + + sage: from sage.geometry.cone_critical_angles import ( + ....: _random_admissible_cone, + ....: compute_gevp_M, + ....: gevp_licis) + sage: n = ZZ.random_element(1,4) + sage: n = ZZ.random_element(1,8) # long time + sage: P = _random_admissible_cone(ambient_dim=n) + sage: Q = _random_admissible_cone(ambient_dim=n) + sage: gs = [g.change_ring(QQ) for g in P] + sage: hs = [h.change_ring(QQ) for h in Q] + sage: M = compute_gevp_M(gs,hs)[0] + sage: f = lambda i,j: gs[i].inner_product(hs[j]) + sage: expected_M = matrix(QQ, P.nrays(), Q.nrays(), f) + sage: M == expected_M + True + sage: G = matrix.column(gs) + sage: H = matrix.column(hs) + sage: def _test_indexing(I,J): + ....: G_I = G.matrix_from_columns(I) + ....: H_J = H.matrix_from_columns(J) + ....: return (G_I.transpose()*H_J == M[I,J] + ....: and + ....: H_J.transpose()*G_I == M.transpose()[J,I]) + sage: G_index_sets = list(gevp_licis(G)) + sage: H_index_sets = list(gevp_licis(H)) + sage: all( _test_indexing(I,J) for I in G_index_sets + ....: for J in H_index_sets ) + True + """ + min_u = gs[0] + min_v = hs[0] + min_ip = min_u.inner_product(min_v) + + M = [] + for g in gs: + M_i = [] + for h in hs: + val = g.inner_product(h) + M_i.append(val) + if (val < min_ip): + min_ip = val + min_u = g + min_v = h + M.append(M_i) + + return (matrix(M), min_ip, min_u, min_v) + + +def check_gevp_feasibility(cos_theta, xi, eta, G_I, G_I_c_T, + H_J, H_J_c_T, epsilon): + r""" + Determine if a solution to the generalized eigenvalue problem + in Theorem 3 [Or2020]_ is feasible. + + Implementation detail: we take four matrices that we are capable + of computing as parameters instead, because we will be called in a + nested loop "for all `I`... and for all `J`..." The data + corresponding to `I` should be computed only once, which means + that we can't do it here -- it needs to be done outside of the `J` + loop. For symmetry (and to avoid relying on too many + cross-function implementation details), we also insist that the + `J` data be passed in. + + INPUT: + + - ``cos_theta`` -- an eigenvalue corresponding to + `( \xi, \eta )` + + - ``xi`` -- first component of the `( \xi, \eta )` eigenvector + + - ``eta`` -- second component of the `( \xi, \eta )` eigenvector + + - ``G_I`` -- the submatrix of `G` with columns indexed by `I` + + - ``G_I_c_T`` -- a matrix whose rows are the non-`I` columns of `G` + + - ``H_J`` -- the submatrix of `H` with columns indexed by `J` + + - ``H_J_c_T`` -- a matrix whose rows are the non-`J` columns of `H` + + - ``epsilon`` -- the tolerance to use when making comparisons + + OUTPUT: + + A triple containing (in order), + + - a boolean, + - a vector in the cone `P` (of the same length as ``xi``), and + - a vector in the cone `Q` (of the same length as ``eta``). + + If `( \xi, \eta )` is feasible, we return ``(True, u, v)`` where `u` + and `v` are the vectors in `P` and `Q` respectively that form the + the angle `\theta`. + + If `( \xi, \eta )` is **not** feasible, then we return ``(False, 0, 0)`` + where ``0`` should be interpreted to mean the zero vector in the + appropriate space. + + EXAMPLES: + + If `\xi` has any components less than "zero," it isn't feasible:: + + sage: from sage.geometry.cone_critical_angles import( + ....: check_gevp_feasibility) + sage: xi = vector(QQ, [-1,1]) + sage: eta = vector(QQ, [1,1,1]) + sage: check_gevp_feasibility(0,xi,eta,None,None,None,None,0) + (False, (0, 0), (0, 0, 0)) + + If `\eta` has any components less than "zero," it isn't feasible:: + + sage: from sage.geometry.cone_critical_angles import( + ....: check_gevp_feasibility) + sage: xi = vector(QQ, [2]) + sage: eta = vector(QQ, [1,-4,4,5]) + sage: check_gevp_feasibility(0,xi,eta,None,None,None,None,0) + (False, (0), (0, 0, 0, 0)) + + If `\xi` and `\eta` are equal and if `G_{I}` and `H_{J}` are not, + then the copy of `\eta` that's been scaled by the norm of `G_{I}\xi` + generally won't satisfy its norm-equality constraint:: + + sage: from sage.geometry.cone_critical_angles import( + ....: check_gevp_feasibility) + sage: xi = vector(QQ, [1,1]) + sage: eta = xi + sage: G_I = matrix.identity(QQ,2) + sage: H_J = 2*G_I + sage: check_gevp_feasibility(0,xi,eta,G_I,None,H_J,None,0) + (False, (0, 0), (0, 0)) + + When `\cos\theta` is zero, the inequality (42) in Theorem 7.3 + [SS2016]_ is just an inner product with `v` which we can make + positive by ensuring that all of the entries of `H_{J}` are + positive. So, if any of the rows of ``G_I_c_T`` contain a negative + entry, (42) will fail:: + + sage: from sage.geometry.cone_critical_angles import( + ....: check_gevp_feasibility) + sage: xi = vector(QQ, [1/2,1/2,1/2,1/2]) + sage: eta = xi + sage: G_I = matrix.identity(QQ,4) + sage: G_I_c_T = matrix(QQ, [[0,-1,0,0]]) + sage: H_J = G_I + sage: check_gevp_feasibility(0,xi,eta,G_I,G_I_c_T,H_J,None,0) + (False, (0, 0, 0, 0), (0, 0, 0, 0)) + + Likewise we can make (43) fail in exactly the same way:: + + sage: from sage.geometry.cone_critical_angles import( + ....: check_gevp_feasibility) + sage: xi = vector(QQ, [1/2,1/2,1/2,1/2]) + sage: eta = xi + sage: G_I = matrix.identity(QQ,4) + sage: G_I_c_T = matrix(QQ, [[0,1,0,0]]) + sage: H_J = G_I + sage: H_J_c_T = matrix(QQ, [[0,-1,0,0]]) + sage: check_gevp_feasibility(0,xi,eta,G_I,G_I_c_T,H_J,H_J_c_T,0) + (False, (0, 0, 0, 0), (0, 0, 0, 0)) + + Finally, if we ensure that everything works, we get back a feasible + result along with the vectors (scaled `\xi` and `\eta`) that worked:: + + sage: from sage.geometry.cone_critical_angles import( + ....: check_gevp_feasibility) + sage: xi = vector(QQ, [1/2,1/2,1/2,1/2]) + sage: eta = xi + sage: G_I = matrix.identity(QQ,4) + sage: G_I_c_T = matrix(QQ, [[0,1,0,0]]) + sage: H_J = G_I + sage: H_J_c_T = matrix(QQ, [[0,1,0,0]]) + sage: check_gevp_feasibility(0,xi,eta,G_I,G_I_c_T,H_J,H_J_c_T,0) + (True, (1/2, 1/2, 1/2, 1/2), (1/2, 1/2, 1/2, 1/2)) + """ + infeasible_result = (False, 0*xi, 0*eta) + if min(xi) <= -epsilon or min(eta) <= -epsilon: + # xi or eta isn't in the interior of the nonnegative orthant, + # so skip this (non-)solution. + return infeasible_result + + # Rescale xi to satisfy (44), and rescale eta by the same amount, + # because (xi,eta) needs to remain in the same one-dimensional + # eigenspace. + scale = ~((G_I*xi).norm()) + xi_hat = xi * scale + eta_hat = eta * scale + + # Now check that (45) is satisfied. + if ((H_J*eta_hat).norm() - 1).abs() > epsilon: + return infeasible_result + + # And check that (42,43) are satisfied. + v = H_J * eta_hat + rhs = v - cos_theta*G_I*xi_hat + + if any(x < -epsilon for x in G_I_c_T * rhs): + return infeasible_result + + u = G_I * xi_hat + rhs = u - cos_theta*H_J*eta_hat + if any(x < -epsilon for x in H_J_c_T * rhs): + return infeasible_result + + return (True, u, v) + + +def max_angle(P, Q, exact, epsilon): + r""" + Find the maximal angle between the cones `P` and `Q`. + + This implements + :meth:`sage.geometry.cone.ConvexRationalPolyhedralCone.max_angle`, + which should be fully documented. + + EXAMPLES: + + For the sake of the user interface, the argument validation for + this function is performed in the associated cone method; we can + therefore crash it by feeding it invalid input like an + inadmissible cone:: + + sage: from sage.geometry.cone_critical_angles import max_angle + sage: K = cones.trivial(3) + sage: max_angle(K,K,True,0) + Traceback (most recent call last): + ... + IndexError: list index out of range + """ + # The lattice dimensions of P and Q are guaranteed to be equal + # because the cone method checks it before calling us. + n = P.lattice_dim() + + ring = RDF + if exact: + ring = AA + # For some reason we can go RR -> QQ -> AA, but not + # straight from RR to AA. + epsilon = QQ(epsilon) + epsilon = ring(epsilon) + + # First check if P is contained in the dual of Q. Keep track of + # the minimum inner product (and associated vectors) while doing + # so; then if P is contained in dual(Q), we just return the pair + # with the smallest inner product. + gs = [g.change_ring(ring).normalized() for g in P] + Q_is_P = (P == Q) # This is used again later + if Q_is_P: + hs = gs + else: + hs = [h.change_ring(ring).normalized() for h in Q] + + (M, min_ip, min_u, min_v) = compute_gevp_M(gs,hs) + + if min_ip >= 0: # The maximal angle is acute! + return (arccos(min_ip), min_u, min_v) + + # Also check to see if the maximal angle is pi. In particular this + # is true when either P or Q is the entire ambient space. + P_and_negative_Q = P.intersection(-Q) + if not (P_and_negative_Q.is_trivial()): + u = P_and_negative_Q.ray(0).change_ring(ring).normalized() + v = -u + return (pi, u, v) + + # When P == Q, GG and HH are both just M. We rule out the + # cardinality ``n`` in the index sets because it will eventually + # result in a GEVP whose only solutions are lambda in {-1,0,1}; + # none of which we want! We rule out 0 and 1 with the acute check, + # and -1 with the pi (P_and_negative_Q) check. + # + # It's VERY IMPORTANT that we construct lists from the index set + # generators, because we're going to use them in a nested loop! + G = matrix.column(gs) + G_index_sets = [s for s in gevp_licis(G) if not len(s) == n] + + if Q_is_P: + GG = M + H = G + HH = M + H_index_sets = G_index_sets + else: + GG = G.transpose() * G + H = matrix.column(hs) + HH = H.transpose() * H + H_index_sets = [s for s in gevp_licis(H) if not len(s) == n] + + # Keep track of the (cos-theta, xi, eta, multiplicity) tuples with + # multiplicity > 1. These are only a problem if they could + # potentially be maximal. Therefore, we want to inspect them AFTER + # we've checked all of the multiplicity=1 angles and found the + # largest. This allows us to ignore most of the problematic + # eigenspaces, independent of the order in which we run through I,J. + big_eigenspaces = [] + + for I in G_index_sets: + G_I = G.matrix_from_columns(I) + I_complement = [i for i in range(P.nrays()) if i not in I] + G_I_c_T = G.matrix_from_columns(I_complement).transpose() + + for J in H_index_sets: + J_complement = [j for j in range(Q.nrays()) if j not in J] + H_J = H.matrix_from_columns(J) + H_J_c_T = H.matrix_from_columns(J_complement).transpose() + + for (cos_theta,xi,eta,mult) in solve_gevp_nonzero(GG, HH, M, I, J): + + if cos_theta >= min_ip: + # This potential critical angle is smaller than or + # equal to one that we've already found. Why + # bother? + continue + + if cos_theta == -1: + # We already ruled this case out with the + # "P_and_negative_Q" trick. + continue + + (is_feasible, u, v) = check_gevp_feasibility(cos_theta, + xi, + eta, + G_I, + G_I_c_T, + H_J, + H_J_c_T, + epsilon) + + if is_feasible: + min_ip = cos_theta + min_u = u + min_v = v + elif mult > 1: + # Save this for later. The eigenvalue cos_theta + # might be so big that we can ignore it. + big_eigenspaces.append((cos_theta, xi, eta, mult)) + continue + + for (cos_theta, xi, eta, mult) in big_eigenspaces: + if cos_theta < min_ip: + # The existence of a big eigenspace is only a problem if + # cos_theta could actually be minimal. + + if exact and P.is_strictly_convex(): + if Q_is_P or Q.is_strictly_convex(): + # The maximal angle composed with the conic hull + # is continuous at (gs,hs), so we can retry with + # inexact arithmetic. That will "perturb" + # everything, hopefully eliminating any larger + # eigenspaces and without changing the answer too + # much. + return max_angle(P, Q, False, epsilon) + + # Either we don't know that the maximal angle of the conic + # hull is continuous at (gs,hs), or we're already using + # inexact arithmetic. There's nothing left to try. (Note + # that the case where either P or Q is the ambient space + # was handled much earlier, since in that case the maximal + # angle is obviously pi.) + raise ValueError('eigenspace of dimension %d > 1 ' + 'corresponding to eigenvalue %s' + % (mult, cos_theta)) + + return (arccos(min_ip), min_u, min_v) diff --git a/src/sage/geometry/polyhedron/backend_cdd.py b/src/sage/geometry/polyhedron/backend_cdd.py index e59f05d09a3..a333defe55c 100644 --- a/src/sage/geometry/polyhedron/backend_cdd.py +++ b/src/sage/geometry/polyhedron/backend_cdd.py @@ -22,9 +22,6 @@ from .base import Polyhedron_base from .base_QQ import Polyhedron_QQ -from sage.misc.lazy_import import lazy_import -lazy_import('sage.geometry.polyhedron.backend_cdd_rdf', 'Polyhedron_RDF_cdd', deprecation=32592) - class Polyhedron_cdd(Polyhedron_base): r""" diff --git a/src/sage/geometry/polyhedron/base3.py b/src/sage/geometry/polyhedron/base3.py index d64cf38d26d..3b57c4f2055 100644 --- a/src/sage/geometry/polyhedron/base3.py +++ b/src/sage/geometry/polyhedron/base3.py @@ -372,7 +372,7 @@ def _test_combinatorial_polyhedron(self, tester=None, **options): prefix=tester._prefix+" ") tester.info(tester._prefix + " ", newline=False) - def face_generator(self, face_dimension=None, algorithm=None, **kwds): + def face_generator(self, face_dimension=None, algorithm=None): r""" Return an iterator over the faces of given dimension. @@ -590,22 +590,6 @@ def face_generator(self, face_dimension=None, algorithm=None, **kwds): sage: f.ambient_Hrepresentation() (An equation (1, 1, 1) x - 6 == 0,) - The ``dual`` keyword is deprecated:: - - sage: P = polytopes.hypercube(4) - sage: list(P.face_generator(dual=False))[:4] - doctest:...: DeprecationWarning: the keyword dual is deprecated; use algorithm instead - See https://github.com/sagemath/sage/issues/33646 for details. - [A 4-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 16 vertices, - A -1-dimensional face of a Polyhedron in ZZ^4, - A 3-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 8 vertices, - A 3-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 8 vertices] - sage: list(P.face_generator(True))[:4] - [A 1-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 2 vertices, - A 1-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 2 vertices, - A 1-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 2 vertices, - A 1-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 2 vertices] - Check that we catch incorrect algorithms:: sage: list(P.face_generator(2, algorithm='integrate'))[:4] @@ -618,19 +602,9 @@ def face_generator(self, face_dimension=None, algorithm=None, **kwds): dual = False elif algorithm == 'dual': dual = True - elif algorithm in (False, True): - from sage.misc.superseded import deprecation - deprecation(33646, "the keyword dual is deprecated; use algorithm instead") - dual = algorithm elif algorithm is not None: raise ValueError("algorithm must be 'primal', 'dual' or None") - if kwds: - from sage.misc.superseded import deprecation - deprecation(33646, "the keyword dual is deprecated; use algorithm instead") - if 'dual' in kwds and dual is None: - dual = kwds['dual'] - from sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator import FaceIterator_geom return FaceIterator_geom(self, output_dimension=face_dimension, dual=dual) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd index a04a1186876..dd5ee0bf472 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd @@ -34,7 +34,6 @@ cdef class CombinatorialPolyhedron(SageObject): cdef tuple Vrep(self) cdef tuple facet_names(self) cdef tuple equations(self) - cdef tuple equalities(self) cdef unsigned int n_Vrepresentation(self) noexcept cdef unsigned int n_Hrepresentation(self) noexcept cdef bint is_bounded(self) noexcept diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 367049b9fc0..d21b824da0c 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -1359,7 +1359,7 @@ cdef class CombinatorialPolyhedron(SageObject): adjacency_matrix.set_immutable() return adjacency_matrix - def ridges(self, add_equations=False, names=True, add_equalities=False, algorithm=None): + def ridges(self, add_equations=False, names=True, algorithm=None): r""" Return the ridges. @@ -1453,21 +1453,7 @@ cdef class CombinatorialPolyhedron(SageObject): sage: C = CombinatorialPolyhedron(polytopes.simplex()) sage: C.ridges(names=False, add_equations=True) ((2, 3), (1, 3), (0, 3), (1, 2), (0, 2), (0, 1)) - - The keyword ``add_equalities`` is deprecated:: - - sage: C = CombinatorialPolyhedron(polytopes.simplex()) - sage: r = C.ridges(add_equations=True) - sage: r1 = C.ridges(add_equalities=True) - doctest:...: DeprecationWarning: the keyword ``add_equalities`` is deprecated; use ``add_equations`` - See https://github.com/sagemath/sage/issues/31834 for details. - sage: r == r1 - True """ - if add_equalities: - from sage.misc.superseded import deprecation - deprecation(31834, "the keyword ``add_equalities`` is deprecated; use ``add_equations``", 3) - add_equations = True self._compute_ridges(self._algorithm_to_dual(algorithm)) cdef size_t n_ridges = self._ridges.length @@ -2673,7 +2659,7 @@ cdef class CombinatorialPolyhedron(SageObject): """ return self.face_generator().meet_of_Hrep(*indices) - def face_generator(self, dimension=None, algorithm=None, **kwds): + def face_generator(self, dimension=None, algorithm=None): r""" Iterator over all proper faces of specified dimension. @@ -2760,16 +2746,6 @@ cdef class CombinatorialPolyhedron(SageObject): (A ray in the direction (1, 0), A vertex at (1, 0)) (A ray in the direction (0, 1), A vertex at (0, 1)) - TESTS: - - The kewword ``dual`` is deprecated:: - - sage: C = CombinatorialPolyhedron([[0,1,2],[0,1,3],[0,2,3],[1,2,3]]) - sage: it = C.face_generator(1, False) - doctest:...: DeprecationWarning: the keyword dual is deprecated; use algorithm instead - See https://github.com/sagemath/sage/issues/33646 for details. - sage: it = C.face_generator(1, dual=True) - .. SEEALSO:: :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator.FaceIterator`, @@ -2777,18 +2753,7 @@ cdef class CombinatorialPolyhedron(SageObject): """ cdef int dual - if algorithm in (False, True): - from sage.misc.superseded import deprecation - deprecation(33646, "the keyword dual is deprecated; use algorithm instead") - dual = int(algorithm) - else: - dual = self._algorithm_to_dual(algorithm) - - if kwds: - from sage.misc.superseded import deprecation - deprecation(33646, "the keyword dual is deprecated; use algorithm instead") - if 'dual' in kwds and dual == -1 and kwds['dual'] in (False, True): - dual = int(kwds['dual']) + dual = self._algorithm_to_dual(algorithm) if dual == -1: # Determine the faster way, to iterate through all faces. @@ -3273,11 +3238,6 @@ cdef class CombinatorialPolyhedron(SageObject): """ return self._equations - cdef tuple equalities(self): - from sage.misc.superseded import deprecation - deprecation(31834, "the method equalities of CombinatorialPolyhedron is deprecated; use equations", 3) - return self.equations() - cdef unsigned int n_Vrepresentation(self) noexcept: r""" Return the number of elements in the Vrepresentation. diff --git a/src/sage/graphs/generators/families.py b/src/sage/graphs/generators/families.py index 0d039d8db05..02998662ba0 100644 --- a/src/sage/graphs/generators/families.py +++ b/src/sage/graphs/generators/families.py @@ -496,7 +496,7 @@ def HammingGraph(n, q, X=None): def BalancedTree(r, h): r""" - Returns the perfectly balanced tree of height `h \geq 1`, + Return the perfectly balanced tree of height `h \geq 1`, whose root has degree `r \geq 2`. The number of vertices of this graph is @@ -513,23 +513,18 @@ def BalancedTree(r, h): OUTPUT: The perfectly balanced tree of height `h \geq 1` and whose root has - degree `r \geq 2`. A :exc:`~networkx.exception.NetworkXError` is raised - if `r < 2` or `h < 1`. - - ALGORITHM: - - Uses the :ref:`NetworkX ` function - :func:`~networkx.generators.classic.balanced_tree`. + degree `r \geq 2`. EXAMPLES: A balanced tree whose root node has degree `r = 2`, and of height `h = 1`, has order 3 and size 2:: - sage: G = graphs.BalancedTree(2, 1); G # needs networkx + sage: G = graphs.BalancedTree(2, 1); G Balanced tree: Graph on 3 vertices - sage: G.order(); G.size() # needs networkx + sage: G.order() 3 + sage: G.size() 2 sage: r = 2; h = 1 sage: v = 1 + r @@ -539,8 +534,9 @@ def BalancedTree(r, h): Plot a balanced tree of height 5, whose root node has degree `r = 3`:: - sage: G = graphs.BalancedTree(3, 5) # needs networkx - sage: G.show() # long time # needs networkx sage.plot + sage: G = graphs.BalancedTree(3, 5) + sage: G.plot() # long time # needs sage.plot + Graphics object consisting of 728 graphics primitives A tree is bipartite. If its vertex set is finite, then it is planar. :: @@ -563,17 +559,40 @@ def BalancedTree(r, h): has degree `r \geq 2`, but the construction degenerates gracefully:: - sage: graphs.BalancedTree(1, 10) # needs networkx + sage: graphs.BalancedTree(1, 10) Balanced tree: Graph on 11 vertices Similarly, we usually want the tree must have height `h \geq 1` but the algorithm also degenerates gracefully here:: - sage: graphs.BalancedTree(3, 0) # needs networkx + sage: graphs.BalancedTree(3, 0) Balanced tree: Graph on 1 vertex + + The construction is the same as the one of networkx:: + + sage: # needs networkx + sage: import networkx + sage: r = randint(2, 4); h = randint(1, 5) + sage: T = graphs.BalancedTree(r, h) + sage: N = Graph(networkx.balanced_tree(r, h), name="Balanced tree") + sage: T.is_isomorphic(N) + True """ - import networkx - return Graph(networkx.balanced_tree(r, h), name="Balanced tree") + # Compute the number of vertices per level of the tree + order = [r**l for l in range(h + 1)] + # Compute the first index of the vertices of a level + begin = [0] + begin.extend(begin[-1] + val for val in order) + # The number of vertices of the tree is the first index of level h + 1 + T = Graph(begin[-1], name="Balanced tree") + + # Add edges of the r-ary tree + for level in range(h): + start = begin[level + 1] + for u in range(begin[level], begin[level + 1]): + T.add_edges((u, v) for v in range(start, start + r)) + start += r + return T def BarbellGraph(n1, n2): diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index c6d429863d3..eee4c61b69c 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -1900,6 +1900,72 @@ def to_dictionary(self, edge_labels=False, multiple_edges=False): return d + def _vertex_indices_and_keys(self, vertices=None, *, sort=None): + r""" + Process a ``vertices`` parameter. + + This is a helper function for :meth:`adjacency_matrix`, + :meth:`incidence_matrix`, :meth:`weighted_adjacency_matrix`, + and :meth:`kirchhoff_matrix`. + + INPUT: + + - ``vertices`` -- list, ``None``, or ``True`` (default: ``None``) + + - when a list, the `i`-th row and column of the matrix correspond to + the `i`-th vertex in the ordering of ``vertices``, + - when ``None``, the `i`-th row and column of the matrix correspond to + the `i`-th vertex in the ordering given by + :meth:`GenericGraph.vertices` + - when ``True``, construct an endomorphism of a free module instead of + a matrix, where the module's basis is indexed by the vertices. + + - ``sort`` -- boolean or ``None`` (default); passed to :meth:`vertices` + when ``vertices`` is not a list. + + OUTPUT: pair of: + + - ``vertex_indices`` -- a dictionary mapping vertices to numerical indices, + - ``keys`` -- either a tuple of basis keys (when using a + :class:`CombinatorialFreeModule`) or ``None`` (when using a + :class:`FreeModule`, :func:`matrix`). + + EXAMPLES:: + + sage: G = graphs.PathGraph(5) + sage: G.relabel(['o....', '.o...', '..o..', '...o.', '....o']) + sage: G._vertex_indices_and_keys(None) + ({'....o': 0, '...o.': 1, '..o..': 2, '.o...': 3, 'o....': 4}, + None) + sage: G._vertex_indices_and_keys(None, sort=False) + ({'....o': 4, '...o.': 3, '..o..': 2, '.o...': 1, 'o....': 0}, + None) + sage: G._vertex_indices_and_keys(['..o..', '.o...', '...o.', 'o....', '....o']) + ({'....o': 4, '...o.': 2, '..o..': 0, '.o...': 1, 'o....': 3}, + None) + sage: G._vertex_indices_and_keys(True) + ({'....o': 4, '...o.': 3, '..o..': 2, '.o...': 1, 'o....': 0}, + ('o....', '.o...', '..o..', '...o.', '....o')) + sage: G._vertex_indices_and_keys(True, sort=True) + ({'....o': 0, '...o.': 1, '..o..': 2, '.o...': 3, 'o....': 4}, + ('....o', '...o.', '..o..', '.o...', 'o....')) + """ + n = self.order() + keys = None + if vertices is True: + vertices = self.vertices(sort=sort if sort is not None else False) + keys = tuple(vertices) # tuple to make it hashable + elif vertices is None: + try: + vertices = self.vertices(sort=sort if sort is not None else True) + except TypeError: + raise TypeError("Vertex labels are not comparable. You must " + "specify an ordering using parameter 'vertices'") + elif (len(vertices) != n or + set(vertices) != set(self.vertex_iterator())): + raise ValueError("parameter 'vertices' must be a permutation of the vertices") + return {v: i for i, v in enumerate(vertices)}, keys + def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds): r""" Return the adjacency matrix of the (di)graph. @@ -1911,10 +1977,16 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds - ``sparse`` -- boolean (default: ``None``); whether to represent with a sparse matrix - - ``vertices`` -- list (default: ``None``); the ordering of - the vertices defining how they should appear in the - matrix. By default, the ordering given by - :meth:`GenericGraph.vertices` with ``sort=True`` is used. + - ``vertices`` -- list, ``None``, or ``True`` (default: ``None``); + + - when a list, the `i`-th row and column of the matrix correspond to + the `i`-th vertex in the ordering of ``vertices``, + - when ``None``, the `i`-th row and column of the matrix correspond to + the `i`-th vertex in the ordering given by + :meth:`GenericGraph.vertices` with ``sort=True``. + - when ``True``, construct an endomorphism of a free module instead of + a matrix, where the module's basis is indexed by the vertices. + If the vertices are not comparable, the keyword ``vertices`` must be used to specify an ordering, or a :class:`TypeError` exception will be raised. @@ -2025,27 +2097,45 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds ValueError: matrix is immutable; please change a copy instead (i.e., use copy(M) to change a copy of M). + Creating a module endomorphism:: + + sage: # needs sage.modules + sage: D12 = posets.DivisorLattice(12).hasse_diagram() + sage: phi = D12.adjacency_matrix(vertices=True); phi + Generic endomorphism of + Free module generated by {1, 2, 3, 4, 6, 12} over Integer Ring + sage: print(phi._unicode_art_matrix()) + 1 2 3 4 6 12 + 1⎛ 0 1 1 0 0 0⎞ + 2⎜ 0 0 0 1 1 0⎟ + 3⎜ 0 0 0 0 1 0⎟ + 4⎜ 0 0 0 0 0 1⎟ + 6⎜ 0 0 0 0 0 1⎟ + 12⎝ 0 0 0 0 0 0⎠ + TESTS:: - sage: graphs.CubeGraph(8).adjacency_matrix().parent() # needs sage.modules + sage: # needs sage.modules + sage: graphs.CubeGraph(8).adjacency_matrix().parent() Full MatrixSpace of 256 by 256 dense matrices over Integer Ring - sage: graphs.CubeGraph(9).adjacency_matrix().parent() # needs sage.modules + sage: graphs.CubeGraph(9).adjacency_matrix().parent() Full MatrixSpace of 512 by 512 sparse matrices over Integer Ring - sage: Graph([(i, i+1) for i in range(500)] + [(0,1),], # needs sage.modules + sage: Graph([(i, i+1) for i in range(500)] + [(0,1),], ....: multiedges=True).adjacency_matrix().parent() Full MatrixSpace of 501 by 501 dense matrices over Integer Ring - sage: graphs.PathGraph(5).adjacency_matrix(vertices=[0,0,0,0,0]) # needs sage.modules + sage: graphs.PathGraph(5).adjacency_matrix(vertices=[0,0,0,0,0]) Traceback (most recent call last): ... - ValueError: parameter vertices must be a permutation of the vertices - sage: graphs.PathGraph(5).adjacency_matrix(vertices=[1,2,3]) # needs sage.modules + ValueError: parameter 'vertices' must be a permutation of the vertices + sage: graphs.PathGraph(5).adjacency_matrix(vertices=[1,2,3]) Traceback (most recent call last): ... - ValueError: parameter vertices must be a permutation of the vertices + ValueError: parameter 'vertices' must be a permutation of the vertices + sage: Graph ([[0, 42, 'John'], [(42, 'John')]]).adjacency_matrix() Traceback (most recent call last): ... - TypeError: Vertex labels are not comparable. You must specify an ordering using parameter ``vertices`` + TypeError: Vertex labels are not comparable. You must specify an ordering using parameter 'vertices' sage: Graph ([[0, 42, 'John'], [(42, 'John')]]).adjacency_matrix(vertices=['John', 42, 0]) [0 1 0] [1 0 0] @@ -2056,25 +2146,17 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds sparse = True if self.has_multiple_edges() or n <= 256 or self.density() > 0.05: sparse = False + vertex_indices, keys = self._vertex_indices_and_keys(vertices) + if keys is not None: + kwds = copy(kwds) + kwds['row_keys'] = kwds['column_keys'] = keys - if vertices is None: - try: - vertices = self.vertices(sort=True) - except TypeError: - raise TypeError("Vertex labels are not comparable. You must " - "specify an ordering using parameter " - "``vertices``") - elif (len(vertices) != n or - set(vertices) != set(self.vertex_iterator())): - raise ValueError("parameter vertices must be a permutation of the vertices") - - new_indices = {v: i for i, v in enumerate(vertices)} D = {} directed = self._directed multiple_edges = self.allows_multiple_edges() for u, v, l in self.edge_iterator(): - i = new_indices[u] - j = new_indices[v] + i = vertex_indices[u] + j = vertex_indices[v] if multiple_edges and (i, j) in D: D[i, j] += 1 if not directed and i != j: @@ -2298,7 +2380,7 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None sage: P5.incidence_matrix(vertices=[1] * P5.order()) # needs sage.modules Traceback (most recent call last): ... - ValueError: parameter vertices must be a permutation of the vertices + ValueError: parameter 'vertices' must be a permutation of the vertices sage: P5.incidence_matrix(edges=[(0, 1)] * P5.size()) # needs sage.modules Traceback (most recent call last): ... @@ -2313,18 +2395,9 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None if oriented is None: oriented = self.is_directed() - row_keys = None - if vertices is True: - vertices = self.vertices(sort=False) - row_keys = tuple(vertices) # because a list is not hashable - elif vertices is None: - vertices = self.vertices(sort=False) - elif (len(vertices) != self.num_verts() or - set(vertices) != set(self.vertex_iterator())): - raise ValueError("parameter vertices must be a permutation of the vertices") + vertex_indices, row_keys = self._vertex_indices_and_keys(vertices, sort=False) column_keys = None - verts = {v: i for i, v in enumerate(vertices)} use_edge_labels = kwds.pop('use_edge_labels', False) if edges is True: edges = self.edges(labels=use_edge_labels) @@ -2336,13 +2409,13 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None else: # We check that we have the same set of unlabeled edges if oriented: - i_edges = [(verts[e[0]], verts[e[1]]) for e in edges] - s_edges = [(verts[u], verts[v]) for u, v in self.edge_iterator(labels=False)] + i_edges = [(vertex_indices[e[0]], vertex_indices[e[1]]) for e in edges] + s_edges = [(vertex_indices[u], vertex_indices[v]) for u, v in self.edge_iterator(labels=False)] else: def reorder(u, v): return (u, v) if u <= v else (v, u) - i_edges = [reorder(verts[e[0]], verts[e[1]]) for e in edges] - s_edges = [reorder(verts[u], verts[v]) for u, v in self.edge_iterator(labels=False)] + i_edges = [reorder(vertex_indices[e[0]], vertex_indices[e[1]]) for e in edges] + s_edges = [reorder(vertex_indices[u], vertex_indices[v]) for u, v in self.edge_iterator(labels=False)] if sorted(i_edges) != sorted(s_edges): raise ValueError("parameter edges must be a permutation of the edges") @@ -2355,12 +2428,12 @@ def reorder(u, v): if oriented: for i, e in enumerate(edges): if e[0] != e[1]: - m[verts[e[0]], i] = -1 - m[verts[e[1]], i] = +1 + m[vertex_indices[e[0]], i] = -1 + m[vertex_indices[e[1]], i] = +1 else: for i, e in enumerate(edges): - m[verts[e[0]], i] += 1 - m[verts[e[1]], i] += 1 + m[vertex_indices[e[0]], i] += 1 + m[vertex_indices[e[1]], i] += 1 if row_keys is not None or column_keys is not None: m.set_immutable() @@ -2524,10 +2597,19 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, - ``sparse`` -- boolean (default: ``True``); whether to use a sparse or a dense matrix - - ``vertices`` -- list (default: ``None``); when specified, each vertex - is represented by its position in the list ``vertices``, otherwise - each vertex is represented by its position in the list returned by - method :meth:`vertices` + - ``vertices`` -- list, ``None``, or ``True`` (default: ``None``); + + - when a list, the `i`-th row and column of the matrix correspond to + the `i`-th vertex in the ordering of ``vertices``, + - when ``None``, the `i`-th row and column of the matrix correspond to + the `i`-th vertex in the ordering given by + :meth:`GenericGraph.vertices` with ``sort=True``. + - when ``True``, construct an endomorphism of a free module instead of + a matrix, where the module's basis is indexed by the vertices. + + If the vertices are not comparable, the keyword ``vertices`` must be + used to specify an ordering, or a :class:`TypeError` exception will + be raised. - ``default_weight`` -- (default: ``None``); specifies the weight to replace any ``None`` edge label. When not specified an error is raised @@ -2579,6 +2661,21 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, ValueError: matrix is immutable; please change a copy instead (i.e., use copy(M) to change a copy of M). + Creating a module morphism:: + + sage: # needs sage.modules + sage: G = Graph(sparse=True, weighted=True) + sage: G.add_edges([('A', 'B', 1), ('B', 'C', 2), ('A', 'C', 3), ('A', 'D', 4)]) + sage: phi = G.weighted_adjacency_matrix(vertices=True); phi + Generic endomorphism of + Free module generated by {'A', 'B', 'C', 'D'} over Integer Ring + sage: print(phi._unicode_art_matrix()) + A B C D + A⎛0 1 3 4⎞ + B⎜1 0 2 0⎟ + C⎜3 2 0 0⎟ + D⎝4 0 0 0⎠ + TESTS: The following doctest verifies that :issue:`4888` is fixed:: @@ -2609,11 +2706,10 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, if self.has_multiple_edges(): raise NotImplementedError("don't know how to represent weights for a multigraph") - if vertices is None: - vertices = self.vertices(sort=True) - elif (len(vertices) != self.num_verts() or - set(vertices) != set(self.vertex_iterator())): - raise ValueError("parameter vertices must be a permutation of the vertices") + vertex_indices, row_column_keys = self._vertex_indices_and_keys(vertices) + if row_column_keys is not None: + kwds = copy(kwds) + kwds['row_keys'] = kwds['column_keys'] = row_column_keys # Method for checking edge weights and setting default weight if default_weight is None: @@ -2628,18 +2724,16 @@ def func(u, v, label): return default_weight return label - new_indices = {v: i for i,v in enumerate(vertices)} - D = {} if self._directed: for u, v, label in self.edge_iterator(): - i = new_indices[u] - j = new_indices[v] + i = vertex_indices[u] + j = vertex_indices[v] D[i, j] = func(u, v, label) else: for u, v, label in self.edge_iterator(): - i = new_indices[u] - j = new_indices[v] + i = vertex_indices[u] + j = vertex_indices[v] label = func(u, v, label) D[i, j] = label D[j, i] = label @@ -2707,6 +2801,20 @@ def kirchhoff_matrix(self, weighted=None, indegree=True, normalized=False, signl - Else, `D-M` is used in calculation of Kirchhoff matrix + - ``vertices`` -- list, ``None``, or ``True`` (default: ``None``); + + - when a list, the `i`-th row and column of the matrix correspond to + the `i`-th vertex in the ordering of ``vertices``, + - when ``None``, the `i`-th row and column of the matrix correspond to + the `i`-th vertex in the ordering given by + :meth:`GenericGraph.vertices` with ``sort=True``. + - when ``True``, construct an endomorphism of a free module instead of + a matrix, where the module's basis is indexed by the vertices. + + If the vertices are not comparable, the keyword ``vertices`` must be + used to specify an ordering, or a :class:`TypeError` exception will + be raised. + Note that any additional keywords will be passed on to either the :meth:`~GenericGraph.adjacency_matrix` or :meth:`~GenericGraph.weighted_adjacency_matrix` method. @@ -2788,18 +2896,36 @@ def kirchhoff_matrix(self, weighted=None, indegree=True, normalized=False, signl sage: M = G.kirchhoff_matrix(vertices=[0, 1], immutable=True) # needs sage.modules sage: M.is_immutable() # needs sage.modules True + + Creating a module morphism:: + + sage: # needs sage.modules + sage: G = Graph(sparse=True, weighted=True) + sage: G.add_edges([('A', 'B', 1), ('B', 'C', 2), ('A', 'C', 3), ('A', 'D', 4)]) + sage: phi = G.laplacian_matrix(weighted=True, vertices=True); phi + Generic endomorphism of + Free module generated by {'A', 'B', 'C', 'D'} over Integer Ring + sage: print(phi._unicode_art_matrix()) + A B C D + A⎛ 8 -1 -3 -4⎞ + B⎜-1 3 -2 0⎟ + C⎜-3 -2 5 0⎟ + D⎝-4 0 0 4⎠ + """ - from sage.matrix.constructor import diagonal_matrix + from sage.matrix.constructor import diagonal_matrix, matrix set_immutable = kwds.pop('immutable', False) + vertex_indices, keys = self._vertex_indices_and_keys(kwds.pop('vertices', None)) + if weighted is None: weighted = self._weighted if weighted: - M = self.weighted_adjacency_matrix(immutable=True, **kwds) + M = self.weighted_adjacency_matrix(vertices=list(vertex_indices), immutable=True, **kwds) else: - M = self.adjacency_matrix(immutable=True, **kwds) + M = self.adjacency_matrix(vertices=list(vertex_indices), immutable=True, **kwds) D = M.parent(0) @@ -2839,6 +2965,8 @@ def kirchhoff_matrix(self, weighted=None, indegree=True, normalized=False, signl else: ret = D - M + if keys is not None: + return matrix(ret, row_keys=keys, column_keys=keys) if set_immutable: ret.set_immutable() return ret @@ -15719,7 +15847,7 @@ def cluster_triangles(self, nbunch=None, implementation=None): sage: F = graphs.FruchtGraph() sage: list(F.cluster_triangles().values()) - [1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0] + [1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0] sage: F.cluster_triangles() {0: 1, 1: 1, 2: 0, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 0, 9: 1, 10: 1, 11: 0} sage: F.cluster_triangles(nbunch=[0, 1, 2]) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index d4f2e18a555..d8e88b023f0 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -6917,27 +6917,29 @@ def cliques_number_of(self, vertices=None, cliques=None): EXAMPLES:: sage: C = Graph('DJ{') - sage: C.cliques_number_of() # needs networkx + sage: C.cliques_number_of() {0: 1, 1: 1, 2: 1, 3: 1, 4: 2} sage: E = C.cliques_maximal() sage: E [[0, 4], [1, 2, 3, 4]] - sage: C.cliques_number_of(cliques=E) # needs networkx + sage: C.cliques_number_of(cliques=E) {0: 1, 1: 1, 2: 1, 3: 1, 4: 2} sage: F = graphs.Grid2dGraph(2,3) - sage: F.cliques_number_of() # needs networkx + sage: F.cliques_number_of() {(0, 0): 2, (0, 1): 3, (0, 2): 2, (1, 0): 2, (1, 1): 3, (1, 2): 2} - sage: F.cliques_number_of(vertices=[(0, 1), (1, 2)]) # needs networkx + sage: F.cliques_number_of(vertices=[(0, 1), (1, 2)]) {(0, 1): 3, (1, 2): 2} sage: F.cliques_number_of(vertices=(0, 1)) 3 sage: G = Graph({0:[1,2,3], 1:[2], 3:[0,1]}) sage: G.show(figsize=[2,2]) # needs sage.plot - sage: G.cliques_number_of() # needs networkx + sage: G.cliques_number_of() {0: 2, 1: 2, 2: 1, 3: 1} """ if cliques is None: - cliques = self.cliques_maximal() + # We use IndependentSets to avoid the construction of the list of + # cliques as currently done by method cliques_maximal. + cliques = IndependentSets(self, maximal=True, complement=True) if vertices in self: # single vertex return sum(1 for c in cliques if vertices in c) @@ -6952,7 +6954,7 @@ def cliques_number_of(self, vertices=None, cliques=None): @doc_index("Clique-related methods") def cliques_get_max_clique_graph(self): - """ + r""" Return the clique graph. Vertices of the result are the maximal cliques of the graph, and edges @@ -6968,26 +6970,52 @@ def cliques_get_max_clique_graph(self): EXAMPLES:: - sage: MCG = graphs.ChvatalGraph().cliques_get_max_clique_graph(); MCG # needs networkx + sage: MCG = graphs.ChvatalGraph().cliques_get_max_clique_graph(); MCG Graph on 24 vertices - sage: MCG.show(figsize=[2,2], vertex_size=20, vertex_labels=False) # needs networkx sage.plot + sage: MCG.show(figsize=[2,2], vertex_size=20, vertex_labels=False) # needs sage.plot sage: G = Graph({0:[1,2,3], 1:[2], 3:[0,1]}) sage: G.show(figsize=[2,2]) # needs sage.plot - sage: G.cliques_get_max_clique_graph() # needs networkx + sage: G.cliques_get_max_clique_graph() Graph on 2 vertices - sage: G.cliques_get_max_clique_graph().show(figsize=[2,2]) # needs networkx sage.plot + sage: G.cliques_get_max_clique_graph().show(figsize=[2,2]) # needs sage.plot + + TESTS:: + + sage: # needs networkx + sage: import networkx + sage: CG = graphs.ChvatalGraph() + sage: S = CG.cliques_get_max_clique_graph() + sage: N = Graph(networkx.make_max_clique_graph(CG.networkx_graph(), + ....: create_using=networkx.MultiGraph()), + ....: multiedges=False) + sage: S.is_isomorphic(N) + True """ - import networkx - return Graph(networkx.make_max_clique_graph(self.networkx_graph(), create_using=networkx.MultiGraph()), - multiedges=False) + # Associate each maximal clique an integer index and record for each + # vertex of self the cliques it belongs to. + # We use IndependentSets to avoid the construction of the list of + # cliques as currently done by method cliques_maximal. + cliques_of_vertex = {u: [] for u in self} + for n, clique in enumerate(IndependentSets(self, maximal=True, complement=True)): + for u in clique: + cliques_of_vertex[u].append(n) + + # Build a graph with one vertex per maximal clique and an edge between + # cliques sharing a vertex of self + G = Graph(n, multiedges=False) + for block in cliques_of_vertex.values(): + G.add_clique(block) + return G @doc_index("Clique-related methods") def cliques_get_clique_bipartite(self, **kwds): - """ - Return a bipartite graph constructed such that maximal cliques are the - right vertices and the left vertices are retained from the given - graph. Right and left vertices are connected if the bottom vertex - belongs to the clique represented by a top vertex. + r""" + Return the vertex-clique bipartite graph of ``self``. + + In the returned bipartite graph, the ``left`` vertices are the vertices + of ``self`` and the ``right`` vertices represent the maximal cliques of + ``self``. There is an edge from vertex `v` to clique `C` in the + bipartite graph if and only if `v` belongs to `C`. .. NOTE:: @@ -6996,18 +7024,32 @@ def cliques_get_clique_bipartite(self, **kwds): EXAMPLES:: - sage: CBG = graphs.ChvatalGraph().cliques_get_clique_bipartite(); CBG # needs networkx + sage: CBG = graphs.ChvatalGraph().cliques_get_clique_bipartite(); CBG Bipartite graph on 36 vertices - sage: CBG.show(figsize=[2,2], vertex_size=20, vertex_labels=False) # needs networkx sage.plot + sage: CBG.show(figsize=[2,2], vertex_size=20, vertex_labels=False) # needs sage.plot sage: G = Graph({0:[1,2,3], 1:[2], 3:[0,1]}) sage: G.show(figsize=[2,2]) # needs sage.plot - sage: G.cliques_get_clique_bipartite() # needs networkx + sage: G.cliques_get_clique_bipartite() Bipartite graph on 6 vertices - sage: G.cliques_get_clique_bipartite().show(figsize=[2,2]) # needs networkx sage.plot + sage: G.cliques_get_clique_bipartite().show(figsize=[2,2]) # needs sage.plot + + TESTS:: + + sage: # needs networkx + sage: import networkx + sage: CG = graphs.ChvatalGraph() + sage: S = CG.cliques_get_clique_bipartite() + sage: N = BipartiteGraph(networkx.make_clique_bipartite(CG.networkx_graph())) + sage: S.is_isomorphic(N) + True """ - from .bipartite_graph import BipartiteGraph - import networkx - return BipartiteGraph(networkx.make_clique_bipartite(self.networkx_graph(), **kwds)) + G = Graph([self, []], format='vertices_and_edges') + for i, clique in enumerate(IndependentSets(self, maximal=True, complement=True)): + idx = - i - 1 + G.add_vertex(idx) + G.add_edges((u, idx) for u in clique) + from sage.graphs.bipartite_graph import BipartiteGraph + return BipartiteGraph(G, check=False) @doc_index("Algorithmically hard stuff") def independent_set(self, algorithm="Cliquer", value_only=False, reduction_rules=True, diff --git a/src/sage/graphs/graph_decompositions/modular_decomposition.py b/src/sage/graphs/graph_decompositions/modular_decomposition.py index 6b1e825ba2e..03e9231bd65 100644 --- a/src/sage/graphs/graph_decompositions/modular_decomposition.py +++ b/src/sage/graphs/graph_decompositions/modular_decomposition.py @@ -371,17 +371,17 @@ def print_md_tree(root): sage: from sage.graphs.graph_decompositions.modular_decomposition import * sage: print_md_tree(modular_decomposition(graphs.IcosahedralGraph())) PRIME + 3 + 4 + 7 + 9 + 11 1 5 - 7 8 - 11 0 2 6 - 3 - 9 - 4 10 """ @@ -494,17 +494,17 @@ def habib_maurer_algorithm(graph, g_classes=None): sage: from sage.graphs.graph_decompositions.modular_decomposition import * sage: print_md_tree(habib_maurer_algorithm(graphs.IcosahedralGraph())) PRIME + 3 + 4 + 7 + 9 + 11 1 5 - 7 8 - 11 0 2 6 - 3 - 9 - 4 10 The Octahedral graph is not Prime:: diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py index 52941853359..ce1f8916423 100644 --- a/src/sage/graphs/graph_input.py +++ b/src/sage/graphs/graph_input.py @@ -462,6 +462,14 @@ def from_dict_of_dicts(G, M, loops=False, multiedges=False, weighted=False, conv sage: g.is_isomorphic(graphs.PetersenGraph()) True + The resulting order of vertices is unspecified but deterministic:: + + sage: from sage.graphs.graph_input import from_dict_of_dicts + sage: g = Graph() + sage: from_dict_of_dicts(g, {i: {} for i in range(99, 90, -1)}) + sage: g.vertices(sort=False) + [99, 98, 97, 96, 95, 94, 93, 92, 91] + TESTS: :issue:`32831` is fixed:: @@ -493,8 +501,11 @@ def from_dict_of_dicts(G, M, loops=False, multiedges=False, weighted=False, conv G.allow_loops(loops, check=False) G.allow_multiple_edges(multiedges, check=False) - verts = set().union(M.keys(), *M.values()) - G.add_vertices(verts) + # Use keys of a dictionary instead of a set, to preserve insertion order + verts = dict(M) + for d in M.values(): + verts.update(d) + G.add_vertices(verts.keys()) if convert_empty_dict_labels_to_None: def relabel(x): return x if x != {} else None @@ -504,7 +515,7 @@ def relabel(x): is_directed = G.is_directed() if not is_directed and multiedges: - v_to_id = {v: i for i, v in enumerate(verts)} + v_to_id = {v: i for i, v in enumerate(verts.keys())} for u in M: for v in M[u]: if v_to_id[u] <= v_to_id[v] or v not in M or u not in M[v] or u == v: @@ -543,8 +554,18 @@ def from_dict_of_lists(G, D, loops=False, multiedges=False, weighted=False): sage: from_dict_of_lists(g, graphs.PetersenGraph().to_dictionary()) sage: g.is_isomorphic(graphs.PetersenGraph()) True + + The resulting order of vertices is unspecified but deterministic:: + + sage: from sage.graphs.graph_input import from_dict_of_lists + sage: g = Graph() + sage: from_dict_of_lists(g, {i: [] for i in range(99, 90, -1)}) + sage: g.vertices(sort=False) + [99, 98, 97, 96, 95, 94, 93, 92, 91] """ - verts = set().union(D.keys(), *D.values()) + # Use keys of a dictionary instead of a set, to preserve insertion order + verts = dict(D) + verts.update({v: None for l in D.values() for v in l}) if not loops: if any(u in neighb for u, neighb in D.items()): if loops is False: @@ -567,11 +588,11 @@ def from_dict_of_lists(G, D, loops=False, multiedges=False, weighted=False): multiedges = False G.allow_loops(loops, check=False) G.allow_multiple_edges(multiedges, check=False) - G.add_vertices(verts) + G.add_vertices(verts.keys()) is_directed = G.is_directed() if not is_directed and multiedges: - v_to_id = {v: i for i, v in enumerate(verts)} + v_to_id = {v: i for i, v in enumerate(verts.keys())} for u in D: for v in D[u]: if (v_to_id[u] <= v_to_id[v] or diff --git a/src/sage/groups/abelian_gps/abelian_group.py b/src/sage/groups/abelian_gps/abelian_group.py index 8a0c5167b16..07ca7c7444e 100644 --- a/src/sage/groups/abelian_gps/abelian_group.py +++ b/src/sage/groups/abelian_gps/abelian_group.py @@ -458,10 +458,16 @@ def is_AbelianGroup(x): sage: F = AbelianGroup(5,[5,5,7,8,9], names=list("abcde")); F Multiplicative Abelian group isomorphic to C5 x C5 x C7 x C8 x C9 sage: is_AbelianGroup(F) + doctest:warning... + DeprecationWarning: the function is_AbelianGroup is deprecated; + use 'isinstance(..., AbelianGroup_class)' instead + See https://github.com/sagemath/sage/issues/37898 for details. True sage: is_AbelianGroup(AbelianGroup(7, [3]*7)) True """ + from sage.misc.superseded import deprecation + deprecation(37898, "the function is_AbelianGroup is deprecated; use 'isinstance(..., AbelianGroup_class)' instead") return isinstance(x, AbelianGroup_class) @@ -569,7 +575,7 @@ def is_isomorphic(left, right): sage: G1.is_isomorphic(G2) True """ - if not is_AbelianGroup(right): + if not isinstance(right, AbelianGroup_class): return False return left.elementary_divisors() == right.elementary_divisors() diff --git a/src/sage/groups/abelian_gps/dual_abelian_group.py b/src/sage/groups/abelian_gps/dual_abelian_group.py index 86966cf74d6..24c910d96d9 100644 --- a/src/sage/groups/abelian_gps/dual_abelian_group.py +++ b/src/sage/groups/abelian_gps/dual_abelian_group.py @@ -86,6 +86,10 @@ def is_DualAbelianGroup(x): sage: F = AbelianGroup(5,[3,5,7,8,9], names=list("abcde")) sage: Fd = F.dual_group() sage: is_DualAbelianGroup(Fd) + doctest:warning... + DeprecationWarning: the function is_DualAbelianGroup is deprecated; + use 'isinstance(..., DualAbelianGroup_class)' instead + See https://github.com/sagemath/sage/issues/37898 for details. True sage: F = AbelianGroup(3,[1,2,3], names='a') sage: Fd = F.dual_group() @@ -94,6 +98,8 @@ def is_DualAbelianGroup(x): sage: F.gens() (1, a1, a2) """ + from sage.misc.superseded import deprecation + deprecation(37898, "the function is_DualAbelianGroup is deprecated; use 'isinstance(..., DualAbelianGroup_class)' instead") return isinstance(x, DualAbelianGroup_class) diff --git a/src/sage/groups/matrix_gps/matrix_group.py b/src/sage/groups/matrix_gps/matrix_group.py index 709a88a6d8e..ef05b55c190 100644 --- a/src/sage/groups/matrix_gps/matrix_group.py +++ b/src/sage/groups/matrix_gps/matrix_group.py @@ -78,6 +78,10 @@ def is_MatrixGroup(x): sage: from sage.groups.matrix_gps.matrix_group import is_MatrixGroup sage: is_MatrixGroup(MatrixSpace(QQ, 3)) + doctest:warning... + DeprecationWarning: the function is_MatrixGroup is deprecated; + use 'isinstance(..., MatrixGroup_base)' instead + See https://github.com/sagemath/sage/issues/37898 for details. False sage: is_MatrixGroup(Mat(QQ, 3)) False @@ -86,6 +90,8 @@ def is_MatrixGroup(x): sage: is_MatrixGroup(MatrixGroup([matrix(2, [1,1,0,1])])) True """ + from sage.misc.superseded import deprecation + deprecation(37898, "the function is_MatrixGroup is deprecated; use 'isinstance(..., MatrixGroup_base)' instead") return isinstance(x, MatrixGroup_base) ################################################################### @@ -499,7 +505,7 @@ def __richcmp__(self, other, op): sage: G != H False """ - if not is_MatrixGroup(other): + if not isinstance(other, MatrixGroup_base): return NotImplemented if self is other: diff --git a/src/sage/interfaces/singular.py b/src/sage/interfaces/singular.py index 59cd9a8afcd..4697b9763d3 100644 --- a/src/sage/interfaces/singular.py +++ b/src/sage/interfaces/singular.py @@ -1409,7 +1409,7 @@ def _repr_(self): # this is our cue that singular uses `rp` instead of `ip` if singular_name_mapping['invlex'] == 'rp' and 'doctest' in str(get_display_manager()): s = re.sub('^(// .*block.* : ordering )rp$', '\\1ip', - s, 0, re.MULTILINE); + s, 0, re.MULTILINE) return s def __copy__(self): diff --git a/src/sage/manifolds/catalog.py b/src/sage/manifolds/catalog.py index bb09ed8885d..91b0997de7b 100644 --- a/src/sage/manifolds/catalog.py +++ b/src/sage/manifolds/catalog.py @@ -127,14 +127,19 @@ def Kerr(m=1, a=0, coordinates="BL", names=None): 4-dimensional Lorentzian manifold M sage: K.atlas() [Chart (M, (t, r, th, ph))] + + The Kerr metric in Boyer-Lindquist coordinates (cf. :wikipedia:`Kerr_metric`):: + sage: K.metric().display() g = (2*m*r/(a^2*cos(th)^2 + r^2) - 1) dt⊗dt - + 2*a*m*r*sin(th)^2/(a^2*cos(th)^2 + r^2) dt⊗dph + - 2*a*m*r*sin(th)^2/(a^2*cos(th)^2 + r^2) dt⊗dph + (a^2*cos(th)^2 + r^2)/(a^2 - 2*m*r + r^2) dr⊗dr + (a^2*cos(th)^2 + r^2) dth⊗dth - + 2*a*m*r*sin(th)^2/(a^2*cos(th)^2 + r^2) dph⊗dt + - 2*a*m*r*sin(th)^2/(a^2*cos(th)^2 + r^2) dph⊗dt + (2*a^2*m*r*sin(th)^2/(a^2*cos(th)^2 + r^2) + a^2 + r^2)*sin(th)^2 dph⊗dph + The Schwarzschild spacetime with the mass parameter set to 1:: + sage: K. = manifolds.Kerr() sage: K 4-dimensional Lorentzian manifold M @@ -144,6 +149,9 @@ def Kerr(m=1, a=0, coordinates="BL", names=None): sage: K.default_chart().coord_range() t: (-oo, +oo); r: (0, +oo); th: (0, pi); ph: [-pi, pi] (periodic) + + The Kerr spacetime in Kerr coordinates:: + sage: m, a = var('m, a') sage: K. = manifolds.Kerr(m, a, coordinates="Kerr") sage: K @@ -205,7 +213,7 @@ def Kerr(m=1, a=0, coordinates="BL", names=None): g[0, 0], g[1, 1], g[2, 2], g[3, 3] = -(1-2*m*r/rho**2), \ rho**2/(r**2-2*m*r+a**2), rho**2, \ (r**2+a**2+2*m*r*a**2/rho**2*sin(th)**2)*sin(th)**2 - g[0, 3] = 2*m*r*a*sin(th)**2/rho**2 + g[0, 3] = -2*m*r*a*sin(th)**2/rho**2 return M raise NotImplementedError("coordinates system not implemented, see help" diff --git a/src/sage/matrix/args.pyx b/src/sage/matrix/args.pyx index b01f3466998..4f578440df6 100644 --- a/src/sage/matrix/args.pyx +++ b/src/sage/matrix/args.pyx @@ -390,10 +390,14 @@ cdef class MatrixArgs: if argi == argc: return - # check nrows and ncols argument + # check positional nrows and ncols argument + # even if redundant with row_keys, column_keys given as keywords; + # but do not check for positional row_keys, column_keys arguments + # -- we do not allow those, as they would be too easy to + # confuse with entries cdef int k cdef long v - if self.nrows == -1 and self.ncols == -1 and self.row_keys is None and self.column_keys is None: + if self.nrows == -1 and self.ncols == -1: for k in range(2): arg = args[argi] if is_numpy_type(type(arg)): @@ -410,7 +414,8 @@ cdef class MatrixArgs: else: self.set_ncols(v) argi += 1 - if argi == argc: return + if argi == argc: + return # check for entries argument if self.entries is None: @@ -931,7 +936,8 @@ cdef class MatrixArgs: Integer Ring in Category of finite dimensional modules with basis over (Dedekind domains and euclidean domains - and infinite enumerated sets and metric spaces); + and noetherian rings and infinite enumerated sets + and metric spaces); typ=ZERO; entries=None> """ if self.column_keys is not None and self.column_keys != column_keys: @@ -967,7 +973,8 @@ cdef class MatrixArgs: to Free module generated by {'u', 'v'} over Integer Ring in Category of finite dimensional modules with basis over (Dedekind domains and euclidean domains - and infinite enumerated sets and metric spaces); + and noetherian rings and infinite enumerated sets + and metric spaces); typ=ZERO; entries=None> """ if self.row_keys is not None and self.row_keys != row_keys: diff --git a/src/sage/matrix/constructor.pyx b/src/sage/matrix/constructor.pyx index 3e8235cb94f..f343fd82578 100644 --- a/src/sage/matrix/constructor.pyx +++ b/src/sage/matrix/constructor.pyx @@ -62,13 +62,11 @@ def matrix(*args, **kwds): determine this from the given entries, falling back to ``ZZ`` if no entries are given. - - ``nrows`` or ``row_keys`` -- the number of rows in the matrix, - or a finite or enumerated family of arbitrary objects - that index the rows of the matrix + - ``nrows`` -- the number of rows in the matrix, or a finite or + enumerated family of arbitrary objects that index the rows of the matrix - - ``ncols`` or ``column_keys`` -- the number of columns in the - matrix, or a finite or enumerated family of arbitrary objects - that index the columns of the matrix + - ``ncols`` -- the number of columns in the matrix, or a finite or + enumerated family of arbitrary objects that index the columns of the matrix - ``entries`` -- see examples below. @@ -82,6 +80,12 @@ def matrix(*args, **kwds): ``True`` when the entries are given as a dictionary, otherwise defaults to ``False``. + - ``row_keys`` -- a finite or enumerated family of arbitrary objects + that index the rows of the matrix + + - ``column_keys`` -- a finite or enumerated family of arbitrary objects + that index the columns of the matrix + - ``space`` -- matrix space which will be the parent of the output matrix. This determines ``base_ring``, ``nrows``, ``row_keys``, ``ncols``, ``column_keys``, and ``sparse``. @@ -257,6 +261,14 @@ def matrix(*args, **kwds): u⎛1 2 3⎞ v⎝4 5 6⎠ + It is allowed to specify dimensions redundantly:: + + sage: M = matrix(2, 3, [[1,2,3], [4,5,6]], + ....: column_keys=['a','b','c'], row_keys=['u','v']); M + Generic morphism: + From: Free module generated by {'a', 'b', 'c'} over Integer Ring + To: Free module generated by {'u', 'v'} over Integer Ring + TESTS: There are many ways to create an empty matrix:: diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx index 3dc47b9df07..c1e538a441e 100644 --- a/src/sage/matrix/matrix0.pyx +++ b/src/sage/matrix/matrix0.pyx @@ -1958,7 +1958,7 @@ cdef class Matrix(sage.structure.element.Matrix): sage: K = (A - e).kernel() sage: P = K.basis_matrix() sage: P.str() - '[ 1.000000000000000? + 0.?e-17*I -2.116651487479748? + 0.0255565807096352?*I -0.2585224251020429? + 0.2886023409047535?*I -0.4847545623533090? - 1.871890760086142?*I]' + '[ 1.000000000000000? + 0.?e-17*I -2.116651487479748? + 0.0255565807096352?*I -0.2585224251020429? + 0.288602340904754?*I -0.4847545623533090? - 1.871890760086142?*I]' Use single-row delimiters where appropriate:: diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 9a7fdd393c5..e36d913bcd9 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -10453,23 +10453,23 @@ cdef class Matrix(Matrix1): ....: [-1, 1, -6, -6, 5]]) sage: Q, R = A.QR() sage: Q - [ -0.4588314677411235? -0.1260506983326509? 0.3812120831224489? -0.394573711338418? -0.6874400625964?] - [ -0.4588314677411235? 0.4726901187474409? -0.05198346588033394? 0.7172941251646595? -0.2209628772631?] - [ 0.2294157338705618? 0.6617661662464172? 0.6619227988762521? -0.1808720937375480? 0.1964114464561?] - [ 0.6882472016116853? 0.1890760474989764? -0.2044682991293135? 0.0966302966543065? -0.6628886317894?] + [ -0.4588314677411235? -0.1260506983326509? 0.3812120831224489? -0.394573711338418? -0.687440062597?] + [ -0.4588314677411235? 0.4726901187474409? -0.05198346588033394? 0.717294125164660? -0.220962877263?] + [ 0.2294157338705618? 0.6617661662464172? 0.6619227988762521? -0.180872093737548? 0.1964114464561?] + [ 0.6882472016116853? 0.1890760474989764? -0.2044682991293135? 0.096630296654307? -0.662888631790?] [ -0.2294157338705618? 0.5357154679137663? -0.609939332995919? -0.536422031427112? 0.0245514308070?] sage: R [ 4.358898943540674? -0.4588314677411235? 13.07669683062202? 6.194224814505168? 2.982404540317303?] [ 0 1.670171752907625? 0.5987408170800917? -1.292019657909672? 6.207996892883057?] - [ 0 0 5.444401659866974? 5.468660610611130? -0.6827161852283857?] - [ 0 0 0 1.027626039419836? -3.619300149686620?] - [ 0 0 0 0 0.024551430807012?] + [ 0 0 5.444401659866974? 5.468660610611130? -0.682716185228386?] + [ 0 0 0 1.027626039419836? -3.61930014968662?] + [ 0 0 0 0 0.02455143080702?] sage: Q.conjugate_transpose()*Q - [1.000000000000000? 0.?e-18 0.?e-17 0.?e-16 0.?e-13] - [ 0.?e-18 1.000000000000000? 0.?e-17 0.?e-16 0.?e-13] - [ 0.?e-17 0.?e-17 1.000000000000000? 0.?e-16 0.?e-13] - [ 0.?e-16 0.?e-16 0.?e-16 1.000000000000000? 0.?e-13] - [ 0.?e-13 0.?e-13 0.?e-13 0.?e-13 1.0000000000000?] + [1.000000000000000? 0.?e-18 0.?e-17 0.?e-15 0.?e-12] + [ 0.?e-18 1.000000000000000? 0.?e-16 0.?e-15 0.?e-12] + [ 0.?e-17 0.?e-16 1.000000000000000? 0.?e-15 0.?e-12] + [ 0.?e-15 0.?e-15 0.?e-15 1.000000000000000? 0.?e-12] + [ 0.?e-12 0.?e-12 0.?e-12 0.?e-12 1.000000000000?] sage: Q * R == A True @@ -10485,24 +10485,24 @@ cdef class Matrix(Matrix1): sage: Q, R = A.QR() sage: Q [ -0.7302967433402215? 0.2070566455055649? + 0.5383472783144687?*I 0.2463049809998642? - 0.0764456358723292?*I 0.2381617683194332? - 0.1036596032779695?*I] - [ 0.0912870929175277? -0.2070566455055649? - 0.3778783780476559?*I 0.3786559533863033? - 0.1952221495524667?*I 0.701244450214469? - 0.3643711650986595?*I] - [ 0.6390096504226938? + 0.0912870929175277?*I 0.1708217325420910? + 0.6677576817554466?*I -0.03411475806452072? + 0.04090198741767143?*I 0.3140171085506764? - 0.0825191718705412?*I] + [ 0.0912870929175277? -0.2070566455055649? - 0.3778783780476559?*I 0.3786559533863032? - 0.1952221495524667?*I 0.701244450214469? - 0.364371165098660?*I] + [ 0.6390096504226938? + 0.0912870929175277?*I 0.1708217325420910? + 0.6677576817554466?*I -0.03411475806452072? + 0.04090198741767143?*I 0.3140171085506763? - 0.0825191718705412?*I] [ 0.1825741858350554? + 0.0912870929175277?*I -0.03623491296347385? + 0.0724698259269477?*I 0.8632284069415110? + 0.06322839976356195?*I -0.4499694867611521? - 0.0116119181208918?*I] sage: R [ 10.95445115010333? 0.?e-18 - 1.917028951268082?*I 5.385938482134133? - 2.190890230020665?*I -0.2738612787525831? - 2.190890230020665?*I] - [ 0 4.829596256417300? + 0.?e-18*I -0.869637911123373? - 5.864879483945125?*I 0.993871898426712? - 0.3054085521207082?*I] + [ 0 4.829596256417300? + 0.?e-17*I -0.869637911123373? - 5.864879483945125?*I 0.993871898426712? - 0.3054085521207082?*I] [ 0 0 12.00160760935814? + 0.?e-16*I -0.2709533402297273? + 0.4420629644486323?*I] [ 0 0 0 1.942963944258992? + 0.?e-16*I] sage: Q.conjugate_transpose()*Q [1.000000000000000? + 0.?e-19*I 0.?e-18 + 0.?e-17*I 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I] [ 0.?e-18 + 0.?e-17*I 1.000000000000000? + 0.?e-17*I 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I] - [ 0.?e-17 + 0.?e-17*I 0.?e-17 + 0.?e-17*I 1.000000000000000? + 0.?e-17*I 0.?e-16 + 0.?e-16*I] - [ 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 1.000000000000000? + 0.?e-16*I] + [ 0.?e-17 + 0.?e-17*I 0.?e-17 + 0.?e-17*I 1.000000000000000? + 0.?e-16*I 0.?e-16 + 0.?e-16*I] + [ 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 1.000000000000000? + 0.?e-15*I] sage: Q*R - A [ 0.?e-17 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I] - [ 0.?e-18 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I] + [ 0.?e-18 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-15 + 0.?e-15*I] [0.?e-17 + 0.?e-18*I 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I] - [0.?e-18 + 0.?e-18*I 0.?e-18 + 0.?e-18*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I] + [0.?e-18 + 0.?e-18*I 0.?e-18 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-15 + 0.?e-16*I] A rank-deficient rectangular matrix, with both values of the ``full`` keyword. :: @@ -12192,9 +12192,9 @@ cdef class Matrix(Matrix1): sage: # needs sage.combinat sage.libs.pari sage: _, T = A.is_similar(B, transformation=True) sage: T - [ 1.00000000000000? + 0.?e-14*I 0.?e-14 + 0.?e-14*I 0.?e-14 + 0.?e-14*I] - [-0.66666666666667? + 0.?e-15*I 0.166666666666667? + 0.?e-15*I -0.83333333333334? + 0.?e-14*I] - [ 0.66666666666667? + 0.?e-14*I 0.?e-14 + 0.?e-14*I -0.33333333333333? + 0.?e-14*I] + [ 1.0000000000000? + 0.?e-13*I 0.?e-13 + 0.?e-13*I 0.?e-13 + 0.?e-13*I] + [-0.6666666666667? + 0.?e-13*I 0.16666666666667? + 0.?e-14*I -0.8333333333334? + 0.?e-13*I] + [ 0.6666666666667? + 0.?e-13*I 0.?e-13 + 0.?e-13*I -0.333333333334? + 0.?e-13*I] sage: T.change_ring(QQ) [ 1 0 0] [-2/3 1/6 -5/6] @@ -18431,12 +18431,13 @@ def _generic_clear_column(m): I = ideal_or_fractional(R, a[0, 0]) # need to make sure we change this when a[0,0] changes for k in range(1, a.nrows()): if a[k, 0] not in I: + new_ideal = ideal_or_fractional(R, a[0, 0], a[k, 0]) try: - v = ideal_or_fractional(R, a[0, 0], a[k, 0]).gens_reduced() + v = new_ideal.gens_reduced() except Exception as msg: raise ArithmeticError("%s\nCan't create ideal on %s and %s" % (msg, a[0, 0], a[k, 0])) if len(v) > 1: - raise ArithmeticError("Ideal %s not principal" % ideal_or_fractional(R, a[0, 0], a[k, 0])) + raise ArithmeticError("Ideal %s not principal" % new_ideal) B = v[0] # now we find c,d, using the fact that c * (a_{0,0}/B) - d * diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index 6683003b6f2..5d0db5fb742 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -58,6 +58,7 @@ from sage.features import PythonModule lazy_import('sage.matrix.matrix_gfpn_dense', ['Matrix_gfpn_dense'], feature=PythonModule('sage.matrix.matrix_gfpn_dense', spkg='meataxe')) +lazy_import('sage.groups.matrix_gps.matrix_group', ['MatrixGroup_base']) _Rings = Rings() _Fields = Fields() @@ -493,10 +494,12 @@ class MatrixSpace(UniqueRepresentation, Parent): sage: MatrixSpace(ZZ, 10, 5).category() Category of infinite enumerated finite dimensional modules with basis over (Dedekind domains and euclidean domains + and noetherian rings and infinite enumerated sets and metric spaces) sage: MatrixSpace(ZZ, 10, 10).category() Category of infinite enumerated finite dimensional algebras with basis over (Dedekind domains and euclidean domains + and noetherian rings and infinite enumerated sets and metric spaces) sage: MatrixSpace(QQ, 10).category() Category of infinite finite dimensional algebras with basis over @@ -554,10 +557,12 @@ class MatrixSpace(UniqueRepresentation, Parent): sage: MatrixSpace(ZZ, 10, 5).category() Category of infinite enumerated finite dimensional modules with basis over (Dedekind domains and euclidean domains + and noetherian rings and infinite enumerated sets and metric spaces) sage: MatrixSpace(ZZ, 10, 10).category() Category of infinite enumerated finite dimensional algebras with basis over (Dedekind domains and euclidean domains + and noetherian rings and infinite enumerated sets and metric spaces) sage: MatrixSpace(QQ, 10).category() Category of infinite finite dimensional algebras with basis over @@ -777,6 +782,61 @@ def __classcall__(cls, base_ring, def __init__(self, base_ring, nrows, ncols, sparse, implementation): r""" + INPUT: + + - ``base_ring`` + + - ``nrows`` -- (positive integer) the number of rows + + - ``ncols`` -- (positive integer, default nrows) the number of + columns + + - ``sparse`` -- (boolean, default ``False``) whether or not matrices + are given a sparse representation + + - ``implementation`` -- (optional, a string or a matrix class) a possible + implementation. Depending on the base ring the string can be + + - ``'generic'`` -- on any base rings + + - ``'flint'`` -- for integers and rationals + + - ``'meataxe'`` -- finite fields, needs to install the optional package meataxe + + - ``m4ri`` -- for characteristic 2 using M4RI library + + - ``linbox-float`` -- for integer mod rings up to `2^8 = 256` + + - ``linbox-double`` -- for integer mod rings up to + `floor(2^26*sqrt(2) + 1/2) = 94906266` + + - ``numpy`` -- for real and complex floating point numbers + + EXAMPLES:: + + sage: MatrixSpace(QQ, 2) + Full MatrixSpace of 2 by 2 dense matrices over Rational Field + sage: MatrixSpace(ZZ, 3, 2) + Full MatrixSpace of 3 by 2 dense matrices over Integer Ring + sage: MatrixSpace(ZZ, 3, sparse=False) + Full MatrixSpace of 3 by 3 dense matrices over Integer Ring + + sage: MatrixSpace(ZZ,10,5) + Full MatrixSpace of 10 by 5 dense matrices over Integer Ring + sage: MatrixSpace(ZZ,10,5).category() + Category of infinite enumerated finite dimensional modules with basis over + (Dedekind domains and euclidean domains + and noetherian rings + and infinite enumerated sets and metric spaces) + sage: MatrixSpace(ZZ,10,10).category() + Category of infinite enumerated finite dimensional algebras with basis over + (Dedekind domains and euclidean domains + and noetherian rings + and infinite enumerated sets and metric spaces) + sage: MatrixSpace(QQ,10).category() + Category of infinite finite dimensional algebras with basis over + (number fields and quotient fields and metric spaces) + TESTS: We test that in the real or complex double dense case, @@ -1392,14 +1452,8 @@ def _coerce_map_from_(self, S): pass else: MS = meth_matrix_space() - - try: - from sage.groups.matrix_gps.matrix_group import is_MatrixGroup - except ImportError: - pass - else: - if is_MatrixGroup(S): - return self.has_coerce_map_from(MS) + if isinstance(S, MatrixGroup_base): + return self.has_coerce_map_from(MS) try: from sage.modular.arithgroup.arithgroup_generic import is_ArithmeticSubgroup diff --git a/src/sage/matroids/constructor.py b/src/sage/matroids/constructor.py index cecfead568a..07e8597fdb8 100644 --- a/src/sage/matroids/constructor.py +++ b/src/sage/matroids/constructor.py @@ -188,6 +188,8 @@ def Matroid(groundset=None, data=None, **kwds): ``reduced_matrix = A`` then the matroid is represented by `[I\ \ A]` where `I` is an appropriately sized identity matrix. + - ``morphism`` -- A morphism representation of the matroid. + - ``reduced_morphism`` -- A reduced morphism representation of the matroid. - ``rank_function`` -- A function that computes the rank of each subset. Can only be provided together with a groundset. - ``circuit_closures`` -- Either a list of tuples ``(k, C)`` with ``C`` @@ -533,6 +535,48 @@ def Matroid(groundset=None, data=None, **kwds): sage: M.base_ring() Integer Ring + A morphism representation of a :class:`LinearMatroid` can also be used as + input:: + + sage: M = matroids.catalog.Fano() + sage: A = M.representation(order=True); A + Generic morphism: + From: Free module generated by {'a', 'b', 'c', 'd', 'e', 'f', 'g'} over + Finite Field of size 2 + To: Free module generated by {0, 1, 2} over Finite Field of size 2 + sage: A._unicode_art_matrix() + a b c d e f g + 0⎛1 0 0 0 1 1 1⎞ + 1⎜0 1 0 1 0 1 1⎟ + 2⎝0 0 1 1 1 0 1⎠ + sage: N = Matroid(A); N + Binary matroid of rank 3 on 7 elements, type (3, 0) + sage: N.groundset() + frozenset({'a', 'b', 'c', 'd', 'e', 'f', 'g'}) + sage: M == N + True + + The keywords ``morphism`` and ``reduced_morphism`` are also available:: + + sage: M = matroids.catalog.RelaxedNonFano("abcdefg") + sage: A = M.representation(order=True, reduced=True); A + Generic morphism: + From: Free module generated by {'d', 'e', 'f', 'g'} over + Finite Field in w of size 2^2 + To: Free module generated by {'a', 'b', 'c'} over + Finite Field in w of size 2^2 + sage: A._unicode_art_matrix() + d e f g + a⎛1 1 0 1⎞ + b⎜1 0 1 1⎟ + c⎝0 1 w 1⎠ + sage: N = Matroid(reduced_morphism=A); N + Quaternary matroid of rank 3 on 7 elements + sage: N.groundset() + frozenset({'a', 'b', 'c', 'd', 'e', 'f', 'g'}) + sage: M == N + True + #. Rank function: Any function mapping subsets to integers can be used as input:: @@ -713,8 +757,8 @@ def Matroid(groundset=None, data=None, **kwds): if data is None: for k in ['bases', 'independent_sets', 'circuits', 'nonspanning_circuits', 'flats', 'graph', 'matrix', - 'reduced_matrix', 'rank_function', 'revlex', - 'circuit_closures', 'matroid']: + 'reduced_matrix', 'morphism', 'reduced_morphism', + 'rank_function', 'revlex', 'circuit_closures', 'matroid']: if k in kwds: data = kwds.pop(k) key = k @@ -732,8 +776,13 @@ def Matroid(groundset=None, data=None, **kwds): Graph = () if isinstance(data, Graph): key = 'graph' - elif is_Matrix(data): + elif is_Matrix(data) or ( + isinstance(data, tuple) and is_Matrix(data[0])): key = 'matrix' + elif isinstance(data, sage.modules.with_basis.morphism.ModuleMorphism) or ( + isinstance(data, tuple) and + isinstance(data[0], sage.modules.with_basis.morphism.ModuleMorphism)): + key = 'morphism' elif isinstance(data, sage.matroids.matroid.Matroid): key = 'matroid' elif isinstance(data, str): @@ -856,9 +905,22 @@ def Matroid(groundset=None, data=None, **kwds): M = GraphicMatroid(G, groundset=groundset) # Matrices: - elif key in ['matrix', 'reduced_matrix']: + elif key in ['matrix', 'reduced_matrix', 'morphism', 'reduced_morphism']: A = data - is_reduced = (key == 'reduced_matrix') + is_reduced = (key == 'reduced_matrix' or key == 'reduced_morphism') + if isinstance(data, tuple): + A = data[0] + if key == 'matrix' or key == 'reduced_matrix': + if groundset is None: + groundset = data[1] + if is_reduced: + groundset += data[2] + if key == 'morphism' or key == 'reduced_morphism': + if groundset is None: + groundset = list(A.domain().basis().keys()) + if is_reduced: + groundset = list(A.codomain().basis().keys()) + groundset + A = A.matrix() # Fix the representation if not is_Matrix(A): diff --git a/src/sage/matroids/graphic_matroid.pxd b/src/sage/matroids/graphic_matroid.pxd new file mode 100644 index 00000000000..ced452ad963 --- /dev/null +++ b/src/sage/matroids/graphic_matroid.pxd @@ -0,0 +1,35 @@ +from .matroid cimport Matroid +from sage.graphs.generic_graph_pyx cimport GenericGraph_pyx + +cdef class GraphicMatroid(Matroid): + cdef frozenset _groundset + cdef readonly GenericGraph_pyx _G + cdef dict _vertex_map + cdef dict _groundset_edge_map + cpdef groundset(self) + cpdef _rank(self, X) + cpdef _vertex_stars(self) + cpdef _minor(self, contractions, deletions) + cpdef _has_minor(self, N, bint certificate=*) + cpdef _corank(self, X) + cpdef _is_circuit(self, X) + cpdef _closure(self, X) + cpdef _max_independent(self, X) + cpdef _max_coindependent(self, X) + cpdef _circuit(self, X) + cpdef _coclosure(self, X) + cpdef _is_closed(self, X) + cpdef _is_isomorphic(self, other, certificate=*) + cpdef _isomorphism(self, other) + cpdef is_valid(self) + cpdef graph(self) + cpdef vertex_map(self) + cpdef groundset_to_edges(self, X) + cpdef _groundset_to_edges(self, X) + cpdef subgraph_from_set(self, X) + cpdef _subgraph_from_set(self, X) + cpdef graphic_extension(self, u, v=*, element=*) + cpdef graphic_coextension(self, u, v=*, X=*, element=*) + cpdef twist(self, X) + cpdef one_sum(self, X, u, v) + cpdef regular_matroid(self) diff --git a/src/sage/matroids/graphic_matroid.py b/src/sage/matroids/graphic_matroid.pyx similarity index 87% rename from src/sage/matroids/graphic_matroid.py rename to src/sage/matroids/graphic_matroid.pyx index 76447037974..295034259a4 100644 --- a/src/sage/matroids/graphic_matroid.py +++ b/src/sage/matroids/graphic_matroid.pyx @@ -22,9 +22,9 @@ Graphic matroids do not have a representation matrix or any of the functionality of regular matroids. It is possible to get an instance of the -:class:`~sage.matroids.linear_matroid.RegularMatroid` class -by using the ``regular`` keyword when constructing the matroid. -It is also possible to cast a GraphicMatroid as a RegularMatroid with the +:class:`~sage.matroids.linear_matroid.RegularMatroid` class by using the +``regular`` keyword when constructing the matroid. It is also possible to cast +a class:`GraphicMatroid` as a class:`RegularMatroid` with the :meth:`~sage.matroids.graphic_matroids.GraphicMatroid.regular_matroid` method:: @@ -74,10 +74,8 @@ AUTHORS: - Zachary Gershkoff (2017-07-07): initial version - -Methods -======= """ + # **************************************************************************** # Copyright (C) 2017 Zachary Gershkoff # @@ -87,32 +85,29 @@ # the License, or (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** -from .matroid import Matroid +from .matroid cimport Matroid from copy import copy, deepcopy from .utilities import newlabel, split_vertex, sanitize_contractions_deletions from itertools import combinations from sage.rings.integer import Integer +from sage.sets.disjoint_set cimport DisjointSet_of_hashables - -class GraphicMatroid(Matroid): +cdef class GraphicMatroid(Matroid): r""" The graphic matroid class. INPUT: - - ``G`` -- a Graph - - ``groundset`` -- (optional) a list in 1-1 correspondence with - ``G.edge_iterator()`` + - ``G`` -- class:`Graph` + - ``groundset`` -- list (optional); in 1-1 correspondence with ``G.edge_iterator()`` - OUTPUT: a ``GraphicMatroid`` instance where the groundset elements are the - edges of ``G`` + OUTPUT: class:`GraphicMatroid` where the groundset elements are the edges of `G` .. NOTE:: - If a disconnected graph is given as input, the instance of - ``GraphicMatroid`` will connect the graph components and store - this as its graph. + If a disconnected graph is given as input, the instance of class:`GraphicMatroid` + will connect the graph components and store this as its graph. EXAMPLES:: @@ -153,7 +148,7 @@ class GraphicMatroid(Matroid): True """ - # Necessary: + # necessary (__init__, groundset, _rank) def __init__(self, G, groundset=None): """ @@ -164,8 +159,7 @@ def __init__(self, G, groundset=None): sage: from sage.matroids.advanced import * sage: G1 = graphs.CycleGraph(3); G2 = graphs.DiamondGraph() sage: G = G1.disjoint_union(G2) - sage: M = GraphicMatroid(G) - sage: M + sage: M = GraphicMatroid(G); M Graphic matroid of rank 5 on 8 elements sage: M.graph() Looped multi-graph on 6 vertices @@ -217,16 +211,16 @@ def __init__(self, G, groundset=None): self._vertex_map[e[1]], groundset[i])) # If the matroid is empty, have the internal graph be a single vertex if edge_list: - self._G = Graph(edge_list, loops=True, multiedges=True, weighted=True, - data_structure='static_sparse') + self._G = Graph(edge_list, loops=True, multiedges=True, + weighted=True, data_structure='static_sparse') else: - self._G = Graph(1, loops=True, multiedges=True, weighted=True, - data_structure='static_sparse') + self._G = Graph(1, loops=True, multiedges=True, + weighted=True, data_structure='static_sparse') # Map groundset elements to graph edges: # The edge labels should already be the elements. self._groundset_edge_map = ({l: (u, v) for (u, v, l) in self._G.edge_iterator()}) - def groundset(self): + cpdef groundset(self): """ Return the groundset of the matroid as a frozenset. @@ -244,12 +238,12 @@ def groundset(self): """ return self._groundset - def _rank(self, X): + cpdef _rank(self, X): """ Return the rank of a set ``X``. - This method does no checking on ``X``, and - ``X`` may be assumed to have the same interface as ``frozenset``. + This method does no checking on ``X``, and ``X`` may be assumed to have + the same interface as ``frozenset``. INPUT: @@ -277,18 +271,17 @@ def _rank(self, X): sage: M.rank([0,3]) 1 """ - from sage.sets.disjoint_set import DisjointSet - - edges = self.groundset_to_edges(X) - vertices = set([u for (u, v, l) in edges]).union( - [v for (u, v, l) in edges]) + cdef DisjointSet_of_hashables DS_vertices + cdef list edges = self.groundset_to_edges(X) + cdef set vertices = set([u for (u, v, l) in edges]).union( + [v for (u, v, l) in edges]) # This counts components: - DS_vertices = DisjointSet(vertices) + DS_vertices = DisjointSet_of_hashables(vertices) for (u, v, l) in edges: DS_vertices.union(u, v) return (len(vertices) - DS_vertices.number_of_subsets()) - # Representation: + # representation: def _repr_(self): """ @@ -296,21 +289,19 @@ def _repr_(self): EXAMPLES:: - sage: M = Matroid(graphs.CompleteGraph(5)) - sage: M + sage: M = Matroid(graphs.CompleteGraph(5)); M Graphic matroid of rank 4 on 10 elements sage: G = Graph([(0, 0), (0, 1), (0, 2), (1, 1), (2, 2)], loops=True) - sage: M = Matroid(G) - sage: M + sage: M = Matroid(G); M Graphic matroid of rank 2 on 5 elements """ - self._mrank = str(self._rank(self._groundset)) - self._elts = str(len(self._groundset)) - return f'Graphic matroid of rank {self._mrank} on {self._elts} elements' + r = self._rank(self._groundset) + n = len(self._groundset) + return f'Graphic matroid of rank {r} on {n} elements' - # Comparison: + # comparison: - def _vertex_stars(self): + cpdef _vertex_stars(self): """ Compute the set of edge labels around each vertex. @@ -380,7 +371,7 @@ def __eq__(self, other): INPUT: - - ``other`` -- a matroid + - ``other`` -- matroid OUTPUT: ``True`` if ``self`` and ``other`` have the same graph; ``False`` otherwise @@ -428,7 +419,7 @@ def __ne__(self, other): INPUT: - - ``other`` -- a matroid + - ``other`` -- matroid OUTPUT: ``False`` if ``self`` and ``other`` have the same graph; ``True`` otherwise @@ -447,7 +438,7 @@ def __ne__(self, other): """ return (not self == other) - # Copying, loading, saving: + # copying, loading, saving def __reduce__(self): """ @@ -466,30 +457,28 @@ def __reduce__(self): version = 0 return unpickle_graphic_matroid, (version, data) - # Overrides: + # overrides - def _minor(self, contractions=frozenset([]), deletions=frozenset([])): + cpdef _minor(self, contractions, deletions): """ Return a minor. INPUT: - - ``contractions`` -- frozenset; subset of ``self.groundset()`` to be - contracted - - ``deletions`` -- frozenset; subset of ``self.groundset()`` to be - deleted + - ``contractions`` -- frozenset; subset of ``self.groundset()`` to be contracted + - ``deletions`` -- frozenset; subset of ``self.groundset()`` to be deleted Assumptions: contractions are independent, deletions are coindependent, contractions and deletions are disjoint. - OUTPUT: an instance of ``GraphicMatroid`` + OUTPUT: class:`GraphicMatroid` EXAMPLES:: sage: M = matroids.CompleteGraphic(5) - sage: M._minor(deletions=frozenset([0,1,2])) + sage: M._minor(deletions=frozenset([0,1,2]), contractions=frozenset([])) Graphic matroid of rank 4 on 7 elements - sage: M._minor(contractions=frozenset([0,1,2])) + sage: M._minor(deletions=frozenset([]), contractions=frozenset([0,1,2])) Graphic matroid of rank 1 on 7 elements sage: M = Matroid(range(15), graphs.PetersenGraph()) sage: N = M._minor(deletions=frozenset([0, 3, 5, 9]), @@ -497,22 +486,21 @@ def _minor(self, contractions=frozenset([]), deletions=frozenset([])): sage: N Graphic matroid of rank 6 on 8 elements """ - g = self.graph() - cont_edges = self._groundset_to_edges(contractions) - del_edges = self._groundset_to_edges(deletions) + cdef GenericGraph_pyx g = self.graph() + cdef list cont_edges = self._groundset_to_edges(contractions) + cdef list del_edges = self._groundset_to_edges(deletions) # deletions first so contractions don't mess up the vertices g.delete_edges(del_edges) g.contract_edges(cont_edges) - return GraphicMatroid(g) - def _has_minor(self, N, certificate=False): + cpdef _has_minor(self, N, bint certificate=False): """ - Check if the matroid has a minor isomorphic to M(H). + Check if the matroid has a minor isomorphic to `M(H)`. INPUT: - - ``N`` - a matroid + - ``N`` - matroid - ``certificate`` - (default: ``False``) if ``True``, returns the certificate isomorphism from the minor of ``self`` to ``N`` @@ -558,8 +546,8 @@ def _has_minor(self, N, certificate=False): sage: N.has_minor(M, certificate=True) (False, None) - If the matroids are not 3-connected, then the default matroid algorithms - are used:: + If the matroids are not 3-connected, then the default matroid + algorithms are used:: sage: M = matroids.CompleteGraphic(6) sage: N = Matroid(graphs.CycleGraph(4)) @@ -637,7 +625,7 @@ def _has_minor(self, N, certificate=False): N = N.regular_matroid() return M._has_minor(N, certificate=certificate) - def _corank(self, X): + cpdef _corank(self, X): """ Return the corank of the set `X` in the matroid. @@ -645,7 +633,7 @@ def _corank(self, X): INPUT: - - ``X`` -- an iterable container of ground set elements + - ``X`` -- an iterable container of groundset elements OUTPUT: integer @@ -657,22 +645,21 @@ def _corank(self, X): sage: M._corank([1,2,3]) 3 """ - from sage.sets.disjoint_set import DisjointSet - - all_vertices = self._G.vertices(sort=False) - not_our_edges = self.groundset_to_edges(self._groundset.difference(X)) - DS_vertices = DisjointSet(all_vertices) + cdef DisjointSet_of_hashables DS_vertices + cdef list all_vertices = self._G.vertices(sort=False) + cdef list not_our_edges = self.groundset_to_edges(self._groundset.difference(X)) + DS_vertices = DisjointSet_of_hashables(all_vertices) for u, v, l in not_our_edges: DS_vertices.union(u, v) return len(X) - (DS_vertices.number_of_subsets() - Integer(1)) - def _is_circuit(self, X): + cpdef _is_circuit(self, X): """ Test if input is a circuit. INPUT: - - ``X`` -- an iterable container of ground set elements + - ``X`` -- an iterable container of groundset elements OUTPUT: boolean @@ -686,16 +673,16 @@ def _is_circuit(self, X): sage: M._is_circuit([0,1,3]) False """ - g = self._subgraph_from_set(X) + cdef GenericGraph_pyx g = self._subgraph_from_set(X) return g.is_cycle() - def _closure(self, X): + cpdef _closure(self, X): """ Return the closure of a set. INPUT: - - ``X`` -- an iterable container of ground set elements + - ``X`` -- an iterable container of groundset elements OUTPUT: a subset of the groundset as a :class:`frozenset` @@ -720,32 +707,33 @@ def _closure(self, X): sage: sorted(M._closure([4])) [0, 4, 5] """ - X = set(X) - Y = self.groundset().difference(X) - edgelist = self._groundset_to_edges(Y) - g = self._subgraph_from_set(X) - V = g.vertices(sort=False) - components = g.connected_components_number() + cdef set XX = set(X) + cdef frozenset Y = self._groundset.difference(XX) + cdef list edgelist = self._groundset_to_edges(Y) + cdef GenericGraph_pyx g = self._subgraph_from_set(XX) + cdef list V = g.vertices(sort=False) + cdef int components = g.connected_components_number() + cdef tuple e for e in edgelist: # a non-loop edge is in the closure iff both its vertices are # in the induced subgraph, and the edge doesn't connect components if e[0] in V and e[1] in V: g.add_edge(e) if g.connected_components_number() >= components: - X.add(e[2]) + XX.add(e[2]) else: g.delete_edge(e) # add all loops - X.update(set([l for (u, v, l) in self._G.loops()])) - return frozenset(X) + XX.update(set([l for (u, v, l) in self._G.loops()])) + return frozenset(XX) - def _max_independent(self, X): + cpdef _max_independent(self, X): """ Compute a maximal independent subset. INPUT: - - ``X`` -- An object with Python's ``frozenset`` interface containing + - ``X`` -- an object with Python's ``frozenset`` interface containing a subset of ``self.groundset()`` OUTPUT: a subset of the groundset as a :class:`frozenset` @@ -765,27 +753,26 @@ def _max_independent(self, X): sage: sorted(N._max_independent(frozenset(['a']))) [] """ - from sage.sets.disjoint_set import DisjointSet - - edges = self.groundset_to_edges(X) - vertices = set([u for (u, v, l) in edges]) + cdef DisjointSet_of_hashables DS_vertices + cdef list edges = self.groundset_to_edges(X) + cdef set vertices = set([u for (u, v, l) in edges]) vertices.update([v for (u, v, l) in edges]) - our_set = set() - DS_vertices = DisjointSet(vertices) + cdef set our_set = set() + DS_vertices = DisjointSet_of_hashables(vertices) for (u, v, l) in edges: if DS_vertices.find(u) != DS_vertices.find(v): DS_vertices.union(u, v) our_set.add(l) return frozenset(our_set) - def _max_coindependent(self, X): + cpdef _max_coindependent(self, X): """ Compute a maximal coindependent subset. INPUT: - - ``X`` -- an iterable container of ground set elements + - ``X`` -- an iterable container of groundset elements OUTPUT: a subset of the groundset as a :class:`frozenset` @@ -800,14 +787,13 @@ def _max_coindependent(self, X): sage: sorted(N.max_coindependent([0,1,2,5])) [1, 2, 5] """ - from sage.sets.disjoint_set import DisjointSet - - edges = self.groundset_to_edges(X) - all_vertices = self._G.vertices(sort=False) - not_our_edges = self.groundset_to_edges(self._groundset.difference(X)) + cdef DisjointSet_of_hashables DS_vertices + cdef list edges = self.groundset_to_edges(X) + cdef list all_vertices = self._G.vertices(sort=False) + cdef list not_our_edges = self.groundset_to_edges(self._groundset.difference(X)) - our_set = set() - DS_vertices = DisjointSet(all_vertices) + cdef set our_set = set() + DS_vertices = DisjointSet_of_hashables(all_vertices) for (u, v, l) in not_our_edges: DS_vertices.union(u, v) @@ -818,18 +804,16 @@ def _max_coindependent(self, X): DS_vertices.union(u, v) return frozenset(our_set) - def _circuit(self, X): + cpdef _circuit(self, X): """ Return a minimal dependent subset. INPUT: - - ``X`` -- an iterable container of ground set elements - - OUTPUT: + - ``X`` -- an iterable container of groundset elements - ``frozenset`` instance containing a subset of ``X``. - A :class:`ValueError` is raised if the set contains no circuit. + OUTPUT: ``frozenset`` instance containing a subset of ``X``; + a :class:`ValueError` is raised if the set contains no circuit EXAMPLES:: @@ -864,14 +848,19 @@ def _circuit(self, X): sage: sorted(M._circuit(M.groundset())) [4, 5] """ - from sage.sets.disjoint_set import DisjointSet + cdef list edges = self.groundset_to_edges(X) + cdef set vertices = set() + cdef list vertex_list = [] + cdef list leaves + cdef tuple leaf + cdef set edge_set = set() + cdef DisjointSet_of_hashables DS_vertices - edges = self.groundset_to_edges(X) - vertices = set([u for (u, v, l) in edges]).union( - set([v for (u, v, l) in edges])) - edge_set = set() - DS_vertices = DisjointSet(vertices) - for u, v, l in edges: + for (u, v, l) in edges: + vertices.add(u) + vertices.add(v) + DS_vertices = DisjointSet_of_hashables(vertices) + for (u, v, l) in edges: edge_set.add((u, v, l)) if DS_vertices.find(u) != DS_vertices.find(v): DS_vertices.union(u, v) @@ -880,7 +869,8 @@ def _circuit(self, X): else: raise ValueError("no circuit in independent set") - vertex_list = [u for u, v, l in edge_set] + [v for u, v, l in edge_set] + for (u, v, l) in edge_set: + vertex_list.extend([u, v]) leaves = [(u, v, l) for (u, v, l) in edge_set if vertex_list.count(u) == 1 or vertex_list.count(v) == 1] while leaves: @@ -893,13 +883,13 @@ def _circuit(self, X): return frozenset([l for (u, v, l) in edge_set]) - def _coclosure(self, X): + cpdef _coclosure(self, X): """ Return the coclosure of a set. INPUT: - - ``X`` -- an iterable container of ground set elements + - ``X`` -- an iterable container of groundset elements OUTPUT: a subset of the groundset as a :class:`frozenset` @@ -917,19 +907,19 @@ def _coclosure(self, X): sage: sorted(N._coclosure([3])) [3, 4, 5] """ - g = self.graph() + cdef GenericGraph_pyx g = self.graph() g.delete_edges(self._groundset_to_edges(X)) - components = g.connected_components_number() - X = set(X) - Y = self.groundset().difference(X) + cdef int components = g.connected_components_number() + cdef set XX = set(X) + cdef frozenset Y = self.groundset().difference(XX) for e in self._groundset_to_edges(Y): g.delete_edge(e) if g.connected_components_number() > components: - X.add(e[2]) + XX.add(e[2]) g.add_edge(e) - return frozenset(X) + return frozenset(XX) - def _is_closed(self, X): + cpdef _is_closed(self, X): """ Test if input is a closed set. @@ -955,18 +945,19 @@ def _is_closed(self, X): # Take the set of vertices of the edges corresponding to the elements, # and check if there are other edges incident with two of those vertices. # Also, there must not be loops outside of X. - X = set(X) - loop_labels = set([l for (u, v, l) in self._G.loops()]) - if not loop_labels.issubset(X): + cdef set XX = set(X) + cdef set loop_labels = set([l for (u, v, l) in self._G.loops()]) + if not loop_labels.issubset(XX): return False - # Remove loops from input since we don't want to count them as components - X.difference_update(loop_labels) - edge_list = self._groundset_to_edges(X) + # Remove loops from input since we don't want to count them as + # components + XX.difference_update(loop_labels) + cdef list edge_list = self._groundset_to_edges(XX) - vertex_set = set() - Y = self.groundset().difference(X) - edge_list2 = self._groundset_to_edges(Y) + cdef set vertex_set = set() + cdef frozenset Y = self.groundset().difference(XX) + cdef list edge_list2 = self._groundset_to_edges(Y) for e in edge_list: vertex_set.add(e[0]) vertex_set.add(e[1]) @@ -975,13 +966,13 @@ def _is_closed(self, X): return False return True - def _is_isomorphic(self, other, certificate=False): + cpdef _is_isomorphic(self, other, certificate=False): """ Test if ``self`` is isomorphic to ``other``. INPUT: - - ``other`` -- a matroid + - ``other`` -- matroid - ``certificate`` -- boolean OUTPUT: @@ -1064,7 +1055,7 @@ def _is_isomorphic(self, other, certificate=False): return (True, {e: iso2[iso1[e]] for e in iso1}) return M._is_isomorphic(other) - def _isomorphism(self, other): + cpdef _isomorphism(self, other): """ Return isomorphism from ``self`` to ``other``, if such an isomorphism exists. @@ -1073,9 +1064,9 @@ def _isomorphism(self, other): INPUT: - - ``other`` -- a matroid + - ``other`` -- matroid - OUTPUT: a dictionary, or ``None`` + OUTPUT: dictionary or ``None`` EXAMPLES:: @@ -1103,7 +1094,7 @@ def _isomorphism(self, other): """ return self.is_isomorphic(other, certificate=True)[1] - def is_valid(self): + cpdef is_valid(self): """ Test if the data obey the matroid axioms. @@ -1148,15 +1139,15 @@ def is_regular(self): """ return True - # Graphic methods: + # graphic methods - def graph(self): + cpdef graph(self): """ Return the graph that represents the matroid. The graph will always have loops and multiedges enabled. - OUTPUT: a graph + OUTPUT: graph EXAMPLES:: @@ -1170,7 +1161,7 @@ def graph(self): # Return a mutable graph return self._G.copy(data_structure='sparse') - def vertex_map(self): + cpdef vertex_map(self): """ Return a dictionary mapping the input vertices to the current vertices. @@ -1180,7 +1171,7 @@ def vertex_map(self): input graph as keys, and the corresponding vertex label after any merging as values. - OUTPUT: a dictionary + OUTPUT: dictionary EXAMPLES:: @@ -1204,22 +1195,22 @@ def vertex_map(self): """ return copy(self._vertex_map) - def groundset_to_edges(self, X): + cpdef groundset_to_edges(self, X): """ Return a list of edges corresponding to a set of groundset elements. INPUT: - - ``X`` -- a subset of the groundset + - ``X`` -- subset of the groundset - OUTPUT: a list of graph edges + OUTPUT: list of graph edges EXAMPLES:: sage: M = Matroid(range(5), graphs.DiamondGraph()) - sage: M.groundset_to_edges([2,3,4]) + sage: M.groundset_to_edges([2, 3, 4]) [(1, 2, 2), (1, 3, 3), (2, 3, 4)] - sage: M.groundset_to_edges([2,3,4,5]) + sage: M.groundset_to_edges([2, 3, 4, 5]) Traceback (most recent call last): ... ValueError: input must be a subset of the groundset @@ -1229,33 +1220,33 @@ def groundset_to_edges(self, X): raise ValueError("input must be a subset of the groundset") return self._groundset_to_edges(X) - def _groundset_to_edges(self, X): + cpdef _groundset_to_edges(self, X): """ Return a list of edges corresponding to a set of groundset elements. INPUT: - - ``X`` -- a subset of the groundset + - ``X`` -- subset of the groundset - OUTPUT: a list of graph edges + OUTPUT: list of graph edges EXAMPLES:: sage: M = Matroid(range(5), graphs.DiamondGraph()) - sage: M._groundset_to_edges([2,3,4]) + sage: M._groundset_to_edges([2, 3, 4]) [(1, 2, 2), (1, 3, 3), (2, 3, 4)] """ return [(self._groundset_edge_map[x][0], self._groundset_edge_map[x][1], x) for x in X] - def subgraph_from_set(self, X): + cpdef subgraph_from_set(self, X): """ Return the subgraph corresponding to the matroid restricted to `X`. INPUT: - - ``X`` -- a subset of the groundset + - ``X`` -- subset of the groundset - OUTPUT: a graph + OUTPUT: graph EXAMPLES:: @@ -1272,28 +1263,27 @@ def subgraph_from_set(self, X): raise ValueError("input must be a subset of the groundset") return self._subgraph_from_set(X) - def _subgraph_from_set(self, X): + cpdef _subgraph_from_set(self, X): """ Return the subgraph corresponding to `M` restricted to `X`. INPUT: - - ``X`` -- a subset of the groundset + - ``X`` -- subset of the groundset - OUTPUT: a graph + OUTPUT: graph EXAMPLES:: sage: M = Matroid(range(5), graphs.DiamondGraph()) - sage: M._subgraph_from_set([0,1,2]) + sage: M._subgraph_from_set([0, 1, 2]) Looped multi-graph on 3 vertices """ from sage.graphs.graph import Graph - edge_list = self._groundset_to_edges(X) return Graph(edge_list, loops=True, multiedges=True) - def graphic_extension(self, u, v=None, element=None): + cpdef graphic_extension(self, u, v=None, element=None): """ Return a graphic matroid extended by a new element. @@ -1302,15 +1292,15 @@ def graphic_extension(self, u, v=None, element=None): INPUT: - - ``u`` -- a vertex in the matroid's graph + - ``u`` -- vertex in the matroid's graph - ``v`` -- (optional) another vertex - ``element`` -- (optional) the label of the new element OUTPUT: - A GraphicMatroid with the specified element added. Note that if ``v`` - is not specifies or if ``v`` is ``u``, then the new element will be a - loop. If the new element's label is not specified, it will be + A class:`GraphicMatroid` with the specified element added. Note that if + ``v`` is not specified or if ``v`` is ``u``, then the new element will + be a loop. If the new element's label is not specified, it will be generated automatically. EXAMPLES:: @@ -1380,8 +1370,8 @@ def graphic_extensions(self, element=None, vertices=None, simple=False): OUTPUT: - An iterable containing instances of ``GraphicMatroid``. If ``vertices`` - is not specified, every vertex is used. + An iterable containing instances of class:`GraphicMatroid`. If + ``vertices`` is not specified, every vertex is used. .. NOTE:: @@ -1436,7 +1426,7 @@ def graphic_extensions(self, element=None, vertices=None, simple=False): yield GraphicMatroid(G) G.delete_edge(p[0], p[1], element) - def graphic_coextension(self, u, v=None, X=None, element=None): + cpdef graphic_coextension(self, u, v=None, X=None, element=None): """ Return a matroid coextended by a new element. @@ -1455,8 +1445,8 @@ def graphic_coextension(self, u, v=None, X=None, element=None): OUTPUT: - An instance of GraphicMatroid coextended by the new element. If ``X`` - is not specified, the new element will be a coloop. + An instance of class:`GraphicMatroid` coextended by the new element. + If ``X`` is not specified, the new element will be a coloop. .. NOTE:: @@ -1589,8 +1579,8 @@ def graphic_coextensions(self, vertices=None, v=None, element=None, cosimple=Fal OUTPUT: - An iterable containing instances of ``GraphicMatroid``. If ``vertices`` - is not specified, the method iterates over all vertices. + An iterable containing instances of class:`GraphicMatroid`. If + ``vertices`` is not specified, the method iterates over all vertices. EXAMPLES:: @@ -1653,7 +1643,8 @@ def graphic_coextensions(self, vertices=None, v=None, element=None, cosimple=Fal non-cosimple, ie. a coloop and one for every coseries class. 12 total:: - sage: edgedict = {0:[1,2,3], 1:[2,4], 2:[3], 3:[6], 4:[5,7], 5:[6,7], 6:[7]} + sage: edgedict = {0: [1, 2, 3], 1: [2, 4], 2: [3], 3: [6], + ....: 4: [5, 7], 5: [6, 7], 6: [7]} sage: M = Matroid(range(12), Graph(edgedict)) sage: sorted(M.coclosure([4])) [4, 6] @@ -1706,7 +1697,7 @@ def graphic_coextensions(self, vertices=None, v=None, element=None, cosimple=Fal # If a vertex has degree 1, or 2, or 3, we already handled it. for u in vertices: if G.degree(u) > 3: - elts_incident = [ll for (_, _, ll) in G.edges_incident(u)] + elts_incident = [l for (_, _, l) in G.edges_incident(u)] x = elts_incident.pop() for i in range(1, (len(elts_incident) - Integer(1))): groups = combinations(elts_incident, i) @@ -1716,7 +1707,7 @@ def graphic_coextensions(self, vertices=None, v=None, element=None, cosimple=Fal yield self.graphic_coextension( X=g, u=u, v=v, element=element) - def twist(self, X): + cpdef twist(self, X): """ Perform a Whitney twist on the graph. @@ -1730,18 +1721,17 @@ def twist(self, X): - ``X`` -- the set of elements to be twisted with respect to the rest of the matroid - OUTPUT: - - An instance of ``GraphicMatroid`` isomorphic to this matroid but with - a graph that is not necessarily isomorphic. + OUTPUT: class:`GraphicMatroid` isomorphic to this matroid but + with a graph that is not necessarily isomorphic EXAMPLES:: - sage: edgelist = [(0,1,0), (1,2,1), (1,2,2), (2,3,3), (2,3,4), (2,3,5), (3,0,6)] + sage: edgelist = [(0, 1, 0), (1, 2, 1), (1, 2, 2), (2, 3, 3), + ....: (2, 3, 4), (2, 3, 5), (3, 0, 6)] sage: M = Matroid(Graph(edgelist, multiedges=True)) - sage: M1 = M.twist([0,1,2]); M1.graph().edges(sort=True) + sage: M1 = M.twist([0, 1, 2]); M1.graph().edges(sort=True) [(0, 1, 1), (0, 1, 2), (0, 3, 6), (1, 2, 0), (2, 3, 3), (2, 3, 4), (2, 3, 5)] - sage: M2 = M.twist([0,1,3]) + sage: M2 = M.twist([0, 1, 3]) Traceback (most recent call last): ... ValueError: the input must display a 2-separation that is not a 1-separation @@ -1825,7 +1815,7 @@ def twist(self, X): G.add_edge(u, v, l) return GraphicMatroid(G) - def one_sum(self, X, u, v): + cpdef one_sum(self, X, u, v): """ Arrange matroid components in the graph. @@ -1837,14 +1827,12 @@ def one_sum(self, X, u, v): INPUT: - - ``X`` -- a subset of the groundset - - ``u`` -- a vertex spanned by the edges of the elements in ``X`` - - ``v`` -- a vertex spanned by the edges of the elements not in ``X`` - - OUTPUT: + - ``X`` -- subset of the groundset + - ``u`` -- vertex spanned by the edges of the elements in ``X`` + - ``v`` -- vertex spanned by the edges of the elements not in ``X`` - An instance of ``GraphicMatroid`` isomorphic to this matroid but with - a graph that is not necessarily isomorphic. + OUTPUT: class:`GraphicMatroid` isomorphic to this matroid but + with a graph that is not necessarily isomorphic EXAMPLES:: @@ -1960,9 +1948,10 @@ def one_sum(self, X, u, v): return GraphicMatroid(G) - def regular_matroid(self): + cpdef regular_matroid(self): """ - Return an instance of RegularMatroid isomorphic to this GraphicMatroid. + Return an instance of class:`RegularMatroid` isomorphic to this + class:`GraphicMatroid`. EXAMPLES:: diff --git a/src/sage/misc/abstract_method.py b/src/sage/misc/abstract_method.py index 6179c5a2b8f..bd78b05c7d5 100644 --- a/src/sage/misc/abstract_method.py +++ b/src/sage/misc/abstract_method.py @@ -138,7 +138,7 @@ def abstract_method(f=None, optional=False): return AbstractMethod(f, optional) -class AbstractMethod(): +class AbstractMethod: def __init__(self, f, optional=False): """ Constructor for abstract methods diff --git a/src/sage/misc/binary_tree.pyx b/src/sage/misc/binary_tree.pyx index 8451e0b1e04..2d41ce45e8c 100644 --- a/src/sage/misc/binary_tree.pyx +++ b/src/sage/misc/binary_tree.pyx @@ -310,7 +310,7 @@ cdef class BinaryTree: sage: t = BinaryTree() sage: t.contains(1) False - sage: t.insert(1,1) + sage: t.insert(1, 1) sage: t.contains(1) True """ diff --git a/src/sage/misc/c3_controlled.pyx b/src/sage/misc/c3_controlled.pyx index befaa7b5b32..437a3da23cf 100644 --- a/src/sage/misc/c3_controlled.pyx +++ b/src/sage/misc/c3_controlled.pyx @@ -295,7 +295,7 @@ Depending on the linear extension `l` it was necessary to add between one and five bases for control; for example, `216` linear extensions required the addition of four bases:: - sage: sorted(Word(stats).evaluation_sparse()) # needs sage.graphs sage.modules + sage: sorted(Word(stats).evaluation_sparse()) # needs sage.combinat sage.graphs sage.modules [(1, 36), (2, 108), (3, 180), (4, 216), (5, 180)] We now consider a hierarchy of categories:: @@ -319,9 +319,9 @@ For a typical category, few bases, if any, need to be added to force sage: x.mro == x.mro_standard False sage: x.all_bases_len() - 70 + 72 sage: x.all_bases_controlled_len() - 74 + 76 sage: C = GradedHopfAlgebrasWithBasis(QQ) sage: x = HierarchyElement(C, attrcall("super_categories"), attrgetter("_cmp_key")) diff --git a/src/sage/misc/call.py b/src/sage/misc/call.py index 6de02981882..1d3055faf78 100644 --- a/src/sage/misc/call.py +++ b/src/sage/misc/call.py @@ -17,7 +17,7 @@ ############################################# # Operators ############################################# -class AttrCallObject(): +class AttrCallObject: def __init__(self, name, args, kwds): """ TESTS:: diff --git a/src/sage/misc/classgraph.py b/src/sage/misc/classgraph.py index ae33fe9277e..9db5ec99639 100644 --- a/src/sage/misc/classgraph.py +++ b/src/sage/misc/classgraph.py @@ -92,7 +92,7 @@ def class_graph(top, depth=5, name_filter=None, classes=None, as_graph=True): # (first recursive call) if classes is None: - classes = dict() + classes = {} # Build the list ``children`` of submodules (resp. base classes) # of ``top`` the function will recurse through diff --git a/src/sage/misc/converting_dict.py b/src/sage/misc/converting_dict.py index 0cd7a47916e..659bf223c50 100644 --- a/src/sage/misc/converting_dict.py +++ b/src/sage/misc/converting_dict.py @@ -1,7 +1,7 @@ r""" Converting Dictionary -At the moment, the only class contained in this model is a key +At the moment, the only class contained in this module is a key converting dictionary, which applies some function (e.g. type conversion function) to all arguments used as keys. diff --git a/src/sage/misc/cython.py b/src/sage/misc/cython.py index 93ce994c537..fbf22dea2c1 100644 --- a/src/sage/misc/cython.py +++ b/src/sage/misc/cython.py @@ -348,7 +348,7 @@ def cython(filename, verbose=0, compile_message=False, libraries=standard_libs, library_dirs=standard_libdirs) - directives = dict(language_level=3, cdivision=True) + directives = {'language_level': 3, 'cdivision': True} try: # Change directories to target_dir so that Cython produces the correct diff --git a/src/sage/misc/decorators.py b/src/sage/misc/decorators.py index 9ded4260e3b..f0b34b8843d 100644 --- a/src/sage/misc/decorators.py +++ b/src/sage/misc/decorators.py @@ -177,7 +177,7 @@ def f(wrapper, assigned=assigned, updated=updated): # Infix operator decorator -class infix_operator(): +class infix_operator: """ A decorator for functions which allows for a hack that makes the function behave like an infix operator. @@ -259,7 +259,7 @@ def __call__(self, func): return wrapper_inst -class _infix_wrapper(): +class _infix_wrapper: function = None def __init__(self, left=None, right=None): @@ -348,7 +348,7 @@ def my_wrap(*args, **kwds): return my_wrap -class suboptions(): +class suboptions: def __init__(self, name, **options): """ A decorator for functions which collects all keywords @@ -433,7 +433,7 @@ def listForNone(l): return wrapper -class options(): +class options: def __init__(self, **options): """ A decorator for functions which allows for default options to be @@ -573,7 +573,7 @@ def reset(): return wrapper -class rename_keyword(): +class rename_keyword: def __init__(self, deprecated=None, deprecation=None, **renames): """ A decorator which renames keyword arguments and optionally diff --git a/src/sage/misc/dev_tools.py b/src/sage/misc/dev_tools.py index 8a4420f2314..05d7c6b877b 100644 --- a/src/sage/misc/dev_tools.py +++ b/src/sage/misc/dev_tools.py @@ -260,7 +260,7 @@ def find_objects_from_name(name, module_name=None, include_lazy_imports=False): :class:`~sage.misc.lazy_import.LazyImport` objects that are resolving to the same object may be included in the output:: - sage: dt.find_objects_from_name('RR', include_lazy_imports=True) + sage: dt.find_objects_from_name('RR', include_lazy_imports=True) # needs sage.rings.real_mpfr [Real Field with 53 bits of precision, ... Real Field with 53 bits of precision, @@ -680,10 +680,7 @@ def is_ascii(s): # is a best one (i.e. the object "obj" is contained in the module and # has name "name") if name is not None: - good_modules = [] - for mod in modules: - if name in modules[mod]: - good_modules.append(mod) + good_modules = [mod for mod in modules if name in modules[mod]] if len(good_modules) == 1: answer[good_modules[0]].append((name, name)) @@ -732,9 +729,8 @@ def is_ascii(s): if lazy: res.append("from sage.misc.lazy_import import lazy_import") - for module_name in sorted(answer): - res.append(import_statement_string(module_name, answer[module_name], - lazy)) + res.extend(import_statement_string(module_name, answer[module_name], lazy) + for module_name in sorted(answer)) if answer_as_str: return '\n'.join(res) diff --git a/src/sage/misc/edit_module.py b/src/sage/misc/edit_module.py index 58a3ab15a2a..63232e89a81 100644 --- a/src/sage/misc/edit_module.py +++ b/src/sage/misc/edit_module.py @@ -102,7 +102,7 @@ def file_and_line(obj): filename = sage_getfile(obj) lineno = sage_getsourcelines(obj)[1] - 1 if filename.endswith('.py'): - infile = open(filename, 'r') + infile = open(filename) infile.readline() if infile.readline().find("*autogenerated*") >= 0: filename = filename[:-3] + '.sage' @@ -171,7 +171,7 @@ def set_edit_template(template_string): if not isinstance(template_string, Template): template_string = Template(template_string) fields = set(template_fields(template_string)) - if not (fields <= set(['file', 'line']) and ('file' in fields)): + if not (fields <= {'file', 'line'} and ('file' in fields)): raise ValueError("Only ${file} and ${line} are allowed as template variables, and ${file} must occur.") edit_template = template_string diff --git a/src/sage/misc/element_with_label.py b/src/sage/misc/element_with_label.py index e7ae38264be..8ae3d4ee523 100644 --- a/src/sage/misc/element_with_label.py +++ b/src/sage/misc/element_with_label.py @@ -14,7 +14,7 @@ from sage.misc.latex import latex -class ElementWithLabel(): +class ElementWithLabel: """ Auxiliary class for showing/viewing :class:`Poset`s with non-injective labelings. diff --git a/src/sage/misc/explain_pickle.py b/src/sage/misc/explain_pickle.py index a0c6323270c..fe62f5ea841 100644 --- a/src/sage/misc/explain_pickle.py +++ b/src/sage/misc/explain_pickle.py @@ -318,7 +318,7 @@ def name_is_valid(name): # This string is used as the representation of a mark. the_mark = 'mark' -class PickleObject(): +class PickleObject: r""" Pickles have a stack-based virtual machine. The :func:`explain_pickle` pickle interpreter mostly uses :class:`sage.misc.sage_input.SageInputExpression` objects @@ -374,7 +374,7 @@ def _sage_input_(self, sib, coerced): self.immutable = True return self.expression -class PickleDict(): +class PickleDict: r""" An object which can be used as the value of a :class:`PickleObject`. The items is a list of key-value pairs, where the keys and values are @@ -394,7 +394,7 @@ def __init__(self, items): """ self.items = items -class PickleInstance(): +class PickleInstance: r""" An object which can be used as the value of a :class:`PickleObject`. Unlike other possible values of a :class:`PickleObject`, a :class:`PickleInstance` doesn't represent @@ -412,7 +412,7 @@ def __init__(self, klass): """ self.klass = klass -class PickleExplainer(): +class PickleExplainer: r""" An interpreter for the pickle virtual machine, that executes symbolically and constructs :class:`SageInputExpression` objects instead of @@ -2681,7 +2681,7 @@ def __hash__(self): return 0 -class EmptyNewstyleClass(): +class EmptyNewstyleClass: r""" A featureless new-style class (inherits from object); used for testing :func:`explain_pickle`. @@ -2842,7 +2842,7 @@ def extend(self): raise NotImplementedError -class TestAppendNonlist(): +class TestAppendNonlist: r""" A list-like class, carefully designed to test exact unpickling behavior. Used for testing :func:`explain_pickle`. @@ -2926,7 +2926,7 @@ def __repr__(self): return repr(self.list) -class TestBuild(): +class TestBuild: r""" A simple class with a :meth:`__getstate__` but no :meth:`__setstate__`. Used for testing :func:`explain_pickle`. @@ -2985,7 +2985,7 @@ def __setstate__(self, state): self.x = state[1]['y'] self.y = state[0]['x'] -class TestGlobalOldName(): +class TestGlobalOldName: r""" A featureless new-style class. When you try to unpickle an instance of this class, it is redirected to create a :class:`TestGlobalNewName` instead. @@ -3000,7 +3000,7 @@ class TestGlobalOldName(): pass -class TestGlobalNewName(): +class TestGlobalNewName: r""" A featureless new-style class. When you try to unpickle an instance of :class:`TestGlobalOldName`, it is redirected to create an instance of this @@ -3032,7 +3032,7 @@ def __repr__(self): register_unpickle_override('sage.misc.explain_pickle', 'TestGlobalOldName', TestGlobalNewName, call_name=('sage.misc.explain_pickle', 'TestGlobalNewName')) -class TestGlobalFunnyName(): +class TestGlobalFunnyName: r""" A featureless new-style class which has a name that's not a legal Python identifier. diff --git a/src/sage/misc/flatten.py b/src/sage/misc/flatten.py index 5ebc8c2ceaa..889cf2e9d1d 100644 --- a/src/sage/misc/flatten.py +++ b/src/sage/misc/flatten.py @@ -23,7 +23,7 @@ def flatten(in_list, ltypes=(list, tuple), max_level=sys.maxsize): [1, 1, 1, 2] sage: flatten([[1,2,3], (4,5), [[[1],[2]]]]) [1, 2, 3, 4, 5, 1, 2] - sage: flatten([[1,2,3], (4,5), [[[1],[2]]]],max_level=1) + sage: flatten([[1,2,3], (4,5), [[[1],[2]]]], max_level=1) [1, 2, 3, 4, 5, [[1], [2]]] sage: flatten([[[3],[]]],max_level=0) [[[3], []]] @@ -64,7 +64,7 @@ def flatten(in_list, ltypes=(list, tuple), max_level=sys.maxsize): """ index = 0 current_level = 0 - new_list = [x for x in in_list] + new_list = list(in_list) level_list = [0] * len(in_list) while index < len(new_list): diff --git a/src/sage/misc/function_mangling.pyx b/src/sage/misc/function_mangling.pyx index 1392fc4f2fd..5f440742dcb 100644 --- a/src/sage/misc/function_mangling.pyx +++ b/src/sage/misc/function_mangling.pyx @@ -202,10 +202,10 @@ cdef class ArgumentFixer: EXAMPLES:: sage: from sage.misc.function_mangling import ArgumentFixer - sage: def sum3(a,b,c=3,*args,**kwargs): - ....: return a+b+c + sage: def sum3(a, b, c=3, *args, **kwargs): + ....: return a + b + c sage: AF = ArgumentFixer(sum3) - sage: AF.fix_to_named(1,2,3,4,5,6,f=14,e=16) + sage: AF.fix_to_named(1, 2, 3, 4, 5, 6, f=14, e=16) ((4, 5, 6), (('a', 1), ('b', 2), ('c', 3), ('e', 16), ('f', 14))) sage: AF.fix_to_named(1,2,f=14) ((), (('a', 1), ('b', 2), ('c', 3), ('f', 14))) diff --git a/src/sage/misc/functional.py b/src/sage/misc/functional.py index 4ae9ecd6a2c..c70d78800e0 100644 --- a/src/sage/misc/functional.py +++ b/src/sage/misc/functional.py @@ -934,7 +934,7 @@ def krull_dimension(x): 1 sage: krull_dimension(ZZ[sqrt(5)]) # needs sage.rings.number_field sage.symbolic 1 - sage: U. = PolynomialRing(ZZ,3); U + sage: U. = PolynomialRing(ZZ, 3); U Multivariate Polynomial Ring in x, y, z over Integer Ring sage: U.krull_dimension() 4 @@ -1732,9 +1732,9 @@ def quotient(x, y, *args, **kwds): EXAMPLES:: - sage: quotient(5,6) + sage: quotient(5, 6) 5/6 - sage: quotient(5.,6.) + sage: quotient(5., 6.) 0.833333333333333 sage: R. = ZZ[]; R Univariate Polynomial Ring in x over Integer Ring diff --git a/src/sage/misc/latex.py b/src/sage/misc/latex.py index db573d16e66..6b9332686d0 100644 --- a/src/sage/misc/latex.py +++ b/src/sage/misc/latex.py @@ -2270,7 +2270,7 @@ def latex_variable_name(x, is_fname=False): return latex_varify(prefix, is_fname) -class LatexExamples(): +class LatexExamples: r""" A catalogue of Sage objects with complicated ``_latex_`` methods. Use these for testing :func:`latex`, :func:`view`, the Typeset diff --git a/src/sage/misc/latex_standalone.py b/src/sage/misc/latex_standalone.py index c21bdefb643..35634b16634 100644 --- a/src/sage/misc/latex_standalone.py +++ b/src/sage/misc/latex_standalone.py @@ -290,8 +290,8 @@ class Standalone(SageObject): """ def __init__(self, content, document_class_options=None, - standalone_config=None, usepackage=None, macros=None, - use_sage_preamble=False): + standalone_config=None, usepackage=None, macros=None, + use_sage_preamble=False): r""" See :class:`Standalone` for full information. @@ -337,10 +337,10 @@ def _latex_file_header_lines(self): lines.append(r"\documentclass[{}]{{standalone}}".format(options)) else: lines.append(r"\documentclass{standalone}") - for config in self._standalone_config: - lines.append(r"\standaloneconfig{{{}}}".format(config)) - for package in self._usepackage: - lines.append(r"\usepackage{{{}}}".format(package)) + lines.extend(r"\standaloneconfig{{{}}}".format(config) + for config in self._standalone_config) + lines.extend(r"\usepackage{{{}}}".format(package) + for package in self._usepackage) lines.extend(self._macros) return lines @@ -1029,7 +1029,7 @@ def svg(self, filename=None, view=True, program='pdftocairo'): cmd = ['pdf2svg', temp_filename_pdf, temp_filename_svg] else: raise ValueError("program(={}) should be 'pdftocairo' or" - " 'pdf2svg'".format(program)) + " 'pdf2svg'".format(program)) # convert to svg result = run(cmd, capture_output=True, text=True) @@ -1146,7 +1146,7 @@ def eps(self, filename=None, view=True, program='dvips'): cmd = ['dvips', '-E', '-o', temp_filename_eps, temp_filename_dvi] else: raise ValueError("program(={}) should be 'pdftocairo' or" - " 'dvips'".format(program)) + " 'dvips'".format(program)) # convert to eps result = run(cmd, capture_output=True, text=True) @@ -1308,6 +1308,7 @@ def save(self, filename, **kwds): raise ValueError("allowed file extensions for images are " ".pdf, .png, .svg, .eps, .dvi!") + class TikzPicture(Standalone): r""" A TikzPicture embedded in a LaTeX standalone document class. @@ -1655,8 +1656,8 @@ def from_graph(cls, graph, merge_multiedges=True, from sage.graphs.graph import Graph graph = Graph(edges, format='list_of_edges', loops=loops) - options = dict(format='dot2tex', edge_labels=True, - color_by_label=False, prog='dot', rankdir='down') + options = {'format': 'dot2tex', 'edge_labels': True, + 'color_by_label': False, 'prog': 'dot', 'rankdir': 'down'} options.update(kwds) graph.latex_options().set_options(**options) @@ -1666,7 +1667,7 @@ def from_graph(cls, graph, merge_multiedges=True, @classmethod @experimental(issue_number=20343) def from_graph_with_pos(cls, graph, scale=1, merge_multiedges=True, - merge_label_function=tuple): + merge_label_function=tuple): r""" Convert a graph with positions defined for vertices to a tikzpicture. diff --git a/src/sage/misc/lazy_import.pyx b/src/sage/misc/lazy_import.pyx index d25b24a10dc..79eda9dec43 100644 --- a/src/sage/misc/lazy_import.pyx +++ b/src/sage/misc/lazy_import.pyx @@ -1076,7 +1076,8 @@ def lazy_import(module, names, as_=None, *, ....: deprecation=14275) sage: my_Qp(5) # needs sage.rings.padics doctest:...: DeprecationWarning: - Importing my_Qp from here is deprecated; please use "from sage.rings.padics.factory import Qp as my_Qp" instead. + Importing my_Qp from here is deprecated; + please use "from sage.rings.padics.factory import Qp as my_Qp" instead. See https://github.com/sagemath/sage/issues/14275 for details. 5-adic Field with capped relative precision 20 @@ -1096,10 +1097,12 @@ def lazy_import(module, names, as_=None, *, ....: feature=PythonModule('ppl', spkg='pplpy', type='standard')) sage: equation # needs pplpy - sage: lazy_import('PyNormaliz', 'NmzListConeProperties', feature=PythonModule('PyNormaliz', spkg='pynormaliz')) # optional - pynormaliz - sage: NmzListConeProperties # optional - pynormaliz + sage: lazy_import('PyNormaliz', 'NmzListConeProperties', + ....: feature=PythonModule('PyNormaliz', spkg='pynormaliz')) + sage: NmzListConeProperties # optional - pynormaliz - sage: lazy_import('foo', 'not_there', feature=PythonModule('foo', spkg='non-existing-package')) + sage: lazy_import('foo', 'not_there', + ....: feature=PythonModule('foo', spkg='non-existing-package')) sage: not_there Failed lazy import: foo is not available. diff --git a/src/sage/misc/misc.py b/src/sage/misc/misc.py index a1f19a37a86..cb049665766 100644 --- a/src/sage/misc/misc.py +++ b/src/sage/misc/misc.py @@ -425,7 +425,7 @@ def __mul__(self, right): r""" EXAMPLES:: - sage: # needs sage.modules + sage: # needs scipy sage.modules sage: A = matrix(RDF, 5, 5, 2) sage: b = vector(RDF, 5, range(5)) sage: v = A \ b diff --git a/src/sage/misc/object_multiplexer.py b/src/sage/misc/object_multiplexer.py index 00fd89d6652..ceaac1b3fb2 100644 --- a/src/sage/misc/object_multiplexer.py +++ b/src/sage/misc/object_multiplexer.py @@ -21,7 +21,7 @@ # **************************************************************************** -class MultiplexFunction(): +class MultiplexFunction: """ A simple wrapper object for functions that are called on a list of objects. @@ -49,15 +49,14 @@ def __call__(self, *args, **kwds): sage: f() ('1', '1/2') """ - l = [] - for child in self.multiplexer.children: - l.append(getattr(child, self.name)(*args, **kwds)) + l = [getattr(child, self.name)(*args, **kwds) + for child in self.multiplexer.children] if all(e is None for e in l): return None return tuple(l) -class Multiplex(): +class Multiplex: """ Object for a list of children such that function calls on this new object implies that the same function is called on all diff --git a/src/sage/misc/package_dir.py b/src/sage/misc/package_dir.py index adfb62331fb..71ec08b3ae0 100644 --- a/src/sage/misc/package_dir.py +++ b/src/sage/misc/package_dir.py @@ -199,7 +199,7 @@ def update_distribution(src_file, distribution, *, verbose=False): distribution = '' directive = 'sage_setup: ' f'distribution = {distribution}'.rstrip() try: - with open(src_file, 'r') as f: + with open(src_file) as f: src_lines = f.read().splitlines() except UnicodeDecodeError: # Silently skip binary files @@ -566,7 +566,7 @@ def handle_file(root, file): distribution_underscore = distribution.replace('-', '_') try: with open(os.path.join(distribution_dir, - f'{distribution_underscore}.egg-info', 'SOURCES.txt'), "r") as f: + f'{distribution_underscore}.egg-info', 'SOURCES.txt')) as f: paths.extend(os.path.join(SAGE_ROOT, 'src', line.strip()) for line in f if line.startswith('sage/')) @@ -615,7 +615,7 @@ def handle_file(root, file): if package in ordinary_packages: pass elif ((missing_all_files := distributions_per_directives - package_distributions_per_all_files[package]) - and not (missing_all_files == set(['']) and len(distributions_per_directives) < 2)): + and not (missing_all_files == {''} and len(distributions_per_directives) < 2)): s = '' if len(missing_all_files) == 1 else 's' print(f'{package}: missing file{s} ' + ', '.join(_all_filename(distribution) for distribution in missing_all_files)) diff --git a/src/sage/misc/replace_dot_all.py b/src/sage/misc/replace_dot_all.py index aa1b1ff593d..2911f72b626 100644 --- a/src/sage/misc/replace_dot_all.py +++ b/src/sage/misc/replace_dot_all.py @@ -123,7 +123,7 @@ def find_replacements(location, package_regex=None, verbose=False): pattern = re.compile(regex) replacements = [] global log_messages, interesting_examples - with open(location, "r") as fp: + with open(location) as fp: skip_line = False lines = fp.readlines() # read all lines using readline() row_index = 0 @@ -368,7 +368,7 @@ def make_replacements_in_file(location, package_regex=None, verbose=False, outpu from sage.plot.line import line """ replacements = find_replacements(location, package_regex, verbose) - with open(location, "r") as file: + with open(location) as file: lines = file.readlines() replaced_content = "" row_index = 0 # keeps track of the line number diff --git a/src/sage/misc/sage_input.py b/src/sage/misc/sage_input.py index be38a27d683..fcb271a7f75 100644 --- a/src/sage/misc/sage_input.py +++ b/src/sage/misc/sage_input.py @@ -1207,7 +1207,7 @@ def result(self, e): _prec_atomic = 42 -class SageInputExpression(): +class SageInputExpression: r""" Subclasses of this class represent expressions for :func:`sage_input`. \sage classes should define a \method{_sage_input_} method, which diff --git a/src/sage/misc/sage_timeit.py b/src/sage/misc/sage_timeit.py index 89c813e563b..b9a6d944795 100644 --- a/src/sage/misc/sage_timeit.py +++ b/src/sage/misc/sage_timeit.py @@ -17,7 +17,7 @@ """ -class SageTimeitResult(): +class SageTimeitResult: r""" Represent the statistics of a timeit() command. diff --git a/src/sage/misc/sage_unittest.py b/src/sage/misc/sage_unittest.py index 9d3ce5db1e8..21298f2f6e0 100644 --- a/src/sage/misc/sage_unittest.py +++ b/src/sage/misc/sage_unittest.py @@ -17,7 +17,7 @@ import traceback -class TestSuite(): +class TestSuite: """ Test suites for Sage objects. @@ -576,7 +576,7 @@ def some_elements(self, S=None, repeat=None): return list(some_tuples(S, repeat, self._max_runs, self._max_samples)) -class PythonObjectWithTests(): +class PythonObjectWithTests: """ Utility class for running basis tests on a plain Python object (that is not in SageObject). More test methods can be added here. diff --git a/src/sage/misc/sagedoc.py b/src/sage/misc/sagedoc.py index 308fa7d8340..68a2af4625d 100644 --- a/src/sage/misc/sagedoc.py +++ b/src/sage/misc/sagedoc.py @@ -640,7 +640,7 @@ def format(s, embedded=False): r"""noreplace Format Sage documentation ``s`` for viewing with IPython. - This calls ``detex`` on ``s`` to convert LaTeX commands to plain + This calls :func:`detex` on ``s`` to convert LaTeX commands to plain text, unless the directive ``nodetex`` is given in the first line of the string. @@ -654,13 +654,13 @@ def format(s, embedded=False): INPUT: - - ``s`` - string - - ``embedded`` - boolean (optional, default False) + - ``s`` -- string + - ``embedded`` -- boolean (optional, default ``False``) OUTPUT: string - Set ``embedded`` equal to True if formatting for use in the - notebook; this just gets passed as an argument to ``detex``. + Set ``embedded`` equal to ``True`` if formatting for use in the + notebook; this just gets passed as an argument to :func:`detex`. .. SEEALSO:: @@ -776,7 +776,7 @@ def format(s, embedded=False): except ImportError: pass - docs = set([]) + docs = set() if 'noreplace' not in directives: i_0 = 0 while True: @@ -843,7 +843,7 @@ def format_src(s): """ if not isinstance(s, str): raise TypeError("s must be a string") - docs = set([]) + docs = set() try: import sage.all @@ -1158,7 +1158,8 @@ def search_src(string, extra1='', extra2='', extra3='', extra4='', sage: s = search_src('MatRiX', path_re='matrix', interact=False); s.find('x') > 0 True - sage: s = search_src('MatRiX', path_re='matrix', interact=False, ignore_case=False); s.find('x') > 0 + sage: s = search_src('MatRiX', path_re='matrix', + ....: interact=False, ignore_case=False); s.find('x') > 0 False Searches are by default restricted to single lines, but this can @@ -1170,7 +1171,8 @@ def search_src(string, extra1='', extra2='', extra3='', extra4='', sage: len(search_src('log', 'derivative', interact=False).splitlines()) < 40 True - sage: len(search_src('log', 'derivative', interact=False, multiline=True).splitlines()) > 70 + sage: len(search_src('log', 'derivative', + ....: interact=False, multiline=True).splitlines()) > 70 True A little recursive narcissism: let's do a doctest that searches for @@ -1372,7 +1374,7 @@ def format_search_as_html(what, results, search): if not isinstance(results, list): results = results.splitlines() - files = set([]) + files = set() for L in results: filename = L.strip().split(':', 1)[0] if filename: diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index f79aeafb937..d370789a67f 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -167,28 +167,6 @@ def is_function_or_cython_function(obj): return hasattr(type(obj), "__code__") -def loadable_module_extension(): - r""" - Return the filename extension of loadable modules, including the dot. - - This function is deprecated. - - EXAMPLES:: - - sage: from sage.misc.sageinspect import loadable_module_extension - sage: from importlib.machinery import EXTENSION_SUFFIXES - sage: loadable_module_extension() in EXTENSION_SUFFIXES - doctest:warning... - DeprecationWarning: loadable_module_extension is deprecated; use importlib.machinery.EXTENSION_SUFFIXES instead - See https://github.com/sagemath/sage/issues/33636 for details. - True - """ - from sage.misc.superseded import deprecation - deprecation(33636, "loadable_module_extension is deprecated; use importlib.machinery.EXTENSION_SUFFIXES instead") - # Return the full platform-specific extension module suffix - return import_machinery.EXTENSION_SUFFIXES[0] - - def isclassinstance(obj): r""" Check if argument is instance of non built-in class @@ -212,7 +190,7 @@ def isclassinstance(obj): sage: isclassinstance(myclass2) False """ - builtin_mods = set(['__builtin__', 'builtins', 'exceptions']) + builtin_mods = {'__builtin__', 'builtins', 'exceptions'} return (not inspect.isclass(obj) and hasattr(obj, '__class__') and @@ -625,10 +603,7 @@ def visit_List(self, node): [[], ['s', 't', 'u'], [['e'], [], ['pi']]] """ - t = [] - for n in node.elts: - t.append(self.visit(n)) - return t + return [self.visit(n) for n in node.elts] def visit_Tuple(self, node): """ @@ -649,10 +624,7 @@ def visit_Tuple(self, node): [(), ('x', 'y'), ('Au', 'Al', 'Cu')] """ - t = [] - for n in node.elts: - t.append(self.visit(n)) - return tuple(t) + return tuple(self.visit(n) for n in node.elts) def visit_Dict(self, node): """ @@ -1572,11 +1544,14 @@ def foo(x, a='\')"', b={not (2+1==3):'bar'}): return ....: cpdef meet(categories, bint as_list = False, tuple ignore_axioms=(), tuple axioms=()): pass ....: ''') sage: sage_getargspec(Foo.join) - FullArgSpec(args=['categories', 'as_list', 'ignore_axioms', 'axioms'], varargs=None, varkw=None, defaults=(False, (), ()), kwonlyargs=[], kwonlydefaults=None, annotations={}) + FullArgSpec(args=['categories', 'as_list', 'ignore_axioms', 'axioms'], varargs=None, varkw=None, + defaults=(False, (), ()), kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: sage_getargspec(Bar.join) - FullArgSpec(args=['categories', 'as_list', 'ignore_axioms', 'axioms'], varargs=None, varkw=None, defaults=(False, (), ()), kwonlyargs=[], kwonlydefaults=None, annotations={}) + FullArgSpec(args=['categories', 'as_list', 'ignore_axioms', 'axioms'], varargs=None, varkw=None, + defaults=(False, (), ()), kwonlyargs=[], kwonlydefaults=None, annotations={}) sage: sage_getargspec(Bar.meet) - FullArgSpec(args=['categories', 'as_list', 'ignore_axioms', 'axioms'], varargs=None, varkw=None, defaults=(False, (), ()), kwonlyargs=[], kwonlydefaults=None, annotations={}) + FullArgSpec(args=['categories', 'as_list', 'ignore_axioms', 'axioms'], varargs=None, varkw=None, + defaults=(False, (), ()), kwonlyargs=[], kwonlydefaults=None, annotations={}) Test that :issue:`17009` is fixed:: @@ -1589,7 +1564,8 @@ def foo(x, a='\')"', b={not (2+1==3):'bar'}): return sage: from sage.misc.nested_class import MainClass sage: sage_getargspec(MainClass.NestedClass.NestedSubClass.dummy) - FullArgSpec(args=['self', 'x', 'r'], varargs='args', varkw='kwds', defaults=((1, 2, 3.4),), kwonlyargs=[], kwonlydefaults=None, annotations={}) + FullArgSpec(args=['self', 'x', 'r'], varargs='args', varkw='kwds', + defaults=((1, 2, 3.4),), kwonlyargs=[], kwonlydefaults=None, annotations={}) In :issue:`18249` was decided to return a generic signature for Python builtin functions, rather than to raise an error (which is what Python's @@ -2335,13 +2311,13 @@ def sage_getsourcelines(obj): (, ) sage: print(sage_getsource(E)) - class Element(): + class Element: "This is a dummy element class" pass sage: print(sage_getsource(P)) class TestNestedParent(UniqueRepresentation, Parent): ... - class Element(): + class Element: "This is a dummy element class" pass diff --git a/src/sage/misc/sphinxify.py b/src/sage/misc/sphinxify.py index 2fb6e95395c..8d6638eba8b 100644 --- a/src/sage/misc/sphinxify.py +++ b/src/sage/misc/sphinxify.py @@ -127,7 +127,7 @@ def sphinxify(docstring, format='html'): builtins.__dict__.pop('_', None) if os.path.exists(output_name): - with open(output_name, 'r') as f: + with open(output_name) as f: output = f.read() output = output.replace('
', '
')
 
diff --git a/src/sage/misc/superseded.py b/src/sage/misc/superseded.py
index 4c4cddddc5c..f646c24e485 100644
--- a/src/sage/misc/superseded.py
+++ b/src/sage/misc/superseded.py
@@ -38,8 +38,8 @@ def _check_issue_number(issue_number):
 
     OUTPUT:
 
-    This function returns nothing. A ``ValueError`` or ``TypeError`` is
-    raised if the argument cannot be a valid issue number.
+    This function returns nothing. A :class:`ValueError` or :class:`TypeError`
+    is raised if the argument cannot be a valid issue number.
 
     EXAMPLES::
 
@@ -212,7 +212,7 @@ def experimental_warning(issue_number, message, stacklevel=4):
     warning(issue_number, message, FutureWarning, stacklevel)
 
 
-class experimental():
+class experimental:
     def __init__(self, issue_number, stacklevel=4):
         """
         A decorator which warns about the experimental/unstable status of
@@ -318,7 +318,7 @@ def wrapper(*args, **kwds):
         return wrapper
 
 
-class __experimental_self_test():
+class __experimental_self_test:
     r"""
     This is a class only to demonstrate with a doc-test that the @experimental
     decorator only issues a warning message once (see :issue:`20601`).
@@ -339,7 +339,7 @@ def __init__(self, x):
         print("I'm " + x)
 
 
-class DeprecatedFunctionAlias():
+class DeprecatedFunctionAlias:
     """
     A wrapper around methods or functions which automatically prints a
     deprecation message. See :func:`deprecated_function_alias`.
diff --git a/src/sage/misc/temporary_file.py b/src/sage/misc/temporary_file.py
index daf3cbac8e7..d761fbd4cc3 100644
--- a/src/sage/misc/temporary_file.py
+++ b/src/sage/misc/temporary_file.py
@@ -134,7 +134,7 @@ def tmp_filename(name="tmp_", ext=""):
 #################################################################
 # write to a temporary file and move it in place
 #################################################################
-class atomic_write():
+class atomic_write:
     """
     Write to a given file using a temporary file and then rename it
     to the target file. This renaming should be atomic on modern
@@ -413,7 +413,7 @@ def __exit__(self, exc_type, exc_val, exc_tb):
 #################################################################
 # write to a temporary directory and move it in place
 #################################################################
-class atomic_dir():
+class atomic_dir:
     """
     Write to a given directory using a temporary directory and then rename it
     to the target directory. This is for creating a directory whose contents
diff --git a/src/sage/misc/test_class_pickling.py b/src/sage/misc/test_class_pickling.py
index f9c8003fa4a..f1074af85cb 100644
--- a/src/sage/misc/test_class_pickling.py
+++ b/src/sage/misc/test_class_pickling.py
@@ -29,7 +29,7 @@ def metaclass(name, bases):
 
     """
     print("constructing class")
-    result = Metaclass(name, bases, dict())
+    result = Metaclass(name, bases, {})
     result.reduce_args = (name, bases)
     return result
 
diff --git a/src/sage/misc/test_nested_class.py b/src/sage/misc/test_nested_class.py
index 40712bfc9fb..918acc30e11 100644
--- a/src/sage/misc/test_nested_class.py
+++ b/src/sage/misc/test_nested_class.py
@@ -150,15 +150,15 @@ class Element(ElementWrapper):
 
 
 # Class for tests:
-class B():
+class B:
     """
     A normal external class.
     """
     pass
 
 
-class ABB():
-    class B():
+class ABB:
+    class B:
         """
         This class is broken and can't be pickled.
         A warning is emmited during compilation.
@@ -166,18 +166,18 @@ class B():
         pass
 
 
-class ABL():
+class ABL:
     """
     There is no problem here.
     """
     B = B
 
 
-class ALB():
+class ALB:
     """
     There is a nested class just below. Which can't be properly sphinxed.
     """
-    class C():
+    class C:
         """
         Internal C class.
 
@@ -191,7 +191,7 @@ class C():
 
 
 class ABBMeta(metaclass=NestedClassMetaclass):
-    class B():
+    class B:
         """
         B interne
         """
@@ -206,7 +206,7 @@ class ALBMeta(metaclass=NestedClassMetaclass):
     """
     There is a nested class just below which is properly sphinxed.
     """
-    class CMeta():
+    class CMeta:
         """
         B interne
         """
@@ -223,6 +223,6 @@ class TestNestedParent(UniqueRepresentation, Parent):
     See the test in ``sage.misc.sageinspect.sage_getsourcelines``.
     """
 
-    class Element():
+    class Element:
         "This is a dummy element class"
         pass
diff --git a/src/sage/misc/timing.py b/src/sage/misc/timing.py
index 5e5c49b9594..c66c8521121 100644
--- a/src/sage/misc/timing.py
+++ b/src/sage/misc/timing.py
@@ -138,7 +138,7 @@ class GlobalCputime:
         sage: t = cputime(subprocesses=True)
         sage: P = PolynomialRing(QQ,7,'x')
         sage: I = sage.rings.ideal.Katsura(P)                                           # needs sage.libs.singular
-        sage: gb = I.groebner_basis() # calls Singular                                  # needs sage.libs.singular
+        sage: gb = I.groebner_basis()  # calls Singular                                 # needs sage.libs.singular
         sage: cputime(subprocesses=True) - t    # output random
         0.462987
 
@@ -184,7 +184,7 @@ def __add__(self, other):
             sage: t = cputime(subprocesses=True)
             sage: P = PolynomialRing(QQ,7,'x')
             sage: I = sage.rings.ideal.Katsura(P)                                       # needs sage.libs.singular
-            sage: gb = I.groebner_basis() # calls Singular                              # needs sage.libs.singular
+            sage: gb = I.groebner_basis()  # calls Singular                             # needs sage.libs.singular
             sage: cputime(subprocesses=True) + t # output random
             2.798708
         """
@@ -200,7 +200,7 @@ def __sub__(self, other):
             sage: t = cputime(subprocesses=True)
             sage: P = PolynomialRing(QQ,7,'x')
             sage: I = sage.rings.ideal.Katsura(P)                                       # needs sage.libs.singular
-            sage: gb = I.groebner_basis() # calls Singular                              # needs sage.libs.singular
+            sage: gb = I.groebner_basis()  # calls Singular                             # needs sage.libs.singular
             sage: cputime(subprocesses=True) - t # output random
             0.462987
         """
diff --git a/src/sage/modular/arithgroup/congroup_generic.py b/src/sage/modular/arithgroup/congroup_generic.py
index 3fd60c06165..871275fdced 100644
--- a/src/sage/modular/arithgroup/congroup_generic.py
+++ b/src/sage/modular/arithgroup/congroup_generic.py
@@ -89,9 +89,9 @@ def CongruenceSubgroup_constructor(*args):
         TypeError: Ring of definition must be Z / NZ for some N
     """
     from sage.groups.matrix_gps.finitely_generated import MatrixGroup
-    from sage.groups.matrix_gps.matrix_group import is_MatrixGroup
+    from sage.groups.matrix_gps.matrix_group import MatrixGroup_base
 
-    if is_MatrixGroup(args[0]):
+    if isinstance(args[0], MatrixGroup_base):
         G = args[0]
 
     elif isinstance(args[0], list):
diff --git a/src/sage/modular/hecke/algebra.py b/src/sage/modular/hecke/algebra.py
index 2f314e5d3ef..7253cf6c960 100644
--- a/src/sage/modular/hecke/algebra.py
+++ b/src/sage/modular/hecke/algebra.py
@@ -179,7 +179,8 @@ def __init__(self, M) -> None:
             M = M[0]
         from . import module
         if not module.is_HeckeModule(M):
-            raise TypeError("M (=%s) must be a HeckeModule" % M)
+            msg = f"M (={M}) must be a HeckeModule"
+            raise TypeError(msg)
         self.__M = M
         cat = Algebras(M.base_ring()).Commutative()
         CommutativeRing.__init__(self, base_ring=M.base_ring(),
@@ -198,7 +199,7 @@ def _an_element_(self):
         """
         return self.hecke_operator(self.level() + 1)
 
-    def __call__(self, x, check=True):
+    def _element_constructor_(self, x, check=True):
         r"""
         Convert x into an element of this Hecke algebra. Here x is either:
 
@@ -230,7 +231,7 @@ def __call__(self, x, check=True):
             sage: T.gen(2).matrix() in T
             Traceback (most recent call last):
             ...
-            NotImplementedError: Membership testing for '...' not implemented
+            NotImplementedError: membership testing for '...' not implemented
             sage: T(T.gen(2).matrix(), check=False)
             Hecke operator on Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign 0 over Rational Field defined by:
             [ 3  0 -1]
@@ -245,49 +246,52 @@ def __call__(self, x, check=True):
             TypeError: Don't know how to construct an element of Anemic Hecke algebra acting on Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign 0 over Rational Field from Hecke operator T_11 on Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign 0 over Rational Field
 
         """
-        from . import hecke_operator
-        try:
-            if not isinstance(x, Element):
-                x = self.base_ring()(x)
-            if x.parent() is self:
-                return x
-            elif hecke_operator.is_HeckeOperator(x):
-                if x.parent() == self \
-                        or (not self.is_anemic() and x.parent() == self.anemic_subalgebra()) \
-                        or (self.is_anemic() and x.parent().anemic_subalgebra() == self and gcd(x.index(), self.level()) == 1):
-                    return hecke_operator.HeckeOperator(self, x.index())
-                else:
-                    raise TypeError
-            elif hecke_operator.is_HeckeAlgebraElement(x):
-                if x.parent() == self or (not self.is_anemic() and x.parent() == self.anemic_subalgebra()):
-                    if x.parent().module().basis_matrix() == self.module().basis_matrix():
-                        return hecke_operator.HeckeAlgebraElement_matrix(self, x.matrix())
-                    else:
-                        A = matrix([self.module().coordinate_vector(x.parent().module().gen(i))
-                                    for i in range(x.parent().module().rank())])
-                        return hecke_operator.HeckeAlgebraElement_matrix(self, ~A * x.matrix() * A)
-                elif x.parent() == self.anemic_subalgebra():
-                    pass
+        from .hecke_operator import HeckeAlgebraElement_matrix, HeckeOperator, is_HeckeOperator, is_HeckeAlgebraElement
+
+        if not isinstance(x, Element):
+            x = self.base_ring()(x)
+
+        parent = x.parent()
+
+        if parent is self.base_ring():
+            return HeckeAlgebraElement_matrix(self, x * self.matrix_space().one())
 
+        if parent is self:
+            return x
+
+        if is_HeckeOperator(x):
+            if x.parent() == self \
+                    or (not self.is_anemic() and x.parent() == self.anemic_subalgebra()) \
+                    or (self.is_anemic() and x.parent().anemic_subalgebra() == self and gcd(x.index(), self.level()) == 1):
+                return HeckeOperator(self, x.index())
+
+        if is_HeckeAlgebraElement(x):
+            if x.parent() == self or (not self.is_anemic() and x.parent() == self.anemic_subalgebra()):
+                if x.parent().module().basis_matrix() == self.module().basis_matrix():
+                    return HeckeAlgebraElement_matrix(self, x.matrix())
                 else:
-                    raise TypeError
-            else:
-                A = self.matrix_space()(x)
-                if check:
-                    if not A.is_scalar():
-                        raise NotImplementedError("Membership testing for '%s' not implemented" % self)
-                return hecke_operator.HeckeAlgebraElement_matrix(self, A)
+                    A = matrix([self.module().coordinate_vector(x.parent().module().gen(i))
+                                for i in range(x.parent().module().rank())])
+                    return HeckeAlgebraElement_matrix(self, ~A * x.matrix() * A)
 
+        try:
+            A = self.matrix_space()(x)
+            if check:
+                if not A.is_scalar():
+                    msg = f"membership testing for '{self}' not implemented"
+                    raise NotImplementedError(msg)
+            return HeckeAlgebraElement_matrix(self, A)
         except TypeError:
             raise TypeError("Don't know how to construct an element of %s from %s" % (self, x))
 
-    def _coerce_impl(self, x):
-        r"""
-        Implicit coercion of x into this Hecke algebra. The only things that
-        coerce implicitly into self are: elements of Hecke algebras which are
-        equal to self, or to the anemic subalgebra of self if self is not
-        anemic; and elements that coerce into the base ring of self.  Bare
-        matrices do *not* coerce implicitly into self.
+    def _coerce_map_from_(self, R):
+        """
+        Coercion of a parent ``R`` into this Hecke algebra.
+
+        The parents that coerce into ``self`` are: Hecke
+        algebras which are equal to ``self``, or to the anemic subalgebra
+        of ``self`` if ``self`` is not anemic; and parents that coerce into
+        the base ring of ``self``.
 
         EXAMPLES::
 
@@ -297,9 +301,11 @@ def _coerce_impl(self, x):
             sage: F.coerce(A.2) # indirect doctest
             Hecke operator T_2 on Cuspidal subspace of dimension 3 of Modular Forms space of dimension 5 for Congruence Subgroup Gamma0(3) of weight 12 over Rational Field
         """
-        if x.parent() == self or (not self.is_anemic() and x.parent() == self.anemic_subalgebra()):
-            return self(x)
-        return self(self.matrix_space()(1) * self.base_ring().coerce(x))
+        if R == self:
+            return True
+        if not self.is_anemic() and R == self.anemic_subalgebra():
+            return True
+        return self.base_ring().has_coerce_map_from(R)
 
     def gen(self, n):
         """
@@ -316,7 +322,9 @@ def gen(self, n):
     def ngens(self):
         r"""
         The size of the set of generators returned by gens(), which is clearly
-        infinity. (This is not necessarily a minimal set of generators.)
+        infinity.
+
+        (This is not necessarily a minimal set of generators.)
 
         EXAMPLES::
 
@@ -325,6 +333,23 @@ def ngens(self):
         """
         return infinity
 
+    def one(self):
+        """
+        Return the unit of the Hecke algebra.
+
+        EXAMPLES::
+
+            sage: M = ModularSymbols(11,2,1)
+            sage: H = M.hecke_algebra()
+            sage: H.one()
+            Hecke operator on Modular Symbols space of dimension 2 for Gamma_0(11) of weight 2 with sign 1 over Rational Field defined by:
+            [1 0]
+            [0 1]
+        """
+        from .hecke_operator import HeckeAlgebraElement_matrix
+        A = self.matrix_space()
+        return HeckeAlgebraElement_matrix(self, A.one())
+
     def is_noetherian(self) -> bool:
         """
         Return ``True`` if this Hecke algebra is Noetherian as a ring.
diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py
index 347b1b4d44c..d79307c7aa4 100644
--- a/src/sage/modular/hypergeometric_motive.py
+++ b/src/sage/modular/hypergeometric_motive.py
@@ -7,7 +7,7 @@
 (E.g., see [BeukersHeckman]_)
 
 The computation of Euler factors is currently only supported for primes `p`
-of good reduction. That is, it is required that `v_p(t) = v_p(t-1) = 0`.
+of good or tame reduction.
 
 AUTHORS:
 
@@ -50,8 +50,8 @@
 
 """
 # ****************************************************************************
-#       Copyright (C) 2017     Frédéric Chapoton
-#                              Kiran S. Kedlaya 
+#       Copyright (C) 2017--2024     Frédéric Chapoton
+#                                    Kiran S. Kedlaya 
 #
 #  Distributed under the terms of the GNU General Public License (GPL)
 #  as published by the Free Software Foundation; either version 2 of
@@ -62,8 +62,7 @@
 
 from collections import defaultdict
 from itertools import combinations
-
-from sage.arith.misc import divisors, gcd, euler_phi, moebius, is_prime
+from sage.arith.misc import divisors, gcd, euler_phi, is_prime, moebius
 from sage.arith.misc import gauss_sum, kronecker_symbol
 from sage.combinat.integer_vector_weighted import WeightedIntegerVectors
 from sage.functions.generalized import sgn
@@ -80,6 +79,7 @@
 from sage.rings.finite_rings.integer_mod_ring import IntegerModRing
 from sage.rings.fraction_field import FractionField
 from sage.rings.integer_ring import ZZ
+from sage.rings.padics.factory import Zp
 from sage.rings.padics.padic_generic_element import gauss_table
 from sage.rings.polynomial.polynomial_ring import polygen, polygens
 from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
@@ -89,7 +89,7 @@
 from sage.schemes.generic.spec import Spec
 
 
-def characteristic_polynomial_from_traces(traces, d, q, i, sign):
+def characteristic_polynomial_from_traces(traces, d, q, i, sign, deg=None, use_fe=True):
     r"""
     Given a sequence of traces `t_1, \dots, t_k`, return the
     corresponding characteristic polynomial with Weil numbers as roots.
@@ -115,10 +115,18 @@ def characteristic_polynomial_from_traces(traces, d, q, i, sign):
 
     - ``sign`` -- integer, the sign
 
+    - ``deg`` -- an integer or ``None``
+
+    - ``use_fe`` -- a boolean (default: ``True``)
+
     OUTPUT:
 
     a polynomial
 
+    If ``deg`` is specified, only the coefficients up to this degree (inclusive) are computed.
+
+    If ``use_fe`` is ``False``, we ignore the local functional equation.
+
     EXAMPLES::
 
         sage: from sage.modular.hypergeometric_motive import characteristic_polynomial_from_traces
@@ -137,15 +145,20 @@ def characteristic_polynomial_from_traces(traces, d, q, i, sign):
         sage: characteristic_polynomial_from_traces([12], 3, 13, 2, -1)
         -2197*T^3 + 156*T^2 - 12*T + 1
 
-        sage: characteristic_polynomial_from_traces([36,7620], 4, 17, 3, 1)
+        sage: characteristic_polynomial_from_traces([36, 7620], 4, 17, 3, 1)
         24137569*T^4 - 176868*T^3 - 3162*T^2 - 36*T + 1
-        sage: characteristic_polynomial_from_traces([-4,276], 4, 5, 3, 1)
+        sage: characteristic_polynomial_from_traces([-4, 276], 4, 5, 3, 1)
         15625*T^4 + 500*T^3 - 130*T^2 + 4*T + 1
-        sage: characteristic_polynomial_from_traces([4,-276], 4, 5, 3, 1)
+        sage: characteristic_polynomial_from_traces([4, -276], 4, 5, 3, 1)
         15625*T^4 - 500*T^3 + 146*T^2 - 4*T + 1
         sage: characteristic_polynomial_from_traces([22, 484], 4, 31, 2, -1)
         -923521*T^4 + 21142*T^3 - 22*T + 1
 
+        sage: characteristic_polynomial_from_traces([22], 4, 31, 2, -1, deg=1)
+        -22*T + 1
+        sage: characteristic_polynomial_from_traces([22, 484], 4, 31, 2, -1, deg=4)
+        -923521*T^4 + 21142*T^3 - 22*T + 1
+
     TESTS::
 
         sage: characteristic_polynomial_from_traces([-36], 4, 17, 3, 1)
@@ -153,26 +166,30 @@ def characteristic_polynomial_from_traces(traces, d, q, i, sign):
         ...
         ValueError: not enough traces were given
     """
-    if len(traces) < d // 2:
+    if use_fe:
+        bound = d // 2 if deg is None else min(d // 2, deg)
+    else:
+        bound = d if deg is None else min(d, deg)
+    if len(traces) < bound:
         raise ValueError('not enough traces were given')
-    if i % 2 and d % 2:
+    if i % 2 and d % 2 and use_fe:
         raise ValueError('i and d may not both be odd')
     t = PowerSeriesRing(QQ, 't').gen()
     ring = PolynomialRing(ZZ, 'T')
 
     series = sum(- api * t**(i + 1) / (i + 1) for i, api in enumerate(traces))
-    series = series.O(d // 2 + 1).exp()
+    series = series.O(bound + 1).exp()
     coeffs = list(series)
-    coeffs += [0] * max(0, d // 2 + 1 - len(coeffs))
+    coeffs += [0] * max(0, bound + 1 - len(coeffs))
 
-    data = [0 for _ in range(d + 1)]
-    for k in range(d // 2 + 1):
+    fulldeg = d if deg is None else deg
+    data = [0] * (fulldeg + 1)
+    for k in range(bound + 1):
         data[k] = coeffs[k]
-    for k in range(d // 2 + 1, d + 1):
+    for k in range(bound + 1, fulldeg + 1):
         data[k] = sign * coeffs[d - k] * q**(i * (k - d / 2))
     return ring(data)
 
-
 def enumerate_hypergeometric_data(d, weight=None):
     r"""
     Return an iterator over parameters of hypergeometric motives (up to swapping).
@@ -247,9 +264,9 @@ def cyclotomic_to_alpha(cyclo) -> list:
         [1/2]
         sage: cyclotomic_to_alpha([5])
         [1/5, 2/5, 3/5, 4/5]
-        sage: cyclotomic_to_alpha([1,2,3,6])
+        sage: cyclotomic_to_alpha([1, 2, 3, 6])
         [0, 1/6, 1/3, 1/2, 2/3, 5/6]
-        sage: cyclotomic_to_alpha([2,3])
+        sage: cyclotomic_to_alpha([2, 3])
         [1/3, 1/2, 2/3]
     """
     alpha = [QQ((k, d)) for d in cyclo
@@ -276,11 +293,11 @@ def alpha_to_cyclotomic(alpha) -> list:
         [1]
         sage: alpha_to_cyclotomic([1/2])
         [2]
-        sage: alpha_to_cyclotomic([1/5,2/5,3/5,4/5])
+        sage: alpha_to_cyclotomic([1/5, 2/5, 3/5, 4/5])
         [5]
         sage: alpha_to_cyclotomic([0, 1/6, 1/3, 1/2, 2/3, 5/6])
         [1, 2, 3, 6]
-        sage: alpha_to_cyclotomic([1/3,2/3,1/2])
+        sage: alpha_to_cyclotomic([1/3, 2/3, 1/2])
         [2, 3]
     """
     cyclo = []
@@ -314,7 +331,7 @@ def capital_M(n):
     EXAMPLES::
 
         sage: from sage.modular.hypergeometric_motive import capital_M
-        sage: [capital_M(i) for i in range(1,8)]
+        sage: [capital_M(i) for i in range(1, 8)]
         [1, 4, 27, 64, 3125, 432, 823543]
     """
     n = ZZ(n)
@@ -383,7 +400,7 @@ def gamma_list_to_cyclotomic(galist):
         sage: gamma_list_to_cyclotomic([-1, 2, 3, -4])
         ([3], [4])
 
-        sage: gamma_list_to_cyclotomic([8,2,2,2,-6,-4,-3,-1])
+        sage: gamma_list_to_cyclotomic([8, 2, 2, 2, -6, -4, -3, -1])
         ([2, 2, 8], [3, 3, 6])
     """
     resu = defaultdict(int)
@@ -423,15 +440,15 @@ def __init__(self, cyclotomic=None, alpha_beta=None, gamma_list=None):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(cyclotomic=([2],[1]))
+            sage: Hyp(cyclotomic=([2], [1]))
             Hypergeometric data for [1/2] and [0]
 
-            sage: Hyp(alpha_beta=([1/2],[0]))
+            sage: Hyp(alpha_beta=([1/2], [0]))
             Hypergeometric data for [1/2] and [0]
-            sage: Hyp(alpha_beta=([1/5,2/5,3/5,4/5],[0,0,0,0]))
+            sage: Hyp(alpha_beta=([1/5,2/5,3/5,4/5], [0,0,0,0]))
             Hypergeometric data for [1/5, 2/5, 3/5, 4/5] and [0, 0, 0, 0]
 
-            sage: Hyp(gamma_list=([5],[1,1,1,1,1]))
+            sage: Hyp(gamma_list=([5], [1,1,1,1,1]))
             Hypergeometric data for [1/5, 2/5, 3/5, 4/5] and [0, 0, 0, 0]
             sage: Hyp(gamma_list=([5,-1,-1,-1,-1,-1]))
             Hypergeometric data for [1/5, 2/5, 3/5, 4/5] and [0, 0, 0, 0]
@@ -509,8 +526,8 @@ def __eq__(self, other) -> bool:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H1 = Hyp(alpha_beta=([1/2],[0]))
-            sage: H2 = Hyp(cyclotomic=([6,2],[1,1,1]))
+            sage: H1 = Hyp(alpha_beta=([1/2], [0]))
+            sage: H2 = Hyp(cyclotomic=([6,2], [1,1,1]))
             sage: H1 == H1
             True
             sage: H1 == H2
@@ -526,8 +543,8 @@ def __ne__(self, other) -> bool:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H1 = Hyp(alpha_beta=([1/2],[0]))
-            sage: H2 = Hyp(cyclotomic=([6,2],[1,1,1]))
+            sage: H1 = Hyp(alpha_beta=([1/2], [0]))
+            sage: H2 = Hyp(cyclotomic=([6,2], [1,1,1]))
             sage: H1 != H1
             False
             sage: H1 != H2
@@ -542,7 +559,7 @@ def __hash__(self):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H1 = Hyp(alpha_beta=([1/2],[0]))
+            sage: H1 = Hyp(alpha_beta=([1/2], [0]))
             sage: h = hash(H1)
         """
         return hash((self._alpha, self._beta))
@@ -555,7 +572,7 @@ def cyclotomic_data(self) -> tuple:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(alpha_beta=([1/2],[0])).cyclotomic_data()
+            sage: Hyp(alpha_beta=([1/2], [0])).cyclotomic_data()
             ([2], [1])
         """
         return (list(self._cyclo_up), list(self._cyclo_down))
@@ -567,7 +584,7 @@ def alpha_beta(self) -> tuple:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(alpha_beta=([1/2],[0])).alpha_beta()
+            sage: Hyp(alpha_beta=([1/2], [0])).alpha_beta()
             ([1/2], [0])
         """
         return (list(self._alpha), list(self._beta))
@@ -579,7 +596,7 @@ def alpha(self) -> list:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(alpha_beta=([1/2],[0])).alpha()
+            sage: Hyp(alpha_beta=([1/2], [0])).alpha()
             [1/2]
         """
         return list(self._alpha)
@@ -591,7 +608,7 @@ def beta(self) -> list:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(alpha_beta=([1/2],[0])).beta()
+            sage: Hyp(alpha_beta=([1/2], [0])).beta()
             [0]
         """
         return list(self._beta)
@@ -603,7 +620,7 @@ def defining_polynomials(self) -> tuple:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(alpha_beta=([1/4,3/4],[0,0])).defining_polynomials()
+            sage: Hyp(alpha_beta=([1/4,3/4], [0,0])).defining_polynomials()
             (x^2 + 1, x^2 - 2*x + 1)
         """
         up = prod(cyclotomic_polynomial(d) for d in self._cyclo_up)
@@ -621,9 +638,9 @@ def gamma_array(self) -> dict:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(alpha_beta=([1/2],[0])).gamma_array()
+            sage: Hyp(alpha_beta=([1/2], [0])).gamma_array()
             {1: -2, 2: 1}
-            sage: Hyp(cyclotomic=([6,2],[1,1,1])).gamma_array()
+            sage: Hyp(cyclotomic=([6,2], [1,1,1])).gamma_array()
             {1: -3, 3: -1, 6: 1}
         """
         return dict(self._gamma_array)
@@ -637,13 +654,13 @@ def gamma_list(self) -> list:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(alpha_beta=([1/2],[0])).gamma_list()
+            sage: Hyp(alpha_beta=([1/2], [0])).gamma_list()
             [-1, -1, 2]
 
-            sage: Hyp(cyclotomic=([6,2],[1,1,1])).gamma_list()
+            sage: Hyp(cyclotomic=([6,2], [1,1,1])).gamma_list()
             [-1, -1, -1, -3, 6]
 
-            sage: Hyp(cyclotomic=([3],[4])).gamma_list()
+            sage: Hyp(cyclotomic=([3], [4])).gamma_list()
             [-1, 2, 3, -4]
         """
         gamma = self.gamma_array()
@@ -659,9 +676,9 @@ def wild_primes(self) -> list:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(cyclotomic=([3],[4])).wild_primes()
+            sage: Hyp(cyclotomic=([3], [4])).wild_primes()
             [2, 3]
-            sage: Hyp(cyclotomic=([2,2,2,2,3,3,3,6,6],[1,1,4,5,9])).wild_primes()
+            sage: Hyp(cyclotomic=([2,2,2,2,3,3,3,6,6], [1,1,4,5,9])).wild_primes()
             [2, 3, 5]
         """
         gamma = self.gamma_array()
@@ -682,10 +699,10 @@ def zigzag(self, x, flip_beta=False):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(alpha_beta=([1/6,1/3,2/3,5/6],[1/8,3/8,5/8,7/8]))
+            sage: H = Hyp(alpha_beta=([1/6,1/3,2/3,5/6], [1/8,3/8,5/8,7/8]))
             sage: [H.zigzag(x) for x in [0, 1/3, 1/2]]
             [0, 1, 0]
-            sage: H = Hyp(cyclotomic=([5],[1,1,1,1]))
+            sage: H = Hyp(cyclotomic=([5], [1,1,1,1]))
             sage: [H.zigzag(x) for x in [0,1/6,1/4,1/2,3/4,5/6]]
             [-4, -4, -3, -2, -1, 0]
 
@@ -707,36 +724,36 @@ def weight(self):
         With rational inputs::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(alpha_beta=([1/2],[0])).weight()
+            sage: Hyp(alpha_beta=([1/2], [0])).weight()
             0
-            sage: Hyp(alpha_beta=([1/4,3/4],[0,0])).weight()
+            sage: Hyp(alpha_beta=([1/4,3/4], [0,0])).weight()
             1
-            sage: Hyp(alpha_beta=([1/6,1/3,2/3,5/6],[0,0,1/4,3/4])).weight()
+            sage: Hyp(alpha_beta=([1/6,1/3,2/3,5/6], [0,0,1/4,3/4])).weight()
             1
-            sage: H = Hyp(alpha_beta=([1/6,1/3,2/3,5/6],[1/8,3/8,5/8,7/8]))
+            sage: H = Hyp(alpha_beta=([1/6,1/3,2/3,5/6], [1/8,3/8,5/8,7/8]))
             sage: H.weight()
             1
 
         With cyclotomic inputs::
 
-            sage: Hyp(cyclotomic=([6,2],[1,1,1])).weight()
+            sage: Hyp(cyclotomic=([6,2], [1,1,1])).weight()
             2
-            sage: Hyp(cyclotomic=([6],[1,2])).weight()
+            sage: Hyp(cyclotomic=([6], [1,2])).weight()
             0
-            sage: Hyp(cyclotomic=([8],[1,2,3])).weight()
+            sage: Hyp(cyclotomic=([8], [1,2,3])).weight()
             0
-            sage: Hyp(cyclotomic=([5],[1,1,1,1])).weight()
+            sage: Hyp(cyclotomic=([5], [1,1,1,1])).weight()
             3
-            sage: Hyp(cyclotomic=([5,6],[1,1,2,2,3])).weight()
+            sage: Hyp(cyclotomic=([5,6], [1,1,2,2,3])).weight()
             1
-            sage: Hyp(cyclotomic=([3,8],[1,1,1,2,6])).weight()
+            sage: Hyp(cyclotomic=([3,8], [1,1,1,2,6])).weight()
             2
-            sage: Hyp(cyclotomic=([3,3],[2,2,4])).weight()
+            sage: Hyp(cyclotomic=([3,3], [2,2,4])).weight()
             1
 
         With gamma list input::
 
-            sage: Hyp(gamma_list=([8,2,2,2],[6,4,3,1])).weight()
+            sage: Hyp(gamma_list=([8,2,2,2], [6,4,3,1])).weight()
             3
         """
         alpha = self._alpha
@@ -757,15 +774,15 @@ def degree(self):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(alpha_beta=([1/2],[0])).degree()
+            sage: Hyp(alpha_beta=([1/2], [0])).degree()
             1
-            sage: Hyp(gamma_list=([2,2,4],[8])).degree()
+            sage: Hyp(gamma_list=([2,2,4], [8])).degree()
             4
-            sage: Hyp(cyclotomic=([5,6],[1,1,2,2,3])).degree()
+            sage: Hyp(cyclotomic=([5,6], [1,1,2,2,3])).degree()
             6
-            sage: Hyp(cyclotomic=([3,8],[1,1,1,2,6])).degree()
+            sage: Hyp(cyclotomic=([3,8], [1,1,1,2,6])).degree()
             6
-            sage: Hyp(cyclotomic=([3,3],[2,2,4])).degree()
+            sage: Hyp(cyclotomic=([3,3], [2,2,4])).degree()
             4
         """
         return self._deg
@@ -781,19 +798,19 @@ def hodge_numbers(self) -> list:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(cyclotomic=([3],[6]))
+            sage: H = Hyp(cyclotomic=([3], [6]))
             sage: H.hodge_numbers()
             [1, 1]
 
-            sage: H = Hyp(cyclotomic=([4],[1,2]))
+            sage: H = Hyp(cyclotomic=([4], [1,2]))
             sage: H.hodge_numbers()
             [2]
 
-            sage: H = Hyp(gamma_list=([8,2,2,2],[6,4,3,1]))
+            sage: H = Hyp(gamma_list=([8,2,2,2], [6,4,3,1]))
             sage: H.hodge_numbers()
             [1, 2, 2, 1]
 
-            sage: H = Hyp(gamma_list=([5],[1,1,1,1,1]))
+            sage: H = Hyp(gamma_list=([5], [1,1,1,1,1]))
             sage: H.hodge_numbers()
             [1, 1, 1, 1]
 
@@ -832,10 +849,10 @@ def hodge_polynomial(self):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(cyclotomic=([6,10],[3,12]))
+            sage: H = Hyp(cyclotomic=([6,10], [3,12]))
             sage: H.hodge_polynomial()
             (T^3 + 2*T^2 + 2*T + 1)/T^2
-            sage: H = Hyp(cyclotomic=([2,2,2,2,3,3,3,6,6],[1,1,4,5,9]))
+            sage: H = Hyp(cyclotomic=([2,2,2,2,3,3,3,6,6], [1,1,4,5,9]))
             sage: H.hodge_polynomial()
             (T^5 + 3*T^4 + 3*T^3 + 3*T^2 + 3*T + 1)/T^2
         """
@@ -860,7 +877,7 @@ def hodge_function(self, x):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(cyclotomic=([6,10],[3,12]))
+            sage: H = Hyp(cyclotomic=([6,10], [3,12]))
             sage: H.hodge_function(3)
             2
             sage: H.hodge_function(4)
@@ -892,10 +909,10 @@ def hodge_polygon_vertices(self) -> list:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(cyclotomic=([6,10],[3,12]))
+            sage: H = Hyp(cyclotomic=([6,10], [3,12]))
             sage: H.hodge_polygon_vertices()
             [(0, 0), (1, 0), (3, 2), (5, 6), (6, 9)]
-            sage: H = Hyp(cyclotomic=([2,2,2,2,3,3,3,6,6],[1,1,4,5,9]))
+            sage: H = Hyp(cyclotomic=([2,2,2,2,3,3,3,6,6], [1,1,4,5,9]))
             sage: H.hodge_polygon_vertices()
             [(0, 0), (1, 0), (4, 3), (7, 9), (10, 18), (13, 30), (14, 35)]
         """
@@ -915,7 +932,7 @@ def E_polynomial(self, vars=None):
 
         INPUT:
 
-        - ``vars`` -- optional pair of variables (default `u,v`)
+        - ``vars`` -- optional pair of variables (default: `u,v`)
 
         REFERENCES:
 
@@ -925,7 +942,7 @@ def E_polynomial(self, vars=None):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData
-            sage: H = HypergeometricData(gamma_list=[-30,-1,6,10,15])
+            sage: H = HypergeometricData(gamma_list=[-30, -1, 6, 10, 15])
             sage: H.E_polynomial()
             8*u*v + 7*u + 7*v + 8
 
@@ -992,12 +1009,12 @@ def M_value(self):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(alpha_beta=([1/6,1/3,2/3,5/6],[1/8,3/8,5/8,7/8]))
+            sage: H = Hyp(alpha_beta=([1/6,1/3,2/3,5/6], [1/8,3/8,5/8,7/8]))
             sage: H.M_value()
             729/4096
-            sage: Hyp(alpha_beta=(([1/2,1/2,1/2,1/2],[0,0,0,0]))).M_value()
+            sage: Hyp(alpha_beta=(([1/2,1/2,1/2,1/2], [0,0,0,0]))).M_value()
             256
-            sage: Hyp(cyclotomic=([5],[1,1,1,1])).M_value()
+            sage: Hyp(cyclotomic=([5], [1,1,1,1])).M_value()
             3125
         """
         return self._M_value
@@ -1013,7 +1030,7 @@ def is_primitive(self) -> bool:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(cyclotomic=([3],[4])).is_primitive()
+            sage: Hyp(cyclotomic=([3], [4])).is_primitive()
             True
             sage: Hyp(gamma_list=[-2, 4, 6, -8]).is_primitive()
             False
@@ -1033,7 +1050,7 @@ def primitive_index(self):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(cyclotomic=([3],[4])).primitive_index()
+            sage: Hyp(cyclotomic=([3], [4])).primitive_index()
             1
             sage: Hyp(gamma_list=[-2, 4, 6, -8]).primitive_index()
             2
@@ -1052,7 +1069,7 @@ def has_symmetry_at_one(self) -> bool:
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: Hyp(alpha_beta=[[1/2]*16,[0]*16]).has_symmetry_at_one()
+            sage: Hyp(alpha_beta=[[1/2]*16, [0]*16]).has_symmetry_at_one()
             True
 
         REFERENCES:
@@ -1064,18 +1081,18 @@ def has_symmetry_at_one(self) -> bool:
 
     def lfunction(self, t, prec=53):
         """
-        Return the L-function of ``self``.
+        Return the `L`-function of ``self``.
 
-        The result is a wrapper around a PARI L-function.
+        The result is a wrapper around a PARI `L`-function.
 
         INPUT:
 
-        - ``prec`` -- precision (default 53)
+        - ``prec`` -- precision (default: 53)
 
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(cyclotomic=([3],[4]))
+            sage: H = Hyp(cyclotomic=([3], [4]))
             sage: L = H.lfunction(1/64); L
             PARI L-function associated to Hypergeometric data for [1/3, 2/3] and [1/4, 3/4]
             sage: L(4)
@@ -1095,7 +1112,7 @@ def canonical_scheme(self, t=None):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(cyclotomic=([3],[4]))
+            sage: H = Hyp(cyclotomic=([3], [4]))
             sage: H.gamma_list()
             [-1, 2, 3, -4]
             sage: H.canonical_scheme()
@@ -1187,13 +1204,13 @@ def twist(self):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(alpha_beta=([1/2],[0]))
+            sage: H = Hyp(alpha_beta=([1/2], [0]))
             sage: H.twist()
             Hypergeometric data for [0] and [1/2]
             sage: H.twist().twist() == H
             True
 
-            sage: Hyp(cyclotomic=([6],[1,2])).twist().cyclotomic_data()
+            sage: Hyp(cyclotomic=([6], [1,2])).twist().cyclotomic_data()
             ([3], [1, 2])
         """
         alpha = [x + QQ((1, 2)) for x in self._alpha]
@@ -1207,7 +1224,7 @@ def swap_alpha_beta(self):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(alpha_beta=([1/2],[0]))
+            sage: H = Hyp(alpha_beta=([1/2], [0]))
             sage: H.swap_alpha_beta()
             Hypergeometric data for [0] and [1/2]
         """
@@ -1225,7 +1242,7 @@ def primitive_data(self):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(cyclotomic=([3],[4]))
+            sage: H = Hyp(cyclotomic=([3], [4]))
             sage: H2 = Hyp(gamma_list=[-2, 4, 6, -8])
             sage: H2.primitive_data() == H
             True
@@ -1247,7 +1264,7 @@ def gauss_table(self, p, f, prec):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(cyclotomic=([3],[4]))
+            sage: H = Hyp(cyclotomic=([3], [4]))
             sage: H.gauss_table(2, 2, 4)
             (4, [1 + 2 + 2^2 + 2^3, 1 + 2 + 2^2 + 2^3, 1 + 2 + 2^2 + 2^3])
         """
@@ -1277,7 +1294,7 @@ def gauss_table_full(self):
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(cyclotomic=([3],[4]))
+            sage: H = Hyp(cyclotomic=([3], [4]))
             sage: H.euler_factor(2, 7, cache_p=True)
             7*T^2 - 3*T + 1
             sage: H.gauss_table_full()[(7, 1)]
@@ -1285,12 +1302,12 @@ def gauss_table_full(self):
 
         Clearing cached values::
 
-            sage: H = Hyp(cyclotomic=([3],[4]))
+            sage: H = Hyp(cyclotomic=([3], [4]))
             sage: H.euler_factor(2, 7, cache_p=True)
             7*T^2 - 3*T + 1
             sage: d = H.gauss_table_full()
             sage: d.clear() # Delete all entries of this dict
-            sage: H1 = Hyp(cyclotomic=([5],[12]))
+            sage: H1 = Hyp(cyclotomic=([5], [12]))
             sage: d1 = H1.gauss_table_full()
             sage: len(d1.keys()) # No cached values
             0
@@ -1313,11 +1330,11 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False):
 
         INPUT:
 
-        - `p` -- a prime number
+        - ``p`` -- a prime number
 
-        - `f` -- an integer such that `q = p^f`
+        - ``f`` -- an integer such that `q = p^f`
 
-        - `t` -- a rational parameter
+        - ``t`` -- a rational parameter
 
         - ``prec`` -- precision (optional)
 
@@ -1332,7 +1349,7 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False):
         From Benasque report [Benasque2009]_, page 8::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(alpha_beta=([1/2]*4,[0]*4))
+            sage: H = Hyp(alpha_beta=([1/2]*4, [0]*4))
             sage: [H.padic_H_value(3,i,-1) for i in range(1,3)]
             [0, -12]
             sage: [H.padic_H_value(5,i,-1) for i in range(1,3)]
@@ -1353,8 +1370,8 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False):
 
         Check issue from :issue:`28404`::
 
-            sage: H1 = Hyp(cyclotomic=([1,1,1],[6,2]))
-            sage: H2 = Hyp(cyclotomic=([6,2],[1,1,1]))
+            sage: H1 = Hyp(cyclotomic=([1,1,1], [6,2]))
+            sage: H2 = Hyp(cyclotomic=([6,2], [1,1,1]))
             sage: [H1.padic_H_value(5,1,i) for i in range(2,5)]
             [1, -4, -4]
             sage: [H2.padic_H_value(5,1,i) for i in range(2,5)]
@@ -1362,13 +1379,13 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False):
 
         Check for potential overflow::
 
-            sage: H = Hyp(cyclotomic=[[10,6],[5,4]])
+            sage: H = Hyp(cyclotomic=[[10,6], [5,4]])
             sage: H.padic_H_value(101, 2, 2)
             -1560629
 
         Check issue from :issue:`29778`::
 
-            sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7]))
+            sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5],  [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7]))
             sage: try:
             ....:     print(H.padic_H_value(373, 4, 2))
             ....: except ValueError as s:
@@ -1377,7 +1394,7 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False):
 
         Check error handling for wild and tame primes::
 
-            sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7]))
+            sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5],  [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7]))
             sage: try:
             ....:     print(H.padic_H_value(5, 1, 2))
             ....: except NotImplementedError as s:
@@ -1391,7 +1408,7 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False):
 
         Check that :issue:`37910` is resolved::
 
-            sage: H = Hyp(alpha_beta=[[1/2,1/2,1/2,1/2,1/2,1/3,2/3,1/6,5/6], [0,0,0,0,0,0,0,0,0]])
+            sage: H = Hyp(alpha_beta=[[1/2,1/2,1/2,1/2,1/2,1/3,2/3,1/6,5/6],  [0,0,0,0,0,0,0,0,0]])
             sage: H.padic_H_value(151, 2, -512000)
             50178940126155881
 
@@ -1406,7 +1423,7 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False):
             raise ValueError('p not prime')
         if not all(x.denominator() % p for x in self._alpha + self._beta):
             raise NotImplementedError('p is wild')
-        if (t.numerator() * t.denominator() % p == 0 or (t - 1) % p == 0):
+        if t.numerator() % p == 0 or t.denominator() % p == 0:
             raise NotImplementedError('p is tame')
 
         if 0 in alpha:
@@ -1441,6 +1458,7 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False):
             trcoeffs = hgm_coeffs(p, f, prec, gamma, m, D, gtab, prec, use_longs)
         sigma = trcoeffs[p - 2]
         p_ring = sigma.parent()
+
         teich = p_ring.teichmuller(M / t)
         for i in range(p - 3, -1, -1):
             sigma = sigma * teich + trcoeffs[i]
@@ -1457,13 +1475,13 @@ def H_value(self, p, f, t, ring=None):
 
         INPUT:
 
-        - `p` -- a prime number
+        - ``p`` -- a prime number
 
-        - `f` -- an integer such that `q = p^f`
+        - ``f`` -- an integer such that `q = p^f`
 
-        - `t` -- a rational parameter
+        - ``t`` -- a rational parameter
 
-        - ``ring`` -- optional (default :class:`UniversalCyclotomicfield`)
+        - ``ring`` -- optional (default: :class:`UniversalCyclotomicfield`)
 
         The ring could be also ``ComplexField(n)`` or ``QQbar``.
 
@@ -1484,7 +1502,7 @@ def H_value(self, p, f, t, ring=None):
         With values in the UniversalCyclotomicField (slow)::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(alpha_beta=([1/2]*4,[0]*4))
+            sage: H = Hyp(alpha_beta=([1/2]*4, [0]*4))
             sage: [H.H_value(3,i,-1) for i in range(1,3)]
             [0, -12]
             sage: [H.H_value(5,i,-1) for i in range(1,3)]
@@ -1503,8 +1521,8 @@ def H_value(self, p, f, t, ring=None):
 
         Check issue from :issue:`28404`::
 
-            sage: H1 = Hyp(cyclotomic=([1,1,1],[6,2]))
-            sage: H2 = Hyp(cyclotomic=([6,2],[1,1,1]))
+            sage: H1 = Hyp(cyclotomic=([1,1,1], [6,2]))
+            sage: H2 = Hyp(cyclotomic=([6,2], [1,1,1]))
             sage: [H1.H_value(5,1,i) for i in range(2,5)]
             [1, -4, -4]
             sage: [H2.H_value(5,1,QQ(i)) for i in range(2,5)]
@@ -1514,7 +1532,7 @@ def H_value(self, p, f, t, ring=None):
 
         Check issue from :issue:`29778`::
 
-            sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7]))
+            sage: H = Hyp(cyclotomic=[[5,5], [4,7]])
             sage: try:
             ....:     print(H.padic_H_value(373, 4, 2))
             ....: except ValueError as s:
@@ -1523,7 +1541,7 @@ def H_value(self, p, f, t, ring=None):
 
         Check error handling for wild and tame primes::
 
-            sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7]))
+            sage: H = Hyp(cyclotomic=[[5,5], [4,7]])
             sage: try:
             ....:     print(H.padic_H_value(5, 1, 2))
             ....: except NotImplementedError as s:
@@ -1589,19 +1607,19 @@ def sign(self, t, p):
         Return the sign of the functional equation for the Euler factor of the motive `H_t` at the prime `p`.
 
         For odd weight, the sign of the functional equation is +1. For even
-        weight, the sign is computed by a recipe found in 11.1 of [Watkins]_
+        weight, the sign is computed by a recipe found in Section 11.1 of [Watkins]_
         (when 0 is not in alpha).
 
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(cyclotomic=([6,2],[1,1,1]))
+            sage: H = Hyp(cyclotomic=([6,2], [1,1,1]))
             sage: H.weight(), H.degree()
             (2, 3)
             sage: [H.sign(1/4,p) for p in [5,7,11,13,17,19]]
             [1, 1, -1, -1, 1, 1]
 
-            sage: H = Hyp(alpha_beta=([1/12,5/12,7/12,11/12],[0,1/2,1/2,1/2]))
+            sage: H = Hyp(alpha_beta=([1/12,5/12,7/12,11/12], [0,1/2,1/2,1/2]))
             sage: H.weight(), H.degree()
             (2, 4)
             sage: t = -5
@@ -1610,7 +1628,7 @@ def sign(self, t, p):
 
         We check that :issue:`28404` is fixed::
 
-            sage: H = Hyp(cyclotomic=([1,1,1],[6,2]))
+            sage: H = Hyp(cyclotomic=([1,1,1], [6,2]))
             sage: [H.sign(4,p) for p in [5,7,11,13,17,19]]
             [1, 1, -1, -1, 1, 1]
 
@@ -1629,16 +1647,115 @@ def sign(self, t, p):
             sign = kronecker_symbol(t * (t - 1) * self._sign_param, p)
         return sign
 
+    def euler_factor_tame_contribution(self, t, p, mo, deg=None):
+        """
+        Return a contribution to the Euler factor of the motive `H_t` at a tame prime.
+
+        The output is only nontrivial when `t` has nonzero `p`-adic valuation.
+        The algorithm is described in Section 11.4.1 of [Watkins]_.
+
+        INPUT:
+
+        - ``t`` -- rational number, not 0 or 1
+
+        - ``p`` -- prime number of good reduction
+
+        - ``mo`` -- integer
+
+        - ``deg`` -- integer (optional)
+
+        OUTPUT:
+
+        a polynomial
+
+        If ``deg`` is specified, the output is truncated to that degree (inclusive).
+
+        EXAMPLES::
+
+            sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
+            sage: H = Hyp(cyclotomic=[[3,7], [4,5,6]])
+            sage: H.euler_factor_tame_contribution(11^2, 11, 4)
+            1
+            sage: H.euler_factor_tame_contribution(11^20, 11, 4)
+            1331*T^2 + 1
+            sage: H.euler_factor_tame_contribution(11^20, 11, 4, deg=1)
+            1
+            sage: H.euler_factor_tame_contribution(11^20, 11, 5)
+            1771561*T^4 + 161051*T^3 + 6171*T^2 + 121*T + 1
+            sage: H.euler_factor_tame_contribution(11^20, 11, 5, deg=3)
+            161051*T^3 + 6171*T^2 + 121*T + 1
+            sage: H.euler_factor_tame_contribution(11^20, 11, 6)
+            1
+        """
+        t = QQ(t)
+        if t in [0, 1]:
+            raise ValueError('invalid t')
+        if not is_prime(p):
+            raise ValueError('p not prime')
+        if not all(x.denominator() % p for x in self._alpha + self._beta):
+            raise NotImplementedError('p is wild')
+
+        e = t.valuation(p)
+        t0 = t / p**e
+        if e > 0:
+            mul = self.cyclotomic_data()[1].count(mo)
+        elif e < 0:
+            mul = self.cyclotomic_data()[0].count(mo)
+        else:
+            mul = None
+        if e % mo or not mul:
+            return ZZ.one()
+        d = euler_phi(mo)
+        f = IntegerModRing(mo)(p).multiplicative_order()
+        if deg is None:
+            deg = d
+        if deg < f:
+            return ZZ.one()
+        q = p ** f
+        prec = ceil(deg*(self.weight()+1-mul)/2 + log(2*d + 1, p))
+        k = (q-1) // mo
+        flip = (f == 1 and prec == 1)
+        gtab_prec, gtab = self.gauss_table(p, f, prec)
+        try:
+            p_ring = gtab[0].parent()
+        except AttributeError:
+            p_ring = Zp(p, prec, 'fixed-mod')
+        M = self.M_value()
+        teich = p_ring.teichmuller(M / t0)
+        m = {r: self._beta.count(QQ((r, q - 1))) for r in range(q - 1)}
+        D = -min(self.zigzag(x, flip_beta=True) for x in self._alpha + self._beta)
+        gamma = self.gamma_array()
+        l = []
+        for j in range(mo):
+            if gcd(j, mo) == 1:
+                r = j * k
+                term = teich**r * ZZ(-1)**m[0]
+                ct = 0
+                for v, gv in gamma.items():
+                    r1 = v * r % (q-1)
+                    ct += gv * sum(r1.digits(p))
+                    term *= p_ring(gtab[r1]) ** (-gv if flip else gv)
+                ct //= p - 1
+                term *= ZZ(-1) ** ct
+                ct += f * (D + m[0] - m[r])
+                l.append(term * p**ct)
+        traces = [0 if j % f else sum(i ** (j//f) for i in l) for j in range(1,d+1)]
+        R = IntegerModRing(p**prec)
+        traces = [R(i).lift_centered() for i in traces]
+        return characteristic_polynomial_from_traces(traces, d, p, 0, 1, deg, use_fe=False)
+
     @cached_method
-    def euler_factor(self, t, p, cache_p=False):
+    def euler_factor(self, t, p, deg=None, cache_p=False):
         """
         Return the Euler factor of the motive `H_t` at prime `p`.
 
         INPUT:
 
-        - `t` -- rational number, not 0 or 1
+        - ``t`` -- rational number, not 0 or 1
+
+        - ``p`` -- prime number of good reduction
 
-        - `p` -- prime number of good reduction
+        - ``deg`` -- integer or ``None``
 
         OUTPUT:
 
@@ -1647,12 +1764,18 @@ def euler_factor(self, t, p, cache_p=False):
         See [Benasque2009]_ for explicit examples of Euler factors.
 
         For odd weight, the sign of the functional equation is +1. For even
-        weight, the sign is computed by a recipe found in 11.1 of [Watkins]_.
+        weight, the sign is computed by a recipe found in Section 11.1 of [Watkins]_.
+
+        If ``deg`` is specified, then the polynomial is only computed up to degree
+        ``deg`` (inclusive).
+
+        The prime `p` may be tame, but not wild. When `v_p(t-1)` is nonzero and even,
+        the Euler factor includes a linear term described in Section 11.2 of [Watkins]_.
 
         EXAMPLES::
 
             sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
-            sage: H = Hyp(alpha_beta=([1/2]*4,[0]*4))
+            sage: H = Hyp(alpha_beta=([1/2]*4, [0]*4))
             sage: H.euler_factor(-1, 5)
             15625*T^4 + 500*T^3 - 130*T^2 + 4*T + 1
 
@@ -1668,7 +1791,7 @@ def euler_factor(self, t, p, cache_p=False):
             23*T^2 + 8*T + 1,
             29*T^2 + 2*T + 1]
 
-            sage: H = Hyp(cyclotomic=([6,2],[1,1,1]))
+            sage: H = Hyp(cyclotomic=([6,2], [1,1,1]))
             sage: H.weight(), H.degree()
             (2, 3)
             sage: [H.euler_factor(1/4,p) for p in [5,7,11,13,17,19]]
@@ -1679,7 +1802,7 @@ def euler_factor(self, t, p, cache_p=False):
              4913*T^3 + 323*T^2 + 19*T + 1,
              6859*T^3 - 57*T^2 - 3*T + 1]
 
-            sage: H = Hyp(alpha_beta=([1/12,5/12,7/12,11/12],[0,1/2,1/2,1/2]))
+            sage: H = Hyp(alpha_beta=([1/12,5/12,7/12,11/12], [0,1/2,1/2,1/2]))
             sage: H.weight(), H.degree()
             (2, 4)
             sage: t = -5
@@ -1696,52 +1819,107 @@ def euler_factor(self, t, p, cache_p=False):
             sage: H = Hyp(cyclotomic=([11], [7, 12]))
             sage: H.euler_factor(2, 13)
             371293*T^10 - 85683*T^9 + 26364*T^8 + 1352*T^7 - 65*T^6 + 394*T^5 - 5*T^4 + 8*T^3 + 12*T^2 - 3*T + 1
+            sage: H.euler_factor(2, 13, deg=4)
+            -5*T^4 + 8*T^3 + 12*T^2 - 3*T + 1
             sage: H.euler_factor(2, 19) # long time
             2476099*T^10 - 651605*T^9 + 233206*T^8 - 77254*T^7 + 20349*T^6 - 4611*T^5 + 1071*T^4 - 214*T^3 + 34*T^2 - 5*T + 1
 
+        This is an example of tame primes::
+
+            sage: H = Hyp(cyclotomic=[[4,2,2], [3,1,1]])
+            sage: H.euler_factor(8, 7)
+            -7*T^3 + 7*T^2 - T + 1
+            sage: H.euler_factor(50, 7)
+            -7*T^3 + 7*T^2 - T + 1
+            sage: H.euler_factor(7, 7)
+            -T + 1
+            sage: H.euler_factor(1/7^2, 7)
+            T + 1
+            sage: H.euler_factor(1/7^4, 7)
+            7*T^3 + 7*T^2 + T + 1
+
         TESTS::
 
-             sage: H1 = Hyp(alpha_beta=([1,1,1],[1/2,1/2,1/2]))
+             sage: H1 = Hyp(alpha_beta=([1,1,1], [1/2,1/2,1/2]))
              sage: H2 = H1.swap_alpha_beta()
              sage: H1.euler_factor(-1, 3)
              27*T^3 + 3*T^2 + T + 1
              sage: H2.euler_factor(-1, 3)
              27*T^3 + 3*T^2 + T + 1
-             sage: H = Hyp(alpha_beta=([0,0,0,1/3,2/3],[1/2,1/5,2/5,3/5,4/5]))
+             sage: H = Hyp(alpha_beta=([0,0,0,1/3,2/3], [1/2,1/5,2/5,3/5,4/5]))
              sage: H.euler_factor(5,7)
              16807*T^5 - 686*T^4 - 105*T^3 - 15*T^2 - 2*T + 1
 
         Check for precision downsampling::
 
-            sage: H = Hyp(cyclotomic=[[3],[4]])
+            sage: H = Hyp(cyclotomic=[[3], [4]])
             sage: H.euler_factor(2, 11, cache_p=True)
             11*T^2 - 3*T + 1
-            sage: H = Hyp(cyclotomic=[[12],[1,2,6]])
+            sage: H = Hyp(cyclotomic=[[12], [1,2,6]])
             sage: H.euler_factor(2, 11, cache_p=True)
             -T^4 + T^3 - T + 1
 
         Check issue from :issue:`29778`::
 
-            sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7]))
+            sage: H = Hyp(cyclotomic=[[5,5], [4,7]])
             sage: try:
             ....:     print(H.euler_factor(2, 373))
             ....: except ValueError as s:
             ....:     print(s)
             p^f cannot exceed 2^31
 
-        Check error handling for wild and tame primes::
+        Check handling of some tame cases::
 
-            sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7]))
+            sage: H = Hyp(cyclotomic=[[4,2,2,2], [3,1,1,1]])
+            sage: H.euler_factor(8, 7)
+            2401*T^4 - 392*T^3 + 46*T^2 - 8*T + 1
+            sage: H.euler_factor(50, 7)
+            16807*T^5 - 343*T^4 - 70*T^3 - 10*T^2 - T + 1
+            sage: H = Hyp(cyclotomic=[[3,7], [4,5,6]])
+            sage: H.euler_factor(11, 11)
+            1
+            sage: H.euler_factor(11**4, 11)
+            1331*T^2 + 1
+            sage: H.euler_factor(11**5, 11)
+            1771561*T^4 + 161051*T^3 + 6171*T^2 + 121*T + 1
+            sage: H.euler_factor(11**5, 11, deg=3)
+            161051*T^3 + 6171*T^2 + 121*T + 1
+            sage: H.euler_factor(11**-3, 11)
+            1331*T^2 + 1
+            sage: H.euler_factor(11**-7, 11)
+            2357947691*T^6 - 58564*T^3 + 1
+            sage: H = Hyp(cyclotomic=[[7], [5,1,1]])
+            sage: H.euler_factor(2, 2)
+            -T + 1
+            sage: H.euler_factor(2^-7, 2)
+            8*T^6 - 2*T^3 + 1
+            sage: H.euler_factor(3, 2)
+            4*T^5 + 4*T^4 + 2*T^3 + 2*T^2 + T + 1
+
+        More examples of tame primes from [Watkins]_::
+
+            sage: H = Hyp(cyclotomic=[[3,12], [6,6,1,1]])
+            sage: H.euler_factor(1/8, 7).factor()
+            (-1) * (7*T - 1) * (117649*T^4 + 2744*T^3 + 105*T^2 + 8*T + 1)
+            sage: H.euler_factor(1/12, 11).factor()
+            (11*T + 1) * (1771561*T^4 - 18634*T^3 + 22*T^2 - 14*T + 1)
+            sage: H = Hyp(cyclotomic=[[4,4,4],[6,2,1,1,1]])
+            sage: H.euler_factor(1/8, 7).factor()
+            (49*T + 1) * (5764801*T^4 - 86436*T^3 + 2758*T^2 - 36*T + 1)
+            sage: H.euler_factor(1/12, 11).factor()
+            (-1) * (121*T - 1) * (214358881*T^4 - 527076*T^3 + 12694*T^2 - 36*T + 1)
+            sage: H = Hyp(cyclotomic=[[10,4,2], [18,1]])
+            sage: H.euler_factor(1/14, 13)
+            -4826809*T^6 + 114244*T^5 + 2197*T^4 - 13*T^2 - 4*T + 1
+
+        Check error handling for wild primes::
+
+            sage: H = Hyp(cyclotomic=[[5,5], [4,7]])
             sage: try:
             ....:     print(H.euler_factor(2, 5))
             ....: except NotImplementedError as s:
             ....:     print(s)
             p is wild
-            sage: try:
-            ....:     print(H.euler_factor(3, 3))
-            ....: except NotImplementedError as s:
-            ....:     print(s)
-            p is tame
 
         REFERENCES:
 
@@ -1751,19 +1929,33 @@ def euler_factor(self, t, p, cache_p=False):
         t = QQ(t)
         if t in [0, 1]:
             raise ValueError('invalid t')
-        alpha = self._alpha
-        if 0 in alpha:
-            return self._swap.euler_factor(~t, p)
-
         if not is_prime(p):
             raise ValueError('p not prime')
         if not all(x.denominator() % p for x in self._alpha + self._beta):
             raise NotImplementedError('p is wild')
-        if (t.numerator() * t.denominator() % p == 0 or (t - 1) % p == 0):
-            raise NotImplementedError('p is tame')
-        # now p is good
-        d = self.degree()
+        if 0 in self._alpha:
+            return self._swap.euler_factor(~t, p)
+        P = PolynomialRing(ZZ, 'T')
+        if t.numerator() % p == 0 or t.denominator() % p == 0:
+            ans = P.one()
+            for m in set(j for i in self.cyclotomic_data() for j in i):
+                ans *= self.euler_factor_tame_contribution(t, p, m, deg)
+            if deg is not None:
+                ans = ans.truncate(deg + 1)
+            return ans
+        # now p is good, or p is tame and t is a p-adic unit
+        elif (t-1) % p == 0:
+            typ = "mult"
+            d = self.degree() - 1
+            if d % 2:
+                d -= 1
+        else:
+            typ = "good"
+            d = self.degree()
         bound = d // 2
+        if deg is not None:
+            bound = min(deg, bound)
+
         if p ** bound > 2 ** 31:
             raise ValueError("p^f cannot exceed 2^31")
 
@@ -1771,5 +1963,55 @@ def euler_factor(self, t, p, cache_p=False):
                   for i in range(bound)]
 
         w = self.weight()
-        sign = self.sign(t, p)
-        return characteristic_polynomial_from_traces(traces, d, p, w, sign)
+        m1 = self.cyclotomic_data()[1].count(1)
+
+        # In the multiplicative case, we sometimes need to pull out a linear factor
+        # in order to apply the functional equation.
+        if typ == "mult":
+            if self.degree() % 2 == 0:
+                sign = 1
+                if w % 2:
+                    assert m1 % 2 == 0
+                    u = (-1) ** (m1//2)
+                    u *= prod(v ** gv for v, gv in self.gamma_array().items())
+                    c = kronecker_symbol(u, p) * p**((w-1)//2)
+                else:
+                    u = (-1) ** (1 + self.degree()//2 + (m1-1)//2)
+                    num, den = self.defining_polynomials()
+                    x = num.parent().gen()
+                    num = num(-x)
+                    num /= (x-1) ** num.valuation(x-1)
+                    den /= (x-1) ** den.valuation(x-1)
+                    u *= 2 * num(1) / den(1)
+                    c = kronecker_symbol(u, p) * p**(w//2)
+                cpow = c
+                for j in range(len(traces)):
+                    traces[j] -= cpow
+                    cpow *= c
+                tmp = 1 - c*P.gen()
+            else:
+                u = (-1) ** (1+(self.degree()-1)//2)
+                num, den = self.defining_polynomials()
+                x = num.parent().gen()
+                den = den(-x)
+                num /= (x-1) ** num.valuation(x-1)
+                den /= (x-1) ** den.valuation(x-1)
+                u *= num(1) / den(1)
+                sign = kronecker_symbol(u, p)
+        else:
+            sign = self.sign(t, p)
+
+        ans = characteristic_polynomial_from_traces(traces, d, p, w, sign, deg=deg)
+
+        # In the multiplicative case, we sometimes need to add extra factors.
+        if typ == "mult":
+            if self.degree() % 2 == 0:
+                ans *= tmp
+            if w % 2 == 0 and (t-1).valuation(p) % 2 == 0:
+                K = (-1) ** ((m1-1)//2)*2*prod(abs(x) for x in self.gamma_list())
+                t0 = (~t-1) / p**((t-1).valuation(p))
+                c = kronecker_symbol(K*t0, p) * p**(w//2)
+                ans *= 1 - c*P.gen()
+            if deg is not None:
+                ans = ans.truncate(deg + 1)
+        return ans
diff --git a/src/sage/modular/quasimodform/element.py b/src/sage/modular/quasimodform/element.py
index dd45fca7713..3a8d445f1be 100644
--- a/src/sage/modular/quasimodform/element.py
+++ b/src/sage/modular/quasimodform/element.py
@@ -5,9 +5,11 @@
 AUTHORS:
 
 - DAVID AYOTTE (2021-03-18): initial version
+- Seewoo Lee (2023-09): coefficients method
 """
 # ****************************************************************************
 #       Copyright (C) 2021 David Ayotte
+#                     2023 Seewoo Lee 
 #
 #
 # This program is free software: you can redistribute it and/or modify
@@ -772,3 +774,73 @@ def derivative(self):
         hom_comp = self.homogeneous_components()
 
         return sum(f.serre_derivative() + R(k) * u * f * E2 for k, f in hom_comp.items())
+
+    def _compute(self, X):
+        r"""
+        Compute the coefficients of `q^n` of the q-expansion of this,
+        graded quasimodular form for `n` in the list `X`.
+
+        The results are not cached.  (Use coefficients for cached results).
+
+        EXAMPLES::
+
+            sage: E2 = QuasiModularForms(1).0
+            sage: E2.q_expansion(10)
+            1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 - 288*q^6 - 192*q^7 - 360*q^8 - 312*q^9 + O(q^10)
+            sage: E2._compute([3, 6])
+            [-96, -288]
+            sage: E2._compute([])
+            []
+        """
+        if not isinstance(X, list) or not X:
+            return []
+        bound = max(X)
+        q_exp = self.q_expansion(bound + 1)
+        return [q_exp[i] for i in X]
+
+    def coefficients(self, X):
+        r"""
+        Return the coefficients of `q^n` of the q-expansion of this,
+        graded quasimodular form for `n` in the list `X`.
+
+        If X is an integer, return coefficients for indices from 1
+        to X. This method caches the result.
+
+        EXAMPLES::
+
+            sage: E2, E4 = QuasiModularForms(1).0, QuasiModularForms(1).1
+            sage: f = E2^2
+            sage: g = E2^3 * E4
+            sage: f.coefficients(10)
+            [-48, 432, 3264, 9456, 21600, 39744, 66432, 105840, 147984, 220320]
+            sage: f.coefficients([0,1])
+            [1, -48]
+            sage: f.coefficients([0,1,2,3])
+            [1, -48, 432, 3264]
+            sage: f.coefficients([2,3])
+            [432, 3264]
+            sage: g.coefficients(10)
+            [168,
+             -13608,
+             210336,
+             1805496,
+             -22562064,
+             -322437024,
+             -2063087808,
+             -9165872520,
+             -32250917496,
+             -96383477232]
+            sage: g.coefficients([3, 7])
+            [210336, -2063087808]
+        """
+        try:
+            self.__coefficients
+        except AttributeError:
+            self.__coefficients = {}
+        if isinstance(X, Integer):
+            X = list(range(1, X + 1))
+        Y = [n for n in X if n not in self.__coefficients]
+        v = self._compute(Y)
+        for i in range(len(v)):
+            self.__coefficients[Y[i]] = v[i]
+        return [self.__coefficients[x] for x in X]
diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py
index f901b28ba3c..1654c8cf6ed 100644
--- a/src/sage/modules/free_module.py
+++ b/src/sage/modules/free_module.py
@@ -1972,7 +1972,8 @@ class FreeModule_generic(Module_free_ambient):
          (finite enumerated fields and subquotients of monoids and quotients of semigroups)
         sage: FreeModule(ZZ,3).category()
         Category of finite dimensional modules with basis over
-         (Dedekind domains and euclidean domains and infinite enumerated sets
+         (Dedekind domains and euclidean domains
+          and noetherian rings and infinite enumerated sets
           and metric spaces)
         sage: (QQ^0).category()
         Category of finite enumerated finite dimensional vector spaces with basis
diff --git a/src/sage/modules/free_module_homspace.py b/src/sage/modules/free_module_homspace.py
index 9513a56ea56..9222bd8105e 100644
--- a/src/sage/modules/free_module_homspace.py
+++ b/src/sage/modules/free_module_homspace.py
@@ -28,6 +28,7 @@
        to Ambient free module of rank 2 over the principal ideal domain Integer Ring
        in Category of finite dimensional modules with basis over
           (Dedekind domains and euclidean domains
+           and noetherian rings
            and infinite enumerated sets and metric spaces)
     sage: B = H.basis()
     sage: len(B)
diff --git a/src/sage/monoids/free_abelian_monoid.py b/src/sage/monoids/free_abelian_monoid.py
index 590a20eaa1a..ee5e8df0257 100644
--- a/src/sage/monoids/free_abelian_monoid.py
+++ b/src/sage/monoids/free_abelian_monoid.py
@@ -169,6 +169,10 @@ def is_FreeAbelianMonoid(x):
 
         sage: from sage.monoids.free_abelian_monoid import is_FreeAbelianMonoid
         sage: is_FreeAbelianMonoid(5)
+        doctest:warning...
+        DeprecationWarning: the function is_FreeAbelianMonoid is deprecated;
+        use 'isinstance(..., FreeAbelianMonoid_class)' instead
+        See https://github.com/sagemath/sage/issues/37897 for details.
         False
         sage: is_FreeAbelianMonoid(FreeAbelianMonoid(7,'a'))
         True
@@ -177,6 +181,8 @@ def is_FreeAbelianMonoid(x):
         sage: is_FreeAbelianMonoid(FreeMonoid(0,''))
         False
     """
+    from sage.misc.superseded import deprecation
+    deprecation(37897, "the function is_FreeAbelianMonoid is deprecated; use 'isinstance(..., FreeAbelianMonoid_class)' instead")
     return isinstance(x, FreeAbelianMonoid_class)
 
 
diff --git a/src/sage/monoids/free_monoid.py b/src/sage/monoids/free_monoid.py
index 88277320a63..3e8ed837cf8 100644
--- a/src/sage/monoids/free_monoid.py
+++ b/src/sage/monoids/free_monoid.py
@@ -44,6 +44,10 @@ def is_FreeMonoid(x):
 
         sage: from sage.monoids.free_monoid import is_FreeMonoid
         sage: is_FreeMonoid(5)
+        doctest:warning...
+        DeprecationWarning: the function is_FreeMonoid is deprecated;
+        use 'isinstance(..., (FreeMonoid, IndexedFreeMonoid))' instead
+        See https://github.com/sagemath/sage/issues/37897 for details.
         False
         sage: is_FreeMonoid(FreeMonoid(7,'a'))
         True
@@ -56,6 +60,8 @@ def is_FreeMonoid(x):
         sage: is_FreeMonoid(FreeAbelianMonoid(index_set=ZZ))
         False
     """
+    from sage.misc.superseded import deprecation
+    deprecation(37897, "the function is_FreeMonoid is deprecated; use 'isinstance(..., (FreeMonoid, IndexedFreeMonoid))' instead")
     if isinstance(x, FreeMonoid):
         return True
     from sage.monoids.indexed_free_monoid import IndexedFreeMonoid
diff --git a/src/sage/monoids/monoid.py b/src/sage/monoids/monoid.py
index 97c0dfa6ad3..c0330ad0eb3 100644
--- a/src/sage/monoids/monoid.py
+++ b/src/sage/monoids/monoid.py
@@ -14,6 +14,10 @@ def is_Monoid(x) -> bool:
 
         sage: from sage.monoids.monoid import is_Monoid
         sage: is_Monoid(0)
+        doctest:warning...
+        DeprecationWarning: the function is_Monoid is deprecated;
+        use 'isinstance(..., Monoid_class)' instead
+        See https://github.com/sagemath/sage/issues/37897 for details.
         False
         sage: is_Monoid(ZZ)   # The technical math meaning of monoid has
         ....:                 # no bearing whatsoever on the result: it's
@@ -26,6 +30,8 @@ def is_Monoid(x) -> bool:
         sage: is_Monoid(F)
         True
     """
+    from sage.misc.superseded import deprecation
+    deprecation(37897, "the function is_Monoid is deprecated; use 'isinstance(..., Monoid_class)' instead")
     return isinstance(x, Monoid_class)
 
 
diff --git a/src/sage/plot/plot3d/implicit_surface.pyx b/src/sage/plot/plot3d/implicit_surface.pyx
index df28f7c6e6b..581eddff2d4 100644
--- a/src/sage/plot/plot3d/implicit_surface.pyx
+++ b/src/sage/plot/plot3d/implicit_surface.pyx
@@ -522,7 +522,7 @@ cdef class MarchingCubesTriangles(MarchingCubes):
                     if not(self.color_function is None):
                         self.apply_color_func(&v.color, self.color_function,
                                               self.colormap, v)
-                    y_vertices[y,z] = v
+                    y_vertices[y,z] = v
                 else:
                     y_vertices[y,z] = None
 
@@ -556,7 +556,7 @@ cdef class MarchingCubesTriangles(MarchingCubes):
                     if not(self.color_function is None):
                         self.apply_color_func(&v.color, self.color_function,
                                               self.colormap, v)
-                    z_vertices[y,z] = v
+                    z_vertices[y,z] = v
                 else:
                     z_vertices[y,z] = None
 
@@ -631,7 +631,7 @@ cdef class MarchingCubesTriangles(MarchingCubes):
                     if not(self.color_function is None):
                         self.apply_color_func(&v.color, self.color_function,
                                               self.colormap, v)
-                    x_vertices[y,z] = v
+                    x_vertices[y,z] = v
                 else:
                     x_vertices[y,z] = None
 
diff --git a/src/sage/repl/ipython_kernel/kernel.py b/src/sage/repl/ipython_kernel/kernel.py
index 5398270160e..72938a799ff 100644
--- a/src/sage/repl/ipython_kernel/kernel.py
+++ b/src/sage/repl/ipython_kernel/kernel.py
@@ -100,62 +100,68 @@ def help_links(self):
             sage: sk = SageKernel.__new__(SageKernel)
             sage: sk.help_links
             [{'text': 'Sage Documentation',
-              'url': 'https://doc.sagemath.org/html/en/index.html'},
+              'url': '.../html/en/index.html'},
              ...]
         """
-        # DEPRECATED: The URLs in the form 'kernelspecs/...' were used for
-        # classical Jupyter notebooks. For instance,
-        #
-        #  'kernelspecs/sagemath/doc/html/en/index.html'
-        #
-        # is constructed by kernel_url('doc/html/en/index.html'), but these
-        # URLs of local files don't work for JupyterLab. Hence all URLs here
-        # have been replaced with URLs of online documents.
-
-        from sage.repl.ipython_kernel.install import SageKernelSpec
-        identifier = SageKernelSpec.identifier()
-
-        def kernel_url(x):
-            # URLs starting with 'kernelspecs' are prepended by the
-            # browser with the appropriate path
-            return 'kernelspecs/{0}/{1}'.format(identifier, x)
+        # A Sage doc server starts when Jupyter notebook launches if the Sage
+        # documentation is available locally.  See the corresponding code in
+        # src/bin/sage-notebook.
+
+        from sage.env import SAGE_DOC_SERVER_URL
+        from sage.features.sagemath import sagemath_doc_html
+
+        if SAGE_DOC_SERVER_URL:
+            def doc_url(path):
+                return f'{SAGE_DOC_SERVER_URL}/{path}'
+        elif sagemath_doc_html().is_present():
+            from sage.env import SAGE_DOC_LOCAL_PORT as port
+
+            def doc_url(path):
+                return f'http://127.0.0.1:{port}/{path}'
+        else:
+            def doc_url(path):
+                return f'https://doc.sagemath.org/{path}'
 
         return [
             {
                 'text': 'Sage Documentation',
-                'url': "https://doc.sagemath.org/html/en/index.html",
+                'url': doc_url('html/en/index.html'),
             },
             {
                 'text': 'A Tour of Sage',
-                'url': "https://doc.sagemath.org/html/en/a_tour_of_sage/index.html",
+                'url': doc_url('html/en/a_tour_of_sage/index.html'),
             },
             {
                 'text': 'Tutorial',
-                'url': "https://doc.sagemath.org/html/en/tutorial/index.html",
+                'url': doc_url('html/en/tutorial/index.html'),
             },
             {
                 'text': 'Thematic Tutorials',
-                'url': "https://doc.sagemath.org/html/en/thematic_tutorials/index.html",
+                'url': doc_url('html/en/thematic_tutorials/index.html'),
             },
             {
                 'text': 'PREP Tutorials',
-                'url': "https://doc.sagemath.org/html/en/prep/index.html",
+                'url': doc_url('html/en/prep/index.html'),
             },
             {
                 'text': 'Constructions',
-                'url': "https://doc.sagemath.org/html/en/constructions/index.html",
+                'url': doc_url('html/en/constructions/index.html'),
             },
             {
                 'text': 'FAQ',
-                'url': "https://doc.sagemath.org/html/en/faq/index.html",
+                'url': doc_url('html/en/faq/index.html'),
+            },
+            {
+                'text': 'Reference Manual',
+                'url': doc_url('html/en/reference/index.html'),
             },
             {
-                'text': 'Reference',
-                'url': "https://doc.sagemath.org/html/en/reference/index.html",
+                'text': "Installation Guide",
+                'url': doc_url('html/en/installation/index.html'),
             },
             {
-                'text': "Developer's Guide",
-                'url': "https://doc.sagemath.org/html/en/developer/index.html",
+                'text': "Developer Guide",
+                'url': doc_url('html/en/developer/index.html'),
             },
             {
                 'text': "Python",
diff --git a/src/sage/rings/complex_interval.pyx b/src/sage/rings/complex_interval.pyx
index d272a8e81db..ea17b530ceb 100644
--- a/src/sage/rings/complex_interval.pyx
+++ b/src/sage/rings/complex_interval.pyx
@@ -791,16 +791,16 @@ cdef class ComplexIntervalFieldElement(FieldElement):
             sage: b = CIF(-1, (2, 3))
             sage: c = a/b
             sage: c.endpoints()
-            (0.500000000000000 - 1.60000000000000*I,
-            1.50000000000000 - 0.600000000000000*I,
-            0.500000000000000 - 0.600000000000000*I,
-            1.50000000000000 - 1.60000000000000*I)
+            (0.200000000000000 - 2.00000000000000*I,
+             2.30000000000000 - 0.500000000000000*I,
+             0.200000000000000 - 0.500000000000000*I,
+             2.30000000000000 - 2.00000000000000*I)
             sage: c = b/a
             sage: c.endpoints()
-            (0.246153846153846 + 0.317647058823529*I,
-            0.841176470588236 + 0.761538461538462*I,
-            0.246153846153846 + 0.761538461538462*I,
-            0.841176470588236 + 0.317647058823529*I)
+            (0.100000000000000 + 0.250000000000000*I,
+             1.15000000000000 + 1.00000000000000*I,
+             0.100000000000000 + 1.00000000000000*I,
+             1.15000000000000 + 0.250000000000000*I)
         """
         return self * right.__invert__()
 
@@ -1123,14 +1123,14 @@ cdef class ComplexIntervalFieldElement(FieldElement):
             sage: I = CIF.0
             sage: a = ~(5+I) # indirect doctest
             sage: a * (5+I)
-            1.000000000000000? + -1.?e-16*I
+            1.000000000000000? + 0.?e-16*I
             sage: a = CIF((1, 2), (3, 4))
             sage: c = a.__invert__()
             sage: c.endpoints()
-            (0.0588235294117647 - 0.300000000000000*I,
-            0.153846153846154 - 0.200000000000000*I,
-            0.0588235294117647 - 0.200000000000000*I,
-            0.153846153846154 - 0.300000000000000*I)
+            (0.0500000000000000 - 0.400000000000000*I,
+             0.200000000000000 - 0.150000000000000*I,
+             0.0500000000000000 - 0.150000000000000*I,
+             0.200000000000000 - 0.400000000000000*I)
 
         TESTS:
 
@@ -1146,266 +1146,36 @@ cdef class ComplexIntervalFieldElement(FieldElement):
 
         Test that the bug reported in :issue:`25414` has been fixed::
 
-            sage: 1 / CIF(RIF(-1,1),0)
-            [.. NaN ..] + [.. NaN ..]*I
+            sage: 1 / CIF(RIF(-1 ,1), 0)
+            [-infinity .. +infinity] + [0.0000000000000000 .. +infinity]*I
+
+        Test that the bug reported in :issue:`37927` is fixed::
+
+            sage: (961 * (1 / CIF(0, 31))**2 + 1).contains_zero()
+            True
 
         REFERENCES:
 
         - [RL1971]_
         """
-        # Constructor sets intervals for real and imaginary part to NaN
+        cdef ComplexIntervalFieldElement x
         x = self._new()
 
-        if mpfi_nan_p(self.__re) or mpfi_nan_p(self.__im):
-            # Early bail
-            return x
+        cdef mpfi_t t0, t1
+        mpfi_init2(t0, self._prec)
+        mpfi_init2(t1, self._prec)
 
-        # We checked for NaN, so we can assume we have
-        # valid intervals now.
-
-        # Allocate memory
-        cdef mpfr_t a, b, c, d
-        mpfr_init2(a, self._prec)
-        mpfr_init2(b, self._prec)
-        mpfr_init2(c, self._prec)
-        mpfr_init2(d, self._prec)
-
-        cdef mpfr_t rmin, rmax, imin, imax
-        mpfr_init2(rmin, self._prec)
-        mpfr_init2(rmax, self._prec)
-        mpfr_init2(imin, self._prec)
-        mpfr_init2(imax, self._prec)
-
-        cdef mpfr_t r
-        mpfr_init2(r, self._prec)
-
-        cdef mpfr_t a2, b2, d2, c2
-        mpfr_init2(a2, self._prec)
-        mpfr_init2(b2, self._prec)
-        mpfr_init2(c2, self._prec)
-        mpfr_init2(d2, self._prec)
-
-        cdef mpfr_t div1, div2, aux, aux2
-        mpfr_init2(div1, self._prec)
-        mpfr_init2(div2, self._prec)
-        mpfr_init2(aux, self._prec)
-        mpfr_init2(aux2, self._prec)
-
-        # Variables to remember what we do to make the complex
-        # interval lie in the first quadrant or cross the line between
-        # first and second quadrant.
-        cdef int flipped_real_imag = 0
-        cdef int negated_real = 0
-        cdef int negated_imag = 0
-
-        # Get endpoints of real and imaginary part
-        mpfi_get_left(a, self.__re)
-        mpfi_get_right(b, self.__re)
-        mpfi_get_left(c, self.__im)
-        mpfi_get_right(d, self.__im)
-
-        # In the next three steps, we try to make the interval lie in the
-        # first quadrant or cross the line between the first and second
-        # quadrant.
-
-        # First, we flip the complex plane about the diagonal if the
-        # input interval crosses the real line.
-        if mpfr_sgn(c) < 0 and mpfr_sgn(d) > 0:
-            # Switch real and imaginary part.
-            flipped_real_imag = 1
-            mpfr_swap(a, c)
-            mpfr_swap(b, d)
-
-        # Second, we flip the complex plane about the real line if
-        # part of the interval is (still) below the real line.
-        if mpfr_sgn(c) < 0:
-            # Negate imaginary part interval.
-            negated_imag = 1
-            mpfr_swap(c, d)
-            mpfr_neg(c, c, MPFR_RNDD)
-            mpfr_neg(d, d, MPFR_RNDU)
-
-        # Third, we flip the complex plane about the imaginary line if
-        # the interval is entirely to the left of the imaginary line
-        # (or touches it).
-        if mpfr_sgn(b) <= 0:
-            # Negate real part
-            negated_real = 1
-            mpfr_swap(a, b)
-            mpfr_neg(a, a, MPFR_RNDD)
-            mpfr_neg(b, b, MPFR_RNDU)
-
-        # The last step ensures that the interval for the real part
-        # always contains a non-negative number.
-
-        # The imaginary part could still contain a negative number, but
-        # only if the input interval contained zero to begin with in
-        # which case we will return NaN anyway. We check for this in
-        # the below branches with mpfr_sgn(c).
-
-        # We now distinguish between the cases where the interval
-        # is entirely contained in the first quadrant and where it is
-        # crossing the line between the first and second quadrant.
-        if mpfr_sgn(a) >= 0 and mpfr_sgn(c)>=0:
-            # Input interval lies in first quadrant
-
-            # Computation follows Rokne-Lancaster.
-
-            # Left endpoint
-            mpfr_mul(a2, a, a, MPFR_RNDU)
-            mpfr_mul(b2, b, b, MPFR_RNDU)
-            mpfr_mul(d2, d, d, MPFR_RNDU)
-            mpfr_add(div1, a2, d2, MPFR_RNDU)
-            mpfr_add(div2, b2, d2, MPFR_RNDU)
-            mpfr_div(rmin, a, div1, MPFR_RNDD)
-            mpfr_div(aux, b, div2, MPFR_RNDD)
-            mpfr_min(rmin, rmin, aux, MPFR_RNDD)
-            # Higher endpoint
-            mpfr_mul(c2, c, c, MPFR_RNDU)
-            mpfr_add(div1, b2, c2, MPFR_RNDU)
-            mpfr_div(imax, c, div1, MPFR_RNDU)
-            mpfr_set_si(aux, 0, MPFR_RNDD)
-            mpfr_sub(imax, aux, imax, MPFR_RNDU)
-            mpfr_div(aux2, d, div2, MPFR_RNDU)
-            mpfr_sub(aux2, aux, aux2, MPFR_RNDU)
-            mpfr_max(imax, aux2, imax, MPFR_RNDU)
-            # Lower endpoint, it is the lowest point of the circle or one of
-            if mpfr_cmp(d, a) >=0 and mpfr_cmp(c, a) <= 0:
-                mpfr_add(imin, a, a, MPFR_RNDD)
-                mpfr_set_si(aux, -1, MPFR_RNDD)
-                mpfr_div(imin, aux, imin, MPFR_RNDD)
-            elif mpfr_cmp(c, a) > 0:
-                mpfr_mul(c2, c, c, MPFR_RNDD)
-                mpfr_mul(a2, a, a, MPFR_RNDD)
-                mpfr_add(div1, a2, c2, MPFR_RNDD)
-                mpfr_div(imin, c, div1, MPFR_RNDU)
-                mpfr_set_si(aux, 0, MPFR_RNDD)
-                mpfr_sub(imin, aux, imin, MPFR_RNDD)
-            else:
-                mpfr_mul(d2, d, d, MPFR_RNDD)
-                mpfr_mul(a2, a, a, MPFR_RNDD)
-                mpfr_add(div1, a2, d2, MPFR_RNDD)
-                mpfr_div(imin, d, div1, MPFR_RNDU)
-                mpfr_set_si(aux, 0, MPFR_RNDD)
-                mpfr_sub(imin, aux, imin, MPFR_RNDD)
-            # Right endpoint
-            if mpfr_cmp(c, a) >=0 and mpfr_cmp(b, c) >= 0:
-                mpfr_add(rmax, c, c, MPFR_RNDD)
-                mpfr_set_si(aux, 1, MPFR_RNDU)
-                mpfr_div(rmax, aux, rmax, MPFR_RNDU)
-            elif mpfr_cmp(a,c) > 0:
-                mpfr_mul(a2, a, a, MPFR_RNDD)
-                mpfr_mul(c2, c, c, MPFR_RNDD)
-                mpfr_add(div1, a2, c2, MPFR_RNDD)
-                mpfr_div(rmax, a, div1, MPFR_RNDU)
-            else:
-                mpfr_mul(b2, b, b, MPFR_RNDD)
-                mpfr_mul(c2, c, c, MPFR_RNDD)
-                mpfr_add(div1, b2, c2, MPFR_RNDD)
-                mpfr_div(rmax, b, div1, MPFR_RNDU)
-        elif mpfr_sgn(c) > 0:
-            # Input interval crosses line between first and second quadrant.
-
-            # Computation follows Rokne-Lancaster.
-
-            # Left endpoint
-            mpfr_abs(aux, a, MPFR_RNDU)
-            if mpfr_cmp(aux, c) >= 0:
-                mpfr_set_str(aux, '-0.5', 10, MPFR_RNDD)
-                mpfr_div(rmin, aux, c, MPFR_RNDD)
-            else:
-                mpfr_mul(a2, a, a, MPFR_RNDD)
-                mpfr_mul(c2, c, c, MPFR_RNDD)
-                mpfr_add(div1, a2, c2, MPFR_RNDD)
-                mpfr_div(rmin, a, div1, MPFR_RNDU)
-            # Lower endpoint
-            mpfr_set_si(aux2, -1, MPFR_RNDD)
-            mpfr_div(imin, aux2, c, MPFR_RNDD)
-            # Right endpoint
-            if mpfr_cmp(b, c) >=0:
-                mpfr_set_str(aux2, '0.5', 10, MPFR_RNDU)
-                mpfr_div(rmax, aux2, c, MPFR_RNDU)
-            else:
-                mpfr_mul(b2, b, b, MPFR_RNDD)
-                mpfr_mul(c2, c, c, MPFR_RNDD)
-                mpfr_add(div1, b2, c2, MPFR_RNDD)
-                mpfr_div(rmax, b, div1, MPFR_RNDU)
-            # Upper endpoint
-            mpfr_mul(a2, a, a, MPFR_RNDU)
-            mpfr_mul(b2, b, b, MPFR_RNDU)
-            mpfr_mul(c2, c, c, MPFR_RNDU)
-            mpfr_mul(d2, d, d, MPFR_RNDU)
-            mpfr_add(div1, a2, c2, MPFR_RNDU)
-            mpfr_div(imax, c, div1, MPFR_RNDD)
-            mpfr_add(div1, b2, c2, MPFR_RNDU)
-            mpfr_div(aux, c, div1, MPFR_RNDD)
-            if mpfr_cmp(imax, aux) > 0:
-                mpfr_set(imax, aux, MPFR_RNDD)
-            mpfr_add(div1, a2, d2, MPFR_RNDU)
-            mpfr_div(aux, d, div1, MPFR_RNDD)
-            if mpfr_cmp(imax, aux) > 0:
-                mpfr_set(imax, aux, MPFR_RNDD)
-            mpfr_add(div1, b2, d2, MPFR_RNDU)
-            mpfr_div(aux, d, div1, MPFR_RNDD)
-            if mpfr_cmp(imax, aux) > 0:
-                mpfr_set(imax, aux, MPFR_RNDD)
-            mpfr_set_zero(aux, -1)
-            mpfr_sub(imax, aux, imax, MPFR_RNDU)
-        else:
-            # The interval must have contained the origin.
+        mpfi_sqr(t0, self.__re)
+        mpfi_sqr(t1, self.__im)
 
-            # Return NaN intervals by doing nothing
-            # (rmin, rmax, imin, imax were initialized as NaN)
-            #
-            # Note that we cannot "return x" here since
-            # that would not call mpfr_clear and produce a memory leak
-            pass
+        mpfi_add(t0, t0, t1)         # now t0 is the norm
+        mpfi_div(x.__re, self.__re, t0)   #     x.__re = self.__re/norm
+
+        mpfi_neg(t1, self.__im)
+        mpfi_div(x.__im, t1, t0)  #     x.__im = -self.__im/norm
 
-        # Negate the real and imaginary part if we did so for the input
-        # interval.
-        # Note that
-        #      Re(1/(b+ai)) = -Im(1/(a+bi))
-        #      Im(1/(b+ai)) = -Re(1/(a+bi))
-        # so we also need to negate (again) both real and imaginary part
-        # again if we flipped them.
-
-        if flipped_real_imag ^ negated_real:
-            mpfr_swap(rmin, rmax)
-            mpfr_neg(rmin, rmin, MPFR_RNDD)
-            mpfr_neg(rmax, rmax, MPFR_RNDU)
-
-        if flipped_real_imag ^ negated_imag:
-            mpfr_swap(imin, imax)
-            mpfr_neg(imin, imin, MPFR_RNDD)
-            mpfr_neg(imax, imax, MPFR_RNDU)
-
-        # Flip real and imaginary part if we did so for the input interval.
-        if flipped_real_imag:
-            mpfr_swap(rmin, imin)
-            mpfr_swap(rmax, imax)
-
-        # Set the intervals.
-        mpfi_interv_fr(x.__re, rmin, rmax)
-        mpfi_interv_fr(x.__im, imin, imax)
-
-        # Free memory
-        mpfr_clear(a)
-        mpfr_clear(b)
-        mpfr_clear(c)
-        mpfr_clear(d)
-        mpfr_clear(imin)
-        mpfr_clear(imax)
-        mpfr_clear(rmin)
-        mpfr_clear(rmax)
-        mpfr_clear(r)
-        mpfr_clear(a2)
-        mpfr_clear(b2)
-        mpfr_clear(c2)
-        mpfr_clear(d2)
-        mpfr_clear(div1)
-        mpfr_clear(div2)
-        mpfr_clear(aux)
-        mpfr_clear(aux2)
+        mpfi_clear(t0)
+        mpfi_clear(t1)
 
         return x
 
@@ -2098,7 +1868,7 @@ cdef class ComplexIntervalFieldElement(FieldElement):
             sage: CIF(1,1).tan()
             0.27175258531952? + 1.08392332733870?*I
             sage: CIF(2).tan()
-            -2.185039863261519?
+            -2.18503986326152?
             sage: CIF(0,2).tan()
             0.964027580075817?*I
         """
@@ -2187,7 +1957,7 @@ cdef class ComplexIntervalFieldElement(FieldElement):
             sage: CIF(2).tanh()
             0.964027580075817?
             sage: CIF(0,2).tanh()
-            -2.185039863261519?*I
+            -2.18503986326152?*I
         """
         return self.sinh() / self.cosh()
 
diff --git a/src/sage/rings/function_field/order.py b/src/sage/rings/function_field/order.py
index 615cbab3690..5b550555f34 100644
--- a/src/sage/rings/function_field/order.py
+++ b/src/sage/rings/function_field/order.py
@@ -162,7 +162,7 @@ def is_field(self, proof=True):
 
     def is_noetherian(self):
         """
-        Return ``True`` since orders in function fields are noetherian.
+        Return ``True`` since orders in function fields are Noetherian.
 
         EXAMPLES::
 
diff --git a/src/sage/rings/ideal.py b/src/sage/rings/ideal.py
index 4dba8ec9d42..f463a7f31d5 100644
--- a/src/sage/rings/ideal.py
+++ b/src/sage/rings/ideal.py
@@ -1077,7 +1077,7 @@ def _mul_(self, other):
         ``self.ngens() * other.ngens()``. So if used repeatedly this method
         will create an ideal with a uselessly large amount of generators.
         Therefore it is advisable to overwrite this method with a method that
-        takes advantage of the structure of the ring your working in.
+        takes advantage of the structure of the ring you are working in.
 
         Example::
 
diff --git a/src/sage/rings/integer_ring.pxd b/src/sage/rings/integer_ring.pxd
index d0af1bc068f..204ccbe141c 100644
--- a/src/sage/rings/integer_ring.pxd
+++ b/src/sage/rings/integer_ring.pxd
@@ -1,7 +1,7 @@
-from sage.rings.ring cimport PrincipalIdealDomain
+from sage.rings.ring cimport CommutativeRing
 from sage.rings.integer cimport Integer
 from sage.libs.gmp.types cimport mpz_t
 
-cdef class IntegerRing_class(PrincipalIdealDomain):
+cdef class IntegerRing_class(CommutativeRing):
     cdef int _randomize_mpz(self, mpz_t value, x, y, distribution) except -1
     cdef object _zero
diff --git a/src/sage/rings/integer_ring.pyx b/src/sage/rings/integer_ring.pyx
index ab445056cc7..315b4c5f529 100644
--- a/src/sage/rings/integer_ring.pyx
+++ b/src/sage/rings/integer_ring.pyx
@@ -58,6 +58,7 @@ import sage.libs.pari.all
 import sage.rings.ideal
 from sage.categories.basic import EuclideanDomains, DedekindDomains
 from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets
+from sage.categories.noetherian_rings import NoetherianRings
 from sage.rings.number_field.number_field_element_base import NumberFieldElement_base
 from sage.structure.coerce cimport is_numpy_type
 from sage.structure.element cimport parent
@@ -106,7 +107,7 @@ def is_IntegerRing(x):
     """
     return isinstance(x, IntegerRing_class)
 
-cdef class IntegerRing_class(PrincipalIdealDomain):
+cdef class IntegerRing_class(CommutativeRing):
     r"""
     The ring of integers.
 
@@ -124,6 +125,7 @@ cdef class IntegerRing_class(PrincipalIdealDomain):
         sage: Z.category()
         Join of Category of Dedekind domains
             and Category of euclidean domains
+            and Category of noetherian rings
             and Category of infinite enumerated sets
             and Category of metric spaces
         sage: Z(2^(2^5) + 1)
@@ -312,8 +314,10 @@ cdef class IntegerRing_class(PrincipalIdealDomain):
             sage: A in InfiniteEnumeratedSets()
             True
         """
+        cat = (EuclideanDomains(), DedekindDomains(),
+               InfiniteEnumeratedSets().Metric(), NoetherianRings())
         Parent.__init__(self, base=self, names=('x',), normalize=False,
-                        category=(EuclideanDomains(), DedekindDomains(), InfiniteEnumeratedSets().Metric()))
+                        category=cat)
         self._populate_coercion_lists_(init_no_parent=True,
                                        convert_method_name='_integer_')
 
@@ -422,7 +426,7 @@ cdef class IntegerRing_class(PrincipalIdealDomain):
             K, _ = parent(x).subfield(x)
             return K.order(K.gen())
 
-        return PrincipalIdealDomain.__getitem__(self, x)
+        return CommutativeRing.__getitem__(self, x)
 
     def range(self, start, end=None, step=None):
         """
diff --git a/src/sage/rings/laurent_series_ring.py b/src/sage/rings/laurent_series_ring.py
index 4e0c6f0d557..b8bd1cf062f 100644
--- a/src/sage/rings/laurent_series_ring.py
+++ b/src/sage/rings/laurent_series_ring.py
@@ -155,7 +155,7 @@ class LaurentSeriesRing(UniqueRepresentation, CommutativeRing):
         sage: LaurentSeriesRing(ZZ, 'x').category()
         Category of infinite commutative no zero divisors algebras
          over (Dedekind domains and euclidean domains
-         and infinite enumerated sets and metric spaces)
+         and noetherian rings and infinite enumerated sets and metric spaces)
         sage: LaurentSeriesRing(QQ, 'x').category()
         Join of Category of complete discrete valuation fields and Category of commutative algebras
          over (number fields and quotient fields and metric spaces) and Category of infinite sets
@@ -227,7 +227,8 @@ def __init__(self, power_series):
             sage: RZZ.category()
             Category of infinite commutative no zero divisors algebras
              over (Dedekind domains and euclidean domains
-             and infinite enumerated sets and metric spaces)
+             and noetherian rings and infinite enumerated sets
+             and metric spaces)
             sage: TestSuite(RZZ).run()
 
             sage: R1 = LaurentSeriesRing(Zmod(1), 't')
diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py
index 06fd7a5d595..6f96ffc839d 100644
--- a/src/sage/rings/lazy_series_ring.py
+++ b/src/sage/rings/lazy_series_ring.py
@@ -1475,6 +1475,7 @@ def __init__(self, base_ring, names, sparse=True, category=None):
             sage: L.category()
             Category of infinite commutative no zero divisors algebras over
              (Dedekind domains and euclidean domains
+              and noetherian rings
               and infinite enumerated sets and metric spaces)
 
             sage: L = LazyLaurentSeriesRing(QQ, 't')
@@ -1490,6 +1491,7 @@ def __init__(self, base_ring, names, sparse=True, category=None):
             Category of infinite commutative no zero divisors algebras over
              (unique factorization domains and commutative algebras over
               (Dedekind domains and euclidean domains
+              and noetherian rings
               and infinite enumerated sets and metric spaces)
               and infinite sets)
 
diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py
index 066459c65a2..349a8ec17dc 100644
--- a/src/sage/rings/padics/local_generic.py
+++ b/src/sage/rings/padics/local_generic.py
@@ -21,13 +21,14 @@
 # *****************************************************************************
 
 from copy import copy
-from sage.rings.ring import CommutativeRing
+
 from sage.categories.complete_discrete_valuation import CompleteDiscreteValuationRings, CompleteDiscreteValuationFields
 from sage.structure.category_object import check_default_category
-from sage.structure.parent import Parent
 from sage.rings.integer import Integer
 from sage.rings.integer_ring import ZZ
 from sage.rings.infinity import Infinity
+from sage.rings.ring import CommutativeRing
+
 
 class LocalGeneric(CommutativeRing):
     def __init__(self, base, prec, names, element_class, category=None):
@@ -73,7 +74,8 @@ def __init__(self, base, prec, names, element_class, category=None):
         category = category.Metric().Complete().Infinite()
         if default_category is not None:
             category = check_default_category(default_category, category)
-        Parent.__init__(self, base, names=(names,), normalize=False, category=category)
+        CommutativeRing.__init__(self, base, names=(names,),
+                                 normalize=False, category=category)
 
     def is_capped_relative(self):
         r"""
@@ -451,7 +453,7 @@ def get_unramified_modulus(q, res_name):
                 kwds['type'] = 'capped-rel'
             elif self._prec_type() == 'fixed-mod':
                 kwds['type'] = 'floating-point'
-                kwds['show_prec'] = False # This can be removed once printing of fixed mod elements is changed.
+                kwds['show_prec'] = False  # This can be removed once printing of fixed mod elements is changed.
 
         # There are two kinds of functors possible:
         # CompletionFunctor and AlgebraicExtensionFunctor
@@ -488,7 +490,7 @@ def get_unramified_modulus(q, res_name):
             # Labels for lattice precision
             if 'label' in kwds:
                 functor.extras['label'] = kwds.pop('label')
-            elif 'label' in functor.extras and functor.type not in ['lattice-cap','lattice-float']:
+            elif 'label' in functor.extras and functor.type not in ['lattice-cap', 'lattice-float']:
                 del functor.extras['label']
             for atr in ('ram_name', 'var_name'):
                 if atr in kwds:
@@ -659,9 +661,9 @@ def defining_polynomial(self, var='x', exact=False):
         from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
         if exact:
             from sage.rings.integer_ring import ZZ
-            return PolynomialRing(ZZ,var).gen()
+            return PolynomialRing(ZZ, var).gen()
         else:
-            return PolynomialRing(self,var).gen()
+            return PolynomialRing(self, var).gen()
 
     def ground_ring(self):
         r"""
@@ -1117,7 +1119,7 @@ def _test_add_bigoh(self, **options):
                 tester.assertLessEqual(y.precision_absolute(), -1)
 
             # make sure that we handle very large values correctly
-            if self._prec_type() not in [ 'lattice-float', 'relaxed' ]:   # no cap in these models
+            if self._prec_type() not in ['lattice-float', 'relaxed']:  # no cap in these models
                 absprec = Integer(2)**1000
                 tester.assertEqual(x.add_bigoh(absprec), x)
 
@@ -1194,15 +1196,15 @@ def _matrix_flatten_precision(self, M):
         cap = parent.precision_cap()
         n = M.nrows()
         m = M.ncols()
-        shift_rows = n * [ ZZ(0) ]
-        shift_cols = m * [ ZZ(0) ]
+        shift_rows = n * [ZZ.zero()]
+        shift_cols = m * [ZZ.zero()]
         for i in range(n):
-            prec = min(M[i,j].precision_absolute() for j in range(m))
+            prec = min(M[i, j].precision_absolute() for j in range(m))
             if prec is Infinity or prec == cap:
                 continue
             shift_rows[i] = s = cap - prec
             for j in range(m):
-                M[i,j] <<= s
+                M[i, j] <<= s
         for j in range(m):
             prec = min(M[i,j].precision_absolute() for i in range(n))
             if prec is Infinity or prec == cap:
diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py
index 6452d77db22..60eb71e9e7c 100644
--- a/src/sage/rings/padics/padic_generic.py
+++ b/src/sage/rings/padics/padic_generic.py
@@ -33,7 +33,6 @@
 from sage.categories.fields import Fields
 from sage.rings.infinity import infinity
 from .local_generic import LocalGeneric
-from sage.rings.ring import PrincipalIdealDomain
 from sage.rings.integer import Integer
 from sage.rings.infinity import Infinity
 from sage.rings.padics.precision_error import PrecisionError
@@ -41,7 +40,7 @@
 from sage.structure.richcmp import richcmp_not_equal
 
 
-class pAdicGeneric(PrincipalIdealDomain, LocalGeneric):
+class pAdicGeneric(LocalGeneric):
     def __init__(self, base, p, prec, print_mode, names, element_class, category=None):
         r"""
         Initialize ``self``.
@@ -68,7 +67,7 @@ def __init__(self, base, p, prec, print_mode, names, element_class, category=Non
             category = category.Metric().Complete()
         LocalGeneric.__init__(self, base, prec, names, element_class, category)
         self._printer = pAdicPrinter(self, print_mode)
-        self._qth_roots_of_unity = [ (1, Infinity) ]
+        self._qth_roots_of_unity = [(1, Infinity)]
 
     def some_elements(self):
         r"""
diff --git a/src/sage/rings/polynomial/binary_form_reduce.py b/src/sage/rings/polynomial/binary_form_reduce.py
index bb6b4296d1a..2e8003c7ce8 100644
--- a/src/sage/rings/polynomial/binary_form_reduce.py
+++ b/src/sage/rings/polynomial/binary_form_reduce.py
@@ -78,8 +78,8 @@ def covariant_z0(F, z0_cov=False, prec=53, emb=None, error_limit=0.000001):
         sage: F = -x^8 + 6*x^7*y - 7*x^6*y^2 - 12*x^5*y^3 + 27*x^4*y^4\
         ....: - 4*x^3*y^5 - 19*x^2*y^6 + 10*x*y^7 - 5*y^8
         sage: covariant_z0(F, prec=80)
-        (0.64189877107807122203366 + 1.1852516565091601348355*I,
-         3134.5148284344627168276)
+        (0.64189877107807122203369 + 1.1852516565091601348355*I,
+         3134.5148284344627168275)
 
     ::
 
diff --git a/src/sage/rings/polynomial/infinite_polynomial_ring.py b/src/sage/rings/polynomial/infinite_polynomial_ring.py
index dbb71289f89..e962e1f1a2e 100644
--- a/src/sage/rings/polynomial/infinite_polynomial_ring.py
+++ b/src/sage/rings/polynomial/infinite_polynomial_ring.py
@@ -1098,11 +1098,11 @@ def is_noetherian(self):
 
         Since Infinite Polynomial Rings must have at least one
         generator, they have infinitely many variables and are thus
-        not noetherian, as a ring.
+        not Noetherian, as a ring.
 
         .. NOTE::
 
-            Infinite Polynomial Rings over a field `F` are noetherian as
+            Infinite Polynomial Rings over a field `F` are Noetherian as
             `F(G)` modules, where `G` is the symmetric group of the
             natural numbers. But this is not what the method
             ``is_noetherian()`` is answering.
diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx
index 37247f36571..3d58de17015 100644
--- a/src/sage/rings/polynomial/multi_polynomial.pyx
+++ b/src/sage/rings/polynomial/multi_polynomial.pyx
@@ -2140,6 +2140,10 @@ cdef class MPolynomial(CommutativePolynomial):
             sage: Pol = QQ['x']['x','y']
             sage: Pol.one().gcd(1)
             1
+
+            sage: P = PolynomialRing(QQ, 'x', 0)
+            sage: P.gens()
+            ()
         """
         flatten = self._parent.flattening_morphism()
         tgt = flatten.codomain()
@@ -2154,7 +2158,13 @@ cdef class MPolynomial(CommutativePolynomial):
         except (TypeError, AttributeError):
             pass
 
-        x = self._parent.gens()[-1]
+        gens = self.parent().gens()
+        if not gens:
+            # no variables
+            base = self.parent().base_ring()
+            return base(self).gcd(base(other))
+
+        x = gens[-1]
         uniself = self.polynomial(x)
         unibase = uniself.base_ring()
         try:
diff --git a/src/sage/rings/polynomial/pbori/blocks.py b/src/sage/rings/polynomial/pbori/blocks.py
index ba6b9c7b493..8280fd7db81 100644
--- a/src/sage/rings/polynomial/pbori/blocks.py
+++ b/src/sage/rings/polynomial/pbori/blocks.py
@@ -5,7 +5,7 @@
 from .PyPolyBoRi import (Ring, Polynomial, VariableFactory, Variable)
 
 
-class Block():
+class Block:
     r"""
     The block class represents a block of variables
     (start_index,...,start_index+size-1), it is the preferred
@@ -37,12 +37,12 @@ def register(self, start, context):
         ring = ring_context['r']
 
         var_func = VariableBlock(self.size, self.start_index, start, self.
-            reverse, ring)
+                                 reverse, ring)
         var_func.__name__ = self.var_name
         context[self.var_name] = var_func
 
 
-class AlternatingBlock():
+class AlternatingBlock:
     r"""
     The Alternating Block class is used for doing tricky variable
     schemes,where base names vary, e.g.
@@ -74,7 +74,7 @@ def __getitem__(self, i):
     def register(self, start, context):
         def gen_var_func(var_pos):
 
-            class var_factory():
+            class var_factory:
                 def __init__(self, ring, index2pos, size):
                     self.ring = ring
                     self.index2pos = index2pos
@@ -82,7 +82,7 @@ def __init__(self, ring, index2pos, size):
 
                 def __call__(self, idx):
                     return self.ring.variable(self.index2pos[idx] * self.size +
-                                        var_pos + start)
+                                              var_pos + start)
             ring_context = context
             while isinstance(ring_context, PrefixedDictProxy):
                 ring_context = ring_context.wrapped
@@ -107,7 +107,8 @@ class AdderBlock(AlternatingBlock):
     def __init__(self, adder_bits, sums="s", carries="c", input1="a",
                  input2="b", start_index=0):
         AlternatingBlock.__init__(self, (sums, carries, input1, input2),
-            adder_bits, start_index=start_index, reverse=True)
+                                  adder_bits, start_index=start_index,
+                                  reverse=True)
         self.input1 = input1
         self.input2 = input2
         self.sums = sums
@@ -124,13 +125,14 @@ def register(self, start, context):
         a = shift(a, self.start_index)
         b = shift(b, self.start_index)
         carries = [Polynomial(a(0).ring().zero())]
+        last = carries[0]
         for i in range(self.adder_bits):
-            c = 1 + (1 + a(i) * b(i)) * (1 + carries[-1] * a(i)) * (1 +
-                carries[-1] * b(i))
+            c = 1 + (1 + a(i) * b(i)) * (1 + last * a(i)) * (1 + last * b(i))
             carries.append(c)
+            last = c
 
-        self.add_results = [a(i) + b(i) + carries[i] for i in range(self.
-            adder_bits)]
+        self.add_results = [a(i) + b(i) + carries[i]
+                            for i in range(self.adder_bits)]
         self.carries_polys = carries[1:]
 
     # def s(i):
@@ -146,7 +148,7 @@ def implement(self, equations):
             equations.append(self.c(i) + self.carries_polys[i])
 
 
-class HigherOrderBlock():
+class HigherOrderBlock:
     r"""
     HigherOrderBlocks are multidimensional blocks of variables.
 
@@ -192,14 +194,14 @@ def var_func(*indices):
         context[self.var_name] = var_func
 
 
-class InOutBlock():
+class InOutBlock:
     def __init__(self, out_size, in_size, output="out", input="in",
                  in_start_index=0, out_start_index=0,
                  out_reverse=False, in_reverse=False):
         self.output = Block(var_name=output, start_index=out_start_index,
-                        size=out_size, reverse=out_reverse)
+                            size=out_size, reverse=out_reverse)
         self.input = Block(var_name=input, start_index=in_start_index,
-                       size=in_size, reverse=in_reverse)
+                           size=in_size, reverse=in_reverse)
         self.out_start_index = out_start_index
 
         self.in_start_index = in_start_index
@@ -219,11 +221,11 @@ def register(self, start, context):
         self.output.register(start, context)
         self.input.register(start + len(self.output), context)
         self.out_vars = shift(context[self.output.var_name], self.
-            out_start_index)
+                              out_start_index)
         self.in_vars = shift(context[self.input.var_name], self.in_start_index)
 
 
-class MultiBlock():
+class MultiBlock:
     def __init__(self, sizes=None, var_names=["v"],
                  start_indices=[], reverses=None):
         if reverses is None:
@@ -236,8 +238,9 @@ def __init__(self, sizes=None, var_names=["v"],
         sizes += [1] * (len(var_names) - len(sizes))
 
         self.blocks = [Block(var_name=var_names[idx], size=sizes[idx],
-            start_index=self.start_indices[idx], reverse=reverses[idx]) for
-            idx in range(len(var_names))]
+                             start_index=self.start_indices[idx],
+                             reverse=reverses[idx])
+                       for idx in range(len(var_names))]
 
     def __iter__(self):
         return chain(*self.blocks)
@@ -247,7 +250,7 @@ def __getitem__(self, i):
         # sum([bl.names for bl in self.blocks])[i]
 
     def __len__(self):
-        return sum((len(bl) for bl in self.blocks))
+        return sum(len(bl) for bl in self.blocks)
 
     def register(self, start, context):
         offset = 0
@@ -255,11 +258,12 @@ def register(self, start, context):
             bl.register(start + offset, context)
             offset += len(bl)
 
-        self.vars = [shift(context[self.blocks[idx].var_name], self.
-            start_indices[idx]) for idx in range(len(self.blocks))]
+        self.vars = [shift(context[self.blocks[idx].var_name],
+                           self.start_indices[idx])
+                     for idx in range(len(self.blocks))]
 
 
-class PrefixedDictProxy():
+class PrefixedDictProxy:
     """docstring for PrefixedDictProxy"""
 
     def __init__(self, wrapped, prefix):
@@ -278,7 +282,7 @@ def __setitem__(self, k, v):
         self.wrapped[self.prefix + k] = v
 
 
-class MacroBlock():
+class MacroBlock:
     def __init__(self, prefix):
 
         self.prefix = prefix
@@ -299,7 +303,7 @@ def __getitem__(self, i):
         return self.prefix + "_" + next(islice(chain(*self.blocks), i, i + 1))
 
     def __len__(self):
-        return sum((len(bl) for bl in self.blocks))
+        return sum(len(bl) for bl in self.blocks)
 
     def resolve(self, localname):
         return self.prefix + "_" + localname
@@ -324,7 +328,7 @@ def implement(self, equations):
         equations += self.connections
 
 
-class IfThen():
+class IfThen:
     def __init__(self, ifpart, thenpart, supposed_to_be_valid=True):
         self.ifpart = [Polynomial(p) for p in ifpart]
         self.thenpart = [Polynomial(p) for p in thenpart]
@@ -369,10 +373,7 @@ def canonicalize(blocks):
     n = 0
 
     for b in blocks:
-        if isinstance(b, str):
-            n = n + 1
-        else:
-            n = n + len(b)
+        n = n + 1 if isinstance(b, str) else n + len(b)
 
     r = Ring(n, names=canonicalize(blocks))
 
diff --git a/src/sage/rings/polynomial/pbori/cnf.py b/src/sage/rings/polynomial/pbori/cnf.py
index 266a6f0f8a8..61d85866013 100644
--- a/src/sage/rings/polynomial/pbori/cnf.py
+++ b/src/sage/rings/polynomial/pbori/cnf.py
@@ -4,7 +4,7 @@
 from .statistics import used_vars_set
 
 
-class CNFEncoder():
+class CNFEncoder:
     def __init__(self, r, random_seed=16):
         self.random_generator = Random(random_seed)
         self.one_set = r.one().set()
@@ -174,7 +174,7 @@ def dimacs_cnf(self, polynomial_system):
         res = ["c cnf generated by PolyBoRi"]
         r = polynomial_system[0].ring()
         n_variables = r.n_variables()
-        res.append("p cnf %s %s" % (n_variables, len(clauses_list)))
+        res.append(f"p cnf {n_variables} {len(clauses_list)}")
         res.extend(clauses_list)
         return "\n".join(res)
 
@@ -213,7 +213,7 @@ def dimacs_encode_polynomial(self, p):
             indices.append(0)
             res = ["x" + " ".join(str(v) for v in indices)]
         self.group_counter = self.group_counter + 1
-        group_comment = "\nc g %s %s" % (self.group_counter, str(p)[:30])
+        group_comment = f"\nc g {self.group_counter} {str(p)[:30]}"
         return [c + group_comment for c in res]
 
     def dimacs_cnf(self, polynomial_system):
@@ -234,6 +234,6 @@ def dimacs_cnf(self, polynomial_system):
         """
         uv = list(used_vars_set(polynomial_system).variables())
         res = super().dimacs_cnf(polynomial_system)
-        res += "\n" + "\n".join("c v %s %s" % (self.to_dimacs_index(v), v)
+        res += "\n" + "\n".join(f"c v {self.to_dimacs_index(v)} {v}"
                                 for v in uv)
         return res
diff --git a/src/sage/rings/polynomial/pbori/gbcore.py b/src/sage/rings/polynomial/pbori/gbcore.py
index 3eb6a73ff25..b5bc0a56192 100644
--- a/src/sage/rings/polynomial/pbori/gbcore.py
+++ b/src/sage/rings/polynomial/pbori/gbcore.py
@@ -1,3 +1,4 @@
+import contextlib
 from copy import copy
 from itertools import chain
 from inspect import getfullargspec as getargspec
@@ -32,7 +33,7 @@ def filter_oldstyle_options(**options):
 def filter_newstyle_options(func, **options):
     allowed = get_options_from_function(func).keys()
     filtered = {}
-    for key in options.keys():
+    for key in options:
         for prefix in ['', 'use_', 'opt_', 'opt_allow_']:
             if prefix + key in allowed:
                 filtered[prefix + key] = options[key]
@@ -80,7 +81,7 @@ def change_order_heuristic(d):
     if not I:
         return d
     switch_table = {OrderCode.lp: OrderCode.dp_asc, OrderCode.dlex: OrderCode.
-        dp_asc}
+                    dp_asc}
     if "other_ordering_first" not in d:
         # TODO after ll situation might look much different, so heuristic is on
         # wrong place
@@ -152,14 +153,12 @@ def trivial_heuristic(d):
     return d
 
 
-class HeuristicalFunction():
+class HeuristicalFunction:
     def __call__(self, *args, **kwds):
         complete_dict = copy(kwds)
         heuristic = True
-        try:
+        with contextlib.suppress(KeyError):
             heuristic = complete_dict["heuristic"]
-        except KeyError:
-            pass
         for (k, v) in zip(self.argnames, args):
             complete_dict[k] = v
         if heuristic:
@@ -172,7 +171,7 @@ def __init__(self, f, heuristic_function):
             self.options = f.options
         else:
             self.options = dict(zip(self.argnames[-len(self.defaults):], self.
-                defaults))
+                                    defaults))
         self.heuristicFunction = heuristic_function
         self.f = f
         self.__doc__ = f.__doc__
@@ -188,42 +187,40 @@ def make_wrapper(f):
 
 def clean_polys_pre(I):
     wrap = (Polynomial(p) for p in I)
-    return (list(set(p for p in wrap if not p.is_zero())), None)
+    return (list({p for p in wrap if not p.is_zero()}), None)
 
 
 def gb_with_pre_post_option(option, pre=None,
-                            post=None, if_not_option=tuple(),
+                            post=None, if_not_option=(),
                             default=False):
     def make_wrapper(f):
         def wrapper(I, **kwds):
             prot = kwds.get("prot", False)
             for o in if_not_option:
                 if (o in kwds and kwds[o]) or (o not in kwds and
-                        groebner_basis.options[o]):
+                                               groebner_basis.options[o]):
                     option_set = False
             if "option_set" not in locals():
                 option_set = kwds.get(option, default)
-            kwds = dict(((o, kwds[o]) for o in kwds if o != option))
+            kwds = {o: kwds[o] for o in kwds if o != option}
             state = None
 
-            if option_set:
-                if pre:
-                    pre_args = getargspec(pre)[0]
-                    if prot:
-                        print("preprocessing for option:", option)
+            if option_set and pre:
+                pre_args = getargspec(pre)[0]
+                if prot:
+                    print("preprocessing for option:", option)
 
-                    local_symbols = copy(locals())
-                    (I, state) = pre(**{k: v for (k, v) in local_symbols.items()
-                                        if k in pre_args})
+                local_symbols = copy(locals())
+                (I, state) = pre(**{k: v for (k, v) in local_symbols.items()
+                                    if k in pre_args})
             I = f(I, **kwds)
-            if option_set:
-                if post:
-                    post_args = getargspec(post)[0]
-                    if prot:
-                        print("postprocessing for option:", option)
-                    local_symbols = copy(locals())
-                    I = post(**{k: v for (k, v) in local_symbols.items()
-                                if k in post_args})
+            if option_set and post:
+                post_args = getargspec(post)[0]
+                if prot:
+                    print("postprocessing for option:", option)
+                local_symbols = copy(locals())
+                I = post(**{k: v for (k, v) in local_symbols.items()
+                            if k in post_args})
 
             return I
         wrapper.__name__ = f.__name__
@@ -271,15 +268,15 @@ def llfirst_pre(I, prot):
 def ll_constants_pre(I):
     ll_res = []
 
-    while len([p for p in I if p.lex_lead_deg() == 1 and
-    (p + p.lex_lead()).constant()]) > 0:
+    while any(p.lex_lead_deg() == 1 and (p + p.lex_lead()).constant()
+              for p in I):
         I_new = []
         ll = []
         leads = set()
         for p in I:
             if p.lex_lead_deg() == 1:
                 l = p.lead()
-                if not (l in leads) and p.is_singleton_or_pair():
+                if l not in leads and p.is_singleton_or_pair():
                     tail = p + l
                     if tail.deg() <= 0:
                         ll.append(p)
@@ -289,9 +286,9 @@ def ll_constants_pre(I):
         encoded = ll_encode(ll)
         reduced = []
         for p in I_new:
-            p = ll_red_nf_redsb(p, encoded)
-            if not p.is_zero():
-                reduced.append(p)
+            rp = ll_red_nf_redsb(p, encoded)
+            if not rp.is_zero():
+                reduced.append(rp)
         I = reduced
         ll_res.extend(ll)
     return (I, ll_res)
@@ -418,12 +415,12 @@ def llfirst_post(I, state, prot, kwds):
         # redsb just for safety, as don't know how option is set
         kwds = copy(kwds)
         kwds.update(
-            dict(llfirst=False,
-            llfirstonthefly=False,
-            ll_constants=False,
-            deg_bound=False,
-            other_ordering_first=False,
-            eliminate_identical_variables=False, redsb=True))
+            {'llfirst': False,
+             'llfirstonthefly': False,
+             'll_constants': False,
+             'deg_bound': False,
+             'other_ordering_first': False,
+             'eliminate_identical_variables': False, 'redsb': True})
         I = groebner_basis(I, **kwds)
     return I
 
@@ -489,56 +486,63 @@ def eliminate_identical_variables_pre(I, prot):
         def my_sort_key(l):
             return l.navigation().value()
 
-        for (t, leads) in rules.items():
+        for t, leads in rules.items():
             if len(leads) > 1:
                 changed = True
-                leads = sorted(leads, key=my_sort_key, reverse=True)
-                chosen = leads[0]
-                ll_system.extend(chosen + v for v in leads[1:])
+                sleads = sorted(leads, key=my_sort_key, reverse=True)
+                chosen = sleads[0]
+                ll_system.extend(chosen + v for v in sleads[1:])
     if ll_system:
         ll_encoded = ll_encode(ll_system, reduce=True)
-        I = set(ll_red_nf_redsb(p, ll_encoded) for p in I)
+        I = {ll_red_nf_redsb(p, ll_encoded) for p in I}
     return (I, ll_system)
 
 
 @gb_with_pre_post_option("clean_arguments", pre=clean_polys_pre, default=True)
 @gb_with_pre_post_option("easy_linear_polynomials",
-    pre=easy_linear_polynomials_pre, default=True)
+                         pre=easy_linear_polynomials_pre, default=True)
 @gb_with_pre_post_option("result_to_list", post=result_to_list_post,
-    default=True)
+                         default=True)
 @with_heuristic(interpolation_gb_heuristic)
 @gb_with_pre_post_option("invert", pre=invert_all_pre,
-    post=invert_all_post, default=False)
+                         post=invert_all_post, default=False)
 @gb_with_pre_post_option("gauss_on_linear", pre=gauss_on_linear_pre,
-    default=True)
+                         default=True)
 @gb_with_pre_post_option("ll_constants", pre=ll_constants_pre,
-    post=ll_constants_post, default=True)
+                         post=ll_constants_post, default=True)
 @gb_with_pre_post_option("eliminate_identical_variables",
-    pre=eliminate_identical_variables_pre, post=llfirst_post, default=True)
+                         pre=eliminate_identical_variables_pre,
+                         post=llfirst_post, default=True)
 @with_heuristic(ll_heuristic)
 @gb_with_pre_post_option("llfirst", if_not_option=["llfirstonthefly"],
-    pre=llfirst_pre, post=llfirst_post, default=False)
+                         pre=llfirst_pre, post=llfirst_post, default=False)
 @gb_with_pre_post_option("llfirstonthefly", pre=llfirstonthefly_pre,
-    post=llfirst_post, default=False)
+                         post=llfirst_post, default=False)
 @gb_with_pre_post_option("incremental", pre=incremental_pre)
 @with_heuristic(change_order_heuristic)
 @gb_with_pre_post_option("other_ordering_first", if_not_option=[
     "interpolation_gb"], pre=other_ordering_pre, default=False)
 @with_heuristic(linear_algebra_heuristic)
 @gb_with_pre_post_option("fix_deg_bound", if_not_option=["interpolation_gb"],
-    post=fix_deg_bound_post, default=True)
-@gb_with_pre_post_option("minsb", post=minsb_post, if_not_option=["redsb",
-    "deg_bound", "interpolation_gb", "convert_with_fglm_from_ring"],
-    default=True)
-@gb_with_pre_post_option("redsb", post=redsb_post, if_not_option=["deg_bound",
-    "interpolation_gb", "convert_with_fglm_from_ring"], default=True)
+                         post=fix_deg_bound_post, default=True)
+@gb_with_pre_post_option("minsb", post=minsb_post,
+                         if_not_option=["redsb", "deg_bound",
+                                        "interpolation_gb",
+                                        "convert_with_fglm_from_ring"],
+                         default=True)
+@gb_with_pre_post_option("redsb", post=redsb_post,
+                         if_not_option=["deg_bound", "interpolation_gb",
+                                        "convert_with_fglm_from_ring"],
+                         default=True)
 def groebner_basis(I, heuristic=True, unique_ideal_generator=False,
-        interpolation_gb=False, clean_and_restart_algorithm=False,
-        convert_with_fglm_from_ring=None, convert_with_fglm_to_ring=None,
-        fglm_bound=40000,
-        modified_linear_algebra=True, preprocessor=None, deg_bound=False,
-        implementation="Python", full_prot=False, prot=False,
-        draw_matrices=False, preprocess_only=False, **impl_options):
+                   interpolation_gb=False, clean_and_restart_algorithm=False,
+                   convert_with_fglm_from_ring=None,
+                   convert_with_fglm_to_ring=None,
+                   fglm_bound=40000,
+                   modified_linear_algebra=True, preprocessor=None,
+                   deg_bound=False,
+                   implementation="Python", full_prot=False, prot=False,
+                   draw_matrices=False, preprocess_only=False, **impl_options):
     """Computes a Groebner basis of a given ideal I, w.r.t options."""
 
     if not I:
@@ -567,10 +571,7 @@ def groebner_basis(I, heuristic=True, unique_ideal_generator=False,
             prod = (p + 1) * prod
         I = [prod + 1]
 
-    if implementation == "Python":
-        implementation = symmGB_F2_python
-    else:
-        implementation = symmGB_F2_C
+    implementation = symmGB_F2_python if implementation == 'Python' else symmGB_F2_C
 
     # custom preprocessing
     if preprocessor:
@@ -584,11 +585,13 @@ def groebner_basis(I, heuristic=True, unique_ideal_generator=False,
 
     def call_algorithm(I, max_generators=None):
         return implementation(I,
-            deg_bound=deg_bound,
-            full_prot=full_prot,
-            prot=prot,
-            max_generators=max_generators, draw_matrices=draw_matrices,
-            **filter_newstyle_options(implementation, **impl_options))
+                              deg_bound=deg_bound,
+                              full_prot=full_prot,
+                              prot=prot,
+                              max_generators=max_generators,
+                              draw_matrices=draw_matrices,
+                              **filter_newstyle_options(implementation,
+                                                        **impl_options))
 
     if clean_and_restart_algorithm:
         for max_generators in [1000, 10000, 50000, 100000, 200000, 300000,
@@ -606,31 +609,26 @@ def call_algorithm(I, max_generators=None):
 
 
 def build_groebner_basis_doc_string():
-    additional_options_from_buchberger = filter_oldstyle_options(**
-        get_options_from_function(symmGB_F2_python))
+    additional_options_from_buchberger = filter_oldstyle_options(
+        **get_options_from_function(symmGB_F2_python))
     for k in list(additional_options_from_buchberger):
         if k in groebner_basis.options:
             del additional_options_from_buchberger[k]
 
-    groebner_basis.__doc__ = (groebner_basis.__doc__ + "\nOptions are:\n" +
-        "\n".join((k + "  :  " + repr(groebner_basis.options[k]) for k in
-        groebner_basis.options)) + """
+    gdoc = groebner_basis.__doc__
+    gdoc += "\nOptions are:\n"
+    gdoc += "\n".join(k + "  :  " + repr(groebner_basis.options[k])
+                      for k in groebner_basis.options)
+    gdoc += """
 
 Turn off heuristic by setting heuristic=False
   Additional options come from the actual buchberger implementation.
   In case of our standard Python implementation these are the following:
 
-""" + "\n".join((k + "  :  " + repr(additional_options_from_buchberger[k])
-                 for k in additional_options_from_buchberger)))
+"""
+    gdoc += "\n".join(k + "  :  " + repr(additional_options_from_buchberger[k])
+                      for k in additional_options_from_buchberger)
+    groebner_basis.__doc__ = gdoc
 
 
 build_groebner_basis_doc_string()
-
-
-def _test():
-    import doctest
-    doctest.testmod()
-
-
-if __name__ == "__main__":
-    _test()
diff --git a/src/sage/rings/polynomial/pbori/gbrefs.py b/src/sage/rings/polynomial/pbori/gbrefs.py
index 70dc795cbab..0f5436c5b5f 100644
--- a/src/sage/rings/polynomial/pbori/gbrefs.py
+++ b/src/sage/rings/polynomial/pbori/gbrefs.py
@@ -74,7 +74,8 @@ def load_ref_gz_uu(s, o, b):
 
 
 def convert_refs(ref_file_orig):
-    content = open(ref_file_orig).read()
+    with open(ref_file_orig) as file:
+        content = file.read()
     buf_out = StringIO()
     zipped = gzip.GzipFile(filename=ref_file_orig, mode="w", fileobj=buf_out)
     zipped.write(content)
diff --git a/src/sage/rings/polynomial/pbori/interpolate.py b/src/sage/rings/polynomial/pbori/interpolate.py
index 68f25d57bb2..783e1209e2a 100644
--- a/src/sage/rings/polynomial/pbori/interpolate.py
+++ b/src/sage/rings/polynomial/pbori/interpolate.py
@@ -89,7 +89,7 @@ def lex_groebner_basis_points(points, variables):
 def lex_groebner_basis_for_polynomial_via_variety(p):
     variables = p.vars_as_monomial()
     return lex_groebner_basis_points(p.zeros_in(variables.divisors()),
-        variables)
+                                     variables)
 
 
 if __name__ == '__main__':
diff --git a/src/sage/rings/polynomial/pbori/interred.py b/src/sage/rings/polynomial/pbori/interred.py
index fe59be30896..9ecb705057f 100644
--- a/src/sage/rings/polynomial/pbori/interred.py
+++ b/src/sage/rings/polynomial/pbori/interred.py
@@ -13,7 +13,7 @@ def interred(l, completely=False):
     If completely is set to ``True``, then also terms in the
     tail are not reducible by other polynomials.
     """
-    l = [Polynomial(p) for p in l if not p == 0]
+    l = [Polynomial(p) for p in l if p != 0]
     if not l:
         return []
     ring = l[0].ring()
@@ -26,8 +26,8 @@ def interred(l, completely=False):
         if completely:
             g.opt_red_tail = True
         for p in l:
-            p = g.nf(p)
-            if not p.is_zero():
-                g.add_generator(p)
+            gp = g.nf(p)
+            if not gp.is_zero():
+                g.add_generator(gp)
         l = tuple(e.p for e in g)
-    return list(l)
+    return l
diff --git a/src/sage/rings/polynomial/pbori/ll.py b/src/sage/rings/polynomial/pbori/ll.py
index 71a01bd3b50..5292050d6a7 100644
--- a/src/sage/rings/polynomial/pbori/ll.py
+++ b/src/sage/rings/polynomial/pbori/ll.py
@@ -20,16 +20,13 @@ def combine(reductors, p, reduce=None):
 
 def llredsb_Cudd_style(polys):
 
-    if polys:
-        reductors = Polynomial(polys[0].ring().one()).set()
-    else:
-        reductors = None
+    reductors = Polynomial(polys[0].ring().one()).set() if polys else None
 
     linear_lead = sorted(polys, key=lead_index, reverse=True)
-    assert len(set(p.lex_lead() for p in linear_lead)) == len(polys)
+    assert len({p.lex_lead() for p in linear_lead}) == len(polys)
     assert not any(p.constant() for p in polys)
     assert len([p for p in polys if p.lex_lead_deg() == 1]) == len(polys)
-    assert len(set(p.navigation().value() for p in polys)) == len(polys)
+    assert len({p.navigation().value() for p in polys}) == len(polys)
     for p in linear_lead:
         reductors = combine(reductors, p, reduce=ll_red_nf_redsb)
     return reductors
@@ -38,26 +35,20 @@ def llredsb_Cudd_style(polys):
 def ll_encode(polys, reduce=False, prot=False, reduce_by_linear=True):
     polys = [Polynomial(p) for p in polys]
     linear_lead = sorted(polys, key=lead_index, reverse=True)
-    assert len(set(p.lex_lead() for p in linear_lead)) == len(polys)
+    assert len({p.lex_lead() for p in linear_lead}) == len(polys)
     assert not any(p.constant() for p in polys)
     assert len([p for p in polys if p.lex_lead_deg() == 1]) == len(polys)
-    assert len(set(p.navigation().value() for p in polys)) == len(polys)
+    assert len({p.navigation().value() for p in polys}) == len(polys)
     if (not reduce) and reduce_by_linear:
         linear_polys = [p for p in polys if p.deg() == 1]
         if linear_polys:
             linear_ll = ll_encode(linear_polys, reduce=True,
-                reduce_by_linear=False)
+                                  reduce_by_linear=False)
             polys = [p.lex_lead() + ll_red_nf_redsb(p + p.lex_lead(),
-                linear_ll) for p in polys]
-    if reduce:
-        reduce = ll_red_nf_redsb
-    else:
-        reduce = None
+                                                    linear_ll) for p in polys]
+    reduce = ll_red_nf_redsb if reduce else None
 
-    if polys:
-        reductors = Polynomial(polys[0].ring().one()).set()
-    else:
-        reductors = None
+    reductors = Polynomial(polys[0].ring().one()).set() if polys else None
 
     last = None
     counter = 0
@@ -119,11 +110,11 @@ def llnf(p):
         reduced_list = []
         reductors = ll_encode(linear_leads, reduce=(not on_the_fly), prot=prot)
         for p in rest:
-            p = reduction_function(p, reductors)
-            if p.is_one():
-                reduced_list = [p]
+            rp = reduction_function(p, reductors)
+            if rp.is_one():
+                reduced_list = [rp]
                 break
-            reduced_list.append(p)
+            reduced_list.append(rp)
 
     return (linear_leads, llnf, reduced_list)
 
@@ -145,7 +136,7 @@ def eliminate_ll_ranked(ll_system, to_reduce,
 
     ll_ranks = rank(ll_system)
     add_vars = set(used_vars_set(to_reduce).variables()).difference(ll_ranks.
-        keys())
+                                                                    keys())
     for v in add_vars:
         ll_ranks[v] = -1
 
@@ -182,10 +173,11 @@ def map_from(p):
 
     def map_back(p):
         return substitute_variables(from_ring, map_back_vec, p)
+
     try:
         ll_opt_encoded = ll_encode([map_from(p) for p in ll_system],
-            prot=False,
-            reduce=reduce_ll_system)
+                                   prot=False,
+                                   reduce=reduce_ll_system)
 
         def llnf(p):
             return map_back(reduction_function(map_from(p), ll_opt_encoded))
@@ -195,7 +187,7 @@ def llnf(p):
     return (llnf, opt_eliminated)
 
 
-class RingMap():
+class RingMap:
     r"""
     Define a mapping between two rings by common variable names.
 
diff --git a/src/sage/rings/polynomial/pbori/nf.py b/src/sage/rings/polynomial/pbori/nf.py
index ac6e447c525..0148826e033 100644
--- a/src/sage/rings/polynomial/pbori/nf.py
+++ b/src/sage/rings/polynomial/pbori/nf.py
@@ -33,21 +33,21 @@ def build_and_print_matrices(v, strat):
     """
     treated = BooleSet()
     v = list(v)
-    rows = 0
-    polys_in_mat = []
     if not v:
         return
+    rows = 0
+    polys_in_mat = []
     while v:
         rows = rows + 1
         p = v[0]
         v = v[1:]
         for m in list(p.terms()):
-            m = Monomial(m)
-            if m not in BooleSet(treated):
-                i = strat.select(m)
+            mom = Monomial(m)
+            if mom not in BooleSet(treated):
+                i = strat.select(mom)
                 if i >= 0:
                     p2 = strat[i]
-                    p2 = p2 * (m // p2.lead())
+                    p2 = p2 * (mom // p2.lead())
                     v.append(p2)
         polys_in_mat.append(p)
         treated = treated.union(p.set())
@@ -63,12 +63,10 @@ def build_and_print_matrices(v, strat):
     rows = len(polys_in_mat)
     cols = len(m2i)
     im = Image.new("1", (cols, rows), "white")
-    for i in range(len(polys_in_mat)):
-        p = polys_in_mat[i]
-        for j in p:
+    for i, pi in enumerate(polys_in_mat):
+        for j in pi:
             assert i < rows
             assert j < cols
-
             im.putpixel((j, i), 0)
 
     file_name = strat.matrix_prefix + str(mat_counter) + ".png"
@@ -120,17 +118,17 @@ def build_and_print_matrices_deg_colored(v, strat):
         p = v[0]
         v = v[1:]
         for m in list(p.terms()):
-            m = Monomial(m)
+            mom = Monomial(m)
             if m not in BooleSet(treated):
-                i = strat.select(m)
+                i = strat.select(mom)
                 if i >= 0:
                     p2 = strat[i]
-                    p2 = p2 * (m // p2.lead())
+                    p2 = p2 * (mom // p2.lead())
                     v.append(p2)
         polys_in_mat.append(p)
         treated = treated.union(p.set())
     m2i = {v: k for k, v in enumerate(BooleSet(treated))}
-    max_deg = max([m.deg() for m in BooleSet(treated)])
+    max_deg = max(m.deg() for m in BooleSet(treated))
     if max_deg == 0:
         max_deg = 1
     i2deg = {m2i[m]: m.deg() for m in BooleSet(treated)}
@@ -149,8 +147,8 @@ def build_and_print_matrices_deg_colored(v, strat):
         for j in p:
             assert i < rows
             assert j < cols
-            im.putpixel((j, i), ImageColor.getrgb("hsl(" + str(270 - (270 *
-                i2deg[j]) / max_deg) + ",100%,50%)"))
+            hsl = str(270 - (270 * i2deg[j]) / max_deg)
+            im.putpixel((j, i), ImageColor.getrgb("hsl(" + hsl + ",100%,50%)"))
     file_name = strat.matrix_prefix + str(mat_counter) + ".png"
     if os.path.exists(file_name):
         os.remove(file_name)
@@ -208,14 +206,17 @@ def high_probability_polynomials_trick(p, strat):
 
 
 def symmGB_F2_python(G, deg_bound=1000000000000, over_deg_bound=0,
-        use_faugere=False, use_noro=False, opt_lazy=True, opt_red_tail=True,
-        max_growth=2.0, step_factor=1.0, implications=False, prot=False,
-        full_prot=False, selection_size=1000, opt_exchange=True,
-        opt_allow_recursion=False, ll=False,
-        opt_linear_algebra_in_last_block=True, max_generators=None,
-        red_tail_deg_growth=True, matrix_prefix='mat',
-        modified_linear_algebra=True, draw_matrices=False,
-        easy_linear_polynomials=True):
+                     use_faugere=False, use_noro=False,
+                     opt_lazy=True, opt_red_tail=True,
+                     max_growth=2.0, step_factor=1.0,
+                     implications=False, prot=False,
+                     full_prot=False, selection_size=1000, opt_exchange=True,
+                     opt_allow_recursion=False, ll=False,
+                     opt_linear_algebra_in_last_block=True,
+                     max_generators=None,
+                     red_tail_deg_growth=True, matrix_prefix='mat',
+                     modified_linear_algebra=True, draw_matrices=False,
+                     easy_linear_polynomials=True):
     if use_noro and use_faugere:
         raise ValueError('both use_noro and use_faugere specified')
 
@@ -227,8 +228,8 @@ def add_to_basis(strat, p):
             if prot:
                 if full_prot:
                     print(p)
-                print("Result: ", "deg:", p.deg(), "lm: ", p.lead(), "el: ", p
-                    .elength())
+                print("Result: ", "deg:", p.deg(), "lm: ",
+                      p.lead(), "el: ", p.elength())
             if easy_linear_polynomials and p.lead_deg() > 2:
                 lin = easy_linear_polynomials_func(p)
                 for q in lin:
@@ -291,7 +292,7 @@ def add_to_basis(strat, p):
                 ps = [strat.reduction_strategy.cheap_reductions(p) for p in ps]
                 ps = [p for p in ps if not p.is_zero()]
                 if ps:
-                    min_deg = min((p.deg() for p in ps))
+                    min_deg = min(p.deg() for p in ps)
                 new_ps = []
                 for p in ps:
                     if p.deg() <= min_deg:
@@ -305,9 +306,9 @@ def add_to_basis(strat, p):
             if prot:
                 print("start reducing")
                 print("Chain Crit. : ", strat.chain_criterions, "VC:", strat.
-                    variable_chain_criterions, "EASYP", strat.
-                    easy_product_criterions, "EXTP", strat.
-                    extended_product_criterions)
+                      variable_chain_criterions, "EASYP", strat.
+                      easy_product_criterions, "EXTP", strat.
+                      extended_product_criterions)
                 print(len(ps), "spolys added")
 
             if use_noro or use_faugere:
@@ -316,18 +317,16 @@ def add_to_basis(strat, p):
                 for p in ps:
                     if not p.is_zero():
                         v.append(p)
-                if use_noro:
-                    res = strat.noro_step(v)
-                else:
-                    res = strat.faugere_step_dense(v)
+                res = strat.noro_step(v) if use_noro else strat.faugere_step_dense(v)
 
             else:
                 v = BoolePolynomialVector()
                 for p in ps:
-                    p = Polynomial(mod_mon_set(BooleSet(p.set()),
-                                        strat.reduction_strategy.monomials))
-                    if not p.is_zero():
-                        v.append(p)
+                    rp = Polynomial(mod_mon_set(
+                        BooleSet(p.set()),
+                        strat.reduction_strategy.monomials))
+                    if not rp.is_zero():
+                        v.append(rp)
                 if len(v) > 100:
                     res = parallel_reduce(v, strat, int(step_factor * 10),
                                           max_growth)
@@ -382,7 +381,7 @@ def step(strat, trace, var, val):
         strat.add_generator_delayed(Polynomial(
             Monomial(Variable(var, strat.r)) + val))
         strat = symmGB_F2_python(strat, prot=True, deg_bound=2,
-            over_deg_bound=10)
+                                 over_deg_bound=10)
         if var <= vars_start:
             strat = symmGB_F2_python(strat, prot=True, opt_lazy=False,
                                      opt_red_tail=False)
@@ -428,7 +427,7 @@ def step(strat, trace, proof_path, pos, val):
         print("npairs", strat.npairs())
         print("pos:", pos)
         strat = symmGB_F2_python(strat, deg_bound=deg_bound, opt_lazy=False,
-            over_deg_bound=over_deg_bound, prot=True)
+                                 over_deg_bound=over_deg_bound, prot=True)
         print("npairs", strat.npairs())
         pos = pos + 1
         if pos >= len(proof_path):
@@ -472,7 +471,8 @@ def step(strat, trace, var, val):
         print("npairs", strat.npairs())
 
         strat = symmGB_F2_python(strat, deg_bound=deg_bound,
-            opt_lazy=opt_lazy, over_deg_bound=over_deg_bound, prot=True)
+                                 opt_lazy=opt_lazy,
+                                 over_deg_bound=over_deg_bound, prot=True)
 
         if not strat.containsOne():
             branch(strat, trace)
@@ -483,8 +483,8 @@ def branch(strat, trace):
 
         if index < 0:
             uv = set(used_vars_set(strat))
-            lv = set(next(iter(p.lead())).index()
-                     for p in strat if p.lead_deg() == 1)
+            lv = {next(iter(p.lead())).index()
+                  for p in strat if p.lead_deg() == 1}
             candidates = uv.difference(lv)
             if candidates:
                 index = next(iter(candidates)).index()
@@ -528,7 +528,9 @@ def sort_crit(p):
             strat.add_as_you_wish(g)
     if initial_bb:
         strat = symmGB_F2_python(strat, deg_bound=max(deg_bound,
-            first_deg_bound), opt_lazy=opt_lazy, over_deg_bound=0, prot=True)
+                                                      first_deg_bound),
+                                 opt_lazy=opt_lazy, over_deg_bound=0,
+                                 prot=True)
     strat.opt_lazy = opt_lazy
     print("INITIALIZED")
     branch(strat, [])
@@ -549,7 +551,7 @@ def step(strat, trace, proof_path, pos, choice):
         print("npairs", strat.npairs())
         print("pos:", pos)
         strat = symmGB_F2_python(strat, deg_bound=deg_bound,
-            over_deg_bound=over_deg_bound, prot=True)
+                                 over_deg_bound=over_deg_bound, prot=True)
         print("npairs", strat.npairs())
         pos = pos + 1
         if pos >= len(proof_path):
@@ -581,16 +583,16 @@ def branch(strat, trace, proof_path, pos):
 
 
 def symmGB_F2_C(G, opt_exchange=True,
-        deg_bound=1000000000000, opt_lazy=False,
-        over_deg_bound=0, opt_red_tail=True,
-        max_growth=2.0, step_factor=1.0,
-        implications=False, prot=False,
-        full_prot=False, selection_size=1000,
-        opt_allow_recursion=False, use_noro=False, use_faugere=False,
-        ll=False, opt_linear_algebra_in_last_block=True,
-        max_generators=None, red_tail_deg_growth=True,
-        modified_linear_algebra=True, matrix_prefix="",
-        draw_matrices=False):
+                deg_bound=1000000000000, opt_lazy=False,
+                over_deg_bound=0, opt_red_tail=True,
+                max_growth=2.0, step_factor=1.0,
+                implications=False, prot=False,
+                full_prot=False, selection_size=1000,
+                opt_allow_recursion=False, use_noro=False, use_faugere=False,
+                ll=False, opt_linear_algebra_in_last_block=True,
+                max_generators=None, red_tail_deg_growth=True,
+                modified_linear_algebra=True, matrix_prefix="",
+                draw_matrices=False):
     if use_noro:
         raise NotImplementedError("noro not implemented for symmgb")
     if isinstance(G, list):
diff --git a/src/sage/rings/polynomial/pbori/parallel.py b/src/sage/rings/polynomial/pbori/parallel.py
index be10865dd50..32eb3894312 100644
--- a/src/sage/rings/polynomial/pbori/parallel.py
+++ b/src/sage/rings/polynomial/pbori/parallel.py
@@ -1,4 +1,3 @@
-# coding=utf-8
 r"""
 parallel.py
 PolyBoRi
diff --git a/src/sage/rings/polynomial/pbori/randompoly.py b/src/sage/rings/polynomial/pbori/randompoly.py
index 1e0dcd56189..34cc53d5dd4 100644
--- a/src/sage/rings/polynomial/pbori/randompoly.py
+++ b/src/sage/rings/polynomial/pbori/randompoly.py
@@ -99,5 +99,5 @@ def sparse_random_system_data_file_content(number_of_variables, **kwds):
                      dummy_dict)
     polynomials = sparse_random_system(r, **kwds)
     polynomials = pformat(polynomials)
-    return "declare_ring(['x'+str(i) for in range(%s)])\nideal=\\\n%s\n\n" % (
+    return "declare_ring(['x'+str(i) for in range({})])\nideal=\\\n{}\n\n".format(
         number_of_variables, polynomials)
diff --git a/src/sage/rings/polynomial/pbori/rank.py b/src/sage/rings/polynomial/pbori/rank.py
index bf16e6220ce..98d0a1727c9 100644
--- a/src/sage/rings/polynomial/pbori/rank.py
+++ b/src/sage/rings/polynomial/pbori/rank.py
@@ -18,7 +18,7 @@ def rank(data):
     def do_rank(v):
         if v in res:
             return res[v]
-        my_res = res[v] = max([do_rank(p) + 1 for p in parents[v]] + [0])
+        my_res = res[v] = max((do_rank(p) + 1 for p in parents[v]), default=0)
         return my_res
     for v in parents:
         do_rank(v)
diff --git a/src/sage/rings/polynomial/pbori/specialsets.py b/src/sage/rings/polynomial/pbori/specialsets.py
index 74f56c2faf5..9c5b4bdefc8 100644
--- a/src/sage/rings/polynomial/pbori/specialsets.py
+++ b/src/sage/rings/polynomial/pbori/specialsets.py
@@ -89,7 +89,8 @@ def power_set(variables):
     print(list(power_set([Variable(i) for i in range(4)])))
     print(list(power_set([])))
     # every monomial in the first 8 var, which is at most linear in the first 5
-    print(list(mod_mon_set(power_set([Variable(i) for i in range(8)]),
+    print(list(mod_mon_set(
+        power_set([Variable(i) for i in range(8)]),
         all_monomials_of_degree_d(2, [Variable(i) for i in range(5)]))))
 
     # specialized normal form computation
@@ -97,7 +98,8 @@ def power_set(variables):
         mod_mon_set(
             (x(1) * x(2) + x(1) + 1).set(),
             all_monomials_of_degree_d(2, [Variable(i) for i in range(1000)]))))
-    print(list(mod_mon_set(power_set([Variable(i) for i in range(50)]),
+    print(list(mod_mon_set(
+        power_set([Variable(i) for i in range(50)]),
         all_monomials_of_degree_d(2, [Variable(i) for i in range(1000)]))))
 
 
diff --git a/src/sage/rings/polynomial/pbori/statistics.py b/src/sage/rings/polynomial/pbori/statistics.py
index 93cced307a7..efd49fcd005 100644
--- a/src/sage/rings/polynomial/pbori/statistics.py
+++ b/src/sage/rings/polynomial/pbori/statistics.py
@@ -21,7 +21,7 @@ def used_vars_set(l, bound=None):
         s.update(Polynomial(p).vars_as_monomial().variables())
         if bound and len(s) > bound:
             break
-    sorted_s = sorted(list(s), key=top_index, reverse=True)
+    sorted_s = sorted(s, key=top_index, reverse=True)
     m = Monomial(next(iter(l)).ring())
     for v in sorted_s:
         m = v * m
diff --git a/src/sage/rings/polynomial/plural.pyx b/src/sage/rings/polynomial/plural.pyx
index 23dd2a11179..b03bd07d34a 100644
--- a/src/sage/rings/polynomial/plural.pyx
+++ b/src/sage/rings/polynomial/plural.pyx
@@ -126,7 +126,8 @@ from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomialRing_
 from sage.rings.polynomial.multi_polynomial_ideal import NCPolynomialIdeal
 
 from sage.rings.polynomial.polydict import ETuple
-from sage.rings.ring import check_default_category, CommutativeRing
+from sage.rings.ring import CommutativeRing
+from sage.structure.category_object cimport check_default_category
 from sage.structure.element cimport CommutativeRingElement, Element, RingElement
 from sage.structure.factory import UniqueFactory
 from sage.structure.richcmp cimport rich_to_bool
diff --git a/src/sage/rings/polynomial/polynomial_ring.py b/src/sage/rings/polynomial/polynomial_ring.py
index d2dab0db512..f5cab5bbc88 100644
--- a/src/sage/rings/polynomial/polynomial_ring.py
+++ b/src/sage/rings/polynomial/polynomial_ring.py
@@ -144,9 +144,11 @@
 import sys
 
 from sage.structure.element import Element
+from sage.structure.category_object import check_default_category
 
 import sage.categories as categories
 from sage.categories.morphism import IdentityMorphism
+from sage.categories.principal_ideal_domains import PrincipalIdealDomains
 from sage.categories.rings import Rings
 
 from sage.rings.ring import (Ring, IntegralDomain, PrincipalIdealDomain)
@@ -246,7 +248,8 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None,
             Join of Category of unique factorization domains
              and Category of commutative algebras over
               (Dedekind domains and euclidean domains
-               and infinite enumerated sets and metric spaces)
+               and noetherian rings and infinite enumerated sets
+               and metric spaces)
              and Category of infinite sets
             sage: category(GF(7)['x'])
             Join of Category of euclidean domains
@@ -264,9 +267,7 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None,
         Check that category for zero ring::
 
             sage: PolynomialRing(Zmod(1), 'x').category()
-            Category of finite commutative algebras over
-            (finite commutative rings and subquotients of monoids and
-            quotients of semigroups and finite enumerated sets)
+            Category of finite commutative rings
 
         Check `is_finite` inherited from category (:issue:`24432`)::
 
@@ -284,11 +285,11 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None,
         """
         # We trust that, if category is given, it is useful and does not need to be joined
         # with the default category
-        if category is None:
-            if base_ring.is_zero():
-                category = categories.rings.Rings().Finite()
-            else:
-                category = polynomial_default_category(base_ring.category(), 1)
+        if base_ring.is_zero():
+            category = categories.rings.Rings().Commutative().Finite()
+        else:
+            defaultcat = polynomial_default_category(base_ring.category(), 1)
+            category = check_default_category(defaultcat, category)
         self.__is_sparse = sparse
         if element_class:
             self._polynomial_class = element_class
@@ -788,6 +789,11 @@ def _coerce_map_from_(self, P):
             False
         """
         base_ring = self.base_ring()
+
+        # workaround, useful for the zero ring
+        if P == base_ring:
+            return self._coerce_map_from_base_ring()
+
         # handle constants that canonically coerce into self.base_ring()
         # first, if possible
         try:
@@ -1781,11 +1787,11 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None,
         if base_ring not in _CommutativeRings:
             raise TypeError("Base ring %s must be a commutative ring." % repr(base_ring))
         # We trust that, if a category is given, that it is useful.
-        if category is None:
-            if base_ring.is_zero():
-                category = categories.algebras.Algebras(base_ring.category()).Commutative().Finite()
-            else:
-                category = polynomial_default_category(base_ring.category(), 1)
+        if base_ring.is_zero():
+            category = categories.algebras.Algebras(base_ring.category()).Commutative().Finite()
+        else:
+            defaultcat = polynomial_default_category(base_ring.category(), 1)
+            category = check_default_category(defaultcat, category)
         PolynomialRing_general.__init__(self, base_ring, name=name,
                                         sparse=sparse, implementation=implementation,
                                         element_class=element_class, category=category)
@@ -3203,7 +3209,7 @@ def __init__(self, base_ring, name=None, implementation=None, element_class=None
 
 class PolynomialRing_dense_mod_n(PolynomialRing_commutative):
     def __init__(self, base_ring, name=None, element_class=None,
-            implementation=None, category=None):
+                 implementation=None, category=None):
         """
         TESTS::
 
@@ -3446,6 +3452,9 @@ def __init__(self, base_ring, name="x", implementation=None, element_class=None,
                     self._implementation_repr = ' (using GF2X)'
                 break
 
+        category = check_default_category(PrincipalIdealDomains(),
+                                          category)
+
         PolynomialRing_dense_mod_n.__init__(self, base_ring, name=name, implementation=implementation,
                                             element_class=element_class, category=category)
 
@@ -3467,6 +3476,10 @@ def _implementation_names_impl(implementation, base_ring, sparse):
             Traceback (most recent call last):
             ...
             ValueError: GF2X only supports modulus 2
+            sage: A = PolynomialRing(Zmod(2), 'x'); A
+            Univariate Polynomial Ring in x over Ring of integers modulo 2 (using GF2X)
+            sage: A in PrincipalIdealDomains()
+            True
 
             sage: PolynomialRing(GF(2), 'x', implementation="FLINT")                    # needs sage.libs.flint
             Univariate Polynomial Ring in x over Finite Field of size 2
diff --git a/src/sage/rings/polynomial/polynomial_ring_constructor.py b/src/sage/rings/polynomial/polynomial_ring_constructor.py
index 2a1fb99131f..e417e8a6779 100644
--- a/src/sage/rings/polynomial/polynomial_ring_constructor.py
+++ b/src/sage/rings/polynomial/polynomial_ring_constructor.py
@@ -22,7 +22,6 @@
 # ****************************************************************************
 
 from sage.structure.category_object import normalize_names
-from sage.rings.ring import IntegralDomain
 
 try:
     import sage.rings.padics.padic_base_leaves as padic_base_leaves
@@ -862,7 +861,7 @@ def _multi_variate(base_ring, names, sparse=None, order="degrevlex", implementat
 
     if R is None and implementation == "generic":
         from . import multi_polynomial_ring
-        if isinstance(base_ring, IntegralDomain):
+        if base_ring in _Domains:
             constructor = multi_polynomial_ring.MPolynomialRing_polydict_domain
         else:
             constructor = multi_polynomial_ring.MPolynomialRing_polydict
diff --git a/src/sage/rings/polynomial/weil/weil_polynomials.pyx b/src/sage/rings/polynomial/weil/weil_polynomials.pyx
index 6a73e2748e4..f9d61a2a352 100755
--- a/src/sage/rings/polynomial/weil/weil_polynomials.pyx
+++ b/src/sage/rings/polynomial/weil/weil_polynomials.pyx
@@ -324,7 +324,7 @@ class WeilPolynomials_iter():
         if node_limit is None:
             node_limit = -1
         force_squarefree = Integer(squarefree)
-        self.process = dfs_manager(d2, q, coefflist, modlist, coeffsign,
+        self.process = None if d2<0 else dfs_manager(d2, q, coefflist, modlist, coeffsign,
                                    num_cofactor, node_limit, parallel,
                                    force_squarefree)
         self.q = q
@@ -537,7 +537,12 @@ class WeilPolynomials():
         sage: list(WeilPolynomials(10, 2, lead=(1,-3,5,-5,5,-5)))
         [x^10 - 3*x^9 + 5*x^8 - 5*x^7 + 5*x^6 - 5*x^5 + 10*x^4 - 20*x^3 + 40*x^2 - 48*x + 32]
 
+    Test that :issue:`37860` is resolved::
 
+        sage: list(WeilPolynomials(-1, 1))
+        []
+        sage: list(WeilPolynomials(0, 1, sign=-1))
+        []
     """
     def __init__(self, d, q, sign=1, lead=1, node_limit=None, parallel=False, squarefree=False, polring=None):
         r"""
diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py
index 4e4454988ca..0c262529852 100644
--- a/src/sage/rings/qqbar.py
+++ b/src/sage/rings/qqbar.py
@@ -543,14 +543,36 @@
     ....: (83132288614462248899077910195645998*a + 254420831388756945210526696075302411552001)*y^2)
     sage: lc = lc.change_ring(QQbar)
     sage: lc.roots(CIF)
-    [(-1.000505492239?, 2),
-     (-1.000000000000?, 2),
-     (-0.999999999662605?, 1),
+    [(-1.0005054922387982573627768714?, 2),
+     (-1.0000000000000000000000000000?, 2),
+     (-0.9999999996626050731848036720993?, 1),
      (0, 2),
-     (1.000000000000?, 2),
-     (1.000505492239?, 2),
-     (0.999999587? + 0.?e-11*I, 1),
-     (0.999999999? + 0.?e-11*I, 1)]
+     (1.0000000000000000000000000000?, 2),
+     (1.0005054922387982573627768714?, 2),
+     (0.999999586737109168744488? + 0.?e-43*I, 1),
+     (0.999999999662605073184804? + 0.?e-43*I, 1)]
+
+Check that issue:`37927` is fixed::
+
+    sage: y = polygen(QQ, 'y')
+    sage: v1 = QQbar.polynomial_root(y**2 + 1, CIF(0, -1))
+    sage: v2 = -QQbar(2).sqrt()
+    sage: M = matrix(QQbar, [[0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
+    ....:              [0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
+    ....:              [-4, 2*v1, 1, 64, -32*v1, -16, 8*v1, 4, -2*v1, -1],
+    ....:              [4*v1, 1, 0, -192*v1, -80, 32*v1, 12, -4*v1, -1, 0],
+    ....:               [2, 0, 0, -480, 160*v1, 48, -12*v1, -2, 0, 0],
+    ....:               [-4, 2*I, 1, 64, -32*I, -16, 8*I, 4, -2*I, -1],
+    ....:               [4*I, 1, 0, -192*I, -80, 32*I, 12, -4*I, -1, 0],
+    ....:               [2, 0, 0, -480, 160*I, 48, -12*I, -2, 0, 0],
+    ....:               [0, 0, 0, 8, 4*v2, 4, 2*v2, 2, v2, 1],
+    ....:               [0, 0, 0, 24*v2, 20, 8*v2, 6, 2*v2, 1, 0],
+    ....:               [0, 0, 0, 8, 4*v2, 4, 2*v2, 2, -v2, 1],
+    ....:               [0, 0, 0, 24*v2, 20, 8*v2, 6, 2*v2, 1, 0],
+    ....:               [0, 0, 0, -4096, -1024*I, 256, 64*I, -16, -4*I, 1],
+    ....:               [0, 0, 0, -4096, 1024*I, 256, -64*I, -16, 4*I, 1]])
+    sage: M.right_kernel_matrix()
+    [   1.000000000000000? + 0.?e-16*I                                 0                                 0 -0.00925925925925926? + 0.?e-19*I               0.?e-35 + 0.?e-18*I -0.11111111111111111? + 0.?e-18*I               0.?e-34 + 0.?e-17*I   0.5555555555555555? + 0.?e-16*I                                 0  -0.5925925925925926? + 0.?e-17*I]
 
 AUTHOR:
 
@@ -4913,8 +4935,8 @@ def _richcmp_(self, other, op):
             [-0.0221204634374361? - 1.090991904211621?*I,
              -0.0221204634374361? + 1.090991904211621?*I,
              -0.8088604911480535?*I,
-             0.?e-215 - 0.7598602580415435?*I,
-             0.?e-229 + 0.7598602580415435?*I,
+             0.?e-182 - 0.7598602580415435?*I,
+             0.?e-249 + 0.7598602580415435?*I,
              0.8088604911480535?*I,
              0.0221204634374361? - 1.090991904211621?*I,
              0.0221204634374361? + 1.090991904211621?*I]
@@ -8789,7 +8811,7 @@ def an_binop_element(a, b, op):
     for t2 in (ANUnaryExpr, ANBinaryExpr, ANRoot):
         _binop_algo[t1, t2] = _binop_algo[t2, t1] = an_binop_expr
 
-qq_generator = AlgebraicGenerator(QQ, ANRoot(AAPoly.gen() - 1, RIF(1)))
+qq_generator = AlgebraicGenerator(QQ, ANRoot(AAPoly([1, -1]), RIF.one()))
 
 
 def _init_qqbar():
diff --git a/src/sage/rings/ring.pxd b/src/sage/rings/ring.pxd
index e74638b91e2..cf5399b66d9 100644
--- a/src/sage/rings/ring.pxd
+++ b/src/sage/rings/ring.pxd
@@ -16,14 +16,13 @@ cdef class CommutativeRing(Ring):
 cdef class IntegralDomain(CommutativeRing):
     pass
 
-cdef class DedekindDomain(IntegralDomain):
+cdef class DedekindDomain(CommutativeRing):
     pass
 
-
-cdef class PrincipalIdealDomain(IntegralDomain):
+cdef class PrincipalIdealDomain(CommutativeRing):
     pass
 
-cdef class Field(PrincipalIdealDomain):
+cdef class Field(CommutativeRing):
     pass
 
 cdef class Algebra(Ring):
diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx
index fe346931ce5..36f8336786c 100644
--- a/src/sage/rings/ring.pyx
+++ b/src/sage/rings/ring.pyx
@@ -25,9 +25,9 @@ The class inheritance hierarchy is:
     - :class:`IntegralDomain` (deprecated)
 
       - :class:`DedekindDomain` (deprecated and essentially removed)
-      - :class:`PrincipalIdealDomain` (deprecated)
+      - :class:`PrincipalIdealDomain` (deprecated and essentially removed)
 
-Subclasses of :class:`PrincipalIdealDomain` are
+Subclasses of :class:`CommutativeRing` are
 
 - :class:`Field`
 
@@ -35,9 +35,7 @@ Subclasses of :class:`PrincipalIdealDomain` are
 
 Some aspects of this structure may seem strange, but this is an unfortunate
 consequence of the fact that Cython classes do not support multiple
-inheritance. Hence, for instance, :class:`Field` cannot be a subclass of both
-:class:`NoetherianRing` and :class:`PrincipalIdealDomain`, although all fields
-are Noetherian PIDs.
+inheritance.
 
 (A distinct but equally awkward issue is that sometimes we may not know *in
 advance* whether or not a ring belongs in one of these classes; e.g. some
@@ -58,7 +56,7 @@ TESTS:
 
 This is to test a deprecation::
 
-    sage: from sage.rings.ring import DedekindDomain, CommutativeAlgebra
+    sage: from sage.rings.ring import DedekindDomain
     sage: class No(DedekindDomain):
     ....:     pass
     sage: F = No(QQ)
@@ -68,6 +66,7 @@ This is to test a deprecation::
     sage: F.category()
     Category of Dedekind domains
 
+    sage: from sage.rings.ring import CommutativeAlgebra
     sage: class Nein(CommutativeAlgebra):
     ....:     pass
     sage: F = Nein(QQ, QQ)
@@ -76,6 +75,16 @@ This is to test a deprecation::
     See https://github.com/sagemath/sage/issues/37999 for details.
     sage: F.category()
     Category of commutative rings
+
+    sage: from sage.rings.ring import PrincipalIdealDomain
+    sage: class Non(PrincipalIdealDomain):
+    ....:     pass
+    sage: F = Non(QQ)
+    ...:
+    DeprecationWarning: use the category PrincipalIdealDomains
+    See https://github.com/sagemath/sage/issues/37719 for details.
+    sage: F.category()
+    Category of principal ideal domains
 """
 
 # ****************************************************************************
@@ -92,7 +101,7 @@ from sage.misc.superseded import deprecation
 
 from sage.structure.coerce cimport coercion_model
 from sage.structure.parent cimport Parent
-from sage.structure.category_object import check_default_category
+from sage.structure.category_object cimport check_default_category
 from sage.structure.sequence import Sequence
 from sage.misc.prandom import randint
 from sage.categories.rings import Rings
@@ -100,6 +109,7 @@ from sage.categories.commutative_rings import CommutativeRings
 from sage.categories.integral_domains import IntegralDomains
 from sage.categories.dedekind_domains import DedekindDomains
 from sage.categories.principal_ideal_domains import PrincipalIdealDomains
+from sage.categories.noetherian_rings import NoetherianRings
 
 _Rings = Rings()
 _CommutativeRings = CommutativeRings()
@@ -225,7 +235,7 @@ cdef class Ring(ParentWithGens):
         # This is a low-level class. For performance, we trust that the category
         # is fine, if it is provided. If it isn't, we use the category of rings.
         if category is None:
-            category=_Rings
+            category = check_default_category(_Rings, category)
         Parent.__init__(self, base=base, names=names, normalize=normalize,
                         category=category)
 
@@ -436,7 +446,7 @@ cdef class Ring(ParentWithGens):
             elif isinstance(first, (list, tuple)):
                 gens = first
             elif is_Parent(first) and self.has_coerce_map_from(first):
-                gens = first.gens() # we have a ring as argument
+                gens = first.gens()  # we have a ring as argument
             else:
                 break
 
@@ -445,14 +455,14 @@ cdef class Ring(ParentWithGens):
 
         if coerce:
             gens = [self(g) for g in gens]
-        if isinstance(self, PrincipalIdealDomain):
+        if self in PrincipalIdealDomains():
             # Use GCD algorithm to obtain a principal ideal
             g = gens[0]
             if len(gens) == 1:
                 try:
                     # note: we set g = gcd(g, g) to "canonicalize" the generator: make polynomials monic, etc.
                     g = g.gcd(g)
-                except (AttributeError, NotImplementedError):
+                except (AttributeError, NotImplementedError, IndexError):
                     pass
             else:
                 for h in gens[1:]:
@@ -531,57 +541,6 @@ cdef class Ring(ParentWithGens):
             else:
                 raise TypeError("Don't know how to transform %s into an ideal of %s" % (self, x))
 
-    def _ideal_class_(self, n=0):
-        r"""
-        Return a callable object that can be used to create ideals in this
-        ring. For generic rings, this returns the factory function
-        :func:`sage.rings.ideal.Ideal`, which does its best to be clever about
-        what is required.
-
-        This class can depend on `n`, the number of generators of the ideal.
-        The default input of `n=0` indicates an unspecified number of generators,
-        in which case a class that works for any number of generators is returned.
-
-        EXAMPLES::
-
-            sage: ZZ._ideal_class_()
-            
-            sage: RR._ideal_class_()
-            
-            sage: R. = GF(5)[]
-            sage: R._ideal_class_(1)
-            
-            sage: S = R.quo(x^3 - y^2)
-            sage: S._ideal_class_(1)
-            
-            sage: S._ideal_class_(2)
-            
-            sage: T. = S[]                                                           # needs sage.libs.singular
-            sage: T._ideal_class_(5)                                                    # needs sage.libs.singular
-            
-            sage: T._ideal_class_(1)                                                    # needs sage.libs.singular
-            
-
-        Since :issue:`7797`, non-commutative rings have ideals as well::
-
-            sage: A = SteenrodAlgebra(2)                                                # needs sage.combinat sage.modules
-            sage: A._ideal_class_()                                                     # needs sage.combinat sage.modules
-            
-
-        """
-        # One might need more than just n, but I can't think of an example.
-        from sage.rings.noncommutative_ideals import Ideal_nc
-        try:
-            if not self.is_commutative():
-                return Ideal_nc
-        except (NotImplementedError, AttributeError):
-            return Ideal_nc
-        from sage.rings.ideal import Ideal_generic, Ideal_principal
-        if n == 1:
-            return Ideal_principal
-        else:
-            return Ideal_generic
-
     def principal_ideal(self, gen, coerce=True):
         """
         Return the principal ideal generated by gen.
@@ -696,7 +655,7 @@ cdef class Ring(ParentWithGens):
             return x
         return self._one_element
 
-    def is_field(self, proof = True):
+    def is_field(self, proof=True):
         """
         Return ``True`` if this ring is a field.
 
@@ -837,99 +796,6 @@ cdef class Ring(ParentWithGens):
         """
         return False
 
-    def is_integral_domain(self, proof = True):
-        """
-        Return ``True`` if this ring is an integral domain.
-
-        INPUT:
-
-        - ``proof`` -- (default: ``True``) Determines what to do in unknown
-          cases
-
-        ALGORITHM:
-
-        If the parameter ``proof`` is set to ``True``, the returned value is
-        correct but the method might throw an error.  Otherwise, if it is set
-        to ``False``, the method returns ``True`` if it can establish that self
-        is an integral domain and ``False`` otherwise.
-
-        EXAMPLES::
-
-            sage: QQ.is_integral_domain()
-            True
-            sage: ZZ.is_integral_domain()
-            True
-            sage: ZZ['x,y,z'].is_integral_domain()
-            True
-            sage: Integers(8).is_integral_domain()
-            False
-            sage: Zp(7).is_integral_domain()                                            # needs sage.rings.padics
-            True
-            sage: Qp(7).is_integral_domain()                                            # needs sage.rings.padics
-            True
-            sage: R. = QQ[]
-            sage: S. = R.quo((b^3))                                                # needs sage.libs.singular
-            sage: S.is_integral_domain()                                                # needs sage.libs.singular
-            False
-
-        This illustrates the use of the ``proof`` parameter::
-
-            sage: R. = ZZ[]
-            sage: S. = R.quo((b^3))                                                # needs sage.libs.singular
-            sage: S.is_integral_domain(proof=True)                                      # needs sage.libs.singular
-            Traceback (most recent call last):
-            ...
-            NotImplementedError
-            sage: S.is_integral_domain(proof=False)                                     # needs sage.libs.singular
-            False
-
-        TESTS:
-
-        Make sure :issue:`10481` is fixed::
-
-            sage: x = polygen(ZZ, 'x')
-            sage: R. = ZZ['x'].quo(x^2)                                              # needs sage.libs.pari
-            sage: R.fraction_field()                                                    # needs sage.libs.pari
-            Traceback (most recent call last):
-            ...
-            TypeError: self must be an integral domain.
-            sage: R.is_integral_domain()                                                # needs sage.libs.pari
-            False
-
-        Forward the proof flag to ``is_field``, see :issue:`22910`::
-
-            sage: # needs sage.libs.singular
-            sage: R1. = GF(5)[]
-            sage: F1 = R1.quotient_ring(x^2 + x + 1)
-            sage: R2. = F1[]
-            sage: F2 = R2.quotient_ring(x^2 + x + 1)
-            sage: F2.is_integral_domain(False)
-            False
-        """
-        if self.is_field(proof):
-            return True
-
-        if self.is_zero():
-            return False
-
-        if proof:
-            raise NotImplementedError
-        else:
-            return False
-
-    def is_noetherian(self):
-        """
-        Return ``True`` if this ring is Noetherian.
-
-        EXAMPLES::
-
-            sage: QQ.is_noetherian()
-            True
-            sage: ZZ.is_noetherian()
-            True
-        """
-        raise NotImplementedError
-
     def order(self):
         """
         The number of elements of ``self``.
@@ -1158,6 +1024,8 @@ cdef class CommutativeRing(Ring):
     """
     Generic commutative ring.
     """
+    _default_category = _CommutativeRings
+
     def __init__(self, base_ring, names=None, normalize=True, category=None):
         """
         Initialize ``self``.
@@ -1175,8 +1043,7 @@ cdef class CommutativeRing(Ring):
         # This is a low-level class. For performance, we trust that
         # the category is fine, if it is provided. If it isn't, we use
         # the category of commutative rings.
-        if category is None:
-            category=_CommutativeRings
+        category = check_default_category(self._default_category, category)
         Ring.__init__(self, base_ring, names=names, normalize=normalize,
                       category=category)
 
@@ -1615,10 +1482,9 @@ cdef class IntegralDomain(CommutativeRing):
 
         This method is used by all the abstract subclasses of
         :class:`IntegralDomain`, like :class:`NoetherianRing`,
-        :class:`PrincipalIdealDomain`,
         :class:`Field`, ... in order to
         avoid cascade calls Field.__init__ ->
-        PrincipalIdealDomain.__init__ -> IntegralDomain.__init__ ->
+        IntegralDomain.__init__ ->
         ...
 
         EXAMPLES::
@@ -1627,56 +1493,22 @@ cdef class IntegralDomain(CommutativeRing):
             sage: F.category()
             Category of integral domains
 
-            sage: F = PrincipalIdealDomain(QQ)
-            sage: F.category()
-            Category of principal ideal domains
-
             sage: F = Field(QQ)
             sage: F.category()
             Category of fields
 
-        If a category is specified, then the category is set to the
-        join of that category with the default category::
-
-            sage: F = PrincipalIdealDomain(QQ, category=EnumeratedSets())
-
         The default value for the category is specified by the class
         attribute ``default_category``::
 
             sage: IntegralDomain._default_category
             Category of integral domains
 
-            sage: PrincipalIdealDomain._default_category
-            Category of principal ideal domains
-
             sage: Field._default_category
             Category of fields
-
         """
-        category = check_default_category(self._default_category, category)
         CommutativeRing.__init__(self, base_ring, names=names, normalize=normalize,
                                  category=category)
 
-    def is_integral_domain(self, proof = True):
-        """
-        Return ``True``, since this ring is an integral domain.
-
-        (This is a naive implementation for objects with type
-        ``IntegralDomain``)
-
-        EXAMPLES::
-
-            sage: ZZ.is_integral_domain()
-            True
-            sage: QQ.is_integral_domain()
-            True
-            sage: ZZ['x'].is_integral_domain()
-            True
-            sage: R = ZZ.quotient(ZZ.ideal(10)); R.is_integral_domain()
-            False
-        """
-        return True
-
     def is_integrally_closed(self):
         r"""
         Return ``True`` if this ring is integrally closed in its field of
@@ -1708,7 +1540,7 @@ cdef class IntegralDomain(CommutativeRing):
         """
         raise NotImplementedError
 
-    def is_field(self, proof = True):
+    def is_field(self, proof=True):
         r"""
         Return ``True`` if this ring is a field.
 
@@ -1733,35 +1565,14 @@ cdef class IntegralDomain(CommutativeRing):
             return False
 
 cdef class NoetherianRing(CommutativeRing):
-    """
-    Generic Noetherian ring class.
-
-    A Noetherian ring is a commutative ring in which every ideal is
-    finitely generated.
-
-    This class is deprecated, and not actually used anywhere in the
-    Sage code base.  If you think you need it, please create a
-    category :class:`NoetherianRings`, move the code of this class
-    there, and use it instead.
-    """
-    def is_noetherian(self):
-        """
-        Return ``True`` since this ring is Noetherian.
-
-        EXAMPLES::
+    _default_category = NoetherianRings()
 
-            sage: ZZ.is_noetherian()
-            True
-            sage: QQ.is_noetherian()
-            True
-            sage: R. = PolynomialRing(QQ)
-            sage: R.is_noetherian()
-            True
-        """
-        return True
+    def __init__(self, *args, **kwds):
+        deprecation(37234, "use the category DedekindDomains")
+        super().__init__(*args, **kwds)
 
 
-cdef class DedekindDomain(IntegralDomain):
+cdef class DedekindDomain(CommutativeRing):
     _default_category = DedekindDomains()
 
     def __init__(self, *args, **kwds):
@@ -1769,154 +1580,12 @@ cdef class DedekindDomain(IntegralDomain):
         super().__init__(*args, **kwds)
 
 
-cdef class PrincipalIdealDomain(IntegralDomain):
-    """
-    Generic principal ideal domain.
-
-    This class is deprecated. Please use the
-    :class:`~sage.categories.principal_ideal_domains.PrincipalIdealDomains`
-    category instead.
-    """
+cdef class PrincipalIdealDomain(CommutativeRing):
     _default_category = PrincipalIdealDomains()
 
-    def is_noetherian(self):
-        """
-        Every principal ideal domain is noetherian, so we return ``True``.
-
-        EXAMPLES::
-
-            sage: Zp(5).is_noetherian()                                                 # needs sage.rings.padics
-            True
-        """
-        return True
-
-    def class_group(self):
-        """
-        Return the trivial group, since the class group of a PID is trivial.
-
-        EXAMPLES::
-
-            sage: QQ.class_group()                                                      # needs sage.groups
-            Trivial Abelian group
-        """
-        from sage.groups.abelian_gps.abelian_group import AbelianGroup
-        return AbelianGroup([])
-
-    def gcd(self, x, y, coerce=True):
-        r"""
-        Return the greatest common divisor of ``x`` and ``y``, as elements
-        of ``self``.
-
-        EXAMPLES:
-
-        The integers are a principal ideal domain and hence a GCD domain::
-
-            sage: ZZ.gcd(42, 48)
-            6
-            sage: 42.factor(); 48.factor()
-            2 * 3 * 7
-            2^4 * 3
-            sage: ZZ.gcd(2^4*7^2*11, 2^3*11*13)
-            88
-            sage: 88.factor()
-            2^3 * 11
-
-        In a field, any nonzero element is a GCD of any nonempty set
-        of nonzero elements. In previous versions, Sage used to return
-        1 in the case of the rational field. However, since :issue:`10771`,
-        the rational field is considered as the
-        *fraction field* of the integer ring. For the fraction field
-        of an integral domain that provides both GCD and LCM, it is
-        possible to pick a GCD that is compatible with the GCD of the
-        base ring::
-
-            sage: QQ.gcd(ZZ(42), ZZ(48)); type(QQ.gcd(ZZ(42), ZZ(48)))
-            6
-            
-            sage: QQ.gcd(1/2, 1/3)
-            1/6
-
-        Polynomial rings over fields are GCD domains as well. Here is a simple
-        example over the ring of polynomials over the rationals as well as
-        over an extension ring. Note that ``gcd`` requires x and y to be
-        coercible::
-
-            sage: # needs sage.rings.number_field
-            sage: R. = PolynomialRing(QQ)
-            sage: S. = NumberField(x^2 - 2, 'a')
-            sage: f = (x - a)*(x + a); g = (x - a)*(x^2 - 2)
-            sage: print(f); print(g)
-            x^2 - 2
-            x^3 - a*x^2 - 2*x + 2*a
-            sage: f in R
-            True
-            sage: g in R
-            False
-            sage: R.gcd(f, g)
-            Traceback (most recent call last):
-            ...
-            TypeError: Unable to coerce 2*a to a rational
-            sage: R.base_extend(S).gcd(f,g)
-            x^2 - 2
-            sage: R.base_extend(S).gcd(f, (x - a)*(x^2 - 3))
-            x - a
-        """
-        if coerce:
-            x = self(x)
-            y = self(y)
-        return x.gcd(y)
-
-    def content(self, x, y, coerce=True):
-        r"""
-        Return the content of `x` and `y`, i.e. the unique element `c` of
-        ``self`` such that `x/c` and `y/c` are coprime and integral.
-
-        EXAMPLES::
-
-            sage: QQ.content(ZZ(42), ZZ(48)); type(QQ.content(ZZ(42), ZZ(48)))
-            6
-            
-            sage: QQ.content(1/2, 1/3)
-            1/6
-            sage: factor(1/2); factor(1/3); factor(1/6)
-            2^-1
-            3^-1
-            2^-1 * 3^-1
-            sage: a = (2*3)/(7*11); b = (13*17)/(19*23)
-            sage: factor(a); factor(b); factor(QQ.content(a,b))
-            2 * 3 * 7^-1 * 11^-1
-            13 * 17 * 19^-1 * 23^-1
-            7^-1 * 11^-1 * 19^-1 * 23^-1
-
-        Note the changes to the second entry::
-
-            sage: c = (2*3)/(7*11); d = (13*17)/(7*19*23)
-            sage: factor(c); factor(d); factor(QQ.content(c,d))
-            2 * 3 * 7^-1 * 11^-1
-            7^-1 * 13 * 17 * 19^-1 * 23^-1
-            7^-1 * 11^-1 * 19^-1 * 23^-1
-            sage: e = (2*3)/(7*11); f = (13*17)/(7^3*19*23)
-            sage: factor(e); factor(f); factor(QQ.content(e,f))
-            2 * 3 * 7^-1 * 11^-1
-            7^-3 * 13 * 17 * 19^-1 * 23^-1
-            7^-3 * 11^-1 * 19^-1 * 23^-1
-        """
-        if coerce:
-            x = self(x)
-            y = self(y)
-        return x.content(y)
-
-    def _ideal_class_(self, n=0):
-        """
-        Ideals in PIDs have their own special class.
-
-        EXAMPLES::
-
-            sage: ZZ._ideal_class_()
-            
-        """
-        from sage.rings.ideal import Ideal_pid
-        return Ideal_pid
+    def __init__(self, *args, **kwds):
+        deprecation(37719, "use the category PrincipalIdealDomains")
+        super().__init__(*args, **kwds)
 
 
 cpdef bint _is_Field(x) except -2:
@@ -1955,7 +1624,7 @@ from sage.categories.commutative_algebras import CommutativeAlgebras
 from sage.categories.fields import Fields
 _Fields = Fields()
 
-cdef class Field(PrincipalIdealDomain):
+cdef class Field(CommutativeRing):
     """
     Generic field
     """
@@ -2154,13 +1823,13 @@ cdef class Algebra(Ring):
         # This is a low-level class. For performance, we trust that the category
         # is fine, if it is provided. If it isn't, we use the category of Algebras(base_ring).
         if category is None:
-            category = Algebras(base_ring)
+            category = check_default_category(Algebras(base_ring), category)
         Ring.__init__(self,base_ring, names=names, normalize=normalize,
                       category=category)
 
 
 cdef class CommutativeAlgebra(CommutativeRing):
-    __default_category = CommutativeRings()
+    __default_category = _CommutativeRings
 
     def __init__(self, base_ring, *args, **kwds):
         deprecation(37999, "use the category CommutativeAlgebras")
diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py
index df4ba63f4e6..c16c9cf6644 100644
--- a/src/sage/schemes/curves/plane_curve_arrangement.py
+++ b/src/sage/schemes/curves/plane_curve_arrangement.py
@@ -200,13 +200,13 @@ def _repr_(self):
             Arrangement of 5 curves in Projective Space of dimension 2 over Rational Field
         """
         if not self:
-            return 'Empty curve arrangement in {0}'.format(self.parent().ambient_space())
+            return 'Empty curve arrangement in {}'.format(self.parent().ambient_space())
         elif len(self) < 5:
             curves = ', '.join(h.defining_polynomial()._repr_()
                                for h in self._curves)
-            return 'Arrangement ({0}) in {1}'.format(curves,
+            return 'Arrangement ({}) in {}'.format(curves,
                                                      self.parent().ambient_space())
-        return 'Arrangement of {0} curves in {1}'.format(len(self),
+        return 'Arrangement of {} curves in {}'.format(len(self),
                                                          self.parent().ambient_space())
 
     def _richcmp_(self, other, op):
@@ -574,7 +574,7 @@ def fundamental_group(self, simplified=True, vertical=True,
             if not vertical:
                 st = self._strands_nonvertical
                 d1 = prod(L).degree()
-                bd = (bm, st, dict(), d1)
+                bd = (bm, st, {}, d1)
             else:
                 st = self._strands_vertical
                 d1 = prod(L).degree(R.gen(1))
@@ -920,7 +920,7 @@ def fundamental_group(self, simplified=True):
                                        projective=proj)
         dic = C_affine.meridians(simplified=simplified, vertical=True)
         if infinity_in_C:
-            dic1 = dict()
+            dic1 = {}
             for k in range(j):
                 dic1[k] = dic[k]
             dic1[j] = dic[n - 1]
@@ -1023,7 +1023,7 @@ def __classcall__(cls, base, names=()):
         names = normalize_names(len(names), names)
         return super().__classcall__(cls, base, names)
 
-    def __init__(self, base_ring, names=tuple()):
+    def __init__(self, base_ring, names=()):
         """
         Initialize ``self``.
 
@@ -1115,7 +1115,7 @@ def _repr_(self):
             sage: L. = AffinePlaneCurveArrangements(QQ);  L
             Curve arrangements in Affine Space of dimension 2 over Rational Field
         """
-        return 'Curve arrangements in {0}'.format(self.ambient_space())
+        return 'Curve arrangements in {}'.format(self.ambient_space())
 
     def _element_constructor_(self, *args, **kwds):
         """
diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py
index c6b5ca66017..7dec97439db 100644
--- a/src/sage/schemes/curves/zariski_vankampen.py
+++ b/src/sage/schemes/curves/zariski_vankampen.py
@@ -608,7 +608,7 @@ def newton(f, x0, i0):
         sage: n
         0.0?
         sage: n.real().endpoints()
-        (-0.0147727272727274, 0.00982142857142862)
+        (-0.0460743801652894, 0.0291454081632654)
         sage: n.imag().endpoints()
         (0.000000000000000, -0.000000000000000)
     """
@@ -1524,14 +1524,15 @@ def braid2rels(L):
     k = min(T1) - 1
     B0 = BraidGroup(m)
     F0 = FreeGroup(m)
-    br0 = B0([j-k for j in T])
+    br0 = B0([j - k for j in T])
     br0_left = leftnormalform(br0)
     q, r = ZZ(br0_left[0][0]).quo_rem(2)
     br1 = B0.delta()**r * prod(map(B0, br0_left[1:]), B0.one())
     cox = prod(F0.gens())
     U0 = [cox**q * (f0 * br1) / cox**q / f0 for f0 in F0.gens()[:-1]]
     U = [tuple(sign(k1) * (abs(k1) + k) for k1 in br.Tietze()) for br in U0]
-    pasos = [B.one()] + list(reversed(L1))
+    pasos = [B.one()]
+    pasos.extend(reversed(L1))
     for C in pasos:
         U = [(F(a) * C.inverse()).Tietze() for a in U]
         ga = F / U
@@ -1884,13 +1885,13 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False,
     x, y = R.gens()
     flist1 = tuple(flist)
     if vertical and vertical_lines_in_braidmon(flist1):
-        infinity = all([Curve(g).is_vertical_line() or
-                        g.degree(y) == g.degree() for g in flist1])
+        infinity = all(Curve(g).is_vertical_line() or
+                       g.degree(y) == g.degree() for g in flist1)
     else:
-        infinity = any([Curve(g).has_vertical_asymptote() or
-                        Curve(g).is_vertical_line() for g in flist1])
+        infinity = any(Curve(g).has_vertical_asymptote() or
+                       Curve(g).is_vertical_line() for g in flist1)
         if not infinity:
-            infinity = all([g.degree(y) == g.degree() for g in flist1])
+            infinity = all(g.degree(y) == g.degree() for g in flist1)
     if braid_data:
         bm, dic, dv, d1 = braid_data
     elif not flist:
@@ -1925,5 +1926,5 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False,
     n = g1.ngens()
     rels = [rel.Tietze() for rel in g1.relations()]
     g1 = FreeGroup(n) / rels
-    dic1 = {i: list(set([g1(el.Tietze()) for el in dic1[i]])) for i in dic1}
+    dic1 = {i: list({g1(el.Tietze()) for el in dic1[i]}) for i in dic1}
     return (g1, dic1)
diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py
index 9889808b35d..99e3e8c81fa 100644
--- a/src/sage/schemes/elliptic_curves/ell_point.py
+++ b/src/sage/schemes/elliptic_curves/ell_point.py
@@ -1818,6 +1818,17 @@ def weil_pairing(self, Q, n, algorithm=None):
             sage: z.multiplicative_order()
             360
 
+        Another larger example::
+
+            sage: F = GF(65537^2, modulus=[3,-1,1], name='a')
+            sage: F.inject_variables()
+            Defining a
+            sage: E = EllipticCurve(F, [0,1])
+            sage: P = E(22, 28891)
+            sage: Q = E(-93, 2728*a + 64173)
+            sage: P.weil_pairing(Q, 7282, algorithm='sage')
+            53278*a + 36700
+
         An example over a number field::
 
             sage: # needs sage.rings.number_field
@@ -1833,16 +1844,20 @@ def weil_pairing(self, Q, n, algorithm=None):
 
         TESTS:
 
-        Check that the original Sage implementation still works::
+        Check that the original Sage implementation still works and
+        that the result coincides with the PARI implementation::
 
             sage: # needs sage.rings.finite_rings
             sage: GF(65537^2).inject_variables()
             Defining z2
             sage: E = EllipticCurve(GF(65537^2), [0,1])
-            sage: P = E(22, 28891)
-            sage: Q = E(-93, 40438*z2 + 31573)
-            sage: P.weil_pairing(Q, 7282, algorithm='sage')
-            19937*z2 + 65384
+            sage: R, S = E.torsion_basis(7282)
+            sage: a, b = ZZ.random_element(), ZZ.random_element()
+            sage: P = a*R + b*S
+            sage: c, d = ZZ.random_element(), ZZ.random_element()
+            sage: Q = c*R + d*S
+            sage: P.weil_pairing(Q, 7282, algorithm='sage') == P.weil_pairing(Q, 7282, algorithm='pari')
+            True
 
         Passing an unknown ``algorithm=`` argument should fail::
 
@@ -2047,19 +2062,19 @@ def tate_pairing(self, Q, n, k, q=None):
             sage: Px.weil_pairing(Qx, 41)^e == num/den
             True
 
-        TESTS:
-
-        Check that the PARI output matches the original Sage implementation::
+        An example over a large base field::
 
-            sage: # needs sage.rings.finite_rings
-            sage: GF(65537^2).inject_variables()
+            sage: F = GF(65537^2, modulus=[3,46810,1], name='z2')
+            sage: F.inject_variables()
             Defining z2
-            sage: E = EllipticCurve(GF(65537^2), [0,1])
+            sage: E = EllipticCurve(F, [0,1])
             sage: P = E(22, 28891)
             sage: Q = E(-93, 40438*z2 + 31573)
             sage: P.tate_pairing(Q, 7282, 2)
             34585*z2 + 4063
 
+        TESTS:
+
         The point ``P (self)`` must have ``n`` torsion::
 
             sage: P.tate_pairing(Q, 163, 2)
diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py
index f49ccfbc568..ce77cdca614 100644
--- a/src/sage/schemes/elliptic_curves/ell_rational_field.py
+++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py
@@ -5823,8 +5823,8 @@ def height(self, precision=None):
         c4 = self.c4()
         c6 = self.c6()
         j = self.j_invariant()
-        log_g2 = R((c4/12)).abs().log()
-        log_g3 = R((c6/216)).abs().log()
+        log_g2 = R(c4/12).abs().log()
+        log_g3 = R(c6/216).abs().log()
 
         if j == 0:
             h_j = R(1)
@@ -6348,7 +6348,7 @@ def point_preprocessing(free,tor):
             #new bound according to low_bound and upper bound
             #[c_5 exp((-c_2*H_q^2)/2)] provided by Corollary 8.7.3
             if low_bound != 0:
-                H_q_new = R((log(low_bound/c5)/(-c2/2))).sqrt()
+                H_q_new = R(log(low_bound/c5)/(-c2/2)).sqrt()
                 H_q_new = H_q_new.ceil()
                 if H_q_new == 1:
                     break_cond = 1 # stops reduction
@@ -7025,9 +7025,8 @@ def S_integral_x_coords_with_abs_bounded_by(abs_bound):
             else:
                 bound_list.append(H_q)
 
-         ##reduction for finite places in S
-            for p in S:
-                bound_list.append(reduction_at(p))
+            # reduction for finite places in S
+            bound_list.extend(reduction_at(p) for p in S)
 
             if verbose:
                 print('bound_list', bound_list)
diff --git a/src/sage/schemes/elliptic_curves/gal_reps_number_field.py b/src/sage/schemes/elliptic_curves/gal_reps_number_field.py
index ea9d3bdc2ed..12f565d9c48 100644
--- a/src/sage/schemes/elliptic_curves/gal_reps_number_field.py
+++ b/src/sage/schemes/elliptic_curves/gal_reps_number_field.py
@@ -739,9 +739,7 @@ def _exceptionals(E, L, patience=1000):
         if (not D) or (patience == 0):
             break
 
-    for l in D:
-        output.append(l)
-
+    output.extend(D)
     output.sort()
     return output
 
diff --git a/src/sage/schemes/elliptic_curves/heegner.py b/src/sage/schemes/elliptic_curves/heegner.py
index df91b74b280..f0ad1420bcf 100644
--- a/src/sage/schemes/elliptic_curves/heegner.py
+++ b/src/sage/schemes/elliptic_curves/heegner.py
@@ -7055,7 +7055,7 @@ def _heegner_index_in_EK(self, D):
     basis = [G(z) for z in E.gens()] + [G(phi(z)) for z in F.gens()]
     # Make a list of the 2-power order torsion points in E(K), including 0.
     T = [G(z) for z in G.torsion_subgroup().list() if z.order() == 1 or
-            ((z.order() % 2 == 0 and len(z.order().factor()) == 1))]
+            (z.order() % 2 == 0 and len(z.order().factor()) == 1)]
 
     r = len(basis)   # rank
     V = QQ**r
@@ -7066,9 +7066,8 @@ def _heegner_index_in_EK(self, D):
         if not v:
             continue
         P = sum([basis[i] for i in range(r) if v[i]])
-        for t in T:
-            if (P+t).is_divisible_by(2):
-                B.append(V(v)/2)
+        w = V(v) / 2
+        B.extend(w for t in T if (P + t).is_divisible_by(2))
 
     A = ZZ**r
     # Take span of our vectors in (1/2)*ZZ^r, along with ZZ^r.  This is E(K)/tor.
diff --git a/src/sage/schemes/elliptic_curves/isogeny_small_degree.py b/src/sage/schemes/elliptic_curves/isogeny_small_degree.py
index 4623828ccc5..b712650bd9b 100644
--- a/src/sage/schemes/elliptic_curves/isogeny_small_degree.py
+++ b/src/sage/schemes/elliptic_curves/isogeny_small_degree.py
@@ -564,7 +564,7 @@ def _sporadic_Q_data(j):
     w = w1 # real period
     if j in [-121, -24729001, -162677523113838677, QQ(-882216989)/131072]:
         w = 2*w2-w1 # imaginary period
-    kerpol = prod(([X-L.elliptic_exponential(n*w/ell)[0] for n in range(1,(ell+1)//2)]))
+    kerpol = prod([X-L.elliptic_exponential(n*w/ell)[0] for n in range(1,(ell+1)//2)])
     if j == -162677523113838677:
         kerpolcoeffs = [(37*c.real()).round()/37 for c in list(kerpol)]
     else:
@@ -2110,7 +2110,7 @@ def isogenies_prime_degree_genus_plus_0_j0(E, l, minimal_models=True):
         raise ValueError("%s must be one of %s." % (l,hyperelliptic_primes))
     F = E.base_field()
     if E.j_invariant() != 0:
-        raise ValueError(("j-invariant must be 0."))
+        raise ValueError("j-invariant must be 0.")
     if F.characteristic() in [2,3,l]:
         raise NotImplementedError("Not implemented in characteristic 2, 3 or l.")
 
diff --git a/src/sage/schemes/elliptic_curves/padic_lseries.py b/src/sage/schemes/elliptic_curves/padic_lseries.py
index 113d7e16104..cd6d034ccdb 100644
--- a/src/sage/schemes/elliptic_curves/padic_lseries.py
+++ b/src/sage/schemes/elliptic_curves/padic_lseries.py
@@ -1554,7 +1554,7 @@ def __phi_bpr(self, prec=0):
         if prec > 10:
             print("Warning: Very large value for the precision.")
         if prec == 0:
-            prec = floor((log(10000)/log(p)))
+            prec = floor(log(10000)/log(p))
             verbose("prec set to %s" % prec)
         eh = E.formal()
         om = eh.differential(prec=p**prec+3)
diff --git a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py
index 422b28b67fe..878b5a78d82 100644
--- a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py
+++ b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py
@@ -32,7 +32,7 @@
 from sage.rings.integer import Integer
 from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
 
-class baseWI():
+class baseWI:
     r"""
     This class implements the basic arithmetic of isomorphisms between
     Weierstrass models of elliptic curves.
diff --git a/src/sage/schemes/product_projective/space.py b/src/sage/schemes/product_projective/space.py
index 3fee46cc15a..2b4e60905df 100644
--- a/src/sage/schemes/product_projective/space.py
+++ b/src/sage/schemes/product_projective/space.py
@@ -256,7 +256,7 @@ def _repr_(self):
             Product of projective spaces P^1 x P^1 x P^1 over Integer Ring
         """
         return ''.join(['Product of projective spaces ',
-                        ' x '.join('P^{0}'.format(d) for d in self._dims),
+                        ' x '.join('P^{}'.format(d) for d in self._dims),
                         ' over ', str(self.base_ring())])
 
     def _repr_generic_point(self, v=None):
@@ -1197,8 +1197,8 @@ def points_of_bounded_height(self, **kwds):
         P = []
         for i in range(m):
             pt = next(iters[i])
-            for j in range(dim[i]):
-                P.append(pt[j]) # initial value of P
+            P.extend(pt[j] for j in range(dim[i]))
+            # initial value of P
         yield self(P)
 
         i = 0
@@ -1248,9 +1248,7 @@ def __iter__(self):
              (1 : 0 : 0 , 1 : 0)]
         """
         iters = [iter(T) for T in self._components]
-        L = []
-        for x in iters:
-            L.append(next(x))  # put at zero
+        L = [next(x) for x in iters]  # put at zero
         yield self(L)
         j = 0
         while j < self.num_components():
diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py
index 6d81b47abc2..a284ecbc585 100644
--- a/src/sage/schemes/projective/projective_morphism.py
+++ b/src/sage/schemes/projective/projective_morphism.py
@@ -1233,9 +1233,8 @@ def dehomogenize(self, n):
             G = phi(self._polys[ind[1]])
             # ind[1] is relative to codomain
             M = self.codomain().ambient_space().dimension_relative()
-            for i in range(0, M + 1):
-                if i != ind[1]:
-                    F.append(phi(self._polys[i]) / G)
+            F.extend(phi(self._polys[i]) / G
+                     for i in range(M + 1) if i != ind[1])
             H = Hom(Aff_domain, self.codomain().affine_patch(ind[1]))
             # since often you dehomogenize at the same coordinate in domain
             # and codomain it should be stored appropriately.
@@ -1720,17 +1719,13 @@ def rational_preimages(self, Q, k=1):
             L2 = []
             for P in L:
                 I = list(self.domain().defining_polynomials())
-                for i in range(N+1):
-                    for j in range(i+1, N+1):
-                        I.append(P[i]*self[j] - P[j]*self[i])
+                I.extend(P[i] * self[j] - P[j] * self[i]
+                         for i in range(N + 1) for j in range(i + 1, N + 1))
                 X = PS.subscheme(I)
                 if X.dimension() > 0:
                     return X
-                preimages = []
-                for T in X.rational_points():
-                    if not all(g(tuple(T)) == 0 for g in self):
-                        preimages.append(PS(T))
-                L2 = L2 + preimages
+                L2.extend(PS(T) for T in X.rational_points()
+                          if not all(g(tuple(T)) == 0 for g in self))
             L = L2
         return L
 
@@ -1770,10 +1765,10 @@ def _number_field_from_algebraics(self):
             Scheme morphism:
               From: Projective Space of dimension 1 over Number Field in a
                     with defining polynomial y^4 + 3*y^2 + 1
-                    with a = 0.?e-113 + 0.618033988749895?*I
+                    with a = 0.?e-151 + 0.618033988749895?*I
               To:   Projective Space of dimension 2 over Number Field in a
                     with defining polynomial y^4 + 3*y^2 + 1
-                    with a = 0.?e-113 + 0.618033988749895?*I
+                    with a = 0.?e-151 + 0.618033988749895?*I
               Defn: Defined on coordinates by sending (x : y) to
                     (x^2 + (a^3 + 2*a)*x*y + 3*y^2 : y^2 : (2*a^2 + 3)*x*y)
 
@@ -2224,8 +2219,8 @@ def reduce_base_field(self):
                         # find the right subfield and it's embedding
                         if M.degree() == da:
                             break
-                    c = M((str(c).replace(c.as_finite_field_element()[0].variable_name(),
-                                          M.variable_name())))
+                    c = M(str(c).replace(c.as_finite_field_element()[0].variable_name(),
+                                          M.variable_name()))
                     new_c.append(M_to_L(c))
                 # reconstruct as a poly in the new domain
                 new_f.append(sum([new_c[i] * prod(new_R.gen(j)**mon_deg[i][j]
@@ -2613,9 +2608,8 @@ def self_with_domain(C):
         polys = list(X.defining_polynomials())
 
         for r in self.representatives():
-            r_proj = r if emb is None else emb*r
-            for p in r_proj:
-                polys.append(p)
+            r_proj = r if emb is None else emb * r
+            polys.extend(r_proj)
 
         return Amb.subscheme(polys).reduce()
 
diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py
index fb6715f5f87..880a4c271f5 100644
--- a/src/sage/schemes/riemann_surfaces/riemann_surface.py
+++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py
@@ -481,7 +481,7 @@ def reparameterize_differential_minpoly(minpoly, z0):
     return mt
 
 
-class RiemannSurface():
+class RiemannSurface:
     r"""
     Construct a Riemann Surface. This is specified by the zeroes of a bivariate
     polynomial with rational coefficients `f(z,w) = 0`.
diff --git a/src/sage/schemes/toric/divisor.py b/src/sage/schemes/toric/divisor.py
index 3a72638ac2d..a80d9a41b85 100644
--- a/src/sage/schemes/toric/divisor.py
+++ b/src/sage/schemes/toric/divisor.py
@@ -346,7 +346,7 @@ def ToricDivisor(toric_variety, arg=None, ring=None, check=True, reduce=True):
     except (AssertionError, TypeError):
         n_rays = toric_variety.fan().nrays()
         assert len(arg) == n_rays, \
-            'Argument list {0} is not of the required length {1}!' \
+            'Argument list {} is not of the required length {}!' \
             .format(arg, n_rays)
         arg = list(zip(arg, toric_variety.gens()))
         reduce = False
diff --git a/src/sage/schemes/toric/morphism.py b/src/sage/schemes/toric/morphism.py
index d5b61a651f1..c22f88e99fb 100644
--- a/src/sage/schemes/toric/morphism.py
+++ b/src/sage/schemes/toric/morphism.py
@@ -1537,12 +1537,11 @@ def fiber_dimension(self, codomain_cone):
         dim = []
         fm = self.fan_morphism()
         base_dim = codomain_cone.dim()
-        for c in fm.primitive_preimage_cones(codomain_cone):
-            dim.append(base_dim - c.dim())
+        dim.extend(base_dim - c.dim()
+                   for c in fm.primitive_preimage_cones(codomain_cone))
         if dim:
             return max(dim) + self.domain().dimension() - self.codomain().dimension()
-        else:
-            return ZZ(-1)
+        return ZZ(-1)
 
     def fiber_graph(self, codomain_cone):
         r"""
diff --git a/src/sage/schemes/toric/points.py b/src/sage/schemes/toric/points.py
index 20507e5c0af..799606a4d1f 100644
--- a/src/sage/schemes/toric/points.py
+++ b/src/sage/schemes/toric/points.py
@@ -44,7 +44,7 @@
 from sage.parallel.decorate import Parallel
 
 
-class InfinitePointEnumerator():
+class InfinitePointEnumerator:
 
     def __init__(self, fan, ring):
         """
@@ -105,7 +105,7 @@ def __iter__(self):
                 yield tuple(p)
 
 
-class NaiveFinitePointEnumerator():
+class NaiveFinitePointEnumerator:
 
     def __init__(self, fan, ring):
         """
@@ -756,7 +756,7 @@ def cardinality(self):
         return n
 
 
-class NaiveSubschemePointEnumerator():
+class NaiveSubschemePointEnumerator:
 
     def __init__(self, polynomials, ambient):
         """
diff --git a/src/sage/schemes/toric/sheaf/constructor.py b/src/sage/schemes/toric/sheaf/constructor.py
index 72ed52a0115..0fb0c0a34e5 100644
--- a/src/sage/schemes/toric/sheaf/constructor.py
+++ b/src/sage/schemes/toric/sheaf/constructor.py
@@ -138,7 +138,7 @@ def LineBundle(X, D):
     return klyachko.Bundle(X, filtrations, check=True)
 
 
-class SheafLibrary():
+class SheafLibrary:
 
     def __init__(self, toric_variety):
         """
diff --git a/src/sage/sets/recursively_enumerated_set.pyx b/src/sage/sets/recursively_enumerated_set.pyx
index 56bd8d16850..183d52b8c4a 100644
--- a/src/sage/sets/recursively_enumerated_set.pyx
+++ b/src/sage/sets/recursively_enumerated_set.pyx
@@ -209,7 +209,7 @@ or even::
 We can then create the :class:`RecursivelyEnumeratedSet` object with either::
 
     sage: S = RecursivelyEnumeratedSet([''],
-    ....:     lambda x: [x+letter for letter in ['a', 'b', 'c']]
+    ....:     lambda x: [x + letter for letter in ['a', 'b', 'c']]
     ....:               if len(x) < 2 else [],
     ....:     structure='forest', enumeration='depth',
     ....:     category=FiniteEnumeratedSets())
@@ -248,8 +248,8 @@ convention is that the generated elements are the ``s := f(n)``, except when
     ....:     st = set(st) # make a copy
     ....:     if st:
     ....:        el = st.pop()
-    ....:        for i in range(len(lst)+1):
-    ....:            yield (lst[0:i]+[el]+lst[i:], st)
+    ....:        for i in range(len(lst) + 1):
+    ....:            yield (lst[0:i] + [el] + lst[i:], st)
     sage: list(children(([1,2], {3,7,9})))
     [([9, 1, 2], {3, 7}), ([1, 9, 2], {3, 7}), ([1, 2, 9], {3, 7})]
     sage: def post_process(node):
@@ -347,8 +347,7 @@ def RecursivelyEnumeratedSet(seeds, successors, structure=None,
     A recursive set with a forest structure::
 
         sage: f = lambda a: [2*a,2*a+1]
-        sage: C = RecursivelyEnumeratedSet([1], f, structure='forest')
-        sage: C
+        sage: C = RecursivelyEnumeratedSet([1], f, structure='forest'); C
         An enumerated set with a forest structure
         sage: it = C.depth_first_search_iterator()
         sage: [next(it) for _ in range(7)]
@@ -676,7 +675,7 @@ cdef class RecursivelyEnumeratedSet_generic(Parent):
 
         EXAMPLES::
 
-            sage: R = RecursivelyEnumeratedSet([1], lambda x: [x+1, x-1])
+            sage: R = RecursivelyEnumeratedSet([1], lambda x: [x + 1, x - 1])
             sage: R.seeds()
             [1]
         """
@@ -1125,7 +1124,7 @@ cdef class RecursivelyEnumeratedSet_symmetric(RecursivelyEnumeratedSet_generic):
             sage: # needs sage.symbolic
             sage: def f(a):
             ....:     sleep(0.05r)
-            ....:     return [a-1,a+1]
+            ....:     return [a - 1, a + 1]
             sage: C = RecursivelyEnumeratedSet([0], f, structure='symmetric')
             sage: it = C.graded_component_iterator()
             sage: next(it)
@@ -1185,7 +1184,7 @@ cdef class RecursivelyEnumeratedSet_symmetric(RecursivelyEnumeratedSet_generic):
 
             sage: def f(a):
             ....:    sleep(0.1r)
-            ....:    return [a-1,a+1]
+            ....:    return [a - 1, a + 1]
             sage: C = RecursivelyEnumeratedSet([0], f, structure='symmetric')
             sage: from cysignals.alarm import alarm
             sage: alarm(0.45); C.graded_component(10)
@@ -1410,7 +1409,7 @@ cdef class RecursivelyEnumeratedSet_graded(RecursivelyEnumeratedSet_generic):
             sage: # needs sage.symbolic
             sage: def f(a):
             ....:    sleep(0.1r)
-            ....:    return [a+1, a+I]
+            ....:    return [a + 1, a + I]
             sage: C = RecursivelyEnumeratedSet([0], f, structure='graded')
             sage: from cysignals.alarm import alarm
             sage: alarm(0.45); C.graded_component(10)
@@ -1481,7 +1480,7 @@ def _imap_and_filter_none(function, iterable):
         sage: p = _imap_and_filter_none(lambda x: x if is_prime(x) else None, range(15))
         sage: [next(p), next(p), next(p), next(p), next(p), next(p)]
         [2, 3, 5, 7, 11, 13]
-        sage: p = _imap_and_filter_none(lambda x: x+x, ['a','b','c','d','e'])
+        sage: p = _imap_and_filter_none(lambda x: x + x, ['a','b','c','d','e'])
         sage: [next(p), next(p), next(p), next(p), next(p)]
         ['aa', 'bb', 'cc', 'dd', 'ee']
     """
@@ -1509,7 +1508,7 @@ def search_forest_iterator(roots, children, algorithm='depth'):
     three, and enumerate its nodes::
 
         sage: from sage.sets.recursively_enumerated_set import search_forest_iterator
-        sage: list(search_forest_iterator([[]], lambda l: [l+[0], l+[1]]
+        sage: list(search_forest_iterator([[]], lambda l: [l + [0], l + [1]]
         ....:                                   if len(l) < 3 else []))
         [[], [0], [0, 0], [0, 0, 0], [0, 0, 1], [0, 1], [0, 1, 0],
          [0, 1, 1], [1], [1, 0], [1, 0, 0], [1, 0, 1], [1, 1], [1, 1, 0], [1, 1, 1]]
@@ -1517,7 +1516,7 @@ def search_forest_iterator(roots, children, algorithm='depth'):
     By default, the nodes are iterated through by depth first search.
     We can instead use a breadth first search (increasing depth)::
 
-        sage: list(search_forest_iterator([[]], lambda l: [l+[0], l+[1]]
+        sage: list(search_forest_iterator([[]], lambda l: [l + [0], l + [1]]
         ....:                                   if len(l) < 3 else [],
         ....:                             algorithm='breadth'))
         [[],
@@ -1528,7 +1527,8 @@ def search_forest_iterator(roots, children, algorithm='depth'):
 
     This allows for iterating through trees of infinite depth::
 
-        sage: it = search_forest_iterator([[]], lambda l: [l+[0], l+[1]], algorithm='breadth')
+        sage: it = search_forest_iterator([[]], lambda l: [l + [0], l + [1]],
+        ....:                             algorithm='breadth')
         sage: [ next(it) for i in range(16) ]
         [[],
          [0], [1], [0, 0], [0, 1], [1, 0], [1, 1],
@@ -1607,7 +1607,7 @@ class RecursivelyEnumeratedSet_forest(Parent):
 
         sage: from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest
         sage: S = RecursivelyEnumeratedSet_forest( [[]],
-        ....:     lambda l: [l+[0], l+[1]] if len(l) < 3 else [],
+        ....:     lambda l: [l + [0], l + [1]] if len(l) < 3 else [],
         ....:     category=FiniteEnumeratedSets())
         sage: S.list()
         [[],
@@ -1691,9 +1691,10 @@ class RecursivelyEnumeratedSet_forest(Parent):
         sage: class A(UniqueRepresentation, RecursivelyEnumeratedSet_forest):
         ....:     def __init__(self):
         ....:         RecursivelyEnumeratedSet_forest.__init__(self, [()],
-        ....:             lambda x : [x+(0,), x+(1,)] if sum(x) < 3 else [],
-        ....:             lambda x : sum(x[i]*2^i for i in range(len(x))) if sum(x) != 0 and x[-1] != 0 else None,
-        ....:             algorithm = 'breadth',
+        ....:             lambda x: [x + (0,), x + (1,)] if sum(x) < 3 else [],
+        ....:             lambda x: sum(x[i]*2^i for i in range(len(x)))
+        ....:                           if sum(x) != 0 and x[-1] != 0 else None,
+        ....:             algorithm='breadth',
         ....:             category=InfiniteEnumeratedSets())
         sage: MyForest = A(); MyForest
         An enumerated set with a forest structure
@@ -1701,7 +1702,8 @@ class RecursivelyEnumeratedSet_forest(Parent):
         Category of infinite enumerated sets
         sage: p = iter(MyForest)
         sage: [next(p) for i in range(30)]
-        [1, 2, 3, 4, 6, 5, 7, 8, 12, 10, 14, 9, 13, 11, 16, 24, 20, 28, 18, 26, 22, 17, 25, 21, 19, 32, 48, 40, 56, 36]
+        [1, 2, 3, 4, 6, 5, 7, 8, 12, 10, 14, 9, 13, 11, 16, 24,
+         20, 28, 18, 26, 22, 17, 25, 21, 19, 32, 48, 40, 56, 36]
 
     An alternative approach is to implement ``roots`` and ``children``
     as methods of the subclass (in fact they could also be attributes
@@ -1714,13 +1716,13 @@ class RecursivelyEnumeratedSet_forest(Parent):
         sage: from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest
         sage: class A(UniqueRepresentation, RecursivelyEnumeratedSet_forest):
         ....:     def __init__(self):
-        ....:         RecursivelyEnumeratedSet_forest.__init__(self, algorithm = 'breadth',
+        ....:         RecursivelyEnumeratedSet_forest.__init__(self, algorithm='breadth',
         ....:                               category=InfiniteEnumeratedSets())
         ....:     def roots(self):
         ....:         return [()]
         ....:     def children(self, x):
         ....:         if sum(x) < 3:
-        ....:             return [x+(0,), x+(1,)]
+        ....:             return [x + (0,), x + (1,)]
         ....:         else:
         ....:             return []
         ....:     def post_process(self, x):
@@ -1734,7 +1736,8 @@ class RecursivelyEnumeratedSet_forest(Parent):
         Category of infinite enumerated sets
         sage: p = iter(MyForest)
         sage: [next(p) for i in range(30)]
-        [1, 2, 3, 4, 6, 5, 7, 8, 12, 10, 14, 9, 13, 11, 16, 24, 20, 28, 18, 26, 22, 17, 25, 21, 19, 32, 48, 40, 56, 36]
+        [1, 2, 3, 4, 6, 5, 7, 8, 12, 10, 14, 9, 13, 11, 16, 24,
+         20, 28, 18, 26, 22, 17, 25, 21, 19, 32, 48, 40, 56, 36]
 
     .. warning::
 
@@ -1743,8 +1746,8 @@ class RecursivelyEnumeratedSet_forest(Parent):
         anonymous or interactively defined functions::
 
             sage: def children(x):
-            ....:     return [x+1]
-            sage: S = RecursivelyEnumeratedSet_forest( [1], children, category=InfiniteEnumeratedSets())
+            ....:     return [x + 1]
+            sage: S = RecursivelyEnumeratedSet_forest([1], children, category=InfiniteEnumeratedSets())
             sage: dumps(S)
             Traceback (most recent call last):
             ...
@@ -1754,7 +1757,7 @@ class RecursivelyEnumeratedSet_forest(Parent):
 
             sage: import __main__
             sage: __main__.children = children
-            sage: S = RecursivelyEnumeratedSet_forest( [1], children, category=InfiniteEnumeratedSets())
+            sage: S = RecursivelyEnumeratedSet_forest([1], children, category=InfiniteEnumeratedSets())
             sage: loads(dumps(S))
             An enumerated set with a forest structure
     """
@@ -1764,7 +1767,7 @@ class RecursivelyEnumeratedSet_forest(Parent):
         TESTS::
 
             sage: from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest
-            sage: S = RecursivelyEnumeratedSet_forest(NN, lambda x : [], lambda x: x^2 if x.is_prime() else None)
+            sage: S = RecursivelyEnumeratedSet_forest(NN, lambda x: [], lambda x: x^2 if x.is_prime() else None)
             sage: S.category()
             Category of enumerated sets
         """
@@ -1837,7 +1840,7 @@ class RecursivelyEnumeratedSet_forest(Parent):
 
             sage: from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest
             sage: def children(l):
-            ....:      return [l+[0], l+[1]]
+            ....:      return [l + [0], l + [1]]
             sage: C = RecursivelyEnumeratedSet_forest(([],), children)
             sage: f = C.__iter__()
             sage: next(f)
@@ -1862,9 +1865,10 @@ class RecursivelyEnumeratedSet_forest(Parent):
 
             sage: from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest
             sage: f = RecursivelyEnumeratedSet_forest([[]],
-            ....:                  lambda l: [l+[0], l+[1]] if len(l) < 3 else [])
+            ....:                  lambda l: [l + [0], l + [1]] if len(l) < 3 else [])
             sage: list(f.depth_first_search_iterator())
-            [[], [0], [0, 0], [0, 0, 0], [0, 0, 1], [0, 1], [0, 1, 0], [0, 1, 1], [1], [1, 0], [1, 0, 0], [1, 0, 1], [1, 1], [1, 1, 0], [1, 1, 1]]
+            [[], [0], [0, 0], [0, 0, 0], [0, 0, 1], [0, 1], [0, 1, 0], [0, 1, 1],
+                 [1], [1, 0], [1, 0, 0], [1, 0, 1], [1, 1], [1, 1, 0], [1, 1, 1]]
         """
         return iter(self)
 
@@ -1963,7 +1967,8 @@ class RecursivelyEnumeratedSet_forest(Parent):
         EXAMPLES::
 
             sage: from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest
-            sage: S = RecursivelyEnumeratedSet_forest( [[]], lambda l: [l+[0], l+[1]] if len(l) < 3 else [], category=FiniteEnumeratedSets())
+            sage: S = RecursivelyEnumeratedSet_forest([[]], lambda l: [l + [0], l + [1]] if len(l) < 3 else [],
+            ....:                                     category=FiniteEnumeratedSets())
             sage: [4] in S
             False
             sage: [1] in S
@@ -1972,12 +1977,12 @@ class RecursivelyEnumeratedSet_forest(Parent):
             False
             sage: all(S.__contains__(i) for i in iter(S))
             True
-            sage: S = RecursivelyEnumeratedSet_forest([1], lambda x: [x+1], category=InfiniteEnumeratedSets())
+            sage: S = RecursivelyEnumeratedSet_forest([1], lambda x: [x + 1], category=InfiniteEnumeratedSets())
             sage: 1 in S
             True
             sage: 732 in S
             True
-            sage: -1 in S # not tested : Will never stop
+            sage: -1 in S  # not tested : Will never stop
 
         The algorithm uses a random enumeration of the nodes of the
         forest. This choice was motivated by examples in which both
@@ -1987,8 +1992,9 @@ class RecursivelyEnumeratedSet_forest(Parent):
         root has an infinite number of children::
 
             sage: from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest
-            sage: S = RecursivelyEnumeratedSet_forest(Family(NN, lambda x : (x, 0)),
-            ....: lambda x : Family(PositiveIntegers(), lambda y : (x[0], y)) if x[1] == 0 else [])
+            sage: S = RecursivelyEnumeratedSet_forest(
+            ....:         Family(NN, lambda x: (x, 0)),
+            ....:         lambda x: Family(PositiveIntegers(), lambda y: (x[0], y)) if x[1] == 0 else [])
             sage: p = S.depth_first_search_iterator()
             sage: [next(p), next(p), next(p), next(p), next(p), next(p), next(p)]
             [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6)]
@@ -2010,7 +2016,8 @@ class RecursivelyEnumeratedSet_forest(Parent):
         child. From each root starts an infinite branch of breadth
         `1`::
 
-            sage: S = RecursivelyEnumeratedSet_forest(Family(NN, lambda x : (x, 0)) , lambda x : [(x[0], x[1]+1)])
+            sage: S = RecursivelyEnumeratedSet_forest(Family(NN, lambda x: (x, 0)),
+            ....:                                     lambda x: [(x[0], x[1] + 1)])
             sage: p = S.depth_first_search_iterator()
             sage: [next(p), next(p), next(p), next(p), next(p), next(p), next(p)]
             [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6)]
@@ -2065,12 +2072,12 @@ class RecursivelyEnumeratedSet_forest(Parent):
 
         EXAMPLES::
 
-            sage: seeds = [([i],i, i) for i in range(1,10)]
+            sage: seeds = [([i], i, i) for i in range(1, 10)]
             sage: def succ(t):
             ....:     list, sum, last = t
             ....:     return [(list + [i], sum + i, i) for i in range(1, last)]
             sage: F = RecursivelyEnumeratedSet(seeds, succ,
-            ....:                       structure='forest', enumeration='depth')
+            ....:         structure='forest', enumeration='depth')
 
             sage: # needs sage.symbolic
             sage: y = var('y')
diff --git a/src/sage/sets/set.py b/src/sage/sets/set.py
index fe351298d0a..bdabfee5098 100644
--- a/src/sage/sets/set.py
+++ b/src/sage/sets/set.py
@@ -1720,7 +1720,9 @@ def __init__(self, X, Y, category=None):
             sage: S = Set(QQ)
             sage: T = Set(ZZ)
             sage: X = S.difference(T); X
-            Set-theoretic difference of Set of elements of Rational Field and Set of elements of Integer Ring
+            Set-theoretic difference of
+             Set of elements of Rational Field and
+             Set of elements of Integer Ring
             sage: X.category()
             Category of sets
             sage: latex(X)
diff --git a/src/sage/sets/totally_ordered_finite_set.py b/src/sage/sets/totally_ordered_finite_set.py
index 7efba359dc0..9191992c285 100644
--- a/src/sage/sets/totally_ordered_finite_set.py
+++ b/src/sage/sets/totally_ordered_finite_set.py
@@ -45,7 +45,7 @@ def __init__(self, parent, data):
         r"""
         TESTS::
 
-            sage: T = TotallyOrderedFiniteSet([3,2,1],facade=False)
+            sage: T = TotallyOrderedFiniteSet([3,2,1], facade=False)
             sage: TestSuite(T.an_element()).run()
         """
         Element.__init__(self, parent)
diff --git a/src/sage/structure/category_object.pyx b/src/sage/structure/category_object.pyx
index 48cee10c2bc..f7e8c4f6d11 100644
--- a/src/sage/structure/category_object.pyx
+++ b/src/sage/structure/category_object.pyx
@@ -64,11 +64,13 @@ from sage.structure.dynamic_class import DynamicMetaclass
 
 
 cpdef inline check_default_category(default_category, category):
-    ## The resulting category is guaranteed to be
-    ## a sub-category of the default.
+    """
+    The resulting category is guaranteed to be
+    a sub-category of the default.
+    """
     if category is None:
         return default_category
-    return default_category.join([default_category,category])
+    return default_category.join([default_category, category])
 
 
 cdef class CategoryObject(SageObject):
@@ -212,6 +214,7 @@ cdef class CategoryObject(SageObject):
             sage: ZZ.categories()
             [Join of Category of Dedekind domains
                  and Category of euclidean domains
+                 and Category of noetherian rings
                  and Category of infinite enumerated sets
                  and Category of metric spaces,
              Category of Dedekind domains,
@@ -220,7 +223,7 @@ cdef class CategoryObject(SageObject):
              Category of unique factorization domains,
              Category of gcd domains,
              Category of integral domains,
-             Category of domains,
+             Category of domains, ...
              Category of commutative rings, ...
              Category of monoids, ...,
              Category of commutative additive groups, ...,
diff --git a/src/sage/structure/coerce.pyx b/src/sage/structure/coerce.pyx
index 888643314f5..9f1819efdc0 100644
--- a/src/sage/structure/coerce.pyx
+++ b/src/sage/structure/coerce.pyx
@@ -491,7 +491,7 @@ cpdef bint is_mpmath_type(t) noexcept:
 
 cdef class CoercionModel:
     """
-    See also sage.categories.pushout
+    See also :mod:`sage.categories.pushout`
 
     EXAMPLES::
 
diff --git a/src/sage/structure/coerce_actions.pyx b/src/sage/structure/coerce_actions.pyx
index e1ff3b378e7..4d7975429e9 100644
--- a/src/sage/structure/coerce_actions.pyx
+++ b/src/sage/structure/coerce_actions.pyx
@@ -70,7 +70,7 @@ cdef class GenericAction(Action):
 
         This will break if we tried to use it::
 
-            sage: sage.structure.coerce_actions.GenericAction(QQ, Z6, True, check=False)
+            sage: GenericAction(QQ, Z6, True, check=False)
             Left action by Rational Field on Ring of integers modulo 6
 
         """
@@ -172,7 +172,8 @@ def detect_element_action(Parent X, Y, bint X_on_left, X_el=None, Y_el=None):
         sage: ZZx = ZZ['x']
         sage: M = MatrixSpace(ZZ, 2)                                                    # needs sage.modules
         sage: detect_element_action(ZZx, ZZ, False)
-        Left scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring
+        Left scalar multiplication by Integer Ring
+         on Univariate Polynomial Ring in x over Integer Ring
         sage: detect_element_action(ZZx, QQ, True)
         Right scalar multiplication by Rational Field
          on Univariate Polynomial Ring in x over Integer Ring
@@ -301,7 +302,8 @@ cdef class ModuleAction(Action):
             sage: LeftModuleAction(QQ, ZZx)
             Left scalar multiplication by Rational Field on Univariate Polynomial Ring in x over Integer Ring
             sage: LeftModuleAction(QQ, ZZxy)
-            Left scalar multiplication by Rational Field on Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Integer Ring
+            Left scalar multiplication by Rational Field
+             on Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Integer Ring
 
         The following tests against a problem that was relevant during work on
         :issue:`9944`::
@@ -313,6 +315,23 @@ cdef class ModuleAction(Action):
             sage: 1/S.0
             1/x
 
+        If there is a coercion from ``G`` to ``S``, we do not create
+        the module action of ``G`` on the pushout of ``G`` and ``S``::
+
+            sage: G = PolynomialRing(QQ, "x")
+            sage: S = PolynomialRing(MatrixSpace(QQ, 2), "x")
+            sage: G.gen() * S.gen()
+            [1 0]
+            [0 1]*x^2
+
+        Contrast the previous example with the following, where we
+        have no coercion from ``G`` to ``S``::
+
+            sage: S = PolynomialRing(MatrixSpace(QQ, 2), "y")
+            sage: G.gen() * S.gen()
+            [x 0]
+            [0 x]*y
+
         """
         Action.__init__(self, G, S, not isinstance(self, RightModuleAction), operator.mul)
         if not isinstance(G, Parent):
@@ -328,6 +347,8 @@ cdef class ModuleAction(Action):
             # first we try the easy case of coercing G to the base ring of S
             self.connecting = base._internal_coerce_map_from(G)
             if self.connecting is None:
+                if S._internal_coerce_map_from(G) is not None:
+                    raise CoercionException("Best viewed as standard coercion multiplication.")
                 # otherwise, we try and find a base extension
                 from sage.categories.pushout import pushout
                 # this may raise a type error, which we propagate
@@ -399,7 +420,8 @@ cdef class ModuleAction(Action):
             sage: from sage.structure.coerce_actions import LeftModuleAction, RightModuleAction
             sage: ZZx = ZZ['x']
             sage: A = LeftModuleAction(ZZ, ZZx); A
-            Left scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring
+            Left scalar multiplication by Integer Ring
+             on Univariate Polynomial Ring in x over Integer Ring
             sage: A._repr_name_()
             'scalar multiplication'
 
@@ -533,7 +555,8 @@ cdef class ModuleAction(Action):
             sage: cm = sage.structure.element.get_coercion_model()
             sage: cm.explain(x, 1, operator.truediv)
             Action discovered.
-                Right inverse action by Symbolic Constants Subring on Univariate Polynomial Ring in x over Symbolic Constants Subring
+                Right inverse action by Symbolic Constants Subring
+                 on Univariate Polynomial Ring in x over Symbolic Constants Subring
                 with precomposition on right by Conversion via _symbolic_ method map:
                   From: Integer Ring
                   To:   Symbolic Constants Subring
diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx
index 6ee4d7c8201..46037b048a5 100644
--- a/src/sage/structure/element.pyx
+++ b/src/sage/structure/element.pyx
@@ -2860,11 +2860,11 @@ cdef class RingElement(ModuleElement):
 
         EXAMPLES::
 
-            sage: RR(-1).abs()
+            sage: RR(-1).abs()                                                          # needs sage.rings.real_mpfr
             1.00000000000000
             sage: ZZ(-1).abs()
             1
-            sage: CC(I).abs()
+            sage: CC(I).abs()                                                           # needs sage.rings.real_mpfr sage.symbolic
             1.00000000000000
             sage: Mod(-15, 37).abs()
             Traceback (most recent call last):
diff --git a/src/sage/structure/factorization.py b/src/sage/structure/factorization.py
index 00ad9658e59..d17480fc6aa 100644
--- a/src/sage/structure/factorization.py
+++ b/src/sage/structure/factorization.py
@@ -876,7 +876,7 @@ def _latex_(self):
             -1 \cdot 2^{2} \cdot 5^{2}
             sage: f._latex_()
             '-1 \\cdot 2^{2} \\cdot 5^{2}'
-            sage: x = AA['x'].0; factor(x^2 + x + 1)._latex_() # trac 12178             # needs sage.rings.number_field
+            sage: x = AA['x'].0; factor(x^2 + x + 1)._latex_()  # Issue #12178          # needs sage.rings.number_field
             '(x^{2} + x + 1.000000000000000?)'
         """
         if len(self) == 0:
diff --git a/src/sage/structure/global_options.py b/src/sage/structure/global_options.py
index 01bbfd79ad9..ce188a58f21 100644
--- a/src/sage/structure/global_options.py
+++ b/src/sage/structure/global_options.py
@@ -560,7 +560,7 @@ def __repr__(self):
 
         EXAMPLES::
 
-            sage: Partitions.options.display # indirect doctest                         # needs sage.combinat
+            sage: Partitions.options.display  # indirect doctest                        # needs sage.combinat
             list
         """
         # NOTE: we intentionally use str() instead of repr()
@@ -1389,7 +1389,7 @@ def __eq__(self, other):
 
         EXAMPLES::
 
-            sage: Partitions.options == PartitionsGreatestLE.options # indirect doctest             # needs sage.combinat
+            sage: Partitions.options == PartitionsGreatestLE.options  # indirect doctest            # needs sage.combinat
             True
             sage: Partitions.options == Tableaux.options                                # needs sage.combinat
             False
diff --git a/src/sage/structure/indexed_generators.py b/src/sage/structure/indexed_generators.py
index f0d521bee87..42bcecf8938 100644
--- a/src/sage/structure/indexed_generators.py
+++ b/src/sage/structure/indexed_generators.py
@@ -248,7 +248,7 @@ def print_options(self, **kwds):
              ('sorting_key',  at ...>),
              ('sorting_reverse', False), ('string_quotes', True),
              ('tensor_symbol', None)]
-            sage: F.print_options(bracket='[') # reset                                  # needs sage.modules
+            sage: F.print_options(bracket='[')  # reset                                 # needs sage.modules
         """
         # don't just use kwds.get(...) because I want to distinguish
         # between an argument like "option=None" and the option not
@@ -399,7 +399,7 @@ def _repr_generator(self, m):
             sage: a              # indirect doctest                                     # needs sage.combinat sage.modules
             2*|1, 2, 3| + 4*|3, 2, 1|
 
-            sage: QS3.print_options(**original_print_options) # reset                   # needs sage.combinat sage.modules
+            sage: QS3.print_options(**original_print_options)  # reset                  # needs sage.combinat sage.modules
 
         TESTS::
 
diff --git a/src/sage/structure/proof/proof.py b/src/sage/structure/proof/proof.py
index 49d4ecebe5a..5f55e04fbb5 100644
--- a/src/sage/structure/proof/proof.py
+++ b/src/sage/structure/proof/proof.py
@@ -192,7 +192,7 @@ def get_flag(t=None, subsystem=None):
 
 class WithProof():
     """
-    Use WithProof to temporarily set the value of one of the proof
+    Use :class:`WithProof` to temporarily set the value of one of the proof
     systems for a block of code, with a guarantee that it will be set
     back to how it was before after the block is done, even if there is an error.
 
diff --git a/src/sage/symbolic/expression_conversion_algebraic.py b/src/sage/symbolic/expression_conversion_algebraic.py
index d5764cb2421..11314aaeb70 100644
--- a/src/sage/symbolic/expression_conversion_algebraic.py
+++ b/src/sage/symbolic/expression_conversion_algebraic.py
@@ -146,9 +146,9 @@ def composition(self, ex, operator):
             sage: a.composition(complex_root_of(x^5 - 1, 3), complex_root_of)
             0.3090169943749474? - 0.9510565162951536?*I
             sage: a.composition(complex_root_of(x^2 + 1, 0), complex_root_of)
-            1.?e-684 - 0.9999999999999999?*I
+            1.?e-884 - 0.9999999999999999?*I
             sage: a.composition(complex_root_of(x^2 + 1, 1), complex_root_of)
-            1.?e-684 + 0.9999999999999999?*I
+            1.?e-884 + 0.9999999999999999?*I
 
         TESTS::
 
diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py
index cb0922eaa10..924dce1dc15 100644
--- a/src/sage/tensor/modules/finite_rank_free_module.py
+++ b/src/sage/tensor/modules/finite_rank_free_module.py
@@ -248,7 +248,7 @@ class :class:`~sage.modules.free_module.FreeModule_generic`
     Category of finite dimensional modules over Integer Ring
     sage: N.category()
     Category of finite dimensional modules with basis over
-     (Dedekind domains and euclidean domains
+     (Dedekind domains and euclidean domains and noetherian rings
      and infinite enumerated sets and metric spaces)
 
 In other words, the module created by ``FreeModule`` is actually `\ZZ^3`,
diff --git a/src/sage/tensor/modules/free_module_homset.py b/src/sage/tensor/modules/free_module_homset.py
index 509ae146d49..ff774f540ed 100644
--- a/src/sage/tensor/modules/free_module_homset.py
+++ b/src/sage/tensor/modules/free_module_homset.py
@@ -4,9 +4,13 @@
 The class :class:`FreeModuleHomset` implements sets of homomorphisms between
 two free modules of finite rank over the same commutative ring.
 
+The subclass :class:`FreeModuleEndset` implements the special case of
+sets of endomorphisms.
+
 AUTHORS:
 
 - Eric Gourgoulhon, Michal Bejger (2014-2015): initial version
+- Matthias Koeppe (2024): add :class:`FreeModuleEndset`
 
 REFERENCES:
 
@@ -25,11 +29,13 @@
 #******************************************************************************
 
 from sage.categories.homset import Homset
-from sage.tensor.modules.free_module_morphism import FiniteRankFreeModuleMorphism
+from sage.misc.classcall_metaclass import ClasscallMetaclass
+from sage.tensor.modules.free_module_morphism import FiniteRankFreeModuleEndomorphism, FiniteRankFreeModuleMorphism
 from sage.tensor.modules.free_module_automorphism import FreeModuleAutomorphism
 from sage.tensor.modules.free_module_tensor import FreeModuleTensor
 
-class FreeModuleHomset(Homset):
+
+class FreeModuleHomset(Homset, metaclass=ClasscallMetaclass):
     r"""
     Set of homomorphisms between free modules of finite rank over a
     commutative ring.
@@ -43,6 +49,8 @@ class FreeModuleHomset(Homset):
     This is a Sage *parent* class, whose *element* class is
     :class:`~sage.tensor.modules.free_module_morphism.FiniteRankFreeModuleMorphism`.
 
+    The case `M=N` (endomorphisms) is delegated to the subclass :class:`FreeModuleEndset`.
+
     INPUT:
 
     - ``fmodule1`` -- free module `M` (domain of the homomorphisms), as an
@@ -117,99 +125,24 @@ class FreeModuleHomset(Homset):
     The test suite for H is passed::
 
         sage: TestSuite(H).run()
-
-    The set of homomorphisms `M\rightarrow M`, i.e. endomorphisms, is
-    obtained by the function ``End``::
-
-        sage: End(M)
-        Set of Morphisms from Rank-3 free module M over the Integer Ring
-         to Rank-3 free module M over the Integer Ring
-         in Category of finite dimensional modules over Integer Ring
-
-    ``End(M)`` is actually identical to ``Hom(M,M)``::
-
-        sage: End(M) is Hom(M,M)
-        True
-
-    The unit of the endomorphism ring is the identity map::
-
-        sage: End(M).one()
-        Identity endomorphism of Rank-3 free module M over the Integer Ring
-
-    whose matrix in any basis is of course the identity matrix::
-
-        sage: End(M).one().matrix(e)
-        [1 0 0]
-        [0 1 0]
-        [0 0 1]
-
-    There is a canonical identification between endomorphisms of `M` and
-    tensors of type `(1,1)` on `M`. Accordingly, coercion maps have been
-    implemented between `\mathrm{End}(M)` and `T^{(1,1)}(M)` (the module of
-    all type-`(1,1)` tensors on `M`, see
-    :class:`~sage.tensor.modules.tensor_free_module.TensorFreeModule`)::
-
-        sage: T11 = M.tensor_module(1,1) ; T11
-        Free module of type-(1,1) tensors on the Rank-3 free module M over
-         the Integer Ring
-        sage: End(M).has_coerce_map_from(T11)
-        True
-        sage: T11.has_coerce_map_from(End(M))
-        True
-
-    See :class:`~sage.tensor.modules.tensor_free_module.TensorFreeModule` for
-    examples of the above coercions.
-
-    There is a coercion `\mathrm{GL}(M) \rightarrow \mathrm{End}(M)`, since
-    every automorphism is an endomorphism::
-
-        sage: GL = M.general_linear_group() ; GL
-        General linear group of the Rank-3 free module M over the Integer Ring
-        sage: End(M).has_coerce_map_from(GL)
-        True
-
-    Of course, there is no coercion in the reverse direction, since only
-    bijective endomorphisms are automorphisms::
-
-        sage: GL.has_coerce_map_from(End(M))
-        False
-
-    The coercion `\mathrm{GL}(M) \rightarrow \mathrm{End}(M)` in action::
-
-        sage: a = GL.an_element() ; a
-        Automorphism of the Rank-3 free module M over the Integer Ring
-        sage: a.matrix(e)
-        [ 1  0  0]
-        [ 0 -1  0]
-        [ 0  0  1]
-        sage: ea = End(M)(a) ; ea
-        Generic endomorphism of Rank-3 free module M over the Integer Ring
-        sage: ea.matrix(e)
-        [ 1  0  0]
-        [ 0 -1  0]
-        [ 0  0  1]
-
     """
 
     Element = FiniteRankFreeModuleMorphism
 
-    def __init__(self, fmodule1, fmodule2, name=None, latex_name=None):
+    @staticmethod
+    def __classcall_private__(cls, fmodule1, fmodule2, name=None, latex_name=None):
         r"""
+        Delegate to the subclass :class:`FreeModuleEndset` if ``fmodule1 == fmodule2``.
+
         TESTS::
 
-            sage: from sage.tensor.modules.free_module_homset import FreeModuleHomset
+            sage: from sage.tensor.modules.free_module_homset import FreeModuleEndset, FreeModuleHomset
             sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
             sage: N = FiniteRankFreeModule(ZZ, 2, name='N')
-            sage: FreeModuleHomset(M, N)
-            Set of Morphisms from Rank-3 free module M over the Integer Ring
-             to Rank-2 free module N over the Integer Ring
-             in Category of finite dimensional modules over Integer Ring
-
-            sage: H = FreeModuleHomset(M, N, name='L(M,N)',
-            ....:                      latex_name=r'\mathcal{L}(M,N)')
-            sage: latex(H)
-            \mathcal{L}(M,N)
-
+            sage: isinstance(FreeModuleHomset(M, N), FreeModuleEndset)
+            False
+            sage: isinstance(FreeModuleHomset(M, M), FreeModuleEndset)
+            True
         """
         from .finite_rank_free_module import FiniteRankFreeModule
         if not isinstance(fmodule1, FiniteRankFreeModule):
@@ -221,19 +154,37 @@ def __init__(self, fmodule1, fmodule2, name=None, latex_name=None):
         if fmodule1.base_ring() != fmodule2.base_ring():
             raise TypeError("the domain and codomain are not defined over " +
                             "the same ring")
-        Homset.__init__(self, fmodule1, fmodule2)
         if name is None:
-            self._name = "Hom(" + fmodule1._name + "," + fmodule2._name + ")"
-        else:
-            self._name = name
+            name = "Hom(" + fmodule1._name + "," + fmodule2._name + ")"
         if latex_name is None:
-            self._latex_name = \
-                    r"\mathrm{Hom}\left(" + fmodule1._latex_name + "," + \
-                    fmodule2._latex_name + r"\right)"
-        else:
-            self._latex_name = latex_name
-        self._one = None # to be set by self.one() if self is an endomorphism
-                         # set (fmodule1 = fmodule2)
+            latex_name = \
+                r"\mathrm{Hom}\left(" + fmodule1._latex_name + "," + \
+                fmodule2._latex_name + r"\right)"
+        if fmodule1 == fmodule2:
+            return FreeModuleEndset(fmodule1, name, latex_name)
+        return type.__call__(cls, fmodule1, fmodule2, name, latex_name)
+
+    def __init__(self, fmodule1, fmodule2, name, latex_name):
+        r"""
+        TESTS::
+
+            sage: from sage.tensor.modules.free_module_homset import FreeModuleHomset
+            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+            sage: N = FiniteRankFreeModule(ZZ, 2, name='N')
+            sage: FreeModuleHomset(M, N)
+            Set of Morphisms from Rank-3 free module M over the Integer Ring
+             to Rank-2 free module N over the Integer Ring
+             in Category of finite dimensional modules over Integer Ring
+
+            sage: H = FreeModuleHomset(M, N, name='L(M,N)',
+            ....:                      latex_name=r'\mathcal{L}(M,N)')
+            sage: latex(H)
+            \mathcal{L}(M,N)
+
+        """
+        Homset.__init__(self, fmodule1, fmodule2)
+        self._name = name
+        self._latex_name = latex_name
 
     def _latex_(self):
         r"""
@@ -370,35 +321,10 @@ def _element_constructor_(self, matrix_rep, bases=None, name=None,
             True
 
         """
-        if isinstance(matrix_rep, FreeModuleTensor):
-            # coercion of a type-(1,1) tensor to an endomorphism
-            # (this includes automorphisms, since the class
-            #  FreeModuleAutomorphism inherits from FreeModuleTensor)
-            tensor = matrix_rep # for readability
-            if tensor.tensor_type() == (1,1) and \
-                                         self.is_endomorphism_set() and \
-                                         tensor.base_module() is self.domain():
-                basis = tensor.pick_a_basis()
-                tcomp = tensor.comp(basis)
-                fmodule = tensor.base_module()
-                mat = [[ tcomp[[i,j]] for j in fmodule.irange()]
-                       for i in fmodule.irange()]
-                if isinstance(tensor, FreeModuleAutomorphism):
-                    is_identity = tensor._is_identity
-                else:
-                    is_identity = False
-                resu = self.element_class(self, mat, bases=(basis,basis),
-                              name=tensor._name, latex_name=tensor._latex_name,
-                              is_identity=is_identity)
-            else:
-                raise TypeError("cannot coerce the {}".format(tensor) +
-                                " to an element of {}".format(self))
-        else:
-            # Standard construction:
-            resu = self.element_class(self, matrix_rep, bases=bases, name=name,
-                                      latex_name=latex_name,
-                                      is_identity=is_identity)
-        return resu
+        # Standard construction:
+        return self.element_class(self, matrix_rep, bases=bases, name=name,
+                                  latex_name=latex_name,
+                                  is_identity=is_identity)
 
     def _an_element_(self):
         r"""
@@ -426,14 +352,136 @@ def _an_element_(self):
         matrix_rep = [[ring.an_element() for i in range(m)] for j in range(n)]
         return self.element_class(self, matrix_rep)
 
+    #### End of methods required for any Parent
+
+
+class FreeModuleEndset(FreeModuleHomset):
+    r"""
+    Ring of endomorphisms of a free module of finite rank over a commutative ring.
+
+    Given a free modules `M` of rank `n` over a commutative ring `R`, the
+    class :class:`FreeModuleEndset` implements the ring `\mathrm{Hom}(M,M)`
+    of endomorphisms `M\rightarrow M`.
+
+    This is a Sage *parent* class, whose *element* class is
+    :class:`~sage.tensor.modules.free_module_morphism.FiniteRankFreeModuleMorphism`.
+
+    INPUT:
+
+    - ``fmodule`` -- free module `M` (domain and codomain of the endomorphisms), as an
+      instance of
+      :class:`~sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule`
+    - ``name`` -- (default: ``None``) string; name given to the end-set; if
+      none is provided, Hom(M,M) will be used
+    - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the
+      hom-set; if none is provided, `\mathrm{Hom}(M,M)` will be used
+
+    EXAMPLES:
+
+    The set of homomorphisms `M\rightarrow M`, i.e. endomorphisms, is
+    obtained by the function :func:`End`::
+
+        sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+        sage: e = M.basis('e')
+        sage: End(M)
+        Set of Morphisms from Rank-3 free module M over the Integer Ring
+         to Rank-3 free module M over the Integer Ring
+         in Category of finite dimensional modules over Integer Ring
+
+    ``End(M)`` is actually identical to ``Hom(M,M)``::
+
+        sage: End(M) is Hom(M,M)
+        True
+
+    The unit of the endomorphism ring is the identity map::
+
+        sage: End(M).one()
+        Identity endomorphism of Rank-3 free module M over the Integer Ring
+
+    whose matrix in any basis is of course the identity matrix::
+
+        sage: End(M).one().matrix(e)
+        [1 0 0]
+        [0 1 0]
+        [0 0 1]
+
+    There is a canonical identification between endomorphisms of `M` and
+    tensors of type `(1,1)` on `M`. Accordingly, coercion maps have been
+    implemented between `\mathrm{End}(M)` and `T^{(1,1)}(M)` (the module of
+    all type-`(1,1)` tensors on `M`, see
+    :class:`~sage.tensor.modules.tensor_free_module.TensorFreeModule`)::
+
+        sage: T11 = M.tensor_module(1,1) ; T11
+        Free module of type-(1,1) tensors on the Rank-3 free module M over
+         the Integer Ring
+        sage: End(M).has_coerce_map_from(T11)
+        True
+        sage: T11.has_coerce_map_from(End(M))
+        True
+
+    See :class:`~sage.tensor.modules.tensor_free_module.TensorFreeModule` for
+    examples of the above coercions.
+
+    There is a coercion `\mathrm{GL}(M) \rightarrow \mathrm{End}(M)`, since
+    every automorphism is an endomorphism::
+
+        sage: GL = M.general_linear_group() ; GL
+        General linear group of the Rank-3 free module M over the Integer Ring
+        sage: End(M).has_coerce_map_from(GL)
+        True
+
+    Of course, there is no coercion in the reverse direction, since only
+    bijective endomorphisms are automorphisms::
+
+        sage: GL.has_coerce_map_from(End(M))
+        False
+
+    The coercion `\mathrm{GL}(M) \rightarrow \mathrm{End}(M)` in action::
+
+        sage: a = GL.an_element() ; a
+        Automorphism of the Rank-3 free module M over the Integer Ring
+        sage: a.matrix(e)
+        [ 1  0  0]
+        [ 0 -1  0]
+        [ 0  0  1]
+        sage: ea = End(M)(a) ; ea
+        Generic endomorphism of Rank-3 free module M over the Integer Ring
+        sage: ea.matrix(e)
+        [ 1  0  0]
+        [ 0 -1  0]
+        [ 0  0  1]
+    """
+
+    Element = FiniteRankFreeModuleEndomorphism
+
+    def __init__(self, fmodule, name, latex_name):
+        r"""
+        TESTS::
+
+            sage: from sage.tensor.modules.free_module_homset import FreeModuleHomset
+            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+            sage: N = FiniteRankFreeModule(ZZ, 2, name='N')
+            sage: FreeModuleHomset(M, N)
+            Set of Morphisms from Rank-3 free module M over the Integer Ring
+             to Rank-2 free module N over the Integer Ring
+             in Category of finite dimensional modules over Integer Ring
+
+            sage: H = FreeModuleHomset(M, N, name='L(M,N)',
+            ....:                      latex_name=r'\mathcal{L}(M,N)')
+            sage: latex(H)
+            \mathcal{L}(M,N)
+
+        """
+        FreeModuleHomset.__init__(self, fmodule, fmodule, name, latex_name)
+        self._one = None  # to be set by self.one()
+
     def _coerce_map_from_(self, other):
         r"""
-        Determine whether coercion to self exists from other parent.
+        Determine whether coercion to ``self`` exists from other parent.
 
         EXAMPLES:
 
-        The module of type-`(1,1)` tensors coerces to ``self``, if the latter
-        is some endomorphism set::
+        The module of type-`(1,1)` tensors coerces to ``self``::
 
             sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
             sage: e = M.basis('e')
@@ -464,17 +512,98 @@ def _coerce_map_from_(self, other):
                                            other.base_module() is self.domain()
         return False
 
-    #### End of methods required for any Parent
+    #### Methods required for any Parent
+
+    def _element_constructor_(self, matrix_rep, bases=None, name=None,
+                              latex_name=None, is_identity=False):
+        r"""
+        Construct an element of ``self``, i.e. a homomorphism M --> N, where
+        M is the domain of ``self`` and N its codomain.
+
+        INPUT:
+
+        - ``matrix_rep`` -- matrix representation of the homomorphism with
+          respect to the bases ``basis1`` and ``basis2``; this entry can
+          actually be any material from which a matrix of size rank(N)*rank(M)
+          can be constructed
+        - ``bases`` -- (default: ``None``) pair (basis_M, basis_N) defining the
+          matrix representation, basis_M being a basis of module `M` and
+          basis_N a basis of module `N` ; if none is provided the pair formed
+          by the default bases of each module is assumed.
+        - ``name`` -- (default: ``None``) string; name given to the
+          homomorphism
+        - ``latex_name`` -- (default: ``None``)string;  LaTeX symbol to denote
+          the homomorphism; if none is provided, ``name`` will be used.
+        - ``is_identity`` -- (default: ``False``) determines whether the
+          constructed object is the identity endomorphism; if set to ``True``,
+          then N must be M and the entry ``matrix_rep`` is not used.
+
+        EXAMPLES:
+
+        Construction of an endomorphism::
+
+            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+            sage: e = M.basis('e')
+            sage: EM = End(M)
+            sage: phi = EM._element_constructor_([[1,2,3],[4,5,6],[7,8,9]],
+            ....:                                name='phi', latex_name=r'\phi')
+            sage: phi
+            Generic endomorphism of Rank-3 free module M over the Integer Ring
+            sage: phi.matrix(e,e)
+            [1 2 3]
+            [4 5 6]
+            [7 8 9]
+
+        Coercion of a type-`(1,1)` tensor to an endomorphism::
 
-    #### Monoid methods (case of an endomorphism set) ####
+            sage: a = M.tensor((1,1))
+            sage: a[:] = [[1,2,3],[4,5,6],[7,8,9]]
+            sage: EM = End(M)
+            sage: phi_a = EM._element_constructor_(a) ; phi_a
+            Generic endomorphism of Rank-3 free module M over the Integer Ring
+            sage: phi_a.matrix(e,e)
+            [1 2 3]
+            [4 5 6]
+            [7 8 9]
+            sage: phi_a == phi
+            True
+            sage: phi_a1 = EM(a) ; phi_a1  # indirect doctest
+            Generic endomorphism of Rank-3 free module M over the Integer Ring
+            sage: phi_a1 == phi
+            True
+
+        """
+        if isinstance(matrix_rep, FreeModuleTensor):
+            # coercion of a type-(1,1) tensor to an endomorphism
+            # (this includes automorphisms, since the class
+            #  FreeModuleAutomorphism inherits from FreeModuleTensor)
+            tensor = matrix_rep  # for readability
+            if tensor.tensor_type() == (1,1) and tensor.base_module() is self.domain():
+                basis = tensor.pick_a_basis()
+                tcomp = tensor.comp(basis)
+                fmodule = tensor.base_module()
+                mat = [[tcomp[[i,j]] for j in fmodule.irange()]
+                       for i in fmodule.irange()]
+                if isinstance(tensor, FreeModuleAutomorphism):
+                    is_identity = tensor._is_identity
+                else:
+                    is_identity = False
+                return self.element_class(self, mat, bases=(basis,basis),
+                                          name=tensor._name,
+                                          latex_name=tensor._latex_name,
+                                          is_identity=is_identity)
+            else:
+                raise TypeError("cannot coerce the {}".format(tensor) +
+                                " to an element of {}".format(self))
+        return super()._element_constructor_(matrix_rep, bases=bases,
+                                             name=name, latex_name=latex_name,
+                                             is_identity=is_identity)
+
+    #### Monoid methods ####
 
     def one(self):
         r"""
-        Return the identity element of ``self`` considered as a monoid (case of
-        an endomorphism set).
-
-        This applies only when the codomain of ``self`` is equal to its domain,
-        i.e. when ``self`` is of the type `\mathrm{Hom}(M,M)`.
+        Return the identity element of ``self`` considered as a monoid.
 
         OUTPUT:
 
@@ -526,8 +655,6 @@ def one(self):
 
         """
         if self._one is None:
-            if self.codomain() != self.domain():
-                raise TypeError("the {} is not a monoid".format(self))
             self._one = self.element_class(self, [], is_identity=True)
         return self._one
 
diff --git a/src/sage/tensor/modules/free_module_morphism.py b/src/sage/tensor/modules/free_module_morphism.py
index b9c53074e32..ba905a989ea 100644
--- a/src/sage/tensor/modules/free_module_morphism.py
+++ b/src/sage/tensor/modules/free_module_morphism.py
@@ -4,9 +4,13 @@
 The class :class:`FiniteRankFreeModuleMorphism` implements homomorphisms
 between two free modules of finite rank over the same commutative ring.
 
+The subclass :class:`FiniteRankFreeModuleEndomorphism` implements the
+special case of endomorphisms.
+
 AUTHORS:
 
 - Eric Gourgoulhon, Michal Bejger (2014-2015): initial version
+- Matthias Koeppe (2024): add subclass :class:`FiniteRankFreeModuleEndomorphism`
 
 REFERENCES:
 
@@ -28,6 +32,7 @@
 from typing import TYPE_CHECKING
 
 from sage.categories.morphism import Morphism
+from sage.misc.lazy_attribute import lazy_attribute
 from sage.rings.integer import Integer
 
 if TYPE_CHECKING:
@@ -50,6 +55,9 @@ class FiniteRankFreeModuleMorphism(Morphism):
     This is a Sage *element* class, the corresponding *parent* class being
     :class:`~sage.tensor.modules.free_module_homset.FreeModuleHomset`.
 
+    For the special case of endomorphisms (`M=N`), use the subclass
+    :class:`FiniteRankFreeModuleEndomorphism`.
+
     INPUT:
 
     - ``parent`` -- hom-set Hom(M,N) to which the homomorphism belongs
@@ -64,10 +72,7 @@ class FiniteRankFreeModuleMorphism(Morphism):
       default bases of each module is assumed.
     - ``name`` -- (default: ``None``) string; name given to the homomorphism
     - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the
-      homomorphism; if None, ``name`` will be used.
-    - ``is_identity`` -- (default: ``False``) determines whether the
-      constructed object is the identity endomorphism; if set to ``True``, then
-      N must be M and the entry ``matrix_rep`` is not used.
+      homomorphism; if ``None``, ``name`` will be used.
 
     EXAMPLES:
 
@@ -166,31 +171,6 @@ class FiniteRankFreeModuleMorphism(Morphism):
         34 f_0 - 51 f_1
         sage: phi(3*u + v) == 3*phi(u) + phi(v)
         True
-
-    The identity endomorphism::
-
-        sage: Id = End(M).one() ; Id
-        Identity endomorphism of Rank-3 free module M over the Integer Ring
-        sage: Id.parent()
-        Set of Morphisms from Rank-3 free module M over the Integer Ring
-         to Rank-3 free module M over the Integer Ring
-         in Category of finite dimensional modules over Integer Ring
-        sage: Id.parent() is End(M)
-        True
-
-    The matrix of Id with respect to the basis e is of course the identity
-    matrix::
-
-        sage: Id.matrix(e)
-        [1 0 0]
-        [0 1 0]
-        [0 0 1]
-
-    The identity acting on a module element::
-
-        sage: Id(v) is v
-        True
-
     """
     def __init__(self, parent, matrix_rep, bases=None, name=None,
                  latex_name=None, is_identity=False):
@@ -214,27 +194,9 @@ def __init__(self, parent, matrix_rep, bases=None, name=None,
             [ 2  1  4]
             sage: latex(phi)
             \phi
-
-        Generic endomorphism::
-
-            sage: phi = FiniteRankFreeModuleMorphism(End(M), [[1,0,-3], [2,1,4], [7,8,9]],
-            ....:                                    name='phi', latex_name=r'\phi')
-            sage: phi
-            Generic endomorphism of Rank-3 free module M over the Integer Ring
-
-        Identity endomorphism::
-
-            sage: phi = FiniteRankFreeModuleMorphism(End(M), 'whatever', is_identity=True)
-            sage: phi
-            Identity endomorphism of Rank-3 free module M over the Integer Ring
-            sage: phi.matrix(e)
-            [1 0 0]
-            [0 1 0]
-            [0 0 1]
-            sage: latex(phi)
-            \mathrm{Id}
-
         """
+        if is_identity:
+            raise TypeError('use the subclass FiniteRankFreeModuleEndomorphism for the identity morphis')
         from sage.matrix.constructor import matrix
         from sage.misc.constant_function import ConstantFunction
         Morphism.__init__(self, parent)
@@ -263,41 +225,12 @@ def __init__(self, parent, matrix_rep, bases=None, name=None,
         ring = parent.base_ring()
         n1 = fmodule1.rank()
         n2 = fmodule2.rank()
-        if is_identity:
-            # Construction of the identity endomorphism
-            if fmodule1 != fmodule2:
-                raise TypeError("the domain and codomain must coincide " +
-                                "for the identity endomorphism.")
-            if bases[0] != bases[1]:
-                raise TypeError("the two bases must coincide for " +
-                                "constructing the identity endomorphism.")
-            self._is_identity = True
-            zero = ring.zero()
-            one = ring.one()
-            matrix_rep = []
-            for i in range(n1):
-                row = [zero]*n1
-                row[i] = one
-                matrix_rep.append(row)
-            if name is None:
-                name = 'Id'
-            if latex_name is None and name == 'Id':
-                latex_name = r'\mathrm{Id}'
-            self._repr_type_str = 'Identity'
-        else:
-            # Construction of a generic morphism
-            self._is_identity = False
-            if isinstance(matrix_rep, ConstantFunction):
-                # the zero morphism
-                if matrix_rep().is_zero():
-                    matrix_rep = 0
-            if matrix_rep == 1:
-                if fmodule1 == fmodule2:
-                    # the identity endomorphism (again):
-                    self._is_identity = True
-                    self._repr_type_str = 'Identity'
-                    name = 'Id'
-                    latex_name = r'\mathrm{Id}'
+        # Construction of a generic morphism
+        self._is_identity = False
+        if isinstance(matrix_rep, ConstantFunction):
+            # the zero morphism
+            if matrix_rep().is_zero():
+                matrix_rep = 0
         self._matrices = {bases: matrix(ring, n2, n1, matrix_rep)}
         self._name = name
         if latex_name is None:
@@ -1355,3 +1288,321 @@ def display(self, basis1=None, basis2=None):
                               left_border=basis2_names)
         resu_latex = latex(matrix)
         return FormattedExpansion(resu_txt, resu_latex)
+
+
+class FiniteRankFreeModuleEndomorphism(FiniteRankFreeModuleMorphism):
+    r"""
+    Endomorphism of a free module of finite rank over a commutative ring.
+
+    An instance of this class is an endomorphism
+
+    .. MATH::
+
+        \phi:\ M \longrightarrow M,
+
+    where `M` is a free module of finite rank over a commutative ring `R`.
+
+    This is a Sage *element* class, the corresponding *parent* class being
+    :class:`~sage.tensor.modules.free_module_homset.FreeModuleEndset`.
+
+    INPUT:
+
+    - ``parent`` -- hom-set Hom(M,M) to which the endomorphism belongs
+    - ``matrix_rep`` -- matrix representation of the endomorphism with
+      respect to the basis ``bases``; this entry can actually
+      be any material from which a matrix of size rank(N)*rank(M) of
+      elements of `R` can be constructed; the *columns* of the matrix give
+      the images of the basis of `M` (see the convention in the example below)
+    - ``bases`` -- (default: ``None``) pair ``(basis_domain, basis_codomain)``
+      defining the matrix representation, ``basis_domain`` and ``basis_codomain``
+      being two bases (typically the same) of the same module `M`; if ``None``,
+      the default basis of `M` is used for both.
+    - ``name`` -- (default: ``None``) string; name given to the endomorphism
+    - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the
+      endomorphism; if ``None``, ``name`` will be used.
+    - ``is_identity`` -- (default: ``False``) determines whether the
+      constructed object is the identity endomorphism; if set to ``True``,
+      then the entry ``matrix_rep`` is not used.
+
+    EXAMPLES:
+
+    The identity endomorphism::
+
+        sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+        sage: e = M.basis('e')
+        sage: Id = End(M).one(); Id
+        Identity endomorphism of Rank-3 free module M over the Integer Ring
+        sage: Id.parent()
+        Set of Morphisms from Rank-3 free module M over the Integer Ring
+         to Rank-3 free module M over the Integer Ring
+         in Category of finite dimensional modules over Integer Ring
+        sage: Id.parent() is End(M)
+        True
+
+    The matrix of ``Id`` with respect to the basis ``e`` is of course the identity
+    matrix::
+
+        sage: Id.matrix(e)
+        [1 0 0]
+        [0 1 0]
+        [0 0 1]
+
+    The identity acting on a module element::
+
+        sage: v = M([-2,1,4], basis=e, name='v'); v.display()
+        v = -2 e_0 + e_1 + 4 e_2
+        sage: Id(v) is v
+        True
+    """
+    def __init__(self, parent, matrix_rep, bases=None, name=None,
+                 latex_name=None, is_identity=False):
+        r"""
+        TESTS::
+
+            sage: from sage.tensor.modules.free_module_morphism import FiniteRankFreeModuleEndomorphism
+            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+            sage: e = M.basis('e')
+
+        Generic endomorphism::
+
+            sage: phi = FiniteRankFreeModuleEndomorphism(End(M),
+            ....:                                        [[1,0,-3], [2,1,4], [7,8,9]],
+            ....:                                        name='phi', latex_name=r'\phi')
+            sage: phi
+            Generic endomorphism of Rank-3 free module M over the Integer Ring
+
+        Identity endomorphism::
+
+            sage: phi = FiniteRankFreeModuleEndomorphism(End(M), 'whatever',
+            ....:                                        is_identity=True)
+            sage: phi
+            Identity endomorphism of Rank-3 free module M over the Integer Ring
+            sage: phi.matrix(e)
+            [1 0 0]
+            [0 1 0]
+            [0 0 1]
+            sage: latex(phi)
+            \mathrm{Id}
+        """
+        from sage.matrix.special import identity_matrix
+        from sage.misc.constant_function import ConstantFunction
+
+        fmodule = parent.domain()
+        if bases is None:
+            def_basis = fmodule.default_basis()
+            if def_basis is None:
+                raise ValueError("the {} has no default ".format(fmodule) +
+                                 "basis")
+            bases = (def_basis, def_basis)
+        else:
+            bases = tuple(bases)  # insures bases is a tuple
+            if len(bases) != 2:
+                raise TypeError("the argument bases must contain 2 bases")
+            if bases[0] not in fmodule.bases():
+                raise TypeError("{} is not a basis on the {}".format(bases[0],
+                                                                     fmodule))
+            if bases[1] not in fmodule.bases():
+                raise TypeError("{} is not a basis on the {}".format(bases[1],
+                                                                     fmodule))
+        if not is_identity:
+            # Construction of a generic endomorphism
+            if isinstance(matrix_rep, ConstantFunction):
+                # the zero morphism
+                if matrix_rep().is_zero():
+                    matrix_rep = 0
+            if bases[0] == bases[1] and matrix_rep == 1:
+                is_identity = True
+        if is_identity:
+            # Construction of the identity endomorphism
+            if bases[0] != bases[1]:
+                raise TypeError("the two bases must coincide for " +
+                                "constructing the identity endomorphism.")
+            matrix_rep = 1
+            if name is None:
+                name = 'Id'
+            if latex_name is None and name == 'Id':
+                latex_name = r'\mathrm{Id}'
+        FiniteRankFreeModuleMorphism.__init__(self, parent, matrix_rep, bases,
+                                              name, latex_name)
+        if is_identity:
+            self._is_identity = True
+            self._repr_type_str = 'Identity'
+
+    def _some_matrix(self):
+        r"""
+        Return the matrix of the endomorphism ``self`` w.r.t. some basis.
+
+        EXAMPLES::
+
+            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+            sage: e = M.basis('e')
+            sage: a = M.automorphism(matrix=[[-1,0,0],[0,1,2],[0,1,3]], basis=e)
+            sage: ep = e.new_basis(a, 'ep', latex_symbol="e'")
+            sage: phi = M.endomorphism([[1,2,3], [4,5,6], [7,8,9]], basis=ep)
+            sage: phi._some_matrix()
+            [1 2 3]
+            [4 5 6]
+            [7 8 9]
+        """
+        for (basis1, basis2), matrix in self._matrices.items():
+            if basis1 == basis2:
+                return matrix
+        for basis in self.domain().bases():
+            try:
+                return self.matrix(basis)
+            except ValueError:
+                pass
+        return self.matrix()
+
+    @lazy_attribute
+    def characteristic_polynomial(self):
+        r"""
+        Return the characteristic polynomial of ``self``.
+
+        :meth:`characteristic_polynomial` and :meth:`charpoly` are the same method.
+
+        INPUT:
+
+        - ``var`` -- string (default: ``'x'``); a variable name
+
+        EXAMPLES::
+
+            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+            sage: e = M.basis('e')
+            sage: a = M.automorphism(matrix=[[-1,0,0],[0,1,2],[0,1,3]], basis=e)
+            sage: ep = e.new_basis(a, 'ep', latex_symbol="e'")
+            sage: phi = M.endomorphism([[1,2,3], [4,5,6], [7,8,9]], basis=ep)
+            sage: phi.matrix(e)
+            [  1  -3   1]
+            [-18  39 -18]
+            [-25  54 -25]
+            sage: phi.characteristic_polynomial()
+            x^3 - 15*x^2 - 18*x
+            sage: phi.charpoly()
+            x^3 - 15*x^2 - 18*x
+            sage: phi.charpoly('T')
+            T^3 - 15*T^2 - 18*T
+        """
+        return self._some_matrix().characteristic_polynomial
+
+    charpoly = characteristic_polynomial
+
+    @lazy_attribute
+    def det(self):
+        r"""
+        Return the determinant of ``self``.
+
+        OUTPUT:
+
+        - element of the base ring of the modules on which ``self`` is defined,
+          equal to the determinant of ``self``.
+
+        EXAMPLES::
+
+            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+            sage: e = M.basis('e')
+            sage: a = M.automorphism(matrix=[[-1,0,0],[0,1,2],[0,1,3]], basis=e)
+            sage: ep = e.new_basis(a, 'ep', latex_symbol="e'")
+            sage: phi = M.endomorphism([[1,2,3], [4,5,6], [7,8,9]], basis=ep)
+            sage: phi.matrix(e)
+            [  1  -3   1]
+            [-18  39 -18]
+            [-25  54 -25]
+            sage: phi.det()
+            0
+            sage: det(phi)
+            0
+        """
+        return self._some_matrix().det
+
+    determinant = det
+
+    @lazy_attribute
+    def fcp(self):
+        r"""
+        Return the factorization of the characteristic polynomial of ``self``.
+
+        INPUT:
+
+        - ``var`` -- string (default: ``'x'``); a variable name
+
+        EXAMPLES::
+
+            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+            sage: e = M.basis('e')
+            sage: a = M.automorphism(matrix=[[-1,0,0],[0,1,2],[0,1,3]], basis=e)
+            sage: ep = e.new_basis(a, 'ep', latex_symbol="e'")
+            sage: phi = M.endomorphism([[1,2,3], [4,5,6], [7,8,9]], basis=ep)
+            sage: phi.matrix(e)
+            [  1  -3   1]
+            [-18  39 -18]
+            [-25  54 -25]
+            sage: phi.fcp()                                                             # needs sage.libs.pari
+            x * (x^2 - 15*x - 18)
+            sage: phi.fcp('T')                                                          # needs sage.libs.pari
+            T * (T^2 - 15*T - 18)
+        """
+        return self._some_matrix().fcp
+
+    @lazy_attribute
+    def minimal_polynomial(self):
+        r"""
+        Return the minimal polynomial of ``self``.
+
+        :meth:`minimal_polynomial` and :meth:`minpoly` are the same method.
+
+        INPUT:
+
+        - ``var`` -- string (default: ``'x'``); a variable name
+
+        EXAMPLES::
+
+            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+            sage: e = M.basis('e')
+            sage: a = M.automorphism(matrix=[[-1,0,0],[0,1,2],[0,1,3]], basis=e)
+            sage: ep = e.new_basis(a, 'ep', latex_symbol="e'")
+            sage: phi = M.endomorphism([[1,2,3], [4,5,6], [7,8,9]], basis=ep)
+            sage: phi.matrix(e)
+            [  1  -3   1]
+            [-18  39 -18]
+            [-25  54 -25]
+            sage: phi.minpoly()                                                         # needs sage.libs.pari
+            x^3 - 15*x^2 - 18*x
+            sage: phi.minimal_polynomial()                                              # needs sage.libs.pari
+            x^3 - 15*x^2 - 18*x
+            sage: phi.minimal_polynomial('T')                                           # needs sage.libs.pari
+            T^3 - 15*T^2 - 18*T
+        """
+        return self._some_matrix().minimal_polynomial
+
+    minpoly = minimal_polynomial
+
+    @lazy_attribute
+    def trace(self):
+        r"""
+        Return the trace of ``self``.
+
+        OUTPUT:
+
+        - element of the base ring of the modules on which ``self`` is defined,
+          equal to the trace of ``self``.
+
+        EXAMPLES::
+
+            sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
+            sage: e = M.basis('e')
+            sage: a = M.automorphism(matrix=[[-1,0,0],[0,1,2],[0,1,3]], basis=e)
+            sage: ep = e.new_basis(a, 'ep', latex_symbol="e'")
+            sage: phi = M.endomorphism([[1,2,3], [4,5,6], [7,8,9]], basis=ep)
+            sage: phi.matrix(e)
+            [  1  -3   1]
+            [-18  39 -18]
+            [-25  54 -25]
+            sage: phi.trace()
+            15
+            sage: id = M.identity_map()
+            sage: id.trace()
+            3
+
+        """
+        return self._some_matrix().trace
diff --git a/src/sage/version.py b/src/sage/version.py
index fd7b8305d1d..35a9364e251 100644
--- a/src/sage/version.py
+++ b/src/sage/version.py
@@ -1,5 +1,5 @@
 # Sage version information for Python scripts
 # This file is auto-generated by the sage-update-version script, do not edit!
-version = '10.4.beta5'
-date = '2024-05-02'
-banner = 'SageMath version 10.4.beta5, Release Date: 2024-05-02'
+version = '10.4.beta6'
+date = '2024-05-12'
+banner = 'SageMath version 10.4.beta6, Release Date: 2024-05-12'