passagemath-modules 10.6.31rc3__cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of passagemath-modules might be problematic. Click here for more details.
- passagemath_modules-10.6.31rc3.dist-info/METADATA +281 -0
- passagemath_modules-10.6.31rc3.dist-info/RECORD +806 -0
- passagemath_modules-10.6.31rc3.dist-info/WHEEL +6 -0
- passagemath_modules-10.6.31rc3.dist-info/top_level.txt +2 -0
- passagemath_modules.libs/libgfortran-e1b7dfc8.so.5.0.0 +0 -0
- passagemath_modules.libs/libgmp-93ebf16a.so.10.5.0 +0 -0
- passagemath_modules.libs/libgsl-e3525837.so.28.0.0 +0 -0
- passagemath_modules.libs/libmpc-c5c421e1.so.3.3.1 +0 -0
- passagemath_modules.libs/libmpfr-e0f11cf3.so.6.2.1 +0 -0
- passagemath_modules.libs/libopenblasp-r0-4c5b64b1.3.29.so +0 -0
- sage/algebras/all__sagemath_modules.py +20 -0
- sage/algebras/catalog.py +148 -0
- sage/algebras/clifford_algebra.py +3107 -0
- sage/algebras/clifford_algebra_element.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/algebras/clifford_algebra_element.pxd +16 -0
- sage/algebras/clifford_algebra_element.pyx +997 -0
- sage/algebras/commutative_dga.py +4252 -0
- sage/algebras/exterior_algebra_groebner.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/algebras/exterior_algebra_groebner.pxd +55 -0
- sage/algebras/exterior_algebra_groebner.pyx +727 -0
- sage/algebras/finite_dimensional_algebras/all.py +2 -0
- sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra.py +1029 -0
- sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element.pxd +12 -0
- sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element.pyx +706 -0
- sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_ideal.py +196 -0
- sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_morphism.py +255 -0
- sage/algebras/finite_gca.py +528 -0
- sage/algebras/group_algebra.py +232 -0
- sage/algebras/lie_algebras/abelian.py +197 -0
- sage/algebras/lie_algebras/affine_lie_algebra.py +1213 -0
- sage/algebras/lie_algebras/all.py +25 -0
- sage/algebras/lie_algebras/all__sagemath_modules.py +1 -0
- sage/algebras/lie_algebras/bch.py +177 -0
- sage/algebras/lie_algebras/bgg_dual_module.py +1184 -0
- sage/algebras/lie_algebras/bgg_resolution.py +232 -0
- sage/algebras/lie_algebras/center_uea.py +767 -0
- sage/algebras/lie_algebras/classical_lie_algebra.py +2516 -0
- sage/algebras/lie_algebras/examples.py +683 -0
- sage/algebras/lie_algebras/free_lie_algebra.py +973 -0
- sage/algebras/lie_algebras/heisenberg.py +820 -0
- sage/algebras/lie_algebras/lie_algebra.py +1562 -0
- sage/algebras/lie_algebras/lie_algebra_element.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/algebras/lie_algebras/lie_algebra_element.pxd +68 -0
- sage/algebras/lie_algebras/lie_algebra_element.pyx +2122 -0
- sage/algebras/lie_algebras/morphism.py +661 -0
- sage/algebras/lie_algebras/nilpotent_lie_algebra.py +457 -0
- sage/algebras/lie_algebras/onsager.py +1324 -0
- sage/algebras/lie_algebras/poincare_birkhoff_witt.py +816 -0
- sage/algebras/lie_algebras/quotient.py +462 -0
- sage/algebras/lie_algebras/rank_two_heisenberg_virasoro.py +355 -0
- sage/algebras/lie_algebras/representation.py +1040 -0
- sage/algebras/lie_algebras/structure_coefficients.py +459 -0
- sage/algebras/lie_algebras/subalgebra.py +967 -0
- sage/algebras/lie_algebras/symplectic_derivation.py +289 -0
- sage/algebras/lie_algebras/verma_module.py +1630 -0
- sage/algebras/lie_algebras/virasoro.py +1186 -0
- sage/algebras/octonion_algebra.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/algebras/octonion_algebra.pxd +20 -0
- sage/algebras/octonion_algebra.pyx +987 -0
- sage/algebras/orlik_solomon.py +907 -0
- sage/algebras/orlik_terao.py +779 -0
- sage/algebras/steenrod/all.py +7 -0
- sage/algebras/steenrod/steenrod_algebra.py +4258 -0
- sage/algebras/steenrod/steenrod_algebra_bases.py +1179 -0
- sage/algebras/steenrod/steenrod_algebra_misc.py +1167 -0
- sage/algebras/steenrod/steenrod_algebra_mult.py +954 -0
- sage/algebras/weyl_algebra.py +1126 -0
- sage/all__sagemath_modules.py +62 -0
- sage/calculus/all__sagemath_modules.py +19 -0
- sage/calculus/expr.py +205 -0
- sage/calculus/integration.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/calculus/integration.pyx +698 -0
- sage/calculus/interpolation.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/calculus/interpolation.pxd +13 -0
- sage/calculus/interpolation.pyx +387 -0
- sage/calculus/interpolators.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/calculus/interpolators.pyx +326 -0
- sage/calculus/ode.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/calculus/ode.pxd +5 -0
- sage/calculus/ode.pyx +610 -0
- sage/calculus/riemann.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/calculus/riemann.pyx +1521 -0
- sage/calculus/test_sympy.py +201 -0
- sage/calculus/transforms/all.py +7 -0
- sage/calculus/transforms/dft.py +844 -0
- sage/calculus/transforms/dwt.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/calculus/transforms/dwt.pxd +7 -0
- sage/calculus/transforms/dwt.pyx +160 -0
- sage/calculus/transforms/fft.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/calculus/transforms/fft.pxd +12 -0
- sage/calculus/transforms/fft.pyx +487 -0
- sage/calculus/wester.py +662 -0
- sage/coding/abstract_code.py +1108 -0
- sage/coding/ag_code.py +868 -0
- sage/coding/ag_code_decoders.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/coding/ag_code_decoders.pyx +2639 -0
- sage/coding/all.py +15 -0
- sage/coding/bch_code.py +494 -0
- sage/coding/binary_code.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/coding/binary_code.pxd +124 -0
- sage/coding/binary_code.pyx +4139 -0
- sage/coding/bounds_catalog.py +43 -0
- sage/coding/channel.py +819 -0
- sage/coding/channels_catalog.py +29 -0
- sage/coding/code_bounds.py +755 -0
- sage/coding/code_constructions.py +804 -0
- sage/coding/codes_catalog.py +111 -0
- sage/coding/cyclic_code.py +1329 -0
- sage/coding/databases.py +316 -0
- sage/coding/decoder.py +373 -0
- sage/coding/decoders_catalog.py +88 -0
- sage/coding/delsarte_bounds.py +709 -0
- sage/coding/encoder.py +390 -0
- sage/coding/encoders_catalog.py +64 -0
- sage/coding/extended_code.py +468 -0
- sage/coding/gabidulin_code.py +1058 -0
- sage/coding/golay_code.py +404 -0
- sage/coding/goppa_code.py +441 -0
- sage/coding/grs_code.py +2371 -0
- sage/coding/guava.py +107 -0
- sage/coding/guruswami_sudan/all.py +1 -0
- sage/coding/guruswami_sudan/gs_decoder.py +897 -0
- sage/coding/guruswami_sudan/interpolation.py +409 -0
- sage/coding/guruswami_sudan/utils.py +176 -0
- sage/coding/hamming_code.py +176 -0
- sage/coding/information_set_decoder.py +1032 -0
- sage/coding/kasami_codes.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/coding/kasami_codes.pyx +351 -0
- sage/coding/linear_code.py +3067 -0
- sage/coding/linear_code_no_metric.py +1354 -0
- sage/coding/linear_rank_metric.py +961 -0
- sage/coding/parity_check_code.py +353 -0
- sage/coding/punctured_code.py +719 -0
- sage/coding/reed_muller_code.py +999 -0
- sage/coding/self_dual_codes.py +942 -0
- sage/coding/source_coding/all.py +2 -0
- sage/coding/source_coding/huffman.py +553 -0
- sage/coding/subfield_subcode.py +423 -0
- sage/coding/two_weight_db.py +399 -0
- sage/combinat/all__sagemath_modules.py +7 -0
- sage/combinat/cartesian_product.py +347 -0
- sage/combinat/family.py +11 -0
- sage/combinat/free_module.py +1977 -0
- sage/combinat/root_system/all.py +147 -0
- sage/combinat/root_system/ambient_space.py +527 -0
- sage/combinat/root_system/associahedron.py +471 -0
- sage/combinat/root_system/braid_move_calculator.py +143 -0
- sage/combinat/root_system/braid_orbit.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/combinat/root_system/braid_orbit.pyx +144 -0
- sage/combinat/root_system/branching_rules.py +2301 -0
- sage/combinat/root_system/cartan_matrix.py +1245 -0
- sage/combinat/root_system/cartan_type.py +3069 -0
- sage/combinat/root_system/coxeter_group.py +162 -0
- sage/combinat/root_system/coxeter_matrix.py +1261 -0
- sage/combinat/root_system/coxeter_type.py +681 -0
- sage/combinat/root_system/dynkin_diagram.py +900 -0
- sage/combinat/root_system/extended_affine_weyl_group.py +2993 -0
- sage/combinat/root_system/fundamental_group.py +795 -0
- sage/combinat/root_system/hecke_algebra_representation.py +1203 -0
- sage/combinat/root_system/integrable_representations.py +1227 -0
- sage/combinat/root_system/non_symmetric_macdonald_polynomials.py +1965 -0
- sage/combinat/root_system/pieri_factors.py +1147 -0
- sage/combinat/root_system/plot.py +1615 -0
- sage/combinat/root_system/root_lattice_realization_algebras.py +1214 -0
- sage/combinat/root_system/root_lattice_realizations.py +4628 -0
- sage/combinat/root_system/root_space.py +487 -0
- sage/combinat/root_system/root_system.py +882 -0
- sage/combinat/root_system/type_A.py +348 -0
- sage/combinat/root_system/type_A_affine.py +227 -0
- sage/combinat/root_system/type_A_infinity.py +241 -0
- sage/combinat/root_system/type_B.py +347 -0
- sage/combinat/root_system/type_BC_affine.py +287 -0
- sage/combinat/root_system/type_B_affine.py +216 -0
- sage/combinat/root_system/type_C.py +317 -0
- sage/combinat/root_system/type_C_affine.py +188 -0
- sage/combinat/root_system/type_D.py +357 -0
- sage/combinat/root_system/type_D_affine.py +208 -0
- sage/combinat/root_system/type_E.py +641 -0
- sage/combinat/root_system/type_E_affine.py +231 -0
- sage/combinat/root_system/type_F.py +387 -0
- sage/combinat/root_system/type_F_affine.py +137 -0
- sage/combinat/root_system/type_G.py +293 -0
- sage/combinat/root_system/type_G_affine.py +132 -0
- sage/combinat/root_system/type_H.py +105 -0
- sage/combinat/root_system/type_I.py +110 -0
- sage/combinat/root_system/type_Q.py +150 -0
- sage/combinat/root_system/type_affine.py +509 -0
- sage/combinat/root_system/type_dual.py +704 -0
- sage/combinat/root_system/type_folded.py +301 -0
- sage/combinat/root_system/type_marked.py +748 -0
- sage/combinat/root_system/type_reducible.py +601 -0
- sage/combinat/root_system/type_relabel.py +730 -0
- sage/combinat/root_system/type_super_A.py +837 -0
- sage/combinat/root_system/weight_lattice_realizations.py +1188 -0
- sage/combinat/root_system/weight_space.py +639 -0
- sage/combinat/root_system/weyl_characters.py +2238 -0
- sage/crypto/__init__.py +4 -0
- sage/crypto/all.py +28 -0
- sage/crypto/block_cipher/all.py +7 -0
- sage/crypto/block_cipher/des.py +1065 -0
- sage/crypto/block_cipher/miniaes.py +2171 -0
- sage/crypto/block_cipher/present.py +909 -0
- sage/crypto/block_cipher/sdes.py +1527 -0
- sage/crypto/boolean_function.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/crypto/boolean_function.pxd +10 -0
- sage/crypto/boolean_function.pyx +1487 -0
- sage/crypto/cipher.py +78 -0
- sage/crypto/classical.py +3668 -0
- sage/crypto/classical_cipher.py +569 -0
- sage/crypto/cryptosystem.py +387 -0
- sage/crypto/key_exchange/all.py +7 -0
- sage/crypto/key_exchange/catalog.py +24 -0
- sage/crypto/key_exchange/diffie_hellman.py +323 -0
- sage/crypto/key_exchange/key_exchange_scheme.py +107 -0
- sage/crypto/lattice.py +312 -0
- sage/crypto/lfsr.py +295 -0
- sage/crypto/lwe.py +840 -0
- sage/crypto/mq/__init__.py +4 -0
- sage/crypto/mq/mpolynomialsystemgenerator.py +204 -0
- sage/crypto/mq/rijndael_gf.py +2345 -0
- sage/crypto/mq/sbox.py +7 -0
- sage/crypto/mq/sr.py +3344 -0
- sage/crypto/public_key/all.py +5 -0
- sage/crypto/public_key/blum_goldwasser.py +776 -0
- sage/crypto/sbox.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/crypto/sbox.pyx +2090 -0
- sage/crypto/sboxes.py +2090 -0
- sage/crypto/stream.py +390 -0
- sage/crypto/stream_cipher.py +297 -0
- sage/crypto/util.py +519 -0
- sage/ext/all__sagemath_modules.py +1 -0
- sage/ext/interpreters/__init__.py +1 -0
- sage/ext/interpreters/all__sagemath_modules.py +2 -0
- sage/ext/interpreters/wrapper_cc.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/ext/interpreters/wrapper_cc.pxd +30 -0
- sage/ext/interpreters/wrapper_cc.pyx +252 -0
- sage/ext/interpreters/wrapper_cdf.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/ext/interpreters/wrapper_cdf.pxd +26 -0
- sage/ext/interpreters/wrapper_cdf.pyx +245 -0
- sage/ext/interpreters/wrapper_rdf.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/ext/interpreters/wrapper_rdf.pxd +23 -0
- sage/ext/interpreters/wrapper_rdf.pyx +221 -0
- sage/ext/interpreters/wrapper_rr.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/ext/interpreters/wrapper_rr.pxd +28 -0
- sage/ext/interpreters/wrapper_rr.pyx +335 -0
- sage/geometry/all__sagemath_modules.py +5 -0
- sage/geometry/toric_lattice.py +1745 -0
- sage/geometry/toric_lattice_element.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/geometry/toric_lattice_element.pyx +432 -0
- sage/groups/abelian_gps/abelian_group.py +1925 -0
- sage/groups/abelian_gps/abelian_group_element.py +164 -0
- sage/groups/abelian_gps/all__sagemath_modules.py +5 -0
- sage/groups/abelian_gps/dual_abelian_group.py +421 -0
- sage/groups/abelian_gps/dual_abelian_group_element.py +179 -0
- sage/groups/abelian_gps/element_base.py +341 -0
- sage/groups/abelian_gps/values.py +488 -0
- sage/groups/additive_abelian/additive_abelian_group.py +476 -0
- sage/groups/additive_abelian/additive_abelian_wrapper.py +857 -0
- sage/groups/additive_abelian/all.py +4 -0
- sage/groups/additive_abelian/qmodnz.py +231 -0
- sage/groups/additive_abelian/qmodnz_element.py +349 -0
- sage/groups/affine_gps/affine_group.py +535 -0
- sage/groups/affine_gps/all.py +1 -0
- sage/groups/affine_gps/catalog.py +17 -0
- sage/groups/affine_gps/euclidean_group.py +246 -0
- sage/groups/affine_gps/group_element.py +562 -0
- sage/groups/all__sagemath_modules.py +12 -0
- sage/groups/galois_group.py +479 -0
- sage/groups/matrix_gps/all.py +4 -0
- sage/groups/matrix_gps/all__sagemath_modules.py +13 -0
- sage/groups/matrix_gps/catalog.py +26 -0
- sage/groups/matrix_gps/coxeter_group.py +927 -0
- sage/groups/matrix_gps/finitely_generated.py +487 -0
- sage/groups/matrix_gps/group_element.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/groups/matrix_gps/group_element.pxd +11 -0
- sage/groups/matrix_gps/group_element.pyx +431 -0
- sage/groups/matrix_gps/linear.py +440 -0
- sage/groups/matrix_gps/matrix_group.py +617 -0
- sage/groups/matrix_gps/named_group.py +296 -0
- sage/groups/matrix_gps/orthogonal.py +544 -0
- sage/groups/matrix_gps/symplectic.py +251 -0
- sage/groups/matrix_gps/unitary.py +436 -0
- sage/groups/misc_gps/all__sagemath_modules.py +1 -0
- sage/groups/misc_gps/argument_groups.py +1905 -0
- sage/groups/misc_gps/imaginary_groups.py +479 -0
- sage/groups/perm_gps/all__sagemath_modules.py +1 -0
- sage/groups/perm_gps/partn_ref/all__sagemath_modules.py +1 -0
- sage/groups/perm_gps/partn_ref/refinement_binary.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/groups/perm_gps/partn_ref/refinement_binary.pxd +41 -0
- sage/groups/perm_gps/partn_ref/refinement_binary.pyx +1167 -0
- sage/groups/perm_gps/partn_ref/refinement_matrices.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/groups/perm_gps/partn_ref/refinement_matrices.pxd +31 -0
- sage/groups/perm_gps/partn_ref/refinement_matrices.pyx +385 -0
- sage/homology/algebraic_topological_model.py +595 -0
- sage/homology/all.py +2 -0
- sage/homology/all__sagemath_modules.py +8 -0
- sage/homology/chain_complex.py +2148 -0
- sage/homology/chain_complex_homspace.py +165 -0
- sage/homology/chain_complex_morphism.py +629 -0
- sage/homology/chain_homotopy.py +604 -0
- sage/homology/chains.py +653 -0
- sage/homology/free_resolution.py +923 -0
- sage/homology/graded_resolution.py +567 -0
- sage/homology/hochschild_complex.py +756 -0
- sage/homology/homology_group.py +188 -0
- sage/homology/homology_morphism.py +422 -0
- sage/homology/homology_vector_space_with_basis.py +1454 -0
- sage/homology/koszul_complex.py +169 -0
- sage/homology/matrix_utils.py +205 -0
- sage/libs/all__sagemath_modules.py +1 -0
- sage/libs/gsl/__init__.py +1 -0
- sage/libs/gsl/airy.pxd +56 -0
- sage/libs/gsl/all.pxd +66 -0
- sage/libs/gsl/array.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/libs/gsl/array.pxd +5 -0
- sage/libs/gsl/array.pyx +102 -0
- sage/libs/gsl/bessel.pxd +208 -0
- sage/libs/gsl/blas.pxd +116 -0
- sage/libs/gsl/blas_types.pxd +34 -0
- sage/libs/gsl/block.pxd +52 -0
- sage/libs/gsl/chebyshev.pxd +37 -0
- sage/libs/gsl/clausen.pxd +12 -0
- sage/libs/gsl/combination.pxd +47 -0
- sage/libs/gsl/complex.pxd +151 -0
- sage/libs/gsl/coulomb.pxd +30 -0
- sage/libs/gsl/coupling.pxd +21 -0
- sage/libs/gsl/dawson.pxd +12 -0
- sage/libs/gsl/debye.pxd +24 -0
- sage/libs/gsl/dilog.pxd +14 -0
- sage/libs/gsl/eigen.pxd +46 -0
- sage/libs/gsl/elementary.pxd +12 -0
- sage/libs/gsl/ellint.pxd +48 -0
- sage/libs/gsl/elljac.pxd +8 -0
- sage/libs/gsl/erf.pxd +32 -0
- sage/libs/gsl/errno.pxd +26 -0
- sage/libs/gsl/exp.pxd +44 -0
- sage/libs/gsl/expint.pxd +44 -0
- sage/libs/gsl/fermi_dirac.pxd +44 -0
- sage/libs/gsl/fft.pxd +121 -0
- sage/libs/gsl/fit.pxd +50 -0
- sage/libs/gsl/gamma.pxd +94 -0
- sage/libs/gsl/gegenbauer.pxd +26 -0
- sage/libs/gsl/histogram.pxd +176 -0
- sage/libs/gsl/hyperg.pxd +52 -0
- sage/libs/gsl/integration.pxd +69 -0
- sage/libs/gsl/interp.pxd +109 -0
- sage/libs/gsl/laguerre.pxd +24 -0
- sage/libs/gsl/lambert.pxd +16 -0
- sage/libs/gsl/legendre.pxd +90 -0
- sage/libs/gsl/linalg.pxd +185 -0
- sage/libs/gsl/log.pxd +26 -0
- sage/libs/gsl/math.pxd +43 -0
- sage/libs/gsl/matrix.pxd +143 -0
- sage/libs/gsl/matrix_complex.pxd +130 -0
- sage/libs/gsl/min.pxd +67 -0
- sage/libs/gsl/monte.pxd +56 -0
- sage/libs/gsl/ntuple.pxd +32 -0
- sage/libs/gsl/odeiv.pxd +70 -0
- sage/libs/gsl/permutation.pxd +78 -0
- sage/libs/gsl/poly.pxd +40 -0
- sage/libs/gsl/pow_int.pxd +12 -0
- sage/libs/gsl/psi.pxd +28 -0
- sage/libs/gsl/qrng.pxd +29 -0
- sage/libs/gsl/random.pxd +257 -0
- sage/libs/gsl/rng.pxd +100 -0
- sage/libs/gsl/roots.pxd +72 -0
- sage/libs/gsl/sort.pxd +36 -0
- sage/libs/gsl/statistics.pxd +59 -0
- sage/libs/gsl/sum.pxd +55 -0
- sage/libs/gsl/synchrotron.pxd +16 -0
- sage/libs/gsl/transport.pxd +24 -0
- sage/libs/gsl/trig.pxd +58 -0
- sage/libs/gsl/types.pxd +137 -0
- sage/libs/gsl/vector.pxd +101 -0
- sage/libs/gsl/vector_complex.pxd +83 -0
- sage/libs/gsl/wavelet.pxd +49 -0
- sage/libs/gsl/zeta.pxd +28 -0
- sage/libs/mpc/__init__.pxd +114 -0
- sage/libs/mpc/types.pxd +28 -0
- sage/libs/mpfr/__init__.pxd +299 -0
- sage/libs/mpfr/types.pxd +26 -0
- sage/libs/mpmath/__init__.py +1 -0
- sage/libs/mpmath/all.py +27 -0
- sage/libs/mpmath/all__sagemath_modules.py +1 -0
- sage/libs/mpmath/utils.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/libs/mpmath/utils.pxd +4 -0
- sage/libs/mpmath/utils.pyx +319 -0
- sage/matrix/action.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/action.pxd +26 -0
- sage/matrix/action.pyx +596 -0
- sage/matrix/all.py +9 -0
- sage/matrix/args.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/args.pxd +144 -0
- sage/matrix/args.pyx +1668 -0
- sage/matrix/benchmark.py +1258 -0
- sage/matrix/berlekamp_massey.py +95 -0
- sage/matrix/compute_J_ideal.py +926 -0
- sage/matrix/constructor.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/constructor.pyx +750 -0
- sage/matrix/docs.py +430 -0
- sage/matrix/echelon_matrix.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/echelon_matrix.pyx +155 -0
- sage/matrix/matrix.pxd +2 -0
- sage/matrix/matrix0.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix0.pxd +68 -0
- sage/matrix/matrix0.pyx +6324 -0
- sage/matrix/matrix1.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix1.pxd +8 -0
- sage/matrix/matrix1.pyx +2851 -0
- sage/matrix/matrix2.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix2.pxd +25 -0
- sage/matrix/matrix2.pyx +20181 -0
- sage/matrix/matrix_cdv.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_cdv.pxd +4 -0
- sage/matrix/matrix_cdv.pyx +93 -0
- sage/matrix/matrix_complex_double_dense.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_complex_double_dense.pxd +5 -0
- sage/matrix/matrix_complex_double_dense.pyx +98 -0
- sage/matrix/matrix_dense.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_dense.pxd +5 -0
- sage/matrix/matrix_dense.pyx +343 -0
- sage/matrix/matrix_domain_dense.pxd +5 -0
- sage/matrix/matrix_domain_sparse.pxd +5 -0
- sage/matrix/matrix_double_dense.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_double_dense.pxd +7 -0
- sage/matrix/matrix_double_dense.pyx +3906 -0
- sage/matrix/matrix_double_sparse.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_double_sparse.pxd +6 -0
- sage/matrix/matrix_double_sparse.pyx +248 -0
- sage/matrix/matrix_generic_dense.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_generic_dense.pxd +7 -0
- sage/matrix/matrix_generic_dense.pyx +354 -0
- sage/matrix/matrix_generic_sparse.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_generic_sparse.pxd +7 -0
- sage/matrix/matrix_generic_sparse.pyx +461 -0
- sage/matrix/matrix_laurent_mpolynomial_dense.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_laurent_mpolynomial_dense.pxd +5 -0
- sage/matrix/matrix_laurent_mpolynomial_dense.pyx +115 -0
- sage/matrix/matrix_misc.py +313 -0
- sage/matrix/matrix_numpy_dense.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_numpy_dense.pxd +14 -0
- sage/matrix/matrix_numpy_dense.pyx +450 -0
- sage/matrix/matrix_numpy_integer_dense.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_numpy_integer_dense.pxd +7 -0
- sage/matrix/matrix_numpy_integer_dense.pyx +59 -0
- sage/matrix/matrix_polynomial_dense.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_polynomial_dense.pxd +5 -0
- sage/matrix/matrix_polynomial_dense.pyx +5341 -0
- sage/matrix/matrix_real_double_dense.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_real_double_dense.pxd +7 -0
- sage/matrix/matrix_real_double_dense.pyx +122 -0
- sage/matrix/matrix_space.py +2848 -0
- sage/matrix/matrix_sparse.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_sparse.pxd +5 -0
- sage/matrix/matrix_sparse.pyx +1222 -0
- sage/matrix/matrix_window.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_window.pxd +37 -0
- sage/matrix/matrix_window.pyx +242 -0
- sage/matrix/misc_mpfr.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/misc_mpfr.pyx +80 -0
- sage/matrix/operation_table.py +1182 -0
- sage/matrix/special.py +3666 -0
- sage/matrix/strassen.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/strassen.pyx +851 -0
- sage/matrix/symplectic_basis.py +541 -0
- sage/matrix/template.pxd +6 -0
- sage/matrix/tests.py +71 -0
- sage/matroids/advanced.py +77 -0
- sage/matroids/all.py +13 -0
- sage/matroids/basis_exchange_matroid.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matroids/basis_exchange_matroid.pxd +96 -0
- sage/matroids/basis_exchange_matroid.pyx +2344 -0
- sage/matroids/basis_matroid.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matroids/basis_matroid.pxd +45 -0
- sage/matroids/basis_matroid.pyx +1217 -0
- sage/matroids/catalog.py +44 -0
- sage/matroids/chow_ring.py +473 -0
- sage/matroids/chow_ring_ideal.py +849 -0
- sage/matroids/circuit_closures_matroid.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matroids/circuit_closures_matroid.pxd +16 -0
- sage/matroids/circuit_closures_matroid.pyx +559 -0
- sage/matroids/circuits_matroid.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matroids/circuits_matroid.pxd +38 -0
- sage/matroids/circuits_matroid.pyx +947 -0
- sage/matroids/constructor.py +1086 -0
- sage/matroids/database_collections.py +365 -0
- sage/matroids/database_matroids.py +5338 -0
- sage/matroids/dual_matroid.py +583 -0
- sage/matroids/extension.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matroids/extension.pxd +34 -0
- sage/matroids/extension.pyx +519 -0
- sage/matroids/flats_matroid.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matroids/flats_matroid.pxd +28 -0
- sage/matroids/flats_matroid.pyx +715 -0
- sage/matroids/gammoid.py +600 -0
- sage/matroids/graphic_matroid.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matroids/graphic_matroid.pxd +39 -0
- sage/matroids/graphic_matroid.pyx +2024 -0
- sage/matroids/lean_matrix.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matroids/lean_matrix.pxd +126 -0
- sage/matroids/lean_matrix.pyx +3667 -0
- sage/matroids/linear_matroid.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matroids/linear_matroid.pxd +180 -0
- sage/matroids/linear_matroid.pyx +6649 -0
- sage/matroids/matroid.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matroids/matroid.pxd +243 -0
- sage/matroids/matroid.pyx +8759 -0
- sage/matroids/matroids_catalog.py +190 -0
- sage/matroids/matroids_plot_helpers.py +890 -0
- sage/matroids/minor_matroid.py +480 -0
- sage/matroids/minorfix.h +9 -0
- sage/matroids/named_matroids.py +5 -0
- sage/matroids/rank_matroid.py +268 -0
- sage/matroids/set_system.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matroids/set_system.pxd +38 -0
- sage/matroids/set_system.pyx +800 -0
- sage/matroids/transversal_matroid.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matroids/transversal_matroid.pxd +14 -0
- sage/matroids/transversal_matroid.pyx +893 -0
- sage/matroids/union_matroid.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matroids/union_matroid.pxd +20 -0
- sage/matroids/union_matroid.pyx +331 -0
- sage/matroids/unpickling.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matroids/unpickling.pyx +843 -0
- sage/matroids/utilities.py +809 -0
- sage/misc/all__sagemath_modules.py +20 -0
- sage/misc/c3.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/misc/c3.pyx +238 -0
- sage/misc/compat.py +87 -0
- sage/misc/element_with_label.py +173 -0
- sage/misc/func_persist.py +79 -0
- sage/misc/pickle_old.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/misc/pickle_old.pyx +19 -0
- sage/misc/proof.py +7 -0
- sage/misc/replace_dot_all.py +472 -0
- sage/misc/sagedoc_conf.py +168 -0
- sage/misc/sphinxify.py +167 -0
- sage/misc/test_class_pickling.py +85 -0
- sage/modules/all.py +42 -0
- sage/modules/complex_double_vector.py +25 -0
- sage/modules/diamond_cutting.py +380 -0
- sage/modules/fg_pid/all.py +1 -0
- sage/modules/fg_pid/fgp_element.py +456 -0
- sage/modules/fg_pid/fgp_module.py +2091 -0
- sage/modules/fg_pid/fgp_morphism.py +550 -0
- sage/modules/filtered_vector_space.py +1271 -0
- sage/modules/finite_submodule_iter.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/modules/finite_submodule_iter.pxd +27 -0
- sage/modules/finite_submodule_iter.pyx +452 -0
- sage/modules/fp_graded/all.py +1 -0
- sage/modules/fp_graded/element.py +346 -0
- sage/modules/fp_graded/free_element.py +298 -0
- sage/modules/fp_graded/free_homspace.py +53 -0
- sage/modules/fp_graded/free_module.py +1060 -0
- sage/modules/fp_graded/free_morphism.py +217 -0
- sage/modules/fp_graded/homspace.py +563 -0
- sage/modules/fp_graded/module.py +1340 -0
- sage/modules/fp_graded/morphism.py +1990 -0
- sage/modules/fp_graded/steenrod/all.py +1 -0
- sage/modules/fp_graded/steenrod/homspace.py +65 -0
- sage/modules/fp_graded/steenrod/module.py +477 -0
- sage/modules/fp_graded/steenrod/morphism.py +404 -0
- sage/modules/fp_graded/steenrod/profile.py +241 -0
- sage/modules/free_module.py +8447 -0
- sage/modules/free_module_element.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/modules/free_module_element.pxd +22 -0
- sage/modules/free_module_element.pyx +5445 -0
- sage/modules/free_module_homspace.py +369 -0
- sage/modules/free_module_integer.py +896 -0
- sage/modules/free_module_morphism.py +823 -0
- sage/modules/free_module_pseudohomspace.py +352 -0
- sage/modules/free_module_pseudomorphism.py +578 -0
- sage/modules/free_quadratic_module.py +1706 -0
- sage/modules/free_quadratic_module_integer_symmetric.py +1790 -0
- sage/modules/matrix_morphism.py +1745 -0
- sage/modules/misc.py +103 -0
- sage/modules/module_functors.py +192 -0
- sage/modules/multi_filtered_vector_space.py +719 -0
- sage/modules/ore_module.py +2208 -0
- sage/modules/ore_module_element.py +178 -0
- sage/modules/ore_module_homspace.py +147 -0
- sage/modules/ore_module_morphism.py +968 -0
- sage/modules/quotient_module.py +699 -0
- sage/modules/real_double_vector.py +22 -0
- sage/modules/submodule.py +255 -0
- sage/modules/tensor_operations.py +567 -0
- sage/modules/torsion_quadratic_module.py +1352 -0
- sage/modules/tutorial_free_modules.py +248 -0
- sage/modules/vector_complex_double_dense.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/modules/vector_complex_double_dense.pxd +6 -0
- sage/modules/vector_complex_double_dense.pyx +117 -0
- sage/modules/vector_double_dense.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/modules/vector_double_dense.pxd +6 -0
- sage/modules/vector_double_dense.pyx +604 -0
- sage/modules/vector_integer_dense.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/modules/vector_integer_dense.pxd +15 -0
- sage/modules/vector_integer_dense.pyx +361 -0
- sage/modules/vector_integer_sparse.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/modules/vector_integer_sparse.pxd +29 -0
- sage/modules/vector_integer_sparse.pyx +406 -0
- sage/modules/vector_modn_dense.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/modules/vector_modn_dense.pxd +12 -0
- sage/modules/vector_modn_dense.pyx +394 -0
- sage/modules/vector_modn_sparse.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/modules/vector_modn_sparse.pxd +21 -0
- sage/modules/vector_modn_sparse.pyx +298 -0
- sage/modules/vector_numpy_dense.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/modules/vector_numpy_dense.pxd +15 -0
- sage/modules/vector_numpy_dense.pyx +304 -0
- sage/modules/vector_numpy_integer_dense.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/modules/vector_numpy_integer_dense.pxd +7 -0
- sage/modules/vector_numpy_integer_dense.pyx +54 -0
- sage/modules/vector_rational_dense.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/modules/vector_rational_dense.pxd +15 -0
- sage/modules/vector_rational_dense.pyx +387 -0
- sage/modules/vector_rational_sparse.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/modules/vector_rational_sparse.pxd +30 -0
- sage/modules/vector_rational_sparse.pyx +413 -0
- sage/modules/vector_real_double_dense.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/modules/vector_real_double_dense.pxd +6 -0
- sage/modules/vector_real_double_dense.pyx +126 -0
- sage/modules/vector_space_homspace.py +430 -0
- sage/modules/vector_space_morphism.py +989 -0
- sage/modules/with_basis/all.py +15 -0
- sage/modules/with_basis/cell_module.py +494 -0
- sage/modules/with_basis/indexed_element.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/modules/with_basis/indexed_element.pxd +13 -0
- sage/modules/with_basis/indexed_element.pyx +1058 -0
- sage/modules/with_basis/invariant.py +1075 -0
- sage/modules/with_basis/morphism.py +1636 -0
- sage/modules/with_basis/representation.py +2939 -0
- sage/modules/with_basis/subquotient.py +685 -0
- sage/numerical/all__sagemath_modules.py +6 -0
- sage/numerical/gauss_legendre.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/numerical/gauss_legendre.pyx +381 -0
- sage/numerical/optimize.py +910 -0
- sage/probability/all.py +10 -0
- sage/probability/probability_distribution.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/probability/probability_distribution.pyx +1242 -0
- sage/probability/random_variable.py +411 -0
- sage/quadratic_forms/all.py +4 -0
- sage/quadratic_forms/all__sagemath_modules.py +15 -0
- sage/quadratic_forms/binary_qf.py +2042 -0
- sage/quadratic_forms/bqf_class_group.py +748 -0
- sage/quadratic_forms/constructions.py +93 -0
- sage/quadratic_forms/count_local_2.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/quadratic_forms/count_local_2.pyx +365 -0
- sage/quadratic_forms/extras.py +195 -0
- sage/quadratic_forms/quadratic_form.py +1753 -0
- sage/quadratic_forms/quadratic_form__count_local_2.py +221 -0
- sage/quadratic_forms/quadratic_form__equivalence_testing.py +708 -0
- sage/quadratic_forms/quadratic_form__evaluate.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/quadratic_forms/quadratic_form__evaluate.pyx +139 -0
- sage/quadratic_forms/quadratic_form__local_density_congruence.py +977 -0
- sage/quadratic_forms/quadratic_form__local_field_invariants.py +1072 -0
- sage/quadratic_forms/quadratic_form__neighbors.py +424 -0
- sage/quadratic_forms/quadratic_form__reduction_theory.py +488 -0
- sage/quadratic_forms/quadratic_form__split_local_covering.py +416 -0
- sage/quadratic_forms/quadratic_form__ternary_Tornaria.py +657 -0
- sage/quadratic_forms/quadratic_form__theta.py +352 -0
- sage/quadratic_forms/quadratic_form__variable_substitutions.py +370 -0
- sage/quadratic_forms/random_quadraticform.py +209 -0
- sage/quadratic_forms/ternary.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/quadratic_forms/ternary.pyx +1154 -0
- sage/quadratic_forms/ternary_qf.py +2027 -0
- sage/rings/all__sagemath_modules.py +28 -0
- sage/rings/asymptotic/all__sagemath_modules.py +1 -0
- sage/rings/asymptotic/misc.py +1252 -0
- sage/rings/cc.py +4 -0
- sage/rings/cfinite_sequence.py +1306 -0
- sage/rings/complex_conversion.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/complex_conversion.pxd +8 -0
- sage/rings/complex_conversion.pyx +23 -0
- sage/rings/complex_double.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/complex_double.pxd +21 -0
- sage/rings/complex_double.pyx +2654 -0
- sage/rings/complex_mpc.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/complex_mpc.pxd +21 -0
- sage/rings/complex_mpc.pyx +2576 -0
- sage/rings/complex_mpfr.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/complex_mpfr.pxd +18 -0
- sage/rings/complex_mpfr.pyx +3602 -0
- sage/rings/derivation.py +2334 -0
- sage/rings/finite_rings/all__sagemath_modules.py +1 -0
- sage/rings/finite_rings/maps_finite_field.py +191 -0
- sage/rings/function_field/all__sagemath_modules.py +8 -0
- sage/rings/function_field/derivations.py +102 -0
- sage/rings/function_field/derivations_rational.py +132 -0
- sage/rings/function_field/differential.py +853 -0
- sage/rings/function_field/divisor.py +1107 -0
- sage/rings/function_field/drinfeld_modules/action.py +199 -0
- sage/rings/function_field/drinfeld_modules/all.py +1 -0
- sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +673 -0
- sage/rings/function_field/drinfeld_modules/drinfeld_module.py +2087 -0
- sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +1131 -0
- sage/rings/function_field/drinfeld_modules/homset.py +420 -0
- sage/rings/function_field/drinfeld_modules/morphism.py +820 -0
- sage/rings/function_field/hermite_form_polynomial.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/function_field/hermite_form_polynomial.pyx +188 -0
- sage/rings/function_field/khuri_makdisi.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/function_field/khuri_makdisi.pyx +935 -0
- sage/rings/invariants/all.py +4 -0
- sage/rings/invariants/invariant_theory.py +4597 -0
- sage/rings/invariants/reconstruction.py +395 -0
- sage/rings/polynomial/all__sagemath_modules.py +17 -0
- sage/rings/polynomial/integer_valued_polynomials.py +1230 -0
- sage/rings/polynomial/laurent_polynomial_mpair.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/laurent_polynomial_mpair.pxd +15 -0
- sage/rings/polynomial/laurent_polynomial_mpair.pyx +2023 -0
- sage/rings/polynomial/ore_function_element.py +952 -0
- sage/rings/polynomial/ore_function_field.py +1028 -0
- sage/rings/polynomial/ore_polynomial_element.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/ore_polynomial_element.pxd +48 -0
- sage/rings/polynomial/ore_polynomial_element.pyx +3145 -0
- sage/rings/polynomial/ore_polynomial_ring.py +1334 -0
- sage/rings/polynomial/polynomial_real_mpfr_dense.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/polynomial_real_mpfr_dense.pyx +788 -0
- sage/rings/polynomial/q_integer_valued_polynomials.py +1264 -0
- sage/rings/polynomial/skew_polynomial_element.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/skew_polynomial_element.pxd +9 -0
- sage/rings/polynomial/skew_polynomial_element.pyx +684 -0
- sage/rings/polynomial/skew_polynomial_finite_field.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/skew_polynomial_finite_field.pxd +19 -0
- sage/rings/polynomial/skew_polynomial_finite_field.pyx +1093 -0
- sage/rings/polynomial/skew_polynomial_finite_order.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/skew_polynomial_finite_order.pxd +10 -0
- sage/rings/polynomial/skew_polynomial_finite_order.pyx +567 -0
- sage/rings/polynomial/skew_polynomial_ring.py +908 -0
- sage/rings/real_double_element_gsl.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/real_double_element_gsl.pxd +8 -0
- sage/rings/real_double_element_gsl.pyx +794 -0
- sage/rings/real_field.py +58 -0
- sage/rings/real_mpfr.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/real_mpfr.pxd +29 -0
- sage/rings/real_mpfr.pyx +6122 -0
- sage/rings/ring_extension.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/ring_extension.pxd +42 -0
- sage/rings/ring_extension.pyx +2779 -0
- sage/rings/ring_extension_conversion.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/ring_extension_conversion.pxd +16 -0
- sage/rings/ring_extension_conversion.pyx +462 -0
- sage/rings/ring_extension_element.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/ring_extension_element.pxd +21 -0
- sage/rings/ring_extension_element.pyx +1635 -0
- sage/rings/ring_extension_homset.py +64 -0
- sage/rings/ring_extension_morphism.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/ring_extension_morphism.pxd +35 -0
- sage/rings/ring_extension_morphism.pyx +920 -0
- sage/schemes/all__sagemath_modules.py +1 -0
- sage/schemes/projective/all__sagemath_modules.py +1 -0
- sage/schemes/projective/coherent_sheaf.py +300 -0
- sage/schemes/projective/cohomology.py +510 -0
- sage/stats/all.py +15 -0
- sage/stats/basic_stats.py +489 -0
- sage/stats/distributions/all.py +7 -0
- sage/stats/distributions/catalog.py +34 -0
- sage/stats/distributions/dgs.h +50 -0
- sage/stats/distributions/dgs.pxd +111 -0
- sage/stats/distributions/dgs_bern.h +400 -0
- sage/stats/distributions/dgs_gauss.h +614 -0
- sage/stats/distributions/dgs_misc.h +104 -0
- sage/stats/distributions/discrete_gaussian_integer.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/stats/distributions/discrete_gaussian_integer.pxd +14 -0
- sage/stats/distributions/discrete_gaussian_integer.pyx +498 -0
- sage/stats/distributions/discrete_gaussian_lattice.py +908 -0
- sage/stats/distributions/discrete_gaussian_polynomial.py +141 -0
- sage/stats/hmm/all.py +15 -0
- sage/stats/hmm/chmm.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/stats/hmm/chmm.pyx +1595 -0
- sage/stats/hmm/distributions.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/stats/hmm/distributions.pxd +29 -0
- sage/stats/hmm/distributions.pyx +531 -0
- sage/stats/hmm/hmm.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/stats/hmm/hmm.pxd +17 -0
- sage/stats/hmm/hmm.pyx +1388 -0
- sage/stats/hmm/util.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/stats/hmm/util.pxd +7 -0
- sage/stats/hmm/util.pyx +165 -0
- sage/stats/intlist.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/stats/intlist.pxd +14 -0
- sage/stats/intlist.pyx +588 -0
- sage/stats/r.py +49 -0
- sage/stats/time_series.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/stats/time_series.pxd +6 -0
- sage/stats/time_series.pyx +2546 -0
- sage/tensor/all.py +2 -0
- sage/tensor/modules/all.py +8 -0
- sage/tensor/modules/alternating_contr_tensor.py +761 -0
- sage/tensor/modules/comp.py +5598 -0
- sage/tensor/modules/ext_pow_free_module.py +824 -0
- sage/tensor/modules/finite_rank_free_module.py +3589 -0
- sage/tensor/modules/format_utilities.py +333 -0
- sage/tensor/modules/free_module_alt_form.py +858 -0
- sage/tensor/modules/free_module_automorphism.py +1207 -0
- sage/tensor/modules/free_module_basis.py +1074 -0
- sage/tensor/modules/free_module_element.py +284 -0
- sage/tensor/modules/free_module_homset.py +652 -0
- sage/tensor/modules/free_module_linear_group.py +564 -0
- sage/tensor/modules/free_module_morphism.py +1581 -0
- sage/tensor/modules/free_module_tensor.py +3289 -0
- sage/tensor/modules/reflexive_module.py +386 -0
- sage/tensor/modules/tensor_free_module.py +780 -0
- sage/tensor/modules/tensor_free_submodule.py +538 -0
- sage/tensor/modules/tensor_free_submodule_basis.py +140 -0
- sage/tensor/modules/tensor_with_indices.py +1043 -0
|
@@ -0,0 +1,2042 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-modules
|
|
2
|
+
"""
|
|
3
|
+
Binary quadratic forms with integer coefficients
|
|
4
|
+
|
|
5
|
+
This module provides a specialized class for working with a binary quadratic
|
|
6
|
+
form `a x^2 + b x y + c y^2`, stored as a triple of integers `(a, b, c)`.
|
|
7
|
+
|
|
8
|
+
EXAMPLES::
|
|
9
|
+
|
|
10
|
+
sage: Q = BinaryQF([1, 2, 3])
|
|
11
|
+
sage: Q
|
|
12
|
+
x^2 + 2*x*y + 3*y^2
|
|
13
|
+
sage: Q.discriminant()
|
|
14
|
+
-8
|
|
15
|
+
sage: Q.reduced_form() # needs sage.libs.pari
|
|
16
|
+
x^2 + 2*y^2
|
|
17
|
+
sage: Q(1, 1)
|
|
18
|
+
6
|
|
19
|
+
|
|
20
|
+
TESTS::
|
|
21
|
+
|
|
22
|
+
sage: Q == loads(dumps(Q))
|
|
23
|
+
True
|
|
24
|
+
|
|
25
|
+
AUTHORS:
|
|
26
|
+
|
|
27
|
+
- Jon Hanke (2006-08-08):
|
|
28
|
+
|
|
29
|
+
- Appended to add the methods :func:`BinaryQF_reduced_representatives`,
|
|
30
|
+
:meth:`~BinaryQF.is_reduced`, and ``__add__`` on 8-3-2006 for Coding Sprint
|
|
31
|
+
#2.
|
|
32
|
+
|
|
33
|
+
- Added Documentation and :meth:`~BinaryQF.complex_point` method on 8-8-2006.
|
|
34
|
+
|
|
35
|
+
- Nick Alexander: add doctests and clean code for Doc Days 2
|
|
36
|
+
|
|
37
|
+
- William Stein (2009-08-05): composition; some ReSTification.
|
|
38
|
+
|
|
39
|
+
- William Stein (2009-09-18): make immutable.
|
|
40
|
+
|
|
41
|
+
- Justin C. Walker (2011-02-06): Add support for indefinite forms.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
# ****************************************************************************
|
|
45
|
+
# Copyright (C) 2006-2009 William Stein and Jon Hanke
|
|
46
|
+
#
|
|
47
|
+
# This program is free software: you can redistribute it and/or modify
|
|
48
|
+
# it under the terms of the GNU General Public License as published by
|
|
49
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
50
|
+
# (at your option) any later version.
|
|
51
|
+
# https://www.gnu.org/licenses/
|
|
52
|
+
# ****************************************************************************
|
|
53
|
+
from functools import total_ordering
|
|
54
|
+
|
|
55
|
+
from sage.rings.integer_ring import ZZ
|
|
56
|
+
from sage.arith.misc import gcd
|
|
57
|
+
from sage.structure.sage_object import SageObject
|
|
58
|
+
from sage.matrix.matrix_space import MatrixSpace
|
|
59
|
+
from sage.matrix.constructor import Matrix
|
|
60
|
+
from sage.misc.cachefunc import cached_method
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
from sage.libs.pari import pari
|
|
65
|
+
from cypari2.gen import Gen as pari_gen
|
|
66
|
+
except ImportError:
|
|
67
|
+
pari_gen = ()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@total_ordering
|
|
71
|
+
class BinaryQF(SageObject):
|
|
72
|
+
r"""
|
|
73
|
+
A binary quadratic form over `\ZZ`.
|
|
74
|
+
|
|
75
|
+
INPUT:
|
|
76
|
+
|
|
77
|
+
One of the following:
|
|
78
|
+
|
|
79
|
+
- ``a`` -- either a 3-tuple of integers, or a quadratic
|
|
80
|
+
homogeneous polynomial in two variables with integer
|
|
81
|
+
coefficients
|
|
82
|
+
|
|
83
|
+
- ``a``, ``b``, ``c`` -- three integers
|
|
84
|
+
|
|
85
|
+
OUTPUT: the binary quadratic form `a x^2 + b xy + c y^2`
|
|
86
|
+
|
|
87
|
+
EXAMPLES::
|
|
88
|
+
|
|
89
|
+
sage: b = BinaryQF([1, 2, 3])
|
|
90
|
+
sage: b.discriminant()
|
|
91
|
+
-8
|
|
92
|
+
sage: b1 = BinaryQF(1, 2, 3)
|
|
93
|
+
sage: b1 == b
|
|
94
|
+
True
|
|
95
|
+
sage: R.<x, y> = ZZ[]
|
|
96
|
+
sage: BinaryQF(x^2 + 2*x*y + 3*y^2) == b
|
|
97
|
+
True
|
|
98
|
+
sage: BinaryQF(1, 0, 1)
|
|
99
|
+
x^2 + y^2
|
|
100
|
+
"""
|
|
101
|
+
def __init__(self, a, b=None, c=None):
|
|
102
|
+
r"""
|
|
103
|
+
Create a binary quadratic form `ax^2 + bxy + cy^2`.
|
|
104
|
+
|
|
105
|
+
INPUT:
|
|
106
|
+
|
|
107
|
+
One of the following:
|
|
108
|
+
|
|
109
|
+
- ``a`` -- either a 3-tuple of integers, or a quadratic
|
|
110
|
+
homogeneous polynomial in two variables with integer
|
|
111
|
+
coefficients
|
|
112
|
+
|
|
113
|
+
- ``a``, ``b``, ``c`` -- three integers
|
|
114
|
+
|
|
115
|
+
EXAMPLES::
|
|
116
|
+
|
|
117
|
+
sage: Q = BinaryQF([1, 2, 3]); Q
|
|
118
|
+
x^2 + 2*x*y + 3*y^2
|
|
119
|
+
sage: Q = BinaryQF([1, 2])
|
|
120
|
+
Traceback (most recent call last):
|
|
121
|
+
...
|
|
122
|
+
TypeError: binary quadratic form must be given by a quadratic homogeneous bivariate integer polynomial or its coefficients
|
|
123
|
+
|
|
124
|
+
sage: R.<x, y> = ZZ[]
|
|
125
|
+
sage: f = x^2 + 2*x*y + 3*y^2
|
|
126
|
+
sage: BinaryQF(f)
|
|
127
|
+
x^2 + 2*x*y + 3*y^2
|
|
128
|
+
sage: BinaryQF(f + x)
|
|
129
|
+
Traceback (most recent call last):
|
|
130
|
+
...
|
|
131
|
+
TypeError: binary quadratic form must be given by a quadratic homogeneous bivariate integer polynomial or its coefficients
|
|
132
|
+
|
|
133
|
+
TESTS::
|
|
134
|
+
|
|
135
|
+
sage: BinaryQF(0)
|
|
136
|
+
0
|
|
137
|
+
"""
|
|
138
|
+
from sage.rings.polynomial.multi_polynomial import MPolynomial
|
|
139
|
+
if b is None and c is None:
|
|
140
|
+
if isinstance(a, (list, tuple)) and len(a) == 3:
|
|
141
|
+
a, b, c = a
|
|
142
|
+
elif a == 0:
|
|
143
|
+
a = b = c = 0
|
|
144
|
+
elif (isinstance(a, MPolynomial) and a.is_homogeneous() and a.base_ring() == ZZ
|
|
145
|
+
and a.degree() == 2 and a.parent().ngens() == 2):
|
|
146
|
+
x, y = a.parent().gens()
|
|
147
|
+
a, b, c = (a.monomial_coefficient(mon) for mon in [x**2, x*y, y**2])
|
|
148
|
+
elif isinstance(a, pari_gen) and a.type() in ('t_QFI', 't_QFR', 't_QFB'):
|
|
149
|
+
# a has 3 or 4 components
|
|
150
|
+
a, b, c = a[0], a[1], a[2]
|
|
151
|
+
try:
|
|
152
|
+
self._a = ZZ(a)
|
|
153
|
+
self._b = ZZ(b)
|
|
154
|
+
self._c = ZZ(c)
|
|
155
|
+
except TypeError:
|
|
156
|
+
raise TypeError('binary quadratic form must be given by a quadratic homogeneous bivariate integer polynomial or its coefficients')
|
|
157
|
+
self._poly = None
|
|
158
|
+
|
|
159
|
+
def _pari_init_(self):
|
|
160
|
+
"""
|
|
161
|
+
Convert this quadratic form to PARI.
|
|
162
|
+
|
|
163
|
+
EXAMPLES::
|
|
164
|
+
|
|
165
|
+
sage: f = BinaryQF([2, 3, 4]); f
|
|
166
|
+
2*x^2 + 3*x*y + 4*y^2
|
|
167
|
+
sage: f._pari_init_()
|
|
168
|
+
'Qfb(2,3,4)'
|
|
169
|
+
|
|
170
|
+
sage: # needs sage.libs.pari
|
|
171
|
+
sage: pari(f)
|
|
172
|
+
Qfb(2, 3, 4)
|
|
173
|
+
sage: type(pari(f))
|
|
174
|
+
<... 'cypari2.gen.Gen'>
|
|
175
|
+
sage: gp(f)
|
|
176
|
+
Qfb(2, 3, 4)
|
|
177
|
+
sage: type(gp(f))
|
|
178
|
+
<class 'sage.interfaces.gp.GpElement'>
|
|
179
|
+
"""
|
|
180
|
+
return f'Qfb({self._a},{self._b},{self._c})'
|
|
181
|
+
|
|
182
|
+
@staticmethod
|
|
183
|
+
def principal(D):
|
|
184
|
+
r"""
|
|
185
|
+
Return the principal binary quadratic form of the given discriminant.
|
|
186
|
+
|
|
187
|
+
EXAMPLES::
|
|
188
|
+
|
|
189
|
+
sage: BinaryQF.principal(8)
|
|
190
|
+
x^2 - 2*y^2
|
|
191
|
+
sage: BinaryQF.principal(5)
|
|
192
|
+
x^2 + x*y - y^2
|
|
193
|
+
sage: BinaryQF.principal(4)
|
|
194
|
+
x^2 - y^2
|
|
195
|
+
sage: BinaryQF.principal(1)
|
|
196
|
+
x^2 + x*y
|
|
197
|
+
sage: BinaryQF.principal(-3)
|
|
198
|
+
x^2 + x*y + y^2
|
|
199
|
+
sage: BinaryQF.principal(-4)
|
|
200
|
+
x^2 + y^2
|
|
201
|
+
sage: BinaryQF.principal(-7)
|
|
202
|
+
x^2 + x*y + 2*y^2
|
|
203
|
+
sage: BinaryQF.principal(-8)
|
|
204
|
+
x^2 + 2*y^2
|
|
205
|
+
|
|
206
|
+
TESTS:
|
|
207
|
+
|
|
208
|
+
Some randomized testing::
|
|
209
|
+
|
|
210
|
+
sage: D = 1
|
|
211
|
+
sage: while D.is_square():
|
|
212
|
+
....: D = choice((-4,+4)) * randrange(9999) + randrange(2)
|
|
213
|
+
sage: Q = BinaryQF.principal(D)
|
|
214
|
+
sage: Q.discriminant() == D # correct discriminant
|
|
215
|
+
True
|
|
216
|
+
sage: (Q*Q).is_equivalent(Q) # idempotent (hence identity) # needs sage.libs.pari
|
|
217
|
+
True
|
|
218
|
+
"""
|
|
219
|
+
D = ZZ(D)
|
|
220
|
+
D4 = D % 4
|
|
221
|
+
if D4 not in (0, 1):
|
|
222
|
+
raise ValueError('discriminant must be congruent to 0 or 1 modulo 4')
|
|
223
|
+
return BinaryQF([1, D4, (D4-D)//4])
|
|
224
|
+
|
|
225
|
+
def __mul__(self, right):
|
|
226
|
+
"""
|
|
227
|
+
Gauss composition or right action by a 2x2 integer matrix.
|
|
228
|
+
|
|
229
|
+
The result need not be reduced.
|
|
230
|
+
|
|
231
|
+
EXAMPLES:
|
|
232
|
+
|
|
233
|
+
We explicitly compute in the group of classes of positive
|
|
234
|
+
definite binary quadratic forms of discriminant -23::
|
|
235
|
+
|
|
236
|
+
sage: # needs sage.libs.pari
|
|
237
|
+
sage: R = BinaryQF_reduced_representatives(-23, primitive_only=False); R
|
|
238
|
+
[x^2 + x*y + 6*y^2, 2*x^2 - x*y + 3*y^2, 2*x^2 + x*y + 3*y^2]
|
|
239
|
+
sage: R[0] * R[0]
|
|
240
|
+
x^2 + x*y + 6*y^2
|
|
241
|
+
sage: R[1] * R[1]
|
|
242
|
+
4*x^2 + 3*x*y + 2*y^2
|
|
243
|
+
sage: (R[1] * R[1]).reduced_form()
|
|
244
|
+
2*x^2 + x*y + 3*y^2
|
|
245
|
+
sage: (R[1] * R[1] * R[1]).reduced_form()
|
|
246
|
+
x^2 + x*y + 6*y^2
|
|
247
|
+
|
|
248
|
+
sage: q1 = BinaryQF(1, 1, 4)
|
|
249
|
+
sage: M = Matrix(ZZ, [[1, 3], [0, 1]])
|
|
250
|
+
sage: q1*M
|
|
251
|
+
x^2 + 7*x*y + 16*y^2
|
|
252
|
+
sage: q1.matrix_action_right(M)
|
|
253
|
+
x^2 + 7*x*y + 16*y^2
|
|
254
|
+
sage: N = Matrix(ZZ, [[1, 0], [1, 0]])
|
|
255
|
+
sage: q1*(M*N) == q1.matrix_action_right(M).matrix_action_right(N)
|
|
256
|
+
True
|
|
257
|
+
"""
|
|
258
|
+
# Either a "right" action by
|
|
259
|
+
# ...or Gaussian composition
|
|
260
|
+
if isinstance(right, BinaryQF):
|
|
261
|
+
return BinaryQF(self.__pari__().qfbcompraw(right))
|
|
262
|
+
# ...or a 2x2 matrix...
|
|
263
|
+
if (isinstance(right.parent(), MatrixSpace)
|
|
264
|
+
and right.nrows() == right.ncols() == 2):
|
|
265
|
+
aa, bb, cc, dd = right.list()
|
|
266
|
+
A = self.polynomial()(aa, cc)
|
|
267
|
+
C = self.polynomial()(bb, dd)
|
|
268
|
+
B = self.polynomial()(aa + bb, cc + dd) - A - C
|
|
269
|
+
return BinaryQF(A, B, C)
|
|
270
|
+
raise TypeError("right operand must be a binary quadratic form or 2x2 matrix")
|
|
271
|
+
|
|
272
|
+
def __getitem__(self, n):
|
|
273
|
+
"""
|
|
274
|
+
Return the `n`-th component of this quadratic form.
|
|
275
|
+
|
|
276
|
+
If this form is `a x^2 + b x y + c y^2`, the 0-th component is `a`,
|
|
277
|
+
the 1-st component is `b`, and `2`-nd component is `c`.
|
|
278
|
+
|
|
279
|
+
Indexing is like lists -- negative indices and slices are allowed.
|
|
280
|
+
|
|
281
|
+
EXAMPLES::
|
|
282
|
+
|
|
283
|
+
sage: Q = BinaryQF([2, 3, 4])
|
|
284
|
+
sage: Q[0]
|
|
285
|
+
2
|
|
286
|
+
sage: Q[2]
|
|
287
|
+
4
|
|
288
|
+
sage: Q[:2]
|
|
289
|
+
(2, 3)
|
|
290
|
+
sage: tuple(Q)
|
|
291
|
+
(2, 3, 4)
|
|
292
|
+
sage: list(Q)
|
|
293
|
+
[2, 3, 4]
|
|
294
|
+
"""
|
|
295
|
+
return (self._a, self._b, self._c)[n]
|
|
296
|
+
|
|
297
|
+
def __call__(self, *args):
|
|
298
|
+
r"""
|
|
299
|
+
Evaluate this quadratic form at a point.
|
|
300
|
+
|
|
301
|
+
INPUT:
|
|
302
|
+
|
|
303
|
+
- ``args`` -- x and y values, as a pair x, y or a list, tuple, or
|
|
304
|
+
vector
|
|
305
|
+
|
|
306
|
+
EXAMPLES::
|
|
307
|
+
|
|
308
|
+
sage: Q = BinaryQF([2, 3, 4])
|
|
309
|
+
sage: Q(1, 2)
|
|
310
|
+
24
|
|
311
|
+
|
|
312
|
+
TESTS::
|
|
313
|
+
|
|
314
|
+
sage: Q = BinaryQF([2, 3, 4])
|
|
315
|
+
sage: Q([1, 2])
|
|
316
|
+
24
|
|
317
|
+
sage: Q((1, 2))
|
|
318
|
+
24
|
|
319
|
+
sage: Q(vector([1, 2]))
|
|
320
|
+
24
|
|
321
|
+
"""
|
|
322
|
+
if len(args) == 1:
|
|
323
|
+
args = args[0]
|
|
324
|
+
x, y = args
|
|
325
|
+
return (self._a * x + self._b * y) * x + self._c * y**2
|
|
326
|
+
|
|
327
|
+
def __hash__(self):
|
|
328
|
+
r"""
|
|
329
|
+
TESTS::
|
|
330
|
+
|
|
331
|
+
sage: hash(BinaryQF([2, 2, 3]))
|
|
332
|
+
802
|
|
333
|
+
sage: hash(BinaryQF([2, 3, 2]))
|
|
334
|
+
562
|
|
335
|
+
sage: hash(BinaryQF([3, 2, 2]))
|
|
336
|
+
547
|
|
337
|
+
"""
|
|
338
|
+
return hash(self._a) ^ (hash(self._b) << 4) ^ (hash(self._c) << 8)
|
|
339
|
+
|
|
340
|
+
def __eq__(self, right):
|
|
341
|
+
"""
|
|
342
|
+
Return ``True`` if ``self`` and ``right`` are identical.
|
|
343
|
+
|
|
344
|
+
This means that they have the same coefficients.
|
|
345
|
+
|
|
346
|
+
EXAMPLES::
|
|
347
|
+
|
|
348
|
+
sage: P = BinaryQF([2, 2, 3])
|
|
349
|
+
sage: Q = BinaryQF([2, 2, 3])
|
|
350
|
+
sage: R = BinaryQF([1, 2, 3])
|
|
351
|
+
sage: P == Q # indirect doctest
|
|
352
|
+
True
|
|
353
|
+
sage: P == R # indirect doctest
|
|
354
|
+
False
|
|
355
|
+
|
|
356
|
+
TESTS::
|
|
357
|
+
|
|
358
|
+
sage: P == P
|
|
359
|
+
True
|
|
360
|
+
sage: Q == P
|
|
361
|
+
True
|
|
362
|
+
sage: R == P
|
|
363
|
+
False
|
|
364
|
+
sage: P == 2
|
|
365
|
+
False
|
|
366
|
+
"""
|
|
367
|
+
if not isinstance(right, BinaryQF):
|
|
368
|
+
return False
|
|
369
|
+
return (self._a, self._b, self._c) == (right._a, right._b, right._c)
|
|
370
|
+
|
|
371
|
+
def __ne__(self, right):
|
|
372
|
+
"""
|
|
373
|
+
Return ``True`` if ``self`` and ``right`` are not identical.
|
|
374
|
+
|
|
375
|
+
This means that they have different coefficients.
|
|
376
|
+
|
|
377
|
+
EXAMPLES::
|
|
378
|
+
|
|
379
|
+
sage: P = BinaryQF([2, 2, 3])
|
|
380
|
+
sage: Q = BinaryQF([2, 2, 3])
|
|
381
|
+
sage: R = BinaryQF([1, 2, 3])
|
|
382
|
+
sage: P != Q # indirect doctest
|
|
383
|
+
False
|
|
384
|
+
sage: P != R # indirect doctest
|
|
385
|
+
True
|
|
386
|
+
"""
|
|
387
|
+
return not (self == right)
|
|
388
|
+
|
|
389
|
+
def __lt__(self, right):
|
|
390
|
+
"""
|
|
391
|
+
Compare the coefficients of ``self`` and ``right``.
|
|
392
|
+
|
|
393
|
+
This is done lexicographically.
|
|
394
|
+
|
|
395
|
+
EXAMPLES::
|
|
396
|
+
|
|
397
|
+
sage: P = BinaryQF([2, 2, 3])
|
|
398
|
+
sage: Q = BinaryQF([1, 2, 3])
|
|
399
|
+
sage: P < Q
|
|
400
|
+
False
|
|
401
|
+
sage: Q < P
|
|
402
|
+
True
|
|
403
|
+
sage: Q <= P
|
|
404
|
+
True
|
|
405
|
+
"""
|
|
406
|
+
if not isinstance(right, BinaryQF):
|
|
407
|
+
return False
|
|
408
|
+
return (self._a, self._b, self._c) < (right._a, right._b, right._c)
|
|
409
|
+
|
|
410
|
+
def __add__(self, Q):
|
|
411
|
+
"""
|
|
412
|
+
Return the component-wise sum of two forms.
|
|
413
|
+
|
|
414
|
+
Given `a_1 x^2 + b_1 x y + c_1 y^2` and `a_2 x^2 + b_2 x y +
|
|
415
|
+
c_2 y^2`, this returns the form `(a_1 + a_2) x^2 + (b_1 + b_2)
|
|
416
|
+
x y + (c_1 + c_2) y^2.`
|
|
417
|
+
|
|
418
|
+
EXAMPLES::
|
|
419
|
+
|
|
420
|
+
sage: P = BinaryQF([2, 2, 3]); P
|
|
421
|
+
2*x^2 + 2*x*y + 3*y^2
|
|
422
|
+
sage: Q = BinaryQF([-1, 2, 2]); Q
|
|
423
|
+
-x^2 + 2*x*y + 2*y^2
|
|
424
|
+
sage: P + Q
|
|
425
|
+
x^2 + 4*x*y + 5*y^2
|
|
426
|
+
sage: P + Q == BinaryQF([1, 4, 5]) # indirect doctest
|
|
427
|
+
True
|
|
428
|
+
|
|
429
|
+
TESTS::
|
|
430
|
+
|
|
431
|
+
sage: Q + P == BinaryQF([1, 4, 5]) # indirect doctest
|
|
432
|
+
True
|
|
433
|
+
"""
|
|
434
|
+
return BinaryQF([self._a + Q._a, self._b + Q._b, self._c + Q._c])
|
|
435
|
+
|
|
436
|
+
def __sub__(self, Q):
|
|
437
|
+
"""
|
|
438
|
+
Return the component-wise difference of two forms.
|
|
439
|
+
|
|
440
|
+
Given two forms `a_1 x^2 + b_1 x y + c_1 y^2` and `a_2 x^2 +
|
|
441
|
+
b_2 x y + c_2 y^2`, this returns the form `(a_1 - a_2) x^2 +
|
|
442
|
+
(b_1 - b_2) x y + (c_1 - c_2) y^2.`
|
|
443
|
+
|
|
444
|
+
EXAMPLES::
|
|
445
|
+
|
|
446
|
+
sage: P = BinaryQF([2, 2, 3]); P
|
|
447
|
+
2*x^2 + 2*x*y + 3*y^2
|
|
448
|
+
sage: Q = BinaryQF([-1, 2, 2]); Q
|
|
449
|
+
-x^2 + 2*x*y + 2*y^2
|
|
450
|
+
sage: P - Q
|
|
451
|
+
3*x^2 + y^2
|
|
452
|
+
sage: P - Q == BinaryQF([3, 0, 1]) # indirect doctest
|
|
453
|
+
True
|
|
454
|
+
|
|
455
|
+
TESTS::
|
|
456
|
+
|
|
457
|
+
sage: Q - P == BinaryQF([3, 0, 1]) # indirect doctest
|
|
458
|
+
False
|
|
459
|
+
sage: Q - P != BinaryQF([3, 0, 1]) # indirect doctest
|
|
460
|
+
True
|
|
461
|
+
"""
|
|
462
|
+
return BinaryQF([self._a - Q._a, self._b - Q._b, self._c - Q._c])
|
|
463
|
+
|
|
464
|
+
def __neg__(self):
|
|
465
|
+
r"""
|
|
466
|
+
Return the negative of this binary quadratic form.
|
|
467
|
+
|
|
468
|
+
EXAMPLES::
|
|
469
|
+
|
|
470
|
+
sage: Q = BinaryQF([1,-2,3])
|
|
471
|
+
sage: -Q
|
|
472
|
+
-x^2 + 2*x*y - 3*y^2
|
|
473
|
+
sage: -Q == BinaryQF([0,0,0]) - Q
|
|
474
|
+
True
|
|
475
|
+
"""
|
|
476
|
+
return BinaryQF([-self._a, -self._b, -self._c])
|
|
477
|
+
|
|
478
|
+
def _repr_(self) -> str:
|
|
479
|
+
"""
|
|
480
|
+
Display the quadratic form.
|
|
481
|
+
|
|
482
|
+
EXAMPLES::
|
|
483
|
+
|
|
484
|
+
sage: Q = BinaryQF([1, 2, 3]); Q # indirect doctest
|
|
485
|
+
x^2 + 2*x*y + 3*y^2
|
|
486
|
+
|
|
487
|
+
sage: Q = BinaryQF([-1, 2, 3]); Q
|
|
488
|
+
-x^2 + 2*x*y + 3*y^2
|
|
489
|
+
|
|
490
|
+
sage: Q = BinaryQF([0, 0, 0]); Q
|
|
491
|
+
0
|
|
492
|
+
"""
|
|
493
|
+
return repr(self.polynomial())
|
|
494
|
+
|
|
495
|
+
def _latex_(self) -> str:
|
|
496
|
+
"""
|
|
497
|
+
Return latex representation of this binary quadratic form.
|
|
498
|
+
|
|
499
|
+
EXAMPLES::
|
|
500
|
+
|
|
501
|
+
sage: f = BinaryQF((778, 1115, 400)); f
|
|
502
|
+
778*x^2 + 1115*x*y + 400*y^2
|
|
503
|
+
sage: latex(f) # indirect doctest
|
|
504
|
+
778 x^{2} + 1115 x y + 400 y^{2}
|
|
505
|
+
"""
|
|
506
|
+
return self.polynomial()._latex_()
|
|
507
|
+
|
|
508
|
+
@cached_method
|
|
509
|
+
def content(self):
|
|
510
|
+
r"""
|
|
511
|
+
Return the content of the form, i.e., the `\gcd` of the coefficients.
|
|
512
|
+
|
|
513
|
+
EXAMPLES::
|
|
514
|
+
|
|
515
|
+
sage: Q = BinaryQF(22, 14, 10)
|
|
516
|
+
sage: Q.content()
|
|
517
|
+
2
|
|
518
|
+
sage: Q = BinaryQF(4, 4, -15)
|
|
519
|
+
sage: Q.content()
|
|
520
|
+
1
|
|
521
|
+
"""
|
|
522
|
+
return gcd([self._a, self._b, self._c])
|
|
523
|
+
|
|
524
|
+
def polynomial(self):
|
|
525
|
+
"""
|
|
526
|
+
Return ``self`` as a homogeneous 2-variable polynomial.
|
|
527
|
+
|
|
528
|
+
EXAMPLES::
|
|
529
|
+
|
|
530
|
+
sage: Q = BinaryQF([1, 2, 3])
|
|
531
|
+
sage: Q.polynomial()
|
|
532
|
+
x^2 + 2*x*y + 3*y^2
|
|
533
|
+
|
|
534
|
+
sage: Q = BinaryQF([-1, -2, 3])
|
|
535
|
+
sage: Q.polynomial()
|
|
536
|
+
-x^2 - 2*x*y + 3*y^2
|
|
537
|
+
|
|
538
|
+
sage: Q = BinaryQF([0, 0, 0])
|
|
539
|
+
sage: Q.polynomial()
|
|
540
|
+
0
|
|
541
|
+
"""
|
|
542
|
+
# Note: Caching in _poly seems to give a very slight
|
|
543
|
+
# improvement (~0.2 usec) in 'timeit()' runs. Not sure it
|
|
544
|
+
# is worth the instance variable.
|
|
545
|
+
if self._poly is None:
|
|
546
|
+
self._poly = self(ZZ['x, y'].gens())
|
|
547
|
+
return self._poly
|
|
548
|
+
|
|
549
|
+
@staticmethod
|
|
550
|
+
def from_polynomial(poly):
|
|
551
|
+
r"""
|
|
552
|
+
Construct a :class:`BinaryQF` from a bivariate polynomial
|
|
553
|
+
with integer coefficients. Inverse of :meth:`polynomial`.
|
|
554
|
+
|
|
555
|
+
EXAMPLES::
|
|
556
|
+
|
|
557
|
+
sage: R.<u,v> = ZZ[]
|
|
558
|
+
sage: f = u^2 + 419*v^2
|
|
559
|
+
sage: Q = BinaryQF.from_polynomial(f); Q
|
|
560
|
+
x^2 + 419*y^2
|
|
561
|
+
sage: Q.polynomial()
|
|
562
|
+
x^2 + 419*y^2
|
|
563
|
+
sage: Q.polynomial()(R.gens()) == f
|
|
564
|
+
True
|
|
565
|
+
|
|
566
|
+
The method fails if the given polynomial is not a quadratic form::
|
|
567
|
+
|
|
568
|
+
sage: BinaryQF.from_polynomial(u^3 - 5*v)
|
|
569
|
+
Traceback (most recent call last):
|
|
570
|
+
...
|
|
571
|
+
ValueError: polynomial has monomials of degree != 2
|
|
572
|
+
|
|
573
|
+
...or if the coefficients aren't integers::
|
|
574
|
+
|
|
575
|
+
sage: BinaryQF.from_polynomial(u^2/7 + v^2)
|
|
576
|
+
Traceback (most recent call last):
|
|
577
|
+
...
|
|
578
|
+
TypeError: no conversion of this rational to integer
|
|
579
|
+
"""
|
|
580
|
+
R = poly.parent()
|
|
581
|
+
from sage.rings.polynomial.multi_polynomial_ring_base import MPolynomialRing_base
|
|
582
|
+
if not isinstance(R, MPolynomialRing_base) or R.ngens() != 2:
|
|
583
|
+
raise TypeError(f'not a bivariate polynomial ring: {R}')
|
|
584
|
+
if not all(mon.degree() == 2 for mon in poly.monomials()):
|
|
585
|
+
raise ValueError('polynomial has monomials of degree != 2')
|
|
586
|
+
x, y = R.gens()
|
|
587
|
+
coeffs = (poly.monomial_coefficient(mon) for mon in (x**2, x*y, y**2))
|
|
588
|
+
a, b, c = map(ZZ, coeffs)
|
|
589
|
+
return BinaryQF(a, b, c)
|
|
590
|
+
|
|
591
|
+
@cached_method
|
|
592
|
+
def discriminant(self):
|
|
593
|
+
"""
|
|
594
|
+
Return the discriminant of ``self``.
|
|
595
|
+
|
|
596
|
+
Given a form `ax^2 + bxy + cy^2`, this returns `b^2 - 4ac`.
|
|
597
|
+
|
|
598
|
+
EXAMPLES::
|
|
599
|
+
|
|
600
|
+
sage: Q = BinaryQF([1, 2, 3])
|
|
601
|
+
sage: Q.discriminant()
|
|
602
|
+
-8
|
|
603
|
+
"""
|
|
604
|
+
return self._b**2 - 4 * self._a * self._c
|
|
605
|
+
|
|
606
|
+
def determinant(self):
|
|
607
|
+
"""
|
|
608
|
+
Return the determinant of the matrix associated to ``self``.
|
|
609
|
+
|
|
610
|
+
The determinant is used by Gauss and by Conway-Sloane, for
|
|
611
|
+
whom an integral quadratic form has coefficients `(a, 2b, c)`
|
|
612
|
+
with `a`, `b`, `c` integers.
|
|
613
|
+
|
|
614
|
+
OUTPUT: the determinant of the matrix::
|
|
615
|
+
|
|
616
|
+
[ a b/2]
|
|
617
|
+
[b/2 c]
|
|
618
|
+
|
|
619
|
+
as a rational.
|
|
620
|
+
|
|
621
|
+
REMARK:
|
|
622
|
+
|
|
623
|
+
This is just `-D/4` where `D` is the discriminant. The return
|
|
624
|
+
type is rational even if `b` (and hence `D`) is even.
|
|
625
|
+
|
|
626
|
+
EXAMPLES::
|
|
627
|
+
|
|
628
|
+
sage: q = BinaryQF(1, -1, 67)
|
|
629
|
+
sage: q.determinant()
|
|
630
|
+
267/4
|
|
631
|
+
"""
|
|
632
|
+
return -self.discriminant() / 4
|
|
633
|
+
|
|
634
|
+
# for consistency with general quadratic form code
|
|
635
|
+
det = determinant
|
|
636
|
+
|
|
637
|
+
@cached_method
|
|
638
|
+
def has_fundamental_discriminant(self) -> bool:
|
|
639
|
+
"""
|
|
640
|
+
Return whether the discriminant `D` of this form is a
|
|
641
|
+
fundamental discriminant (i.e. `D` is the smallest element
|
|
642
|
+
of its squareclass with `D = 0` or `1` modulo `4`).
|
|
643
|
+
|
|
644
|
+
EXAMPLES::
|
|
645
|
+
|
|
646
|
+
sage: Q = BinaryQF([1, 0, 1])
|
|
647
|
+
sage: Q.discriminant()
|
|
648
|
+
-4
|
|
649
|
+
sage: Q.has_fundamental_discriminant() # needs sage.libs.pari
|
|
650
|
+
True
|
|
651
|
+
|
|
652
|
+
sage: Q = BinaryQF([2, 0, 2])
|
|
653
|
+
sage: Q.discriminant()
|
|
654
|
+
-16
|
|
655
|
+
sage: Q.has_fundamental_discriminant() # needs sage.libs.pari
|
|
656
|
+
False
|
|
657
|
+
"""
|
|
658
|
+
return self.discriminant().is_fundamental_discriminant()
|
|
659
|
+
|
|
660
|
+
def is_primitive(self) -> bool:
|
|
661
|
+
r"""
|
|
662
|
+
Return whether the form `ax^2 + bxy + cy^2` satisfies
|
|
663
|
+
`\gcd(a, b, c) = 1`, i.e., is primitive.
|
|
664
|
+
|
|
665
|
+
EXAMPLES::
|
|
666
|
+
|
|
667
|
+
sage: Q = BinaryQF([6, 3, 9])
|
|
668
|
+
sage: Q.is_primitive()
|
|
669
|
+
False
|
|
670
|
+
|
|
671
|
+
sage: Q = BinaryQF([1, 1, 1])
|
|
672
|
+
sage: Q.is_primitive()
|
|
673
|
+
True
|
|
674
|
+
|
|
675
|
+
sage: Q = BinaryQF([2, 2, 2])
|
|
676
|
+
sage: Q.is_primitive()
|
|
677
|
+
False
|
|
678
|
+
|
|
679
|
+
sage: rqf = BinaryQF_reduced_representatives(-23*9, primitive_only=False)
|
|
680
|
+
sage: [qf.is_primitive() for qf in rqf]
|
|
681
|
+
[True, True, True, False, True, True, False, False, True]
|
|
682
|
+
sage: rqf
|
|
683
|
+
[x^2 + x*y + 52*y^2,
|
|
684
|
+
2*x^2 - x*y + 26*y^2,
|
|
685
|
+
2*x^2 + x*y + 26*y^2,
|
|
686
|
+
3*x^2 + 3*x*y + 18*y^2,
|
|
687
|
+
4*x^2 - x*y + 13*y^2,
|
|
688
|
+
4*x^2 + x*y + 13*y^2,
|
|
689
|
+
6*x^2 - 3*x*y + 9*y^2,
|
|
690
|
+
6*x^2 + 3*x*y + 9*y^2,
|
|
691
|
+
8*x^2 + 7*x*y + 8*y^2]
|
|
692
|
+
sage: [qf for qf in rqf if qf.is_primitive()]
|
|
693
|
+
[x^2 + x*y + 52*y^2,
|
|
694
|
+
2*x^2 - x*y + 26*y^2,
|
|
695
|
+
2*x^2 + x*y + 26*y^2,
|
|
696
|
+
4*x^2 - x*y + 13*y^2,
|
|
697
|
+
4*x^2 + x*y + 13*y^2,
|
|
698
|
+
8*x^2 + 7*x*y + 8*y^2]
|
|
699
|
+
|
|
700
|
+
.. SEEALSO::
|
|
701
|
+
|
|
702
|
+
:meth:`content`
|
|
703
|
+
"""
|
|
704
|
+
return self.content().is_one()
|
|
705
|
+
|
|
706
|
+
def is_zero(self) -> bool:
|
|
707
|
+
"""
|
|
708
|
+
Return whether ``self`` is identically zero.
|
|
709
|
+
|
|
710
|
+
EXAMPLES::
|
|
711
|
+
|
|
712
|
+
sage: Q = BinaryQF(195751, 37615, 1807)
|
|
713
|
+
sage: Q.is_zero()
|
|
714
|
+
False
|
|
715
|
+
sage: Q = BinaryQF(0, 0, 0)
|
|
716
|
+
sage: Q.is_zero()
|
|
717
|
+
True
|
|
718
|
+
"""
|
|
719
|
+
return self.content().is_zero()
|
|
720
|
+
|
|
721
|
+
@cached_method
|
|
722
|
+
def is_weakly_reduced(self) -> bool:
|
|
723
|
+
r"""
|
|
724
|
+
Check if the form `ax^2 + bxy + cy^2` satisfies
|
|
725
|
+
`|b| \leq a \leq c`, i.e., is weakly reduced.
|
|
726
|
+
|
|
727
|
+
EXAMPLES::
|
|
728
|
+
|
|
729
|
+
sage: Q = BinaryQF([1, 2, 3])
|
|
730
|
+
sage: Q.is_weakly_reduced()
|
|
731
|
+
False
|
|
732
|
+
|
|
733
|
+
sage: Q = BinaryQF([2, 1, 3])
|
|
734
|
+
sage: Q.is_weakly_reduced()
|
|
735
|
+
True
|
|
736
|
+
|
|
737
|
+
sage: Q = BinaryQF([1, -1, 1])
|
|
738
|
+
sage: Q.is_weakly_reduced()
|
|
739
|
+
True
|
|
740
|
+
"""
|
|
741
|
+
if self.discriminant() >= 0:
|
|
742
|
+
raise ValueError("only defined for negative discriminant")
|
|
743
|
+
return (abs(self._b) <= self._a) and (self._a <= self._c)
|
|
744
|
+
|
|
745
|
+
@cached_method
|
|
746
|
+
def is_reducible(self) -> bool:
|
|
747
|
+
r"""
|
|
748
|
+
Return whether this form is reducible and cache the result.
|
|
749
|
+
|
|
750
|
+
A binary form `q` is called reducible if it is the product of
|
|
751
|
+
two linear forms `q = (a x + b y) (c x + d y)`, or
|
|
752
|
+
equivalently if its discriminant is a square.
|
|
753
|
+
|
|
754
|
+
EXAMPLES::
|
|
755
|
+
|
|
756
|
+
sage: q = BinaryQF([1, 0, -1])
|
|
757
|
+
sage: q.is_reducible()
|
|
758
|
+
True
|
|
759
|
+
|
|
760
|
+
.. WARNING::
|
|
761
|
+
|
|
762
|
+
Despite the similar name, this method is unrelated to
|
|
763
|
+
reduction of binary quadratic forms as implemented by
|
|
764
|
+
:meth:`reduced_form` and :meth:`is_reduced`.
|
|
765
|
+
"""
|
|
766
|
+
return self.discriminant().is_square()
|
|
767
|
+
|
|
768
|
+
def _reduce_indef(self, transformation=False):
|
|
769
|
+
"""
|
|
770
|
+
Reduce an indefinite, non-reduced form.
|
|
771
|
+
|
|
772
|
+
INPUT:
|
|
773
|
+
|
|
774
|
+
- ``transformation`` -- boolean (default: ``False``); if ``True``,
|
|
775
|
+
return both the reduced form and a matrix transforming ``self`` into
|
|
776
|
+
the reduced form
|
|
777
|
+
|
|
778
|
+
TESTS::
|
|
779
|
+
|
|
780
|
+
sage: f = BinaryQF(-1, 0, 3)
|
|
781
|
+
sage: f._reduce_indef(transformation=False)
|
|
782
|
+
-x^2 + 2*x*y + 2*y^2
|
|
783
|
+
sage: red, trans = f._reduce_indef(transformation=True)
|
|
784
|
+
sage: red
|
|
785
|
+
-x^2 + 2*x*y + 2*y^2
|
|
786
|
+
sage: trans
|
|
787
|
+
[-1 1]
|
|
788
|
+
[ 0 -1]
|
|
789
|
+
sage: red == f*trans
|
|
790
|
+
True
|
|
791
|
+
|
|
792
|
+
sage: f = BinaryQF(0, 5, 24)
|
|
793
|
+
sage: red, trans = f._reduce_indef(transformation=True)
|
|
794
|
+
sage: red == f*trans
|
|
795
|
+
True
|
|
796
|
+
"""
|
|
797
|
+
if transformation:
|
|
798
|
+
U = Matrix(ZZ, 2, 2, [1, 0, 0, 1])
|
|
799
|
+
d = self.discriminant().sqrt(prec=53)
|
|
800
|
+
Q = self
|
|
801
|
+
while not Q.is_reduced():
|
|
802
|
+
a = Q._a
|
|
803
|
+
b = Q._b
|
|
804
|
+
c = Q._c
|
|
805
|
+
cabs = c.abs()
|
|
806
|
+
# rho(f) as defined in [BUVO2007]_ p. 112 equation (6.12)
|
|
807
|
+
if cabs != 0:
|
|
808
|
+
if cabs >= d:
|
|
809
|
+
s = c.sign() * ((cabs + b) / (2 * cabs)).floor()
|
|
810
|
+
else:
|
|
811
|
+
s = c.sign() * ((d + b) / (2 * cabs)).floor()
|
|
812
|
+
if transformation:
|
|
813
|
+
T = Matrix(ZZ, 2, 2, [0, -1, 1, s])
|
|
814
|
+
U = U * T
|
|
815
|
+
Q = BinaryQF(c, -b + 2*s*c, c*s*s - b*s + a)
|
|
816
|
+
else:
|
|
817
|
+
if b < 0:
|
|
818
|
+
Q = BinaryQF(a, -b, c)
|
|
819
|
+
if transformation:
|
|
820
|
+
T = Matrix(ZZ, 2, 2, [1, 0, 0, -1])
|
|
821
|
+
U = U * T
|
|
822
|
+
else:
|
|
823
|
+
q, r = a.quo_rem(b)
|
|
824
|
+
if 2*r > b:
|
|
825
|
+
q, r = a.quo_rem(-b)
|
|
826
|
+
q = -q
|
|
827
|
+
if transformation:
|
|
828
|
+
T = Matrix(ZZ, 2, 2, [1, 0, -q, 1])
|
|
829
|
+
U = U * T
|
|
830
|
+
Q = BinaryQF(r, b, c)
|
|
831
|
+
if transformation:
|
|
832
|
+
return Q, U
|
|
833
|
+
return Q
|
|
834
|
+
|
|
835
|
+
@cached_method
|
|
836
|
+
def reduced_form(self, transformation=False, algorithm='default'):
|
|
837
|
+
"""
|
|
838
|
+
Return a reduced form equivalent to ``self``.
|
|
839
|
+
|
|
840
|
+
INPUT:
|
|
841
|
+
|
|
842
|
+
- ``self`` -- binary quadratic form of non-square discriminant
|
|
843
|
+
|
|
844
|
+
- ``transformation`` -- boolean (default: ``False``); if ``True``, return
|
|
845
|
+
both the reduced form and a matrix whose :meth:`matrix_action_right`
|
|
846
|
+
transforms ``self`` into the reduced form
|
|
847
|
+
|
|
848
|
+
- ``algorithm`` -- string; the algorithm to use. Valid options are:
|
|
849
|
+
|
|
850
|
+
* ``'default'`` -- let Sage pick an algorithm (default)
|
|
851
|
+
* ``'pari'`` -- use PARI (:pari:`qfbred` or :pari:`qfbredsl2`)
|
|
852
|
+
* ``'sage'`` -- use Sage
|
|
853
|
+
|
|
854
|
+
.. SEEALSO::
|
|
855
|
+
|
|
856
|
+
- :meth:`is_reduced`
|
|
857
|
+
- :meth:`is_equivalent`
|
|
858
|
+
|
|
859
|
+
EXAMPLES::
|
|
860
|
+
|
|
861
|
+
sage: a = BinaryQF([33, 11, 5])
|
|
862
|
+
sage: a.is_reduced()
|
|
863
|
+
False
|
|
864
|
+
sage: b = a.reduced_form(); b # needs sage.libs.pari
|
|
865
|
+
5*x^2 - x*y + 27*y^2
|
|
866
|
+
sage: b.is_reduced() # needs sage.libs.pari
|
|
867
|
+
True
|
|
868
|
+
|
|
869
|
+
sage: a = BinaryQF([15, 0, 15])
|
|
870
|
+
sage: a.is_reduced()
|
|
871
|
+
True
|
|
872
|
+
sage: b = a.reduced_form(); b # needs sage.libs.pari
|
|
873
|
+
15*x^2 + 15*y^2
|
|
874
|
+
sage: b.is_reduced() # needs sage.libs.pari
|
|
875
|
+
True
|
|
876
|
+
|
|
877
|
+
Examples of reducing indefinite forms::
|
|
878
|
+
|
|
879
|
+
sage: f = BinaryQF(1, 0, -3)
|
|
880
|
+
sage: f.is_reduced()
|
|
881
|
+
False
|
|
882
|
+
sage: g = f.reduced_form(); g # needs sage.libs.pari
|
|
883
|
+
x^2 + 2*x*y - 2*y^2
|
|
884
|
+
sage: g.is_reduced() # needs sage.libs.pari
|
|
885
|
+
True
|
|
886
|
+
|
|
887
|
+
sage: q = BinaryQF(1, 0, -1)
|
|
888
|
+
sage: q.reduced_form() # needs sage.libs.pari
|
|
889
|
+
x^2 + 2*x*y
|
|
890
|
+
|
|
891
|
+
sage: BinaryQF(1, 9, 4).reduced_form(transformation=True) # needs sage.libs.pari
|
|
892
|
+
(
|
|
893
|
+
[ 0 -1]
|
|
894
|
+
4*x^2 + 7*x*y - y^2, [ 1 2]
|
|
895
|
+
)
|
|
896
|
+
sage: BinaryQF(3, 7, -2).reduced_form(transformation=True) # needs sage.libs.pari
|
|
897
|
+
(
|
|
898
|
+
[1 0]
|
|
899
|
+
3*x^2 + 7*x*y - 2*y^2, [0 1]
|
|
900
|
+
)
|
|
901
|
+
sage: BinaryQF(-6, 6, -1).reduced_form(transformation=True) # needs sage.libs.pari
|
|
902
|
+
(
|
|
903
|
+
[ 0 -1]
|
|
904
|
+
-x^2 + 2*x*y + 2*y^2, [ 1 -4]
|
|
905
|
+
)
|
|
906
|
+
|
|
907
|
+
TESTS:
|
|
908
|
+
|
|
909
|
+
Check for :issue:`34229`::
|
|
910
|
+
|
|
911
|
+
sage: BinaryQF([1,2,3]).reduced_form(transformation=True) # needs sage.libs.pari
|
|
912
|
+
(
|
|
913
|
+
[ 1 -1]
|
|
914
|
+
x^2 + 2*y^2, [ 0 1]
|
|
915
|
+
)
|
|
916
|
+
sage: BinaryQF([-225, -743, -743]).reduced_form().is_reduced() # needs sage.libs.pari
|
|
917
|
+
True
|
|
918
|
+
|
|
919
|
+
Some randomized testing::
|
|
920
|
+
|
|
921
|
+
sage: # needs sage.libs.pari
|
|
922
|
+
sage: while True:
|
|
923
|
+
....: f = BinaryQF([randrange(-10^3, 10^3) for _ in 'abc'])
|
|
924
|
+
....: if not f.discriminant().is_square():
|
|
925
|
+
....: break
|
|
926
|
+
sage: algos = ['default']
|
|
927
|
+
sage: assert pari; algos.append('pari')
|
|
928
|
+
sage: if f.discriminant() > 0:
|
|
929
|
+
....: algos.append('sage')
|
|
930
|
+
sage: a = choice(algos)
|
|
931
|
+
sage: g = f.reduced_form(algorithm=a)
|
|
932
|
+
sage: g.is_reduced()
|
|
933
|
+
True
|
|
934
|
+
sage: g.is_equivalent(f)
|
|
935
|
+
True
|
|
936
|
+
sage: g,M = f.reduced_form(transformation=True, algorithm=a)
|
|
937
|
+
sage: g.is_reduced()
|
|
938
|
+
True
|
|
939
|
+
sage: g.is_equivalent(f)
|
|
940
|
+
True
|
|
941
|
+
sage: f * M == g
|
|
942
|
+
True
|
|
943
|
+
"""
|
|
944
|
+
if self.is_reduced():
|
|
945
|
+
if transformation:
|
|
946
|
+
return self, Matrix.identity(2)
|
|
947
|
+
return self
|
|
948
|
+
|
|
949
|
+
if algorithm == "default":
|
|
950
|
+
algorithm = 'sage'
|
|
951
|
+
if not self.is_reducible():
|
|
952
|
+
try:
|
|
953
|
+
import sage.libs.pari
|
|
954
|
+
except ImportError:
|
|
955
|
+
pass
|
|
956
|
+
else:
|
|
957
|
+
algorithm = 'pari'
|
|
958
|
+
|
|
959
|
+
if algorithm == 'sage':
|
|
960
|
+
if self.discriminant() <= 0:
|
|
961
|
+
raise NotImplementedError('reduction of definite binary '
|
|
962
|
+
'quadratic forms is not implemented '
|
|
963
|
+
'in Sage')
|
|
964
|
+
return self._reduce_indef(transformation)
|
|
965
|
+
|
|
966
|
+
elif algorithm == 'pari':
|
|
967
|
+
# Negative definite forms are not supported by PARI. We can
|
|
968
|
+
# work around this by reducing [-a,b,-c] instead of [a,b,c].
|
|
969
|
+
if self.is_negative_definite():
|
|
970
|
+
M = Matrix.diagonal([-1, 1])
|
|
971
|
+
r = (-self*M).reduced_form(transformation=transformation, algorithm=algorithm)
|
|
972
|
+
if transformation:
|
|
973
|
+
return (-r[0]*M, M*r[1]*M)
|
|
974
|
+
return -r*M
|
|
975
|
+
|
|
976
|
+
if self.is_reducible():
|
|
977
|
+
raise NotImplementedError('reducible forms are not '
|
|
978
|
+
'supported using PARI')
|
|
979
|
+
|
|
980
|
+
if transformation:
|
|
981
|
+
y, g = self.__pari__().qfbredsl2()
|
|
982
|
+
return BinaryQF(y), Matrix(ZZ, g)
|
|
983
|
+
return BinaryQF(self.__pari__().qfbred())
|
|
984
|
+
|
|
985
|
+
else:
|
|
986
|
+
raise ValueError('unknown implementation for binary quadratic form '
|
|
987
|
+
'reduction: %s' % algorithm)
|
|
988
|
+
|
|
989
|
+
# Buchmann/Vollmer cycle algorithm
|
|
990
|
+
def _RhoTau(self):
|
|
991
|
+
"""
|
|
992
|
+
Apply Rho and Tau operators to this form, returning a new form `Q`.
|
|
993
|
+
|
|
994
|
+
EXAMPLES::
|
|
995
|
+
|
|
996
|
+
sage: f = BinaryQF(1, 8, -3)
|
|
997
|
+
sage: f._RhoTau()
|
|
998
|
+
3*x^2 + 4*x*y - 5*y^2
|
|
999
|
+
"""
|
|
1000
|
+
d = self.discriminant().sqrt(prec=53)
|
|
1001
|
+
a = self._a
|
|
1002
|
+
b = self._b
|
|
1003
|
+
c = self._c
|
|
1004
|
+
cabs = c.abs()
|
|
1005
|
+
sign = c.sign()
|
|
1006
|
+
if cabs >= d:
|
|
1007
|
+
s = sign * ((cabs+b) / (2*cabs)).floor()
|
|
1008
|
+
else:
|
|
1009
|
+
s = sign * ((d+b) / (2*cabs)).floor()
|
|
1010
|
+
Q = BinaryQF(-c, -b + 2*s*c, -(a - b*s + c*s*s))
|
|
1011
|
+
return Q
|
|
1012
|
+
|
|
1013
|
+
def _Rho(self):
|
|
1014
|
+
"""
|
|
1015
|
+
Apply the Rho operator to this form, returning a new form `Q`.
|
|
1016
|
+
|
|
1017
|
+
EXAMPLES::
|
|
1018
|
+
|
|
1019
|
+
sage: f = BinaryQF(1, 8, -3)
|
|
1020
|
+
sage: f._Rho()
|
|
1021
|
+
-3*x^2 + 4*x*y + 5*y^2
|
|
1022
|
+
"""
|
|
1023
|
+
d = self.discriminant().sqrt(prec=53)
|
|
1024
|
+
a = self._a
|
|
1025
|
+
b = self._b
|
|
1026
|
+
c = self._c
|
|
1027
|
+
cabs = c.abs()
|
|
1028
|
+
sign = c.sign()
|
|
1029
|
+
if cabs >= d:
|
|
1030
|
+
s = sign * ((cabs+b) / (2*cabs)).floor()
|
|
1031
|
+
else:
|
|
1032
|
+
s = sign * ((d+b) / (2*cabs)).floor()
|
|
1033
|
+
Q = BinaryQF(c, -b + 2*s*c, a - b*s + c*s*s)
|
|
1034
|
+
return Q
|
|
1035
|
+
|
|
1036
|
+
def _Tau(self):
|
|
1037
|
+
"""
|
|
1038
|
+
Apply the Tau operator to this form, returning a new form `Q`.
|
|
1039
|
+
|
|
1040
|
+
EXAMPLES::
|
|
1041
|
+
|
|
1042
|
+
sage: f = BinaryQF(1, 8, -3)
|
|
1043
|
+
sage: f._Tau()
|
|
1044
|
+
-x^2 + 8*x*y + 3*y^2
|
|
1045
|
+
"""
|
|
1046
|
+
a = self._a
|
|
1047
|
+
b = self._b
|
|
1048
|
+
c = self._c
|
|
1049
|
+
Q = BinaryQF(-a, b, -c)
|
|
1050
|
+
return Q
|
|
1051
|
+
|
|
1052
|
+
def cycle(self, proper=False):
|
|
1053
|
+
"""
|
|
1054
|
+
Return the cycle of reduced forms to which ``self`` belongs.
|
|
1055
|
+
|
|
1056
|
+
This is based on Algorithm 6.1 of [BUVO2007]_.
|
|
1057
|
+
|
|
1058
|
+
INPUT:
|
|
1059
|
+
|
|
1060
|
+
- ``self`` -- reduced, indefinite form of non-square discriminant
|
|
1061
|
+
|
|
1062
|
+
- ``proper`` -- boolean (default: ``False``); if ``True``, return the
|
|
1063
|
+
proper cycle
|
|
1064
|
+
|
|
1065
|
+
The proper cycle of a form `f` consists of all reduced forms that are
|
|
1066
|
+
properly equivalent to `f`. This is useful when testing for proper
|
|
1067
|
+
equivalence (or equivalence) between indefinite forms.
|
|
1068
|
+
|
|
1069
|
+
The cycle of `f` is a technical tool that is used when computing the proper
|
|
1070
|
+
cycle. Our definition of the cycle is slightly different from the one
|
|
1071
|
+
in [BUVO2007]_. In our definition, the cycle consists of all reduced
|
|
1072
|
+
forms `g`, such that the `a`-coefficient of `g` has the same sign as the
|
|
1073
|
+
`a`-coefficient of `f`, and `g` can be obtained from `f` by performing a
|
|
1074
|
+
change of variables, and then multiplying by the determinant of the
|
|
1075
|
+
change-of-variables matrix. It is important to note that `g` might not be
|
|
1076
|
+
equivalent to `f` (because of multiplying by the determinant). However,
|
|
1077
|
+
either `g` or `-g` must be equivalent to `f`. Also note that the cycle
|
|
1078
|
+
does contain `f`. (Under the definition in [BUVO2007]_, the cycle might
|
|
1079
|
+
not contain `f`, because all forms in the cycle are required to have
|
|
1080
|
+
positive `a`-coefficient, even if the `a`-coefficient of `f` is negative.)
|
|
1081
|
+
|
|
1082
|
+
EXAMPLES::
|
|
1083
|
+
|
|
1084
|
+
sage: Q = BinaryQF(14, 17, -2)
|
|
1085
|
+
sage: Q.cycle()
|
|
1086
|
+
[14*x^2 + 17*x*y - 2*y^2,
|
|
1087
|
+
2*x^2 + 19*x*y - 5*y^2,
|
|
1088
|
+
5*x^2 + 11*x*y - 14*y^2]
|
|
1089
|
+
sage: Q.cycle(proper=True)
|
|
1090
|
+
[14*x^2 + 17*x*y - 2*y^2,
|
|
1091
|
+
-2*x^2 + 19*x*y + 5*y^2,
|
|
1092
|
+
5*x^2 + 11*x*y - 14*y^2,
|
|
1093
|
+
-14*x^2 + 17*x*y + 2*y^2,
|
|
1094
|
+
2*x^2 + 19*x*y - 5*y^2,
|
|
1095
|
+
-5*x^2 + 11*x*y + 14*y^2]
|
|
1096
|
+
|
|
1097
|
+
sage: Q = BinaryQF(1, 8, -3)
|
|
1098
|
+
sage: Q.cycle()
|
|
1099
|
+
[x^2 + 8*x*y - 3*y^2,
|
|
1100
|
+
3*x^2 + 4*x*y - 5*y^2,
|
|
1101
|
+
5*x^2 + 6*x*y - 2*y^2,
|
|
1102
|
+
2*x^2 + 6*x*y - 5*y^2,
|
|
1103
|
+
5*x^2 + 4*x*y - 3*y^2,
|
|
1104
|
+
3*x^2 + 8*x*y - y^2]
|
|
1105
|
+
sage: Q.cycle(proper=True)
|
|
1106
|
+
[x^2 + 8*x*y - 3*y^2,
|
|
1107
|
+
-3*x^2 + 4*x*y + 5*y^2,
|
|
1108
|
+
5*x^2 + 6*x*y - 2*y^2,
|
|
1109
|
+
-2*x^2 + 6*x*y + 5*y^2,
|
|
1110
|
+
5*x^2 + 4*x*y - 3*y^2,
|
|
1111
|
+
-3*x^2 + 8*x*y + y^2]
|
|
1112
|
+
|
|
1113
|
+
sage: Q = BinaryQF(1, 7, -6)
|
|
1114
|
+
sage: Q.cycle()
|
|
1115
|
+
[x^2 + 7*x*y - 6*y^2,
|
|
1116
|
+
6*x^2 + 5*x*y - 2*y^2,
|
|
1117
|
+
2*x^2 + 7*x*y - 3*y^2,
|
|
1118
|
+
3*x^2 + 5*x*y - 4*y^2,
|
|
1119
|
+
4*x^2 + 3*x*y - 4*y^2,
|
|
1120
|
+
4*x^2 + 5*x*y - 3*y^2,
|
|
1121
|
+
3*x^2 + 7*x*y - 2*y^2,
|
|
1122
|
+
2*x^2 + 5*x*y - 6*y^2,
|
|
1123
|
+
6*x^2 + 7*x*y - y^2]
|
|
1124
|
+
|
|
1125
|
+
TESTS:
|
|
1126
|
+
|
|
1127
|
+
Check an example in :issue:`28989`::
|
|
1128
|
+
|
|
1129
|
+
sage: Q = BinaryQF(1, 1, -1)
|
|
1130
|
+
sage: Q.cycle(proper=True)
|
|
1131
|
+
[x^2 + x*y - y^2, -x^2 + x*y + y^2]
|
|
1132
|
+
|
|
1133
|
+
This is Example 6.10.6 of [BUVO2007]_::
|
|
1134
|
+
|
|
1135
|
+
sage: Q = BinaryQF(1, 7, -6)
|
|
1136
|
+
sage: Q.cycle()
|
|
1137
|
+
[x^2 + 7*x*y - 6*y^2,
|
|
1138
|
+
6*x^2 + 5*x*y - 2*y^2,
|
|
1139
|
+
2*x^2 + 7*x*y - 3*y^2,
|
|
1140
|
+
3*x^2 + 5*x*y - 4*y^2,
|
|
1141
|
+
4*x^2 + 3*x*y - 4*y^2,
|
|
1142
|
+
4*x^2 + 5*x*y - 3*y^2,
|
|
1143
|
+
3*x^2 + 7*x*y - 2*y^2,
|
|
1144
|
+
2*x^2 + 5*x*y - 6*y^2,
|
|
1145
|
+
6*x^2 + 7*x*y - y^2]
|
|
1146
|
+
sage: Q.cycle(proper=True)
|
|
1147
|
+
[x^2 + 7*x*y - 6*y^2,
|
|
1148
|
+
-6*x^2 + 5*x*y + 2*y^2,
|
|
1149
|
+
2*x^2 + 7*x*y - 3*y^2,
|
|
1150
|
+
-3*x^2 + 5*x*y + 4*y^2,
|
|
1151
|
+
4*x^2 + 3*x*y - 4*y^2,
|
|
1152
|
+
-4*x^2 + 5*x*y + 3*y^2,
|
|
1153
|
+
3*x^2 + 7*x*y - 2*y^2,
|
|
1154
|
+
-2*x^2 + 5*x*y + 6*y^2,
|
|
1155
|
+
6*x^2 + 7*x*y - y^2,
|
|
1156
|
+
-x^2 + 7*x*y + 6*y^2,
|
|
1157
|
+
6*x^2 + 5*x*y - 2*y^2,
|
|
1158
|
+
-2*x^2 + 7*x*y + 3*y^2,
|
|
1159
|
+
3*x^2 + 5*x*y - 4*y^2,
|
|
1160
|
+
-4*x^2 + 3*x*y + 4*y^2,
|
|
1161
|
+
4*x^2 + 5*x*y - 3*y^2,
|
|
1162
|
+
-3*x^2 + 7*x*y + 2*y^2,
|
|
1163
|
+
2*x^2 + 5*x*y - 6*y^2,
|
|
1164
|
+
-6*x^2 + 7*x*y + y^2]
|
|
1165
|
+
|
|
1166
|
+
This is Example 6.10.7 of [BUVO2007]_::
|
|
1167
|
+
|
|
1168
|
+
sage: Q = BinaryQF(1, 8, -3)
|
|
1169
|
+
sage: Q.cycle()
|
|
1170
|
+
[x^2 + 8*x*y - 3*y^2,
|
|
1171
|
+
3*x^2 + 4*x*y - 5*y^2,
|
|
1172
|
+
5*x^2 + 6*x*y - 2*y^2,
|
|
1173
|
+
2*x^2 + 6*x*y - 5*y^2,
|
|
1174
|
+
5*x^2 + 4*x*y - 3*y^2,
|
|
1175
|
+
3*x^2 + 8*x*y - y^2]
|
|
1176
|
+
sage: Q.cycle(proper=True)
|
|
1177
|
+
[x^2 + 8*x*y - 3*y^2,
|
|
1178
|
+
-3*x^2 + 4*x*y + 5*y^2,
|
|
1179
|
+
5*x^2 + 6*x*y - 2*y^2,
|
|
1180
|
+
-2*x^2 + 6*x*y + 5*y^2,
|
|
1181
|
+
5*x^2 + 4*x*y - 3*y^2,
|
|
1182
|
+
-3*x^2 + 8*x*y + y^2]
|
|
1183
|
+
sage: Q.cycle(proper=True) # should be the same as the previous one
|
|
1184
|
+
[x^2 + 8*x*y - 3*y^2,
|
|
1185
|
+
-3*x^2 + 4*x*y + 5*y^2,
|
|
1186
|
+
5*x^2 + 6*x*y - 2*y^2,
|
|
1187
|
+
-2*x^2 + 6*x*y + 5*y^2,
|
|
1188
|
+
5*x^2 + 4*x*y - 3*y^2,
|
|
1189
|
+
-3*x^2 + 8*x*y + y^2]
|
|
1190
|
+
|
|
1191
|
+
Try an example where a is negative::
|
|
1192
|
+
|
|
1193
|
+
sage: Q = BinaryQF(-1, 8, 3)
|
|
1194
|
+
sage: Q.cycle(proper=True)
|
|
1195
|
+
[-x^2 + 8*x*y + 3*y^2,
|
|
1196
|
+
3*x^2 + 4*x*y - 5*y^2,
|
|
1197
|
+
-5*x^2 + 6*x*y + 2*y^2,
|
|
1198
|
+
2*x^2 + 6*x*y - 5*y^2,
|
|
1199
|
+
-5*x^2 + 4*x*y + 3*y^2,
|
|
1200
|
+
3*x^2 + 8*x*y - y^2]
|
|
1201
|
+
"""
|
|
1202
|
+
if not (self.is_indef() and self.is_reduced()):
|
|
1203
|
+
raise ValueError("%s must be indefinite and reduced" % self)
|
|
1204
|
+
if self.discriminant().is_square():
|
|
1205
|
+
# Buchmann/Vollmer assume the discriminant to be non-square
|
|
1206
|
+
raise NotImplementedError('computation of cycles is only '
|
|
1207
|
+
'implemented for non-square '
|
|
1208
|
+
'discriminants')
|
|
1209
|
+
if proper:
|
|
1210
|
+
# Prop 6.10.5 in Buchmann Vollmer
|
|
1211
|
+
C = list(self.cycle(proper=False)) # make a copy that we can modify
|
|
1212
|
+
if len(C) % 2:
|
|
1213
|
+
C += C
|
|
1214
|
+
for i in range(len(C) // 2):
|
|
1215
|
+
C[2 * i + 1] = C[2 * i + 1]._Tau()
|
|
1216
|
+
return C
|
|
1217
|
+
if not hasattr(self, '_cycle_list'):
|
|
1218
|
+
C = [self]
|
|
1219
|
+
Q1 = self._RhoTau()
|
|
1220
|
+
while not self == Q1:
|
|
1221
|
+
C.append(Q1)
|
|
1222
|
+
Q1 = Q1._RhoTau()
|
|
1223
|
+
self._cycle_list = C
|
|
1224
|
+
return self._cycle_list
|
|
1225
|
+
|
|
1226
|
+
def is_positive_definite(self) -> bool:
|
|
1227
|
+
"""
|
|
1228
|
+
Return ``True`` if ``self`` is positive definite, i.e., has
|
|
1229
|
+
negative discriminant with `a > 0`.
|
|
1230
|
+
|
|
1231
|
+
EXAMPLES::
|
|
1232
|
+
|
|
1233
|
+
sage: Q = BinaryQF(195751, 37615, 1807)
|
|
1234
|
+
sage: Q.is_positive_definite()
|
|
1235
|
+
True
|
|
1236
|
+
sage: Q = BinaryQF(195751, 1212121, -1876411)
|
|
1237
|
+
sage: Q.is_positive_definite()
|
|
1238
|
+
False
|
|
1239
|
+
"""
|
|
1240
|
+
return self.discriminant() < 0 and self._a > 0
|
|
1241
|
+
|
|
1242
|
+
is_posdef = is_positive_definite
|
|
1243
|
+
|
|
1244
|
+
def is_negative_definite(self) -> bool:
|
|
1245
|
+
"""
|
|
1246
|
+
Return ``True`` if ``self`` is negative definite, i.e., has
|
|
1247
|
+
negative discriminant with `a < 0`.
|
|
1248
|
+
|
|
1249
|
+
EXAMPLES::
|
|
1250
|
+
|
|
1251
|
+
sage: Q = BinaryQF(-1, 3, -5)
|
|
1252
|
+
sage: Q.is_positive_definite()
|
|
1253
|
+
False
|
|
1254
|
+
sage: Q.is_negative_definite()
|
|
1255
|
+
True
|
|
1256
|
+
"""
|
|
1257
|
+
return self.discriminant() < 0 and self._a < 0
|
|
1258
|
+
|
|
1259
|
+
is_negdef = is_negative_definite
|
|
1260
|
+
|
|
1261
|
+
def is_indefinite(self) -> bool:
|
|
1262
|
+
"""
|
|
1263
|
+
Return whether ``self`` is indefinite, i.e., has positive discriminant.
|
|
1264
|
+
|
|
1265
|
+
EXAMPLES::
|
|
1266
|
+
|
|
1267
|
+
sage: Q = BinaryQF(1, 3, -5)
|
|
1268
|
+
sage: Q.is_indef()
|
|
1269
|
+
True
|
|
1270
|
+
"""
|
|
1271
|
+
return self.discriminant() > 0
|
|
1272
|
+
|
|
1273
|
+
is_indef = is_indefinite
|
|
1274
|
+
|
|
1275
|
+
def is_singular(self) -> bool:
|
|
1276
|
+
"""
|
|
1277
|
+
Return whether ``self`` is singular, i.e., has zero discriminant.
|
|
1278
|
+
|
|
1279
|
+
EXAMPLES::
|
|
1280
|
+
|
|
1281
|
+
sage: Q = BinaryQF(1, 3, -5)
|
|
1282
|
+
sage: Q.is_singular()
|
|
1283
|
+
False
|
|
1284
|
+
sage: Q = BinaryQF(1, 2, 1)
|
|
1285
|
+
sage: Q.is_singular()
|
|
1286
|
+
True
|
|
1287
|
+
"""
|
|
1288
|
+
return self.discriminant().is_zero()
|
|
1289
|
+
|
|
1290
|
+
def is_nonsingular(self) -> bool:
|
|
1291
|
+
"""
|
|
1292
|
+
Return whether this form is nonsingular, i.e., has nonzero discriminant.
|
|
1293
|
+
|
|
1294
|
+
EXAMPLES::
|
|
1295
|
+
|
|
1296
|
+
sage: Q = BinaryQF(1, 3, -5)
|
|
1297
|
+
sage: Q.is_nonsingular()
|
|
1298
|
+
True
|
|
1299
|
+
sage: Q = BinaryQF(1, 2, 1)
|
|
1300
|
+
sage: Q.is_nonsingular()
|
|
1301
|
+
False
|
|
1302
|
+
"""
|
|
1303
|
+
return not self.discriminant().is_zero()
|
|
1304
|
+
|
|
1305
|
+
def is_equivalent(self, other, proper=True) -> bool:
|
|
1306
|
+
"""
|
|
1307
|
+
Return whether ``self`` is equivalent to ``other``.
|
|
1308
|
+
|
|
1309
|
+
INPUT:
|
|
1310
|
+
|
|
1311
|
+
- ``proper`` -- boolean (default: ``True``); if ``True`` use proper
|
|
1312
|
+
equivalence
|
|
1313
|
+
- ``other`` -- a binary quadratic form
|
|
1314
|
+
|
|
1315
|
+
EXAMPLES::
|
|
1316
|
+
|
|
1317
|
+
sage: # needs sage.libs.pari
|
|
1318
|
+
sage: Q3 = BinaryQF(4, 4, 15)
|
|
1319
|
+
sage: Q2 = BinaryQF(4, -4, 15)
|
|
1320
|
+
sage: Q2.is_equivalent(Q3)
|
|
1321
|
+
True
|
|
1322
|
+
sage: a = BinaryQF([33, 11, 5])
|
|
1323
|
+
sage: b = a.reduced_form(); b
|
|
1324
|
+
5*x^2 - x*y + 27*y^2
|
|
1325
|
+
sage: a.is_equivalent(b)
|
|
1326
|
+
True
|
|
1327
|
+
sage: a.is_equivalent(BinaryQF((3, 4, 5)))
|
|
1328
|
+
False
|
|
1329
|
+
|
|
1330
|
+
Some indefinite examples::
|
|
1331
|
+
|
|
1332
|
+
sage: Q1 = BinaryQF(9, 8, -7)
|
|
1333
|
+
sage: Q2 = BinaryQF(9, -8, -7)
|
|
1334
|
+
sage: Q1.is_equivalent(Q2, proper=True) # needs sage.libs.pari
|
|
1335
|
+
False
|
|
1336
|
+
sage: Q1.is_equivalent(Q2, proper=False) # needs sage.libs.pari
|
|
1337
|
+
True
|
|
1338
|
+
|
|
1339
|
+
TESTS:
|
|
1340
|
+
|
|
1341
|
+
We check that :issue:`25888` is fixed::
|
|
1342
|
+
|
|
1343
|
+
sage: # needs sage.libs.pari
|
|
1344
|
+
sage: Q1 = BinaryQF(3, 4, -2)
|
|
1345
|
+
sage: Q2 = BinaryQF(-2, 4, 3)
|
|
1346
|
+
sage: Q1.is_equivalent(Q2) == Q2.is_equivalent(Q1)
|
|
1347
|
+
True
|
|
1348
|
+
sage: Q1.is_equivalent(Q2, proper=False) == Q2.is_equivalent(Q1, proper=False)
|
|
1349
|
+
True
|
|
1350
|
+
sage: Q1.is_equivalent(Q2, proper=True)
|
|
1351
|
+
True
|
|
1352
|
+
|
|
1353
|
+
We check that the first part of :issue:`29028` is fixed::
|
|
1354
|
+
|
|
1355
|
+
sage: Q = BinaryQF(0, 2, 0)
|
|
1356
|
+
sage: Q.discriminant()
|
|
1357
|
+
4
|
|
1358
|
+
sage: Q.is_equivalent(Q, proper=True) # needs sage.libs.pari
|
|
1359
|
+
True
|
|
1360
|
+
sage: Q.is_equivalent(Q, proper=False) # needs sage.libs.pari
|
|
1361
|
+
True
|
|
1362
|
+
|
|
1363
|
+
A test for rational forms::
|
|
1364
|
+
|
|
1365
|
+
sage: Q1 = BinaryQF(0, 4, 2)
|
|
1366
|
+
sage: Q2 = BinaryQF(2, 4, 0)
|
|
1367
|
+
sage: Q1.is_equivalent(Q2, proper=False) # needs sage.libs.pari
|
|
1368
|
+
True
|
|
1369
|
+
|
|
1370
|
+
Test another part of :issue:`28989`::
|
|
1371
|
+
|
|
1372
|
+
sage: Q1, Q2 = BinaryQF(1, 1, -1), BinaryQF(-1, 1, 1)
|
|
1373
|
+
sage: Q1.is_equivalent(Q2, proper=True) # needs sage.libs.pari
|
|
1374
|
+
True
|
|
1375
|
+
"""
|
|
1376
|
+
if not isinstance(other, BinaryQF):
|
|
1377
|
+
raise TypeError("%s is not a BinaryQF" % other)
|
|
1378
|
+
if self.discriminant() != other.discriminant():
|
|
1379
|
+
return False
|
|
1380
|
+
if self.is_indef():
|
|
1381
|
+
# First, reduce self and other
|
|
1382
|
+
selfred = self.reduced_form()
|
|
1383
|
+
otherred = other.reduced_form()
|
|
1384
|
+
if self.discriminant().is_square():
|
|
1385
|
+
# make sure we terminate in a form
|
|
1386
|
+
# with c = 0
|
|
1387
|
+
while selfred[2] != 0:
|
|
1388
|
+
selfred = selfred._Rho()
|
|
1389
|
+
while otherred[2] != 0:
|
|
1390
|
+
otherred = otherred._Rho()
|
|
1391
|
+
b = selfred._b
|
|
1392
|
+
a = selfred._a
|
|
1393
|
+
ao = otherred._a
|
|
1394
|
+
assert otherred._b == b
|
|
1395
|
+
# p. 359 of Conway-Sloane [CS1999]_
|
|
1396
|
+
# but `2b` in their notation is `b` in our notation
|
|
1397
|
+
is_properly_equiv = ((a-ao) % b == 0)
|
|
1398
|
+
if proper:
|
|
1399
|
+
return is_properly_equiv
|
|
1400
|
+
else:
|
|
1401
|
+
g = gcd(a, b)
|
|
1402
|
+
return is_properly_equiv or ((gcd(ao, b) == g) and ((a*ao - g**2) % (b*g) == 0))
|
|
1403
|
+
|
|
1404
|
+
proper_cycle = otherred.cycle(proper=True)
|
|
1405
|
+
|
|
1406
|
+
is_prop = selfred in proper_cycle
|
|
1407
|
+
if proper or is_prop:
|
|
1408
|
+
return is_prop
|
|
1409
|
+
# note that our definition of improper equivalence
|
|
1410
|
+
# differs from that of Buchmann and Vollmer
|
|
1411
|
+
# their action is det f * q(f(x, y))
|
|
1412
|
+
# ours is q(f(x, y))
|
|
1413
|
+
|
|
1414
|
+
# an improper equivalence in our convention
|
|
1415
|
+
selfred = BinaryQF(selfred._c, selfred._b, selfred._a)
|
|
1416
|
+
assert selfred.is_reduced()
|
|
1417
|
+
|
|
1418
|
+
return selfred in proper_cycle
|
|
1419
|
+
|
|
1420
|
+
# Else we're dealing with definite forms.
|
|
1421
|
+
if self.is_posdef() and not other.is_posdef():
|
|
1422
|
+
return False
|
|
1423
|
+
if self.is_negdef() and not other.is_negdef():
|
|
1424
|
+
return False
|
|
1425
|
+
Q1 = self.reduced_form()
|
|
1426
|
+
Q2 = other.reduced_form()
|
|
1427
|
+
if Q1 == Q2:
|
|
1428
|
+
return True
|
|
1429
|
+
if not proper:
|
|
1430
|
+
Q1e = BinaryQF(self._c, self._b, self._a).reduced_form()
|
|
1431
|
+
return Q1e == Q2
|
|
1432
|
+
return False
|
|
1433
|
+
|
|
1434
|
+
@cached_method
|
|
1435
|
+
def is_reduced(self) -> bool:
|
|
1436
|
+
r"""
|
|
1437
|
+
Return whether ``self`` is reduced.
|
|
1438
|
+
|
|
1439
|
+
Let `f = a x^2 + b xy + c y^2` be a binary quadratic form of
|
|
1440
|
+
discriminant `D`.
|
|
1441
|
+
|
|
1442
|
+
- If `f` is positive definite (`D < 0` and `a > 0`), then `f`
|
|
1443
|
+
is reduced if and only if `|b|\leq a \leq c`, and `b\geq 0`
|
|
1444
|
+
if either `a = b` or `a = c`.
|
|
1445
|
+
|
|
1446
|
+
- If `f` is negative definite (`D < 0` and `a < 0`), then `f`
|
|
1447
|
+
is reduced if and only if the positive definite form with
|
|
1448
|
+
coefficients `(-a, b, -c)` is reduced.
|
|
1449
|
+
|
|
1450
|
+
- If `f` is indefinite (`D > 0`), then `f` is reduced if and
|
|
1451
|
+
only if [`b > 0`, `ac < 0` and `(a-c)^2 < D`]
|
|
1452
|
+
(equivalently if `|\sqrt{D} - 2|a|| < b < \sqrt{D}`)
|
|
1453
|
+
or [`a = 0` and `-b < 2c \leq b`]
|
|
1454
|
+
or [`c = 0` and `-b < 2a \leq b`].
|
|
1455
|
+
|
|
1456
|
+
EXAMPLES::
|
|
1457
|
+
|
|
1458
|
+
sage: Q = BinaryQF([1, 2, 3])
|
|
1459
|
+
sage: Q.is_reduced()
|
|
1460
|
+
False
|
|
1461
|
+
|
|
1462
|
+
sage: Q = BinaryQF([2, 1, 3])
|
|
1463
|
+
sage: Q.is_reduced()
|
|
1464
|
+
True
|
|
1465
|
+
|
|
1466
|
+
sage: Q = BinaryQF([1, -1, 1])
|
|
1467
|
+
sage: Q.is_reduced()
|
|
1468
|
+
False
|
|
1469
|
+
|
|
1470
|
+
sage: Q = BinaryQF([1, 1, 1])
|
|
1471
|
+
sage: Q.is_reduced()
|
|
1472
|
+
True
|
|
1473
|
+
|
|
1474
|
+
Examples using indefinite forms::
|
|
1475
|
+
|
|
1476
|
+
sage: f = BinaryQF(-1, 2, 2)
|
|
1477
|
+
sage: f.is_reduced()
|
|
1478
|
+
True
|
|
1479
|
+
sage: BinaryQF(1, 9, 4).is_reduced()
|
|
1480
|
+
False
|
|
1481
|
+
sage: BinaryQF(1, 5, -1).is_reduced()
|
|
1482
|
+
True
|
|
1483
|
+
|
|
1484
|
+
TESTS:
|
|
1485
|
+
|
|
1486
|
+
We check that :issue:`37635` is fixed::
|
|
1487
|
+
|
|
1488
|
+
sage: list = range(0xa19ae44106b09bfffffffffff0, 0xa19ae44106b09c000000000010)
|
|
1489
|
+
sage: all(BinaryQF([1, 0, -x]).reduced_form().is_reduced() for x in list) # needs sage.libs.pari
|
|
1490
|
+
True
|
|
1491
|
+
"""
|
|
1492
|
+
D = self.discriminant()
|
|
1493
|
+
if D.is_zero():
|
|
1494
|
+
raise ValueError('the quadratic form must be non-singular')
|
|
1495
|
+
|
|
1496
|
+
a = self._a
|
|
1497
|
+
b = self._b
|
|
1498
|
+
c = self._c
|
|
1499
|
+
if D < 0 and a > 0:
|
|
1500
|
+
return ((-a < b <= a < c)
|
|
1501
|
+
or (ZZ(0) <= b <= a == c))
|
|
1502
|
+
elif D < 0 and a < 0:
|
|
1503
|
+
return ((a < b <= -a < -c)
|
|
1504
|
+
or (ZZ(0) <= b <= -a == -c))
|
|
1505
|
+
|
|
1506
|
+
# Note that a = 0 implies D > 0 here
|
|
1507
|
+
else:
|
|
1508
|
+
return ((b > 0 and a*c < 0 and (a-c)**2 < D)
|
|
1509
|
+
or (0 == a and -b < 2*c <= b)
|
|
1510
|
+
or (0 == c and -b < 2*a <= b))
|
|
1511
|
+
|
|
1512
|
+
def complex_point(self):
|
|
1513
|
+
r"""
|
|
1514
|
+
Return the point in the complex upper half-plane associated to ``self``.
|
|
1515
|
+
|
|
1516
|
+
This form, `ax^2 + b xy + cy^2`, must be definite with
|
|
1517
|
+
negative discriminant `b^2 - 4 a c < 0`.
|
|
1518
|
+
|
|
1519
|
+
OUTPUT:
|
|
1520
|
+
|
|
1521
|
+
- the unique complex root of `a x^2 + b x + c` with positive
|
|
1522
|
+
imaginary part
|
|
1523
|
+
|
|
1524
|
+
EXAMPLES::
|
|
1525
|
+
|
|
1526
|
+
sage: Q = BinaryQF([1, 0, 1])
|
|
1527
|
+
sage: Q.complex_point() # needs sage.libs.pari
|
|
1528
|
+
1.00000000000000*I
|
|
1529
|
+
"""
|
|
1530
|
+
if self.discriminant() >= 0:
|
|
1531
|
+
raise ValueError("only defined for negative discriminant")
|
|
1532
|
+
Q1 = ZZ['x']([self._c, self._b, self._a])
|
|
1533
|
+
return [z for z in Q1.complex_roots() if z.imag() > 0][0]
|
|
1534
|
+
|
|
1535
|
+
def matrix_action_left(self, M):
|
|
1536
|
+
r"""
|
|
1537
|
+
Return the binary quadratic form resulting from the left action
|
|
1538
|
+
of the 2-by-2 matrix `M` on ``self``.
|
|
1539
|
+
|
|
1540
|
+
Here the action of the matrix `M = \begin{pmatrix} a & b \\ c & d
|
|
1541
|
+
\end{pmatrix}` on the form `Q(x, y)` produces the form `Q(ax+cy,
|
|
1542
|
+
bx+dy)`.
|
|
1543
|
+
|
|
1544
|
+
EXAMPLES::
|
|
1545
|
+
|
|
1546
|
+
sage: Q = BinaryQF([2, 1, 3]); Q
|
|
1547
|
+
2*x^2 + x*y + 3*y^2
|
|
1548
|
+
sage: M = matrix(ZZ, [[1, 2], [3, 5]])
|
|
1549
|
+
sage: Q.matrix_action_left(M)
|
|
1550
|
+
16*x^2 + 83*x*y + 108*y^2
|
|
1551
|
+
"""
|
|
1552
|
+
v, w = M.rows()
|
|
1553
|
+
a1 = self(v)
|
|
1554
|
+
c1 = self(w)
|
|
1555
|
+
b1 = self(v + w) - a1 - c1
|
|
1556
|
+
return BinaryQF([a1, b1, c1])
|
|
1557
|
+
|
|
1558
|
+
def matrix_action_right(self, M):
|
|
1559
|
+
r"""
|
|
1560
|
+
Return the binary quadratic form resulting from the right action
|
|
1561
|
+
of the 2-by-2 matrix `M` on ``self``.
|
|
1562
|
+
|
|
1563
|
+
Here the action of the matrix `M = \begin{pmatrix} a & b \\ c & d
|
|
1564
|
+
\end{pmatrix}` on the form `Q(x, y)` produces the form `Q(ax+by,
|
|
1565
|
+
cx+dy)`.
|
|
1566
|
+
|
|
1567
|
+
EXAMPLES::
|
|
1568
|
+
|
|
1569
|
+
sage: Q = BinaryQF([2, 1, 3]); Q
|
|
1570
|
+
2*x^2 + x*y + 3*y^2
|
|
1571
|
+
sage: M = matrix(ZZ, [[1, 2], [3, 5]])
|
|
1572
|
+
sage: Q.matrix_action_right(M)
|
|
1573
|
+
32*x^2 + 109*x*y + 93*y^2
|
|
1574
|
+
"""
|
|
1575
|
+
v, w = M.columns()
|
|
1576
|
+
a1 = self(v)
|
|
1577
|
+
c1 = self(w)
|
|
1578
|
+
b1 = self(v + w) - a1 - c1
|
|
1579
|
+
return BinaryQF([a1, b1, c1])
|
|
1580
|
+
|
|
1581
|
+
def small_prime_value(self, Bmax=1000):
|
|
1582
|
+
r"""
|
|
1583
|
+
Return a prime represented by this (primitive positive definite) binary form.
|
|
1584
|
+
|
|
1585
|
+
INPUT:
|
|
1586
|
+
|
|
1587
|
+
- ``Bmax`` -- a positive bound on the representing integers
|
|
1588
|
+
|
|
1589
|
+
OUTPUT: a prime number represented by the form
|
|
1590
|
+
|
|
1591
|
+
.. NOTE::
|
|
1592
|
+
|
|
1593
|
+
This is a very elementary implementation which just substitutes
|
|
1594
|
+
values until a prime is found.
|
|
1595
|
+
|
|
1596
|
+
EXAMPLES::
|
|
1597
|
+
|
|
1598
|
+
sage: [Q.small_prime_value() # needs sage.libs.pari
|
|
1599
|
+
....: for Q in BinaryQF_reduced_representatives(-23, primitive_only=True)]
|
|
1600
|
+
[23, 2, 2]
|
|
1601
|
+
sage: [Q.small_prime_value() # needs sage.libs.pari
|
|
1602
|
+
....: for Q in BinaryQF_reduced_representatives(-47, primitive_only=True)]
|
|
1603
|
+
[47, 2, 2, 3, 3]
|
|
1604
|
+
"""
|
|
1605
|
+
from sage.sets.set import Set
|
|
1606
|
+
from sage.arith.srange import xsrange
|
|
1607
|
+
B = 10
|
|
1608
|
+
while True:
|
|
1609
|
+
llist = list(Set([self(x, y) for x in xsrange(-B, B) for y in xsrange(B)]))
|
|
1610
|
+
llist = sorted([l for l in llist if l.is_prime()])
|
|
1611
|
+
if llist:
|
|
1612
|
+
return llist[0]
|
|
1613
|
+
if B >= Bmax:
|
|
1614
|
+
raise ValueError("Unable to find a prime value of %s" % self)
|
|
1615
|
+
B += 10
|
|
1616
|
+
|
|
1617
|
+
def solve_integer(self, n, *, algorithm='general', _flag=2):
|
|
1618
|
+
r"""
|
|
1619
|
+
Solve `Q(x, y) = n` in integers `x` and `y` where `Q` is this
|
|
1620
|
+
quadratic form.
|
|
1621
|
+
|
|
1622
|
+
INPUT:
|
|
1623
|
+
|
|
1624
|
+
- ``n`` -- positive integer or a
|
|
1625
|
+
`:sage:`~sage.structure.factorization.Factorization` object
|
|
1626
|
+
|
|
1627
|
+
- ``algorithm`` -- ``'general'`` (default) or ``'cornacchia'``
|
|
1628
|
+
|
|
1629
|
+
- ``_flag`` -- ``1``, ``2`` (default) or ``3``; passed onto the pari
|
|
1630
|
+
function``qfbsolve``. For internal use only.
|
|
1631
|
+
|
|
1632
|
+
To use the Cornacchia algorithm, the quadratic form must have
|
|
1633
|
+
`a=1` and `b=0` and `c>0`, and ``n`` must be a prime or four
|
|
1634
|
+
times a prime (but this is not checked).
|
|
1635
|
+
|
|
1636
|
+
OUTPUT:
|
|
1637
|
+
|
|
1638
|
+
A tuple `(x, y)` of integers satisfying `Q(x, y) = n`, or ``None``
|
|
1639
|
+
if no solution exists.
|
|
1640
|
+
|
|
1641
|
+
ALGORITHM: :pari:`qfbsolve` or :pari:`qfbcornacchia`
|
|
1642
|
+
|
|
1643
|
+
TODO:: Replace ``_flag`` with human-readable parameters c.f. :issue:`37119`
|
|
1644
|
+
|
|
1645
|
+
EXAMPLES::
|
|
1646
|
+
|
|
1647
|
+
sage: Q = BinaryQF([1, 0, 419])
|
|
1648
|
+
sage: Q.solve_integer(773187972) # needs sage.libs.pari
|
|
1649
|
+
(4919, 1337)
|
|
1650
|
+
|
|
1651
|
+
If `Q` is of the form `[1,0,c]` as above and `n` is a prime
|
|
1652
|
+
(or four times a prime whenever `c \equiv 3 \pmod 4`), then
|
|
1653
|
+
Cornacchia's algorithm can be used, which is typically much
|
|
1654
|
+
faster than the general method::
|
|
1655
|
+
|
|
1656
|
+
sage: Q = BinaryQF([1, 0, 12345])
|
|
1657
|
+
sage: n = 2^99 + 5273
|
|
1658
|
+
sage: Q.solve_integer(n) # needs sage.libs.pari
|
|
1659
|
+
(67446480057659, 7139620553488)
|
|
1660
|
+
sage: Q.solve_integer(n, algorithm='cornacchia') # needs sage.libs.pari
|
|
1661
|
+
(67446480057659, 7139620553488)
|
|
1662
|
+
sage: timeit('Q.solve_integer(n)') # not tested
|
|
1663
|
+
125 loops, best of 3: 3.13 ms per loop
|
|
1664
|
+
sage: timeit('Q.solve_integer(n, algorithm="cornacchia")') # not tested
|
|
1665
|
+
625 loops, best of 3: 18.6 μs per loop
|
|
1666
|
+
|
|
1667
|
+
::
|
|
1668
|
+
|
|
1669
|
+
sage: # needs sage.libs.pari
|
|
1670
|
+
sage: Qs = BinaryQF_reduced_representatives(-23, primitive_only=True)
|
|
1671
|
+
sage: Qs
|
|
1672
|
+
[x^2 + x*y + 6*y^2, 2*x^2 - x*y + 3*y^2, 2*x^2 + x*y + 3*y^2]
|
|
1673
|
+
sage: [Q.solve_integer(3) for Q in Qs]
|
|
1674
|
+
[None, (0, 1), (0, 1)]
|
|
1675
|
+
sage: [Q.solve_integer(5) for Q in Qs]
|
|
1676
|
+
[None, None, None]
|
|
1677
|
+
sage: [Q.solve_integer(6) for Q in Qs]
|
|
1678
|
+
[(1, -1), (1, -1), (-1, -1)]
|
|
1679
|
+
|
|
1680
|
+
::
|
|
1681
|
+
|
|
1682
|
+
sage: # needs sage.libs.pari
|
|
1683
|
+
sage: n = factor(126)
|
|
1684
|
+
sage: Q = BinaryQF([1, 0, 5])
|
|
1685
|
+
sage: Q.solve_integer(n)
|
|
1686
|
+
(11, -1)
|
|
1687
|
+
|
|
1688
|
+
TESTS:
|
|
1689
|
+
|
|
1690
|
+
The returned solutions are correct (random inputs)::
|
|
1691
|
+
|
|
1692
|
+
sage: Q = BinaryQF([randrange(-10^3, 10^3) for _ in 'abc'])
|
|
1693
|
+
sage: n = randrange(-10^9, 10^9)
|
|
1694
|
+
sage: xy = Q.solve_integer(n) # needs sage.libs.pari
|
|
1695
|
+
sage: xy is None or Q(*xy) == n # needs sage.libs.pari
|
|
1696
|
+
True
|
|
1697
|
+
|
|
1698
|
+
Also when using the ``'cornacchia'`` algorithm::
|
|
1699
|
+
|
|
1700
|
+
sage: # needs sage.libs.pari
|
|
1701
|
+
sage: n = random_prime(10^9)
|
|
1702
|
+
sage: c = randrange(1, 10^3)
|
|
1703
|
+
|
|
1704
|
+
sage: # needs sage.libs.pari
|
|
1705
|
+
sage: Q1 = BinaryQF(1, 0, c)
|
|
1706
|
+
sage: xy = Q1.solve_integer(n, algorithm='cornacchia')
|
|
1707
|
+
sage: xy is None or Q1(*xy) == n
|
|
1708
|
+
True
|
|
1709
|
+
sage: (xy is None) == (Q1.solve_integer(n) is None)
|
|
1710
|
+
True
|
|
1711
|
+
|
|
1712
|
+
sage: # needs sage.libs.pari
|
|
1713
|
+
sage: Q3 = BinaryQF(1, 0, 4*c+3)
|
|
1714
|
+
sage: xy = Q3.solve_integer(n, algorithm='cornacchia')
|
|
1715
|
+
sage: xy is None or Q3(*xy) == n
|
|
1716
|
+
True
|
|
1717
|
+
sage: (xy is None) == (Q3.solve_integer(n) is None)
|
|
1718
|
+
True
|
|
1719
|
+
|
|
1720
|
+
sage: # needs sage.libs.pari
|
|
1721
|
+
sage: xy = Q3.solve_integer(4*n, algorithm='cornacchia')
|
|
1722
|
+
sage: xy is None or Q3(*xy) == 4*n
|
|
1723
|
+
True
|
|
1724
|
+
sage: (xy is None) == (Q3.solve_integer(4*n) is None)
|
|
1725
|
+
True
|
|
1726
|
+
|
|
1727
|
+
Test for square discriminants specifically (:issue:`33026`)::
|
|
1728
|
+
|
|
1729
|
+
sage: n = randrange(-10^3, 10^3)
|
|
1730
|
+
sage: Q = BinaryQF([n, randrange(-10^3, 10^3), 0][::(-1)**randrange(2)])
|
|
1731
|
+
sage: U = random_matrix(ZZ, 2, 2, 'unimodular')
|
|
1732
|
+
sage: U.rescale_row(0, choice((+1,-1)))
|
|
1733
|
+
sage: assert U.det() in (+1,-1)
|
|
1734
|
+
sage: Q = Q.matrix_action_right(U)
|
|
1735
|
+
sage: Q.discriminant().is_square()
|
|
1736
|
+
True
|
|
1737
|
+
sage: # needs sage.libs.pari
|
|
1738
|
+
sage: xy = Q.solve_integer(n)
|
|
1739
|
+
sage: Q(*xy) == n
|
|
1740
|
+
True
|
|
1741
|
+
|
|
1742
|
+
Also test the `n=0` special case separately::
|
|
1743
|
+
|
|
1744
|
+
sage: # needs sage.libs.pari
|
|
1745
|
+
sage: xy = Q.solve_integer(0)
|
|
1746
|
+
sage: Q(*xy)
|
|
1747
|
+
0
|
|
1748
|
+
|
|
1749
|
+
Test for different ``_flag`` values::
|
|
1750
|
+
|
|
1751
|
+
sage: # needs sage.libs.pari
|
|
1752
|
+
sage: Q = BinaryQF([1, 0, 5])
|
|
1753
|
+
sage: Q.solve_integer(126, _flag=1)
|
|
1754
|
+
[(-11, -1), (-1, -5), (-1, 5), (11, -1)]
|
|
1755
|
+
sage: Q.solve_integer(126, _flag=2)
|
|
1756
|
+
(11, -1)
|
|
1757
|
+
sage: Q.solve_integer(126, _flag=3)
|
|
1758
|
+
[(-11, -1), (-9, -3), (-1, -5), (-1, 5), (9, -3), (11, -1)]
|
|
1759
|
+
"""
|
|
1760
|
+
if self.is_negative_definite(): # not supported by PARI
|
|
1761
|
+
return (-self).solve_integer(-n)
|
|
1762
|
+
|
|
1763
|
+
if self.is_reducible(): # square discriminant; not supported by PARI
|
|
1764
|
+
from sage.structure.factorization import Factorization
|
|
1765
|
+
if isinstance(n, Factorization):
|
|
1766
|
+
n = ZZ(n.value())
|
|
1767
|
+
else:
|
|
1768
|
+
n = ZZ(n)
|
|
1769
|
+
|
|
1770
|
+
if self._a:
|
|
1771
|
+
# https://math.stackexchange.com/a/980075
|
|
1772
|
+
w = self.discriminant().sqrt()
|
|
1773
|
+
r = (-self._b + (w if w != self._b else -w)) / (2*self._a)
|
|
1774
|
+
p, q = r.as_integer_ratio()
|
|
1775
|
+
_, u, v = p.xgcd(q)
|
|
1776
|
+
M = Matrix(ZZ, [[v, p], [-u, q]])
|
|
1777
|
+
elif self._c:
|
|
1778
|
+
M = Matrix(ZZ, [[0, 1], [1, 0]])
|
|
1779
|
+
else:
|
|
1780
|
+
M = Matrix(ZZ, [[1, 0], [0, 1]])
|
|
1781
|
+
assert M.is_unit()
|
|
1782
|
+
Q = self.matrix_action_right(M)
|
|
1783
|
+
assert not Q._c
|
|
1784
|
+
|
|
1785
|
+
if not Q._b:
|
|
1786
|
+
# at this point, Q = a*x^2
|
|
1787
|
+
if Q._a.divides(n) and (n // Q._a).is_square():
|
|
1788
|
+
x = (n // Q._a).isqrt()
|
|
1789
|
+
return tuple(row[0] * x for row in M.rows())
|
|
1790
|
+
return None
|
|
1791
|
+
|
|
1792
|
+
# at this point, Q = a*x^2 + b*x*y
|
|
1793
|
+
if not n:
|
|
1794
|
+
return tuple(M.columns()[1])
|
|
1795
|
+
for x in n.divisors():
|
|
1796
|
+
y_num = n // x - Q._a * x
|
|
1797
|
+
if Q._b.divides(y_num):
|
|
1798
|
+
y = y_num // Q._b
|
|
1799
|
+
return tuple([row[0]*x + row[1]*y for row in M.rows()])
|
|
1800
|
+
|
|
1801
|
+
return None
|
|
1802
|
+
|
|
1803
|
+
if algorithm == 'cornacchia':
|
|
1804
|
+
if not (self._a.is_one() and self._b.is_zero() and self._c > 0):
|
|
1805
|
+
raise ValueError("Cornacchia's algorithm requires a=1 and b=0 and c>0")
|
|
1806
|
+
sol = pari.qfbcornacchia(self._c, n)
|
|
1807
|
+
return tuple(map(ZZ, sol)) if sol else None
|
|
1808
|
+
|
|
1809
|
+
if algorithm != 'general':
|
|
1810
|
+
raise ValueError(f'algorithm {algorithm!r} is not a valid algorithm')
|
|
1811
|
+
|
|
1812
|
+
sol = self.__pari__().qfbsolve(n, _flag)
|
|
1813
|
+
if _flag == 2:
|
|
1814
|
+
return tuple(map(ZZ, sol)) if sol else None
|
|
1815
|
+
return [tuple(map(ZZ, tup)) for tup in sol]
|
|
1816
|
+
|
|
1817
|
+
def form_class(self):
|
|
1818
|
+
r"""
|
|
1819
|
+
Return the class of this form modulo equivalence.
|
|
1820
|
+
|
|
1821
|
+
EXAMPLES::
|
|
1822
|
+
|
|
1823
|
+
sage: # needs sage.libs.pari
|
|
1824
|
+
sage: F = BinaryQF([3, -16, 161])
|
|
1825
|
+
sage: cl = F.form_class(); cl
|
|
1826
|
+
Class of 3*x^2 + 2*x*y + 140*y^2
|
|
1827
|
+
sage: cl.parent()
|
|
1828
|
+
Form Class Group of Discriminant -1676
|
|
1829
|
+
sage: cl.parent() is BQFClassGroup(-4*419)
|
|
1830
|
+
True
|
|
1831
|
+
"""
|
|
1832
|
+
from sage.quadratic_forms.bqf_class_group import BQFClassGroup
|
|
1833
|
+
return BQFClassGroup(self.discriminant())(self)
|
|
1834
|
+
|
|
1835
|
+
|
|
1836
|
+
def BinaryQF_reduced_representatives(D, primitive_only=False, proper=True):
|
|
1837
|
+
r"""
|
|
1838
|
+
Return representatives for the classes of binary quadratic forms
|
|
1839
|
+
of discriminant `D`.
|
|
1840
|
+
|
|
1841
|
+
INPUT:
|
|
1842
|
+
|
|
1843
|
+
- ``D`` -- integer; a discriminant
|
|
1844
|
+
|
|
1845
|
+
- ``primitive_only`` -- boolean (default: ``True``); if ``True``, only
|
|
1846
|
+
return primitive forms
|
|
1847
|
+
|
|
1848
|
+
- ``proper`` -- boolean (default: ``True``)
|
|
1849
|
+
|
|
1850
|
+
OUTPUT:
|
|
1851
|
+
|
|
1852
|
+
(list) A lexicographically-ordered list of inequivalent reduced
|
|
1853
|
+
representatives for the (im)proper equivalence classes of binary quadratic
|
|
1854
|
+
forms of discriminant `D`. If ``primitive_only`` is ``True`` then
|
|
1855
|
+
imprimitive forms (which only exist when `D` is not fundamental) are
|
|
1856
|
+
omitted; otherwise they are included.
|
|
1857
|
+
|
|
1858
|
+
EXAMPLES::
|
|
1859
|
+
|
|
1860
|
+
sage: BinaryQF_reduced_representatives(-4)
|
|
1861
|
+
[x^2 + y^2]
|
|
1862
|
+
|
|
1863
|
+
sage: BinaryQF_reduced_representatives(-163)
|
|
1864
|
+
[x^2 + x*y + 41*y^2]
|
|
1865
|
+
|
|
1866
|
+
sage: BinaryQF_reduced_representatives(-12)
|
|
1867
|
+
[x^2 + 3*y^2, 2*x^2 + 2*x*y + 2*y^2]
|
|
1868
|
+
|
|
1869
|
+
sage: BinaryQF_reduced_representatives(-16)
|
|
1870
|
+
[x^2 + 4*y^2, 2*x^2 + 2*y^2]
|
|
1871
|
+
|
|
1872
|
+
sage: BinaryQF_reduced_representatives(-63)
|
|
1873
|
+
[x^2 + x*y + 16*y^2, 2*x^2 - x*y + 8*y^2, 2*x^2 + x*y + 8*y^2,
|
|
1874
|
+
3*x^2 + 3*x*y + 6*y^2, 4*x^2 + x*y + 4*y^2]
|
|
1875
|
+
|
|
1876
|
+
The number of inequivalent reduced binary forms with a fixed negative
|
|
1877
|
+
fundamental discriminant `D` is the class number of the quadratic field
|
|
1878
|
+
`\QQ(\sqrt{D})`::
|
|
1879
|
+
|
|
1880
|
+
sage: len(BinaryQF_reduced_representatives(-13*4))
|
|
1881
|
+
2
|
|
1882
|
+
sage: QuadraticField(-13*4, 'a').class_number() # needs sage.rings.number_field
|
|
1883
|
+
2
|
|
1884
|
+
|
|
1885
|
+
sage: # needs sage.libs.pari
|
|
1886
|
+
sage: p = next_prime(2^20); p
|
|
1887
|
+
1048583
|
|
1888
|
+
sage: len(BinaryQF_reduced_representatives(-p))
|
|
1889
|
+
689
|
|
1890
|
+
sage: QuadraticField(-p, 'a').class_number() # needs sage.rings.number_field
|
|
1891
|
+
689
|
|
1892
|
+
sage: BinaryQF_reduced_representatives(-23*9)
|
|
1893
|
+
[x^2 + x*y + 52*y^2,
|
|
1894
|
+
2*x^2 - x*y + 26*y^2,
|
|
1895
|
+
2*x^2 + x*y + 26*y^2,
|
|
1896
|
+
3*x^2 + 3*x*y + 18*y^2,
|
|
1897
|
+
4*x^2 - x*y + 13*y^2,
|
|
1898
|
+
4*x^2 + x*y + 13*y^2,
|
|
1899
|
+
6*x^2 - 3*x*y + 9*y^2,
|
|
1900
|
+
6*x^2 + 3*x*y + 9*y^2,
|
|
1901
|
+
8*x^2 + 7*x*y + 8*y^2]
|
|
1902
|
+
sage: BinaryQF_reduced_representatives(-23*9, primitive_only=True)
|
|
1903
|
+
[x^2 + x*y + 52*y^2,
|
|
1904
|
+
2*x^2 - x*y + 26*y^2,
|
|
1905
|
+
2*x^2 + x*y + 26*y^2,
|
|
1906
|
+
4*x^2 - x*y + 13*y^2,
|
|
1907
|
+
4*x^2 + x*y + 13*y^2,
|
|
1908
|
+
8*x^2 + 7*x*y + 8*y^2]
|
|
1909
|
+
|
|
1910
|
+
TESTS::
|
|
1911
|
+
|
|
1912
|
+
sage: BinaryQF_reduced_representatives(73)
|
|
1913
|
+
[4*x^2 + 3*x*y - 4*y^2]
|
|
1914
|
+
sage: BinaryQF_reduced_representatives(76, primitive_only=True) # needs sage.libs.pari
|
|
1915
|
+
[-3*x^2 + 4*x*y + 5*y^2,
|
|
1916
|
+
3*x^2 + 4*x*y - 5*y^2]
|
|
1917
|
+
sage: BinaryQF_reduced_representatives(136)
|
|
1918
|
+
[-5*x^2 + 4*x*y + 6*y^2,
|
|
1919
|
+
-2*x^2 + 8*x*y + 9*y^2,
|
|
1920
|
+
2*x^2 + 8*x*y - 9*y^2,
|
|
1921
|
+
5*x^2 + 4*x*y - 6*y^2]
|
|
1922
|
+
sage: BinaryQF_reduced_representatives(136, proper=False)
|
|
1923
|
+
[-2*x^2 + 8*x*y + 9*y^2, 2*x^2 + 8*x*y - 9*y^2, 5*x^2 + 4*x*y - 6*y^2]
|
|
1924
|
+
|
|
1925
|
+
Check that the primitive_only keyword does something::
|
|
1926
|
+
|
|
1927
|
+
sage: BinaryQF_reduced_representatives(148, proper=False, primitive_only=False)
|
|
1928
|
+
[x^2 + 12*x*y - y^2, 4*x^2 + 6*x*y - 7*y^2, 6*x^2 + 2*x*y - 6*y^2]
|
|
1929
|
+
sage: BinaryQF_reduced_representatives(148, proper=False, primitive_only=True) # needs sage.libs.pari
|
|
1930
|
+
[x^2 + 12*x*y - y^2, 4*x^2 + 6*x*y - 7*y^2]
|
|
1931
|
+
sage: BinaryQF_reduced_representatives(148, proper=True, primitive_only=True) # needs sage.libs.pari
|
|
1932
|
+
[-7*x^2 + 6*x*y + 4*y^2, x^2 + 12*x*y - y^2, 4*x^2 + 6*x*y - 7*y^2]
|
|
1933
|
+
sage: BinaryQF_reduced_representatives(148, proper=True, primitive_only=False)
|
|
1934
|
+
[-7*x^2 + 6*x*y + 4*y^2,
|
|
1935
|
+
x^2 + 12*x*y - y^2,
|
|
1936
|
+
4*x^2 + 6*x*y - 7*y^2,
|
|
1937
|
+
6*x^2 + 2*x*y - 6*y^2]
|
|
1938
|
+
|
|
1939
|
+
Test another part of :issue:`29028`::
|
|
1940
|
+
|
|
1941
|
+
sage: BinaryQF_reduced_representatives(10^2, proper=False, primitive_only=False)
|
|
1942
|
+
[-4*x^2 + 10*x*y,
|
|
1943
|
+
-3*x^2 + 10*x*y,
|
|
1944
|
+
-2*x^2 + 10*x*y,
|
|
1945
|
+
-x^2 + 10*x*y,
|
|
1946
|
+
10*x*y,
|
|
1947
|
+
x^2 + 10*x*y,
|
|
1948
|
+
2*x^2 + 10*x*y,
|
|
1949
|
+
5*x^2 + 10*x*y]
|
|
1950
|
+
sage: BinaryQF_reduced_representatives(10^2, proper=False, primitive_only=True) # needs sage.libs.pari
|
|
1951
|
+
[-3*x^2 + 10*x*y, -x^2 + 10*x*y, x^2 + 10*x*y]
|
|
1952
|
+
sage: BinaryQF_reduced_representatives(10^2, proper=True, primitive_only=True) # needs sage.libs.pari
|
|
1953
|
+
[-3*x^2 + 10*x*y, -x^2 + 10*x*y, x^2 + 10*x*y, 3*x^2 + 10*x*y]
|
|
1954
|
+
sage: BinaryQF_reduced_representatives(10^2, proper=True, primitive_only=False)
|
|
1955
|
+
[-4*x^2 + 10*x*y,
|
|
1956
|
+
-3*x^2 + 10*x*y,
|
|
1957
|
+
-2*x^2 + 10*x*y,
|
|
1958
|
+
-x^2 + 10*x*y,
|
|
1959
|
+
10*x*y,
|
|
1960
|
+
x^2 + 10*x*y,
|
|
1961
|
+
2*x^2 + 10*x*y,
|
|
1962
|
+
3*x^2 + 10*x*y,
|
|
1963
|
+
4*x^2 + 10*x*y,
|
|
1964
|
+
5*x^2 + 10*x*y]
|
|
1965
|
+
"""
|
|
1966
|
+
D = ZZ(D)
|
|
1967
|
+
|
|
1968
|
+
# For a fundamental discriminant all forms are primitive so we need not check:
|
|
1969
|
+
if primitive_only:
|
|
1970
|
+
primitive_only = not D.is_fundamental_discriminant()
|
|
1971
|
+
|
|
1972
|
+
form_list = []
|
|
1973
|
+
|
|
1974
|
+
from sage.arith.srange import xsrange
|
|
1975
|
+
|
|
1976
|
+
D4 = D % 4
|
|
1977
|
+
if D4 == 2 or D4 == 3:
|
|
1978
|
+
raise ValueError("%s is not a discriminant" % D)
|
|
1979
|
+
if D > 0: # Indefinite
|
|
1980
|
+
if D.is_square():
|
|
1981
|
+
b = D.sqrt()
|
|
1982
|
+
c = ZZ.zero()
|
|
1983
|
+
# -b/2 < a <= b/2
|
|
1984
|
+
form_list.extend(BinaryQF(a, b, c)
|
|
1985
|
+
for a in xsrange((-b / 2).floor() + 1,
|
|
1986
|
+
(b / 2).floor() + 1)
|
|
1987
|
+
if not primitive_only or (gcd([a, b, c]) == 1))
|
|
1988
|
+
|
|
1989
|
+
# We follow the description of Buchmann/Vollmer 6.7.1. They
|
|
1990
|
+
# enumerate all reduced forms. We only want representatives.
|
|
1991
|
+
else:
|
|
1992
|
+
sqrt_d = D.sqrt(prec=53)
|
|
1993
|
+
for b in xsrange(1, sqrt_d.floor() + 1):
|
|
1994
|
+
if (D - b) % 2:
|
|
1995
|
+
continue
|
|
1996
|
+
A = (D - b**2) / 4
|
|
1997
|
+
Low_a = ((sqrt_d - b) / 2).ceil()
|
|
1998
|
+
High_a = (A.sqrt(prec=53)).floor()
|
|
1999
|
+
for a in xsrange(Low_a, High_a + 1):
|
|
2000
|
+
if a == 0:
|
|
2001
|
+
continue
|
|
2002
|
+
c = -A/a
|
|
2003
|
+
if c in ZZ:
|
|
2004
|
+
if (not primitive_only) or gcd([a, b, c]) == 1:
|
|
2005
|
+
Q = BinaryQF(a, b, c)
|
|
2006
|
+
Q1 = BinaryQF(-a, b, -c)
|
|
2007
|
+
form_list.append(Q)
|
|
2008
|
+
form_list.append(Q1)
|
|
2009
|
+
if a.abs() != c.abs():
|
|
2010
|
+
Q = BinaryQF(c, b, a)
|
|
2011
|
+
Q1 = BinaryQF(-c, b, -a)
|
|
2012
|
+
form_list.append(Q)
|
|
2013
|
+
form_list.append(Q1)
|
|
2014
|
+
else: # Definite
|
|
2015
|
+
# Only iterate over positive a and over b of the same
|
|
2016
|
+
# parity as D such that 4a^2 + D <= b^2 <= a^2
|
|
2017
|
+
for a in xsrange(1, 1+((-D)//3).isqrt()):
|
|
2018
|
+
a4 = 4*a
|
|
2019
|
+
s = D + a*a4
|
|
2020
|
+
w = 1+(s-1).isqrt() if s > 0 else 0
|
|
2021
|
+
if w % 2 != D % 2:
|
|
2022
|
+
w += 1
|
|
2023
|
+
for b in xsrange(w, a+1, 2):
|
|
2024
|
+
t = b*b-D
|
|
2025
|
+
if t % a4 == 0:
|
|
2026
|
+
c = t // a4
|
|
2027
|
+
if not primitive_only or gcd([a, b, c]) == 1:
|
|
2028
|
+
if c > a > b > 0:
|
|
2029
|
+
form_list.append(BinaryQF([a, -b, c]))
|
|
2030
|
+
form_list.append(BinaryQF([a, b, c]))
|
|
2031
|
+
if not proper or D > 0:
|
|
2032
|
+
# TODO:
|
|
2033
|
+
# instead of filtering, enumerate only improper classes to start with
|
|
2034
|
+
# filter for equivalence classes
|
|
2035
|
+
form_list_new = []
|
|
2036
|
+
for q in form_list:
|
|
2037
|
+
if not any(q.is_equivalent(q1, proper=proper) for q1 in form_list_new):
|
|
2038
|
+
form_list_new.append(q)
|
|
2039
|
+
form_list = form_list_new
|
|
2040
|
+
|
|
2041
|
+
form_list.sort()
|
|
2042
|
+
return form_list
|