passagemath-singular 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-singular might be problematic. Click here for more details.
- PySingular.cpython-314-aarch64-linux-gnu.so +0 -0
- passagemath_singular-10.6.31rc3.dist-info/METADATA +183 -0
- passagemath_singular-10.6.31rc3.dist-info/RECORD +490 -0
- passagemath_singular-10.6.31rc3.dist-info/WHEEL +6 -0
- passagemath_singular-10.6.31rc3.dist-info/top_level.txt +3 -0
- passagemath_singular.libs/libSingular-4-6a2a8666.4.1.so +0 -0
- passagemath_singular.libs/libcddgmp-ac579979.so.0.1.3 +0 -0
- passagemath_singular.libs/libfactory-4-66e33516.4.1.so +0 -0
- passagemath_singular.libs/libflint-81de1160.so.21.0.0 +0 -0
- passagemath_singular.libs/libgf2x-fbd36f80.so.3.0.0 +0 -0
- passagemath_singular.libs/libgfortran-e1b7dfc8.so.5.0.0 +0 -0
- passagemath_singular.libs/libgmp-93ebf16a.so.10.5.0 +0 -0
- passagemath_singular.libs/libgsl-e3525837.so.28.0.0 +0 -0
- passagemath_singular.libs/libmpfr-e0f11cf3.so.6.2.1 +0 -0
- passagemath_singular.libs/libntl-0043a3a2.so.44.0.1 +0 -0
- passagemath_singular.libs/libomalloc-0-06512335.9.6.so +0 -0
- passagemath_singular.libs/libopenblasp-r0-4c5b64b1.3.29.so +0 -0
- passagemath_singular.libs/libpolys-4-cb7246b5.4.1.so +0 -0
- passagemath_singular.libs/libreadline-28330744.so.8.2 +0 -0
- passagemath_singular.libs/libsingular_resources-4-8c425241.4.1.so +0 -0
- passagemath_singular.libs/libtinfo-f81c2d16.so.6.3 +0 -0
- sage/algebras/all__sagemath_singular.py +3 -0
- sage/algebras/fusion_rings/all.py +19 -0
- sage/algebras/fusion_rings/f_matrix.py +2448 -0
- sage/algebras/fusion_rings/fast_parallel_fmats_methods.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/algebras/fusion_rings/fast_parallel_fmats_methods.pxd +5 -0
- sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx +538 -0
- sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pxd +3 -0
- sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx +331 -0
- sage/algebras/fusion_rings/fusion_double.py +899 -0
- sage/algebras/fusion_rings/fusion_ring.py +1580 -0
- sage/algebras/fusion_rings/poly_tup_engine.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/algebras/fusion_rings/poly_tup_engine.pxd +24 -0
- sage/algebras/fusion_rings/poly_tup_engine.pyx +579 -0
- sage/algebras/fusion_rings/shm_managers.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/algebras/fusion_rings/shm_managers.pxd +24 -0
- sage/algebras/fusion_rings/shm_managers.pyx +780 -0
- sage/algebras/letterplace/all.py +1 -0
- sage/algebras/letterplace/free_algebra_element_letterplace.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/algebras/letterplace/free_algebra_element_letterplace.pxd +18 -0
- sage/algebras/letterplace/free_algebra_element_letterplace.pyx +755 -0
- sage/algebras/letterplace/free_algebra_letterplace.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/algebras/letterplace/free_algebra_letterplace.pxd +35 -0
- sage/algebras/letterplace/free_algebra_letterplace.pyx +914 -0
- sage/algebras/letterplace/letterplace_ideal.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/algebras/letterplace/letterplace_ideal.pyx +408 -0
- sage/algebras/quatalg/all.py +2 -0
- sage/algebras/quatalg/quaternion_algebra.py +4778 -0
- sage/algebras/quatalg/quaternion_algebra_cython.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/algebras/quatalg/quaternion_algebra_cython.pyx +261 -0
- sage/algebras/quatalg/quaternion_algebra_element.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/algebras/quatalg/quaternion_algebra_element.pxd +29 -0
- sage/algebras/quatalg/quaternion_algebra_element.pyx +2176 -0
- sage/all__sagemath_singular.py +11 -0
- sage/ext_data/all__sagemath_singular.py +1 -0
- sage/ext_data/singular/function_field/core.lib +98 -0
- sage/interfaces/all__sagemath_singular.py +1 -0
- sage/interfaces/singular.py +2835 -0
- sage/libs/all__sagemath_singular.py +1 -0
- sage/libs/singular/__init__.py +1 -0
- sage/libs/singular/decl.pxd +1168 -0
- sage/libs/singular/function.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/libs/singular/function.pxd +87 -0
- sage/libs/singular/function.pyx +1901 -0
- sage/libs/singular/function_factory.py +61 -0
- sage/libs/singular/groebner_strategy.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/libs/singular/groebner_strategy.pxd +22 -0
- sage/libs/singular/groebner_strategy.pyx +582 -0
- sage/libs/singular/option.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/libs/singular/option.pyx +671 -0
- sage/libs/singular/polynomial.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/libs/singular/polynomial.pxd +39 -0
- sage/libs/singular/polynomial.pyx +661 -0
- sage/libs/singular/ring.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/libs/singular/ring.pxd +58 -0
- sage/libs/singular/ring.pyx +893 -0
- sage/libs/singular/singular.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/libs/singular/singular.pxd +72 -0
- sage/libs/singular/singular.pyx +1944 -0
- sage/libs/singular/standard_options.py +145 -0
- sage/matrix/all__sagemath_singular.py +1 -0
- sage/matrix/matrix_mpolynomial_dense.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_mpolynomial_dense.pxd +7 -0
- sage/matrix/matrix_mpolynomial_dense.pyx +615 -0
- sage/rings/all__sagemath_singular.py +1 -0
- sage/rings/function_field/all__sagemath_singular.py +1 -0
- sage/rings/function_field/derivations_polymod.py +911 -0
- sage/rings/function_field/element_polymod.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/function_field/element_polymod.pyx +406 -0
- sage/rings/function_field/function_field_polymod.py +2611 -0
- sage/rings/function_field/ideal_polymod.py +1775 -0
- sage/rings/function_field/order_polymod.py +1475 -0
- sage/rings/function_field/place_polymod.py +681 -0
- sage/rings/polynomial/all__sagemath_singular.py +1 -0
- sage/rings/polynomial/multi_polynomial_ideal_libsingular.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/multi_polynomial_ideal_libsingular.pxd +5 -0
- sage/rings/polynomial/multi_polynomial_ideal_libsingular.pyx +339 -0
- sage/rings/polynomial/multi_polynomial_libsingular.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/multi_polynomial_libsingular.pxd +30 -0
- sage/rings/polynomial/multi_polynomial_libsingular.pyx +6277 -0
- sage/rings/polynomial/plural.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/plural.pxd +48 -0
- sage/rings/polynomial/plural.pyx +3171 -0
- sage/symbolic/all__sagemath_singular.py +1 -0
- sage/symbolic/comparison_impl.pxi +428 -0
- sage/symbolic/constants_c_impl.pxi +178 -0
- sage/symbolic/expression.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/symbolic/expression.pxd +7 -0
- sage/symbolic/expression.pyx +14200 -0
- sage/symbolic/getitem_impl.pxi +202 -0
- sage/symbolic/pynac.pxi +572 -0
- sage/symbolic/pynac_constant_impl.pxi +133 -0
- sage/symbolic/pynac_function_impl.pxi +206 -0
- sage/symbolic/pynac_impl.pxi +2576 -0
- sage/symbolic/pynac_wrap.h +124 -0
- sage/symbolic/series_impl.pxi +272 -0
- sage/symbolic/substitution_map_impl.pxi +94 -0
- sage_wheels/bin/ESingular +0 -0
- sage_wheels/bin/Singular +0 -0
- sage_wheels/bin/TSingular +0 -0
- sage_wheels/lib/singular/MOD/cohomo.la +41 -0
- sage_wheels/lib/singular/MOD/cohomo.so +0 -0
- sage_wheels/lib/singular/MOD/customstd.la +41 -0
- sage_wheels/lib/singular/MOD/customstd.so +0 -0
- sage_wheels/lib/singular/MOD/freealgebra.la +41 -0
- sage_wheels/lib/singular/MOD/freealgebra.so +0 -0
- sage_wheels/lib/singular/MOD/gfanlib.la +41 -0
- sage_wheels/lib/singular/MOD/gfanlib.so +0 -0
- sage_wheels/lib/singular/MOD/gitfan.la +41 -0
- sage_wheels/lib/singular/MOD/gitfan.so +0 -0
- sage_wheels/lib/singular/MOD/interval.la +41 -0
- sage_wheels/lib/singular/MOD/interval.so +0 -0
- sage_wheels/lib/singular/MOD/loctriv.la +41 -0
- sage_wheels/lib/singular/MOD/loctriv.so +0 -0
- sage_wheels/lib/singular/MOD/machinelearning.la +41 -0
- sage_wheels/lib/singular/MOD/machinelearning.so +0 -0
- sage_wheels/lib/singular/MOD/p_Procs_FieldGeneral.la +41 -0
- sage_wheels/lib/singular/MOD/p_Procs_FieldGeneral.so +0 -0
- sage_wheels/lib/singular/MOD/p_Procs_FieldIndep.la +41 -0
- sage_wheels/lib/singular/MOD/p_Procs_FieldIndep.so +0 -0
- sage_wheels/lib/singular/MOD/p_Procs_FieldQ.la +41 -0
- sage_wheels/lib/singular/MOD/p_Procs_FieldQ.so +0 -0
- sage_wheels/lib/singular/MOD/p_Procs_FieldZp.la +41 -0
- sage_wheels/lib/singular/MOD/p_Procs_FieldZp.so +0 -0
- sage_wheels/lib/singular/MOD/partialgb.la +41 -0
- sage_wheels/lib/singular/MOD/partialgb.so +0 -0
- sage_wheels/lib/singular/MOD/pyobject.la +41 -0
- sage_wheels/lib/singular/MOD/pyobject.so +0 -0
- sage_wheels/lib/singular/MOD/singmathic.la +41 -0
- sage_wheels/lib/singular/MOD/singmathic.so +0 -0
- sage_wheels/lib/singular/MOD/sispasm.la +41 -0
- sage_wheels/lib/singular/MOD/sispasm.so +0 -0
- sage_wheels/lib/singular/MOD/subsets.la +41 -0
- sage_wheels/lib/singular/MOD/subsets.so +0 -0
- sage_wheels/lib/singular/MOD/systhreads.la +41 -0
- sage_wheels/lib/singular/MOD/systhreads.so +0 -0
- sage_wheels/lib/singular/MOD/syzextra.la +41 -0
- sage_wheels/lib/singular/MOD/syzextra.so +0 -0
- sage_wheels/libexec/singular/MOD/change_cost +0 -0
- sage_wheels/libexec/singular/MOD/singularsurf +11 -0
- sage_wheels/libexec/singular/MOD/singularsurf_jupyter +9 -0
- sage_wheels/libexec/singular/MOD/singularsurf_win +10 -0
- sage_wheels/libexec/singular/MOD/solve_IP +0 -0
- sage_wheels/libexec/singular/MOD/surfex +16 -0
- sage_wheels/libexec/singular/MOD/toric_ideal +0 -0
- sage_wheels/share/factory/gftables/10201 +342 -0
- sage_wheels/share/factory/gftables/1024 +37 -0
- sage_wheels/share/factory/gftables/10609 +356 -0
- sage_wheels/share/factory/gftables/11449 +384 -0
- sage_wheels/share/factory/gftables/11881 +398 -0
- sage_wheels/share/factory/gftables/121 +6 -0
- sage_wheels/share/factory/gftables/12167 +408 -0
- sage_wheels/share/factory/gftables/125 +7 -0
- sage_wheels/share/factory/gftables/12769 +428 -0
- sage_wheels/share/factory/gftables/128 +7 -0
- sage_wheels/share/factory/gftables/1331 +47 -0
- sage_wheels/share/factory/gftables/1369 +48 -0
- sage_wheels/share/factory/gftables/14641 +490 -0
- sage_wheels/share/factory/gftables/15625 +523 -0
- sage_wheels/share/factory/gftables/16 +3 -0
- sage_wheels/share/factory/gftables/16129 +540 -0
- sage_wheels/share/factory/gftables/16384 +549 -0
- sage_wheels/share/factory/gftables/16807 +563 -0
- sage_wheels/share/factory/gftables/1681 +58 -0
- sage_wheels/share/factory/gftables/169 +8 -0
- sage_wheels/share/factory/gftables/17161 +574 -0
- sage_wheels/share/factory/gftables/1849 +64 -0
- sage_wheels/share/factory/gftables/18769 +628 -0
- sage_wheels/share/factory/gftables/19321 +646 -0
- sage_wheels/share/factory/gftables/19683 +659 -0
- sage_wheels/share/factory/gftables/2048 +71 -0
- sage_wheels/share/factory/gftables/2187 +75 -0
- sage_wheels/share/factory/gftables/2197 +76 -0
- sage_wheels/share/factory/gftables/2209 +76 -0
- sage_wheels/share/factory/gftables/22201 +742 -0
- sage_wheels/share/factory/gftables/22801 +762 -0
- sage_wheels/share/factory/gftables/2401 +82 -0
- sage_wheels/share/factory/gftables/243 +11 -0
- sage_wheels/share/factory/gftables/24389 +815 -0
- sage_wheels/share/factory/gftables/24649 +824 -0
- sage_wheels/share/factory/gftables/25 +3 -0
- sage_wheels/share/factory/gftables/256 +11 -0
- sage_wheels/share/factory/gftables/26569 +888 -0
- sage_wheels/share/factory/gftables/27 +3 -0
- sage_wheels/share/factory/gftables/27889 +932 -0
- sage_wheels/share/factory/gftables/2809 +96 -0
- sage_wheels/share/factory/gftables/28561 +954 -0
- sage_wheels/share/factory/gftables/289 +12 -0
- sage_wheels/share/factory/gftables/29791 +995 -0
- sage_wheels/share/factory/gftables/29929 +1000 -0
- sage_wheels/share/factory/gftables/3125 +107 -0
- sage_wheels/share/factory/gftables/32 +4 -0
- sage_wheels/share/factory/gftables/32041 +1070 -0
- sage_wheels/share/factory/gftables/32761 +1094 -0
- sage_wheels/share/factory/gftables/32768 +1095 -0
- sage_wheels/share/factory/gftables/343 +14 -0
- sage_wheels/share/factory/gftables/3481 +118 -0
- sage_wheels/share/factory/gftables/361 +14 -0
- sage_wheels/share/factory/gftables/36481 +1218 -0
- sage_wheels/share/factory/gftables/3721 +126 -0
- sage_wheels/share/factory/gftables/37249 +1244 -0
- sage_wheels/share/factory/gftables/38809 +1296 -0
- sage_wheels/share/factory/gftables/39601 +1322 -0
- sage_wheels/share/factory/gftables/4 +3 -0
- sage_wheels/share/factory/gftables/4096 +139 -0
- sage_wheels/share/factory/gftables/44521 +1486 -0
- sage_wheels/share/factory/gftables/4489 +152 -0
- sage_wheels/share/factory/gftables/49 +4 -0
- sage_wheels/share/factory/gftables/4913 +166 -0
- sage_wheels/share/factory/gftables/49729 +1660 -0
- sage_wheels/share/factory/gftables/5041 +170 -0
- sage_wheels/share/factory/gftables/50653 +1691 -0
- sage_wheels/share/factory/gftables/512 +20 -0
- sage_wheels/share/factory/gftables/51529 +1720 -0
- sage_wheels/share/factory/gftables/52441 +1750 -0
- sage_wheels/share/factory/gftables/529 +20 -0
- sage_wheels/share/factory/gftables/5329 +180 -0
- sage_wheels/share/factory/gftables/54289 +1812 -0
- sage_wheels/share/factory/gftables/57121 +1906 -0
- sage_wheels/share/factory/gftables/58081 +1938 -0
- sage_wheels/share/factory/gftables/59049 +1971 -0
- sage_wheels/share/factory/gftables/6241 +210 -0
- sage_wheels/share/factory/gftables/625 +23 -0
- sage_wheels/share/factory/gftables/63001 +2102 -0
- sage_wheels/share/factory/gftables/64 +5 -0
- sage_wheels/share/factory/gftables/6561 +221 -0
- sage_wheels/share/factory/gftables/6859 +231 -0
- sage_wheels/share/factory/gftables/6889 +232 -0
- sage_wheels/share/factory/gftables/729 +27 -0
- sage_wheels/share/factory/gftables/7921 +266 -0
- sage_wheels/share/factory/gftables/8 +3 -0
- sage_wheels/share/factory/gftables/81 +5 -0
- sage_wheels/share/factory/gftables/8192 +276 -0
- sage_wheels/share/factory/gftables/841 +30 -0
- sage_wheels/share/factory/gftables/9 +3 -0
- sage_wheels/share/factory/gftables/9409 +316 -0
- sage_wheels/share/factory/gftables/961 +34 -0
- sage_wheels/share/info/singular.info +191898 -0
- sage_wheels/share/singular/LIB/GND.lib +1359 -0
- sage_wheels/share/singular/LIB/JMBTest.lib +976 -0
- sage_wheels/share/singular/LIB/JMSConst.lib +1363 -0
- sage_wheels/share/singular/LIB/KVequiv.lib +699 -0
- sage_wheels/share/singular/LIB/SingularityDBM.lib +491 -0
- sage_wheels/share/singular/LIB/VecField.lib +1542 -0
- sage_wheels/share/singular/LIB/absfact.lib +959 -0
- sage_wheels/share/singular/LIB/ainvar.lib +730 -0
- sage_wheels/share/singular/LIB/aksaka.lib +419 -0
- sage_wheels/share/singular/LIB/alexpoly.lib +2542 -0
- sage_wheels/share/singular/LIB/algebra.lib +1193 -0
- sage_wheels/share/singular/LIB/all.lib +136 -0
- sage_wheels/share/singular/LIB/arcpoint.lib +514 -0
- sage_wheels/share/singular/LIB/arnold.lib +4553 -0
- sage_wheels/share/singular/LIB/arnoldclassify.lib +2058 -0
- sage_wheels/share/singular/LIB/arr.lib +3486 -0
- sage_wheels/share/singular/LIB/assprimeszerodim.lib +755 -0
- sage_wheels/share/singular/LIB/autgradalg.lib +3361 -0
- sage_wheels/share/singular/LIB/bfun.lib +1964 -0
- sage_wheels/share/singular/LIB/bimodules.lib +774 -0
- sage_wheels/share/singular/LIB/brillnoether.lib +226 -0
- sage_wheels/share/singular/LIB/brnoeth.lib +5017 -0
- sage_wheels/share/singular/LIB/central.lib +2169 -0
- sage_wheels/share/singular/LIB/chern.lib +4162 -0
- sage_wheels/share/singular/LIB/cimonom.lib +571 -0
- sage_wheels/share/singular/LIB/cisimplicial.lib +1835 -0
- sage_wheels/share/singular/LIB/classify.lib +3239 -0
- sage_wheels/share/singular/LIB/classify2.lib +1462 -0
- sage_wheels/share/singular/LIB/classifyMapGerms.lib +1515 -0
- sage_wheels/share/singular/LIB/classify_aeq.lib +3253 -0
- sage_wheels/share/singular/LIB/classifyceq.lib +2092 -0
- sage_wheels/share/singular/LIB/classifyci.lib +1133 -0
- sage_wheels/share/singular/LIB/combinat.lib +91 -0
- sage_wheels/share/singular/LIB/compregb.lib +276 -0
- sage_wheels/share/singular/LIB/control.lib +1636 -0
- sage_wheels/share/singular/LIB/crypto.lib +3795 -0
- sage_wheels/share/singular/LIB/curveInv.lib +667 -0
- sage_wheels/share/singular/LIB/curvepar.lib +1817 -0
- sage_wheels/share/singular/LIB/customstd.lib +100 -0
- sage_wheels/share/singular/LIB/deRham.lib +5979 -0
- sage_wheels/share/singular/LIB/decodegb.lib +2134 -0
- sage_wheels/share/singular/LIB/decomp.lib +1655 -0
- sage_wheels/share/singular/LIB/deflation.lib +872 -0
- sage_wheels/share/singular/LIB/deform.lib +925 -0
- sage_wheels/share/singular/LIB/difform.lib +3055 -0
- sage_wheels/share/singular/LIB/divisors.lib +750 -0
- sage_wheels/share/singular/LIB/dmod.lib +5817 -0
- sage_wheels/share/singular/LIB/dmodapp.lib +3269 -0
- sage_wheels/share/singular/LIB/dmodideal.lib +1211 -0
- sage_wheels/share/singular/LIB/dmodloc.lib +2645 -0
- sage_wheels/share/singular/LIB/dmodvar.lib +818 -0
- sage_wheels/share/singular/LIB/dummy.lib +17 -0
- sage_wheels/share/singular/LIB/elim.lib +1009 -0
- sage_wheels/share/singular/LIB/ellipticcovers.lib +548 -0
- sage_wheels/share/singular/LIB/enumpoints.lib +146 -0
- sage_wheels/share/singular/LIB/equising.lib +2127 -0
- sage_wheels/share/singular/LIB/ffmodstd.lib +2384 -0
- sage_wheels/share/singular/LIB/ffsolve.lib +1289 -0
- sage_wheels/share/singular/LIB/findifs.lib +778 -0
- sage_wheels/share/singular/LIB/finitediff.lib +1768 -0
- sage_wheels/share/singular/LIB/finvar.lib +7989 -0
- sage_wheels/share/singular/LIB/fpadim.lib +2429 -0
- sage_wheels/share/singular/LIB/fpalgebras.lib +1666 -0
- sage_wheels/share/singular/LIB/fpaprops.lib +1462 -0
- sage_wheels/share/singular/LIB/freegb.lib +3853 -0
- sage_wheels/share/singular/LIB/general.lib +1350 -0
- sage_wheels/share/singular/LIB/gfan.lib +1768 -0
- sage_wheels/share/singular/LIB/gitfan.lib +3130 -0
- sage_wheels/share/singular/LIB/gkdim.lib +99 -0
- sage_wheels/share/singular/LIB/gmspoly.lib +589 -0
- sage_wheels/share/singular/LIB/gmssing.lib +1739 -0
- sage_wheels/share/singular/LIB/goettsche.lib +909 -0
- sage_wheels/share/singular/LIB/graal.lib +1366 -0
- sage_wheels/share/singular/LIB/gradedModules.lib +2541 -0
- sage_wheels/share/singular/LIB/graphics.lib +360 -0
- sage_wheels/share/singular/LIB/grobcov.lib +7706 -0
- sage_wheels/share/singular/LIB/groups.lib +1123 -0
- sage_wheels/share/singular/LIB/grwalk.lib +507 -0
- sage_wheels/share/singular/LIB/hdepth.lib +194 -0
- sage_wheels/share/singular/LIB/help.cnf +57 -0
- sage_wheels/share/singular/LIB/hess.lib +1946 -0
- sage_wheels/share/singular/LIB/hnoether.lib +4292 -0
- sage_wheels/share/singular/LIB/hodge.lib +400 -0
- sage_wheels/share/singular/LIB/homolog.lib +1965 -0
- sage_wheels/share/singular/LIB/hyperel.lib +975 -0
- sage_wheels/share/singular/LIB/inout.lib +679 -0
- sage_wheels/share/singular/LIB/integralbasis.lib +6224 -0
- sage_wheels/share/singular/LIB/interval.lib +1418 -0
- sage_wheels/share/singular/LIB/intprog.lib +778 -0
- sage_wheels/share/singular/LIB/invar.lib +443 -0
- sage_wheels/share/singular/LIB/involut.lib +980 -0
- sage_wheels/share/singular/LIB/jacobson.lib +1215 -0
- sage_wheels/share/singular/LIB/kskernel.lib +534 -0
- sage_wheels/share/singular/LIB/latex.lib +3146 -0
- sage_wheels/share/singular/LIB/lejeune.lib +651 -0
- sage_wheels/share/singular/LIB/linalg.lib +2040 -0
- sage_wheels/share/singular/LIB/locnormal.lib +212 -0
- sage_wheels/share/singular/LIB/lrcalc.lib +526 -0
- sage_wheels/share/singular/LIB/makedbm.lib +294 -0
- sage_wheels/share/singular/LIB/mathml.lib +813 -0
- sage_wheels/share/singular/LIB/matrix.lib +1372 -0
- sage_wheels/share/singular/LIB/maxlike.lib +1132 -0
- sage_wheels/share/singular/LIB/methods.lib +212 -0
- sage_wheels/share/singular/LIB/moddiq.lib +322 -0
- sage_wheels/share/singular/LIB/modfinduni.lib +181 -0
- sage_wheels/share/singular/LIB/modnormal.lib +218 -0
- sage_wheels/share/singular/LIB/modprimdec.lib +1278 -0
- sage_wheels/share/singular/LIB/modquotient.lib +269 -0
- sage_wheels/share/singular/LIB/modstd.lib +1024 -0
- sage_wheels/share/singular/LIB/modular.lib +545 -0
- sage_wheels/share/singular/LIB/modules.lib +2561 -0
- sage_wheels/share/singular/LIB/modwalk.lib +609 -0
- sage_wheels/share/singular/LIB/mondromy.lib +1016 -0
- sage_wheels/share/singular/LIB/monomialideal.lib +3851 -0
- sage_wheels/share/singular/LIB/mprimdec.lib +2353 -0
- sage_wheels/share/singular/LIB/mregular.lib +1863 -0
- sage_wheels/share/singular/LIB/multigrading.lib +5629 -0
- sage_wheels/share/singular/LIB/ncHilb.lib +777 -0
- sage_wheels/share/singular/LIB/ncModslimgb.lib +791 -0
- sage_wheels/share/singular/LIB/ncalg.lib +16311 -0
- sage_wheels/share/singular/LIB/ncall.lib +31 -0
- sage_wheels/share/singular/LIB/ncdecomp.lib +468 -0
- sage_wheels/share/singular/LIB/ncfactor.lib +13371 -0
- sage_wheels/share/singular/LIB/ncfrac.lib +1023 -0
- sage_wheels/share/singular/LIB/nchilbert.lib +448 -0
- sage_wheels/share/singular/LIB/nchomolog.lib +759 -0
- sage_wheels/share/singular/LIB/ncloc.lib +361 -0
- sage_wheels/share/singular/LIB/ncpreim.lib +795 -0
- sage_wheels/share/singular/LIB/ncrat.lib +2849 -0
- sage_wheels/share/singular/LIB/nctools.lib +1887 -0
- sage_wheels/share/singular/LIB/nets.lib +1456 -0
- sage_wheels/share/singular/LIB/nfmodstd.lib +1000 -0
- sage_wheels/share/singular/LIB/nfmodsyz.lib +732 -0
- sage_wheels/share/singular/LIB/noether.lib +1106 -0
- sage_wheels/share/singular/LIB/normal.lib +8700 -0
- sage_wheels/share/singular/LIB/normaliz.lib +2226 -0
- sage_wheels/share/singular/LIB/ntsolve.lib +362 -0
- sage_wheels/share/singular/LIB/numerAlg.lib +560 -0
- sage_wheels/share/singular/LIB/numerDecom.lib +2261 -0
- sage_wheels/share/singular/LIB/olga.lib +1933 -0
- sage_wheels/share/singular/LIB/orbitparam.lib +351 -0
- sage_wheels/share/singular/LIB/parallel.lib +319 -0
- sage_wheels/share/singular/LIB/paraplanecurves.lib +3110 -0
- sage_wheels/share/singular/LIB/perron.lib +202 -0
- sage_wheels/share/singular/LIB/pfd.lib +2223 -0
- sage_wheels/share/singular/LIB/phindex.lib +642 -0
- sage_wheels/share/singular/LIB/pointid.lib +673 -0
- sage_wheels/share/singular/LIB/polybori.lib +1430 -0
- sage_wheels/share/singular/LIB/polyclass.lib +525 -0
- sage_wheels/share/singular/LIB/polylib.lib +1174 -0
- sage_wheels/share/singular/LIB/polymake.lib +1902 -0
- sage_wheels/share/singular/LIB/presolve.lib +1533 -0
- sage_wheels/share/singular/LIB/primdec.lib +9576 -0
- sage_wheels/share/singular/LIB/primdecint.lib +1782 -0
- sage_wheels/share/singular/LIB/primitiv.lib +401 -0
- sage_wheels/share/singular/LIB/puiseuxexpansions.lib +1631 -0
- sage_wheels/share/singular/LIB/purityfiltration.lib +960 -0
- sage_wheels/share/singular/LIB/qhmoduli.lib +1561 -0
- sage_wheels/share/singular/LIB/qmatrix.lib +293 -0
- sage_wheels/share/singular/LIB/random.lib +455 -0
- sage_wheels/share/singular/LIB/ratgb.lib +489 -0
- sage_wheels/share/singular/LIB/realclassify.lib +5759 -0
- sage_wheels/share/singular/LIB/realizationMatroids.lib +772 -0
- sage_wheels/share/singular/LIB/realrad.lib +1197 -0
- sage_wheels/share/singular/LIB/recover.lib +2628 -0
- sage_wheels/share/singular/LIB/redcgs.lib +3984 -0
- sage_wheels/share/singular/LIB/reesclos.lib +465 -0
- sage_wheels/share/singular/LIB/resbinomial.lib +2802 -0
- sage_wheels/share/singular/LIB/resgraph.lib +789 -0
- sage_wheels/share/singular/LIB/resjung.lib +820 -0
- sage_wheels/share/singular/LIB/resolve.lib +5110 -0
- sage_wheels/share/singular/LIB/resources.lib +170 -0
- sage_wheels/share/singular/LIB/reszeta.lib +5473 -0
- sage_wheels/share/singular/LIB/ring.lib +1328 -0
- sage_wheels/share/singular/LIB/ringgb.lib +343 -0
- sage_wheels/share/singular/LIB/rinvar.lib +1153 -0
- sage_wheels/share/singular/LIB/rootisolation.lib +1481 -0
- sage_wheels/share/singular/LIB/rootsmr.lib +709 -0
- sage_wheels/share/singular/LIB/rootsur.lib +886 -0
- sage_wheels/share/singular/LIB/rstandard.lib +607 -0
- sage_wheels/share/singular/LIB/rwalk.lib +336 -0
- sage_wheels/share/singular/LIB/sagbi.lib +1353 -0
- sage_wheels/share/singular/LIB/sagbiNormaliz.lib +1622 -0
- sage_wheels/share/singular/LIB/sagbiNormaliz0.lib +1498 -0
- sage_wheels/share/singular/LIB/sagbigrob.lib +449 -0
- sage_wheels/share/singular/LIB/schreyer.lib +321 -0
- sage_wheels/share/singular/LIB/schubert.lib +2551 -0
- sage_wheels/share/singular/LIB/sets.lib +524 -0
- sage_wheels/share/singular/LIB/sheafcoh.lib +1663 -0
- sage_wheels/share/singular/LIB/signcond.lib +437 -0
- sage_wheels/share/singular/LIB/sing.lib +1094 -0
- sage_wheels/share/singular/LIB/sing4ti2.lib +419 -0
- sage_wheels/share/singular/LIB/solve.lib +2243 -0
- sage_wheels/share/singular/LIB/spcurve.lib +1077 -0
- sage_wheels/share/singular/LIB/spectrum.lib +62 -0
- sage_wheels/share/singular/LIB/sresext.lib +757 -0
- sage_wheels/share/singular/LIB/ssi.lib +143 -0
- sage_wheels/share/singular/LIB/standard.lib +2769 -0
- sage_wheels/share/singular/LIB/stanleyreisner.lib +473 -0
- sage_wheels/share/singular/LIB/stdmodule.lib +547 -0
- sage_wheels/share/singular/LIB/stratify.lib +1070 -0
- sage_wheels/share/singular/LIB/surf.lib +506 -0
- sage_wheels/share/singular/LIB/surf_jupyter.lib +223 -0
- sage_wheels/share/singular/LIB/surfacesignature.lib +522 -0
- sage_wheels/share/singular/LIB/surfex.lib +1462 -0
- sage_wheels/share/singular/LIB/swalk.lib +877 -0
- sage_wheels/share/singular/LIB/symodstd.lib +1570 -0
- sage_wheels/share/singular/LIB/systhreads.lib +74 -0
- sage_wheels/share/singular/LIB/tasks.lib +1324 -0
- sage_wheels/share/singular/LIB/tateProdCplxNegGrad.lib +2412 -0
- sage_wheels/share/singular/LIB/teachstd.lib +858 -0
- sage_wheels/share/singular/LIB/template.lib +116 -0
- sage_wheels/share/singular/LIB/toric.lib +1119 -0
- sage_wheels/share/singular/LIB/transformation.lib +116 -0
- sage_wheels/share/singular/LIB/triang.lib +1197 -0
- sage_wheels/share/singular/LIB/tropical.lib +8741 -0
- sage_wheels/share/singular/LIB/tropicalEllipticCovers.lib +2922 -0
- sage_wheels/share/singular/LIB/tropicalNewton.lib +1128 -0
- sage_wheels/share/singular/LIB/tst.lib +1108 -0
- sage_wheels/share/singular/LIB/weierstr.lib +241 -0
- sage_wheels/share/singular/LIB/zeroset.lib +1478 -0
- sage_wheels/share/singular/emacs/.emacs-general +184 -0
- sage_wheels/share/singular/emacs/.emacs-singular +234 -0
- sage_wheels/share/singular/emacs/COPYING +44 -0
- sage_wheels/share/singular/emacs/cmd-cmpl.el +241 -0
- sage_wheels/share/singular/emacs/ex-cmpl.el +1681 -0
- sage_wheels/share/singular/emacs/hlp-cmpl.el +4318 -0
- sage_wheels/share/singular/emacs/lib-cmpl.el +179 -0
- sage_wheels/share/singular/emacs/singular.el +4273 -0
- sage_wheels/share/singular/emacs/singular.xpm +39 -0
- sage_wheels/share/singular/singular.idx +5002 -0
|
@@ -0,0 +1,2176 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-singular
|
|
2
|
+
# distutils: language = c++
|
|
3
|
+
# distutils: libraries = gmp M_LIBRARIES NTL_LIBRARIES
|
|
4
|
+
# distutils: extra_compile_args = NTL_CFLAGS
|
|
5
|
+
# distutils: include_dirs = NTL_INCDIR
|
|
6
|
+
# distutils: library_dirs = NTL_LIBDIR
|
|
7
|
+
# distutils: extra_link_args = NTL_LIBEXTRA
|
|
8
|
+
"""
|
|
9
|
+
Elements of Quaternion Algebras
|
|
10
|
+
|
|
11
|
+
Sage allows for computation with elements of quaternion algebras over
|
|
12
|
+
a nearly arbitrary base field of characteristic not 2. Sage also has
|
|
13
|
+
very highly optimized implementation of arithmetic in rational
|
|
14
|
+
quaternion algebras and quaternion algebras over number fields.
|
|
15
|
+
|
|
16
|
+
TESTS:
|
|
17
|
+
|
|
18
|
+
Check that :issue:`20829` is fixed::
|
|
19
|
+
|
|
20
|
+
sage: D.<i,j,k> = QuaternionAlgebra(QQ,-1,-3)
|
|
21
|
+
sage: hash(i)
|
|
22
|
+
184301497
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
# ****************************************************************************
|
|
26
|
+
# Copyright (C) 2009 William Stein <wstein@gmail.com>
|
|
27
|
+
# Copyright (C) 2009 Jonathan Bober <jwbober@gmail.com>
|
|
28
|
+
#
|
|
29
|
+
# This program is free software: you can redistribute it and/or modify
|
|
30
|
+
# it under the terms of the GNU General Public License as published by
|
|
31
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
32
|
+
# (at your option) any later version.
|
|
33
|
+
# https://www.gnu.org/licenses/
|
|
34
|
+
# ****************************************************************************
|
|
35
|
+
|
|
36
|
+
from sage.structure.element cimport AlgebraElement, Element
|
|
37
|
+
from sage.structure.richcmp cimport rich_to_bool, rich_to_bool_sgn, richcmp_item
|
|
38
|
+
from sage.rings.rational cimport Rational
|
|
39
|
+
from sage.rings.integer cimport Integer
|
|
40
|
+
from sage.rings.number_field.number_field_element cimport NumberFieldElement
|
|
41
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
|
42
|
+
from sage.matrix.constructor import matrix
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
from sage.libs.gmp.mpz cimport *
|
|
46
|
+
from sage.libs.gmp.mpq cimport *
|
|
47
|
+
from sage.libs.ntl.convert cimport mpz_to_ZZ, ZZ_to_mpz
|
|
48
|
+
from sage.libs.flint.fmpz cimport *
|
|
49
|
+
from sage.libs.flint.fmpz_poly cimport *
|
|
50
|
+
from sage.libs.flint.fmpz_poly_sage cimport *
|
|
51
|
+
from sage.libs.flint.ntl_interface cimport *
|
|
52
|
+
|
|
53
|
+
# variables for holding temporary values computed in
|
|
54
|
+
# QuaternionAlgebraElement_rational_field._mul_()
|
|
55
|
+
cdef mpz_t T1, T2, t3, t4, t5, t6, t7, t8, s1, s2, U1, U2
|
|
56
|
+
cdef fmpz_poly_t fT1, fT2, ft3, ft4, ft5, ft6, ft7, ft8, fs1, fs2, fU1, fU2
|
|
57
|
+
|
|
58
|
+
cdef _clear_globals():
|
|
59
|
+
"""
|
|
60
|
+
Clear all global variables allocated for optimization of
|
|
61
|
+
quaternion algebra arithmetic.
|
|
62
|
+
|
|
63
|
+
Do *not* call this yourself.
|
|
64
|
+
"""
|
|
65
|
+
mpz_clear(T1)
|
|
66
|
+
mpz_clear(T2)
|
|
67
|
+
mpz_clear(t3)
|
|
68
|
+
mpz_clear(t4)
|
|
69
|
+
mpz_clear(t5)
|
|
70
|
+
mpz_clear(t6)
|
|
71
|
+
mpz_clear(t7)
|
|
72
|
+
mpz_clear(t8)
|
|
73
|
+
|
|
74
|
+
mpz_clear(s1)
|
|
75
|
+
mpz_clear(s2)
|
|
76
|
+
|
|
77
|
+
mpz_clear(U1)
|
|
78
|
+
mpz_clear(U2)
|
|
79
|
+
|
|
80
|
+
fmpz_poly_clear(fT1)
|
|
81
|
+
fmpz_poly_clear(fT2)
|
|
82
|
+
fmpz_poly_clear(ft3)
|
|
83
|
+
fmpz_poly_clear(ft4)
|
|
84
|
+
fmpz_poly_clear(ft5)
|
|
85
|
+
fmpz_poly_clear(ft6)
|
|
86
|
+
fmpz_poly_clear(ft7)
|
|
87
|
+
fmpz_poly_clear(ft8)
|
|
88
|
+
|
|
89
|
+
fmpz_poly_clear(fs1)
|
|
90
|
+
fmpz_poly_clear(fs2)
|
|
91
|
+
|
|
92
|
+
fmpz_poly_clear(fU1)
|
|
93
|
+
fmpz_poly_clear(fU2)
|
|
94
|
+
|
|
95
|
+
cdef _init_globals():
|
|
96
|
+
"""
|
|
97
|
+
Initialize all global variables allocated for optimization of
|
|
98
|
+
quaternion algebra arithmetic, and register a hook to eventually
|
|
99
|
+
clear them.
|
|
100
|
+
|
|
101
|
+
Do *not* call this yourself.
|
|
102
|
+
"""
|
|
103
|
+
# over QQ
|
|
104
|
+
mpz_init(T1)
|
|
105
|
+
mpz_init(T2)
|
|
106
|
+
mpz_init(t3)
|
|
107
|
+
mpz_init(t4)
|
|
108
|
+
mpz_init(t5)
|
|
109
|
+
mpz_init(t6)
|
|
110
|
+
mpz_init(t7)
|
|
111
|
+
mpz_init(t8)
|
|
112
|
+
|
|
113
|
+
mpz_init(s1)
|
|
114
|
+
mpz_init(s2)
|
|
115
|
+
|
|
116
|
+
mpz_init(U1)
|
|
117
|
+
mpz_init(U2)
|
|
118
|
+
|
|
119
|
+
# Number fields
|
|
120
|
+
fmpz_poly_init(fT1)
|
|
121
|
+
fmpz_poly_init(fT2)
|
|
122
|
+
fmpz_poly_init(ft3)
|
|
123
|
+
fmpz_poly_init(ft4)
|
|
124
|
+
fmpz_poly_init(ft5)
|
|
125
|
+
fmpz_poly_init(ft6)
|
|
126
|
+
fmpz_poly_init(ft7)
|
|
127
|
+
fmpz_poly_init(ft8)
|
|
128
|
+
|
|
129
|
+
fmpz_poly_init(fs1)
|
|
130
|
+
fmpz_poly_init(fs2)
|
|
131
|
+
|
|
132
|
+
fmpz_poly_init(fU1)
|
|
133
|
+
fmpz_poly_init(fU2)
|
|
134
|
+
|
|
135
|
+
# ...and clear them when sage terminates.
|
|
136
|
+
import atexit
|
|
137
|
+
atexit.register(_clear_globals)
|
|
138
|
+
|
|
139
|
+
# Initialize module-scope global C variables.
|
|
140
|
+
_init_globals()
|
|
141
|
+
|
|
142
|
+
cdef to_quaternion(R, x):
|
|
143
|
+
"""
|
|
144
|
+
Internal function used implicitly by quaternion algebra creation.
|
|
145
|
+
|
|
146
|
+
INPUT:
|
|
147
|
+
|
|
148
|
+
- ``R`` -- callable
|
|
149
|
+
- ``x`` -- element or 4-tuple
|
|
150
|
+
|
|
151
|
+
Given a callable R and an x that defines a quaternion, which can be a
|
|
152
|
+
4-tuple, list of length 4, or something that coerces to R, return
|
|
153
|
+
4-tuple of elements of R.
|
|
154
|
+
|
|
155
|
+
EXAMPLES::
|
|
156
|
+
|
|
157
|
+
sage: Q.<i,j,kkkk> = QuaternionAlgebra(QQ,-7, 13)
|
|
158
|
+
sage: kkkk._repr_() # implicit doctest
|
|
159
|
+
'kkkk'
|
|
160
|
+
"""
|
|
161
|
+
if isinstance(x, (list, tuple)):
|
|
162
|
+
return R(x[0]), R(x[1]), R(x[2]), R(x[3])
|
|
163
|
+
else:
|
|
164
|
+
return R(x), R(0), R(0), R(0)
|
|
165
|
+
|
|
166
|
+
cdef inline print_coeff(y, i, bint atomic):
|
|
167
|
+
"""
|
|
168
|
+
Internal function used implicitly by all quaternion algebra printing.
|
|
169
|
+
|
|
170
|
+
INPUT:
|
|
171
|
+
|
|
172
|
+
- ``y`` -- coefficient
|
|
173
|
+
- ``i`` -- string (name of a generator)
|
|
174
|
+
- ``atomic`` -- boolean int; whether or not elements of base ring
|
|
175
|
+
print atomically
|
|
176
|
+
|
|
177
|
+
EXAMPLES::
|
|
178
|
+
|
|
179
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(QQ,-7, 13)
|
|
180
|
+
sage: i._repr_() # implicit doctest
|
|
181
|
+
'i'
|
|
182
|
+
"""
|
|
183
|
+
if not y:
|
|
184
|
+
return ''
|
|
185
|
+
if y == 1:
|
|
186
|
+
return i
|
|
187
|
+
elif y == -1:
|
|
188
|
+
return "-%s" % i
|
|
189
|
+
y = str(y)
|
|
190
|
+
if not atomic and ('+' in y or '-' in y):
|
|
191
|
+
return '(%s)*%s' % (y, i)
|
|
192
|
+
else:
|
|
193
|
+
return '%s*%s' % (y, i)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
cdef class QuaternionAlgebraElement_abstract(AlgebraElement):
|
|
197
|
+
def __hash__(self):
|
|
198
|
+
r"""
|
|
199
|
+
TESTS::
|
|
200
|
+
|
|
201
|
+
sage: from itertools import product
|
|
202
|
+
sage: for K in [QQ, QuadraticField(2), AA, Frac(QQ['x'])]:
|
|
203
|
+
....: Q.<i,j,k> = QuaternionAlgebra(K,-5,-2)
|
|
204
|
+
....: assert hash(Q.one()) == hash(K.one())
|
|
205
|
+
....: assert hash(Q(2)) == hash(K(2))
|
|
206
|
+
....: elts = []
|
|
207
|
+
....: for (x,y,z,w) in product([K(0), K(1), K(2), K(-1)], repeat=4):
|
|
208
|
+
....: elts.append(x + y*i + z*j + w*k)
|
|
209
|
+
....: assert len(set(map(hash, elts))) == len(elts)
|
|
210
|
+
"""
|
|
211
|
+
cdef long h
|
|
212
|
+
h = hash(self[0])
|
|
213
|
+
x = self[1]
|
|
214
|
+
if x:
|
|
215
|
+
h = ((h+14152L)*13023L) ^ hash(x)
|
|
216
|
+
x = self[2]
|
|
217
|
+
if x:
|
|
218
|
+
h = ((h+33325L)*31321L) ^ hash(x)
|
|
219
|
+
x = self[3]
|
|
220
|
+
if x:
|
|
221
|
+
h = ((h+34125L)*51125L) ^ hash(x)
|
|
222
|
+
return h
|
|
223
|
+
|
|
224
|
+
cpdef bint is_constant(self) noexcept:
|
|
225
|
+
"""
|
|
226
|
+
Return ``True`` if this quaternion is constant, i.e., has no `i`, `j`,
|
|
227
|
+
or `k` term.
|
|
228
|
+
|
|
229
|
+
OUTPUT: boolean
|
|
230
|
+
|
|
231
|
+
EXAMPLES::
|
|
232
|
+
|
|
233
|
+
sage: A.<i,j,k> = QuaternionAlgebra(-1,-2)
|
|
234
|
+
sage: A(1).is_constant()
|
|
235
|
+
True
|
|
236
|
+
sage: A(1+i).is_constant()
|
|
237
|
+
False
|
|
238
|
+
sage: A(i).is_constant()
|
|
239
|
+
False
|
|
240
|
+
"""
|
|
241
|
+
return not (self[1] or self[2] or self[3])
|
|
242
|
+
|
|
243
|
+
def __int__(self):
|
|
244
|
+
"""
|
|
245
|
+
Try to coerce this quaternion to a Python int.
|
|
246
|
+
|
|
247
|
+
EXAMPLES::
|
|
248
|
+
|
|
249
|
+
sage: A.<i,j,k>=QuaternionAlgebra(-1,-2)
|
|
250
|
+
sage: int(A(-3))
|
|
251
|
+
-3
|
|
252
|
+
sage: int(A(-3/2))
|
|
253
|
+
-1
|
|
254
|
+
sage: int(-3 + i)
|
|
255
|
+
Traceback (most recent call last):
|
|
256
|
+
...
|
|
257
|
+
TypeError
|
|
258
|
+
"""
|
|
259
|
+
if self.is_constant():
|
|
260
|
+
return int(self[0])
|
|
261
|
+
raise TypeError
|
|
262
|
+
|
|
263
|
+
def __float__(self):
|
|
264
|
+
"""
|
|
265
|
+
Try to coerce this quaternion to a Python float.
|
|
266
|
+
|
|
267
|
+
EXAMPLES::
|
|
268
|
+
|
|
269
|
+
sage: A.<i,j,k>=QuaternionAlgebra(-1,-2)
|
|
270
|
+
sage: float(A(-3/2))
|
|
271
|
+
-1.5
|
|
272
|
+
sage: float(A(-3))
|
|
273
|
+
-3.0
|
|
274
|
+
sage: float(-3 + i)
|
|
275
|
+
Traceback (most recent call last):
|
|
276
|
+
...
|
|
277
|
+
TypeError
|
|
278
|
+
"""
|
|
279
|
+
if self.is_constant():
|
|
280
|
+
return float(self[0])
|
|
281
|
+
raise TypeError
|
|
282
|
+
|
|
283
|
+
def _integer_(self, ZZ=None):
|
|
284
|
+
"""
|
|
285
|
+
Try to coerce this quaternion to an Integer.
|
|
286
|
+
|
|
287
|
+
EXAMPLES::
|
|
288
|
+
|
|
289
|
+
sage: A.<i,j,k> = QuaternionAlgebra(-1,-2)
|
|
290
|
+
sage: Integer(A(-3)) # indirect doctest
|
|
291
|
+
-3
|
|
292
|
+
sage: type(Integer(A(-3)))
|
|
293
|
+
<class 'sage.rings.integer.Integer'>
|
|
294
|
+
sage: Integer(A(-3/2))
|
|
295
|
+
Traceback (most recent call last):
|
|
296
|
+
...
|
|
297
|
+
TypeError: no conversion of this rational to integer
|
|
298
|
+
sage: Integer(-3 + i)
|
|
299
|
+
Traceback (most recent call last):
|
|
300
|
+
...
|
|
301
|
+
TypeError
|
|
302
|
+
"""
|
|
303
|
+
if self.is_constant():
|
|
304
|
+
return Integer(self[0])
|
|
305
|
+
raise TypeError
|
|
306
|
+
|
|
307
|
+
def _rational_(self):
|
|
308
|
+
"""
|
|
309
|
+
Try to coerce this quaternion to a Rational.
|
|
310
|
+
|
|
311
|
+
EXAMPLES::
|
|
312
|
+
|
|
313
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(Frac(QQ['x']),-5,-2)
|
|
314
|
+
sage: Rational(Q(2/3)) # indirect doctest
|
|
315
|
+
2/3
|
|
316
|
+
sage: Rational(2/3 + i)
|
|
317
|
+
Traceback (most recent call last):
|
|
318
|
+
...
|
|
319
|
+
TypeError
|
|
320
|
+
"""
|
|
321
|
+
if self.is_constant():
|
|
322
|
+
return Rational(self[0])
|
|
323
|
+
raise TypeError
|
|
324
|
+
|
|
325
|
+
def __bool__(self):
|
|
326
|
+
"""
|
|
327
|
+
Return ``True`` if this quaternion is nonzero.
|
|
328
|
+
|
|
329
|
+
EXAMPLES::
|
|
330
|
+
|
|
331
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(Frac(QQ['x']),-5,-2)
|
|
332
|
+
sage: bool(i+j)
|
|
333
|
+
True
|
|
334
|
+
sage: bool(Q(0))
|
|
335
|
+
False
|
|
336
|
+
"""
|
|
337
|
+
return self[0] or self[1] or self[2] or self[3]
|
|
338
|
+
|
|
339
|
+
cdef _do_print(self, x, y, z, w):
|
|
340
|
+
"""
|
|
341
|
+
Used internally by the print function.
|
|
342
|
+
|
|
343
|
+
EXAMPLES::
|
|
344
|
+
|
|
345
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(-17,-19)
|
|
346
|
+
sage: str(i+j+k-3/4) # indirect doctest
|
|
347
|
+
'-3/4 + i + j + k'
|
|
348
|
+
"""
|
|
349
|
+
cdef bint atomic = self._parent._base._repr_option('element_is_atomic')
|
|
350
|
+
v = []
|
|
351
|
+
i, j, k = self._parent.variable_names()
|
|
352
|
+
if x:
|
|
353
|
+
v.append(str(x))
|
|
354
|
+
c = print_coeff(y, i, atomic)
|
|
355
|
+
if c:
|
|
356
|
+
v.append(c)
|
|
357
|
+
c = print_coeff(z, j, atomic)
|
|
358
|
+
if c:
|
|
359
|
+
v.append(c)
|
|
360
|
+
c = print_coeff(w, k, atomic)
|
|
361
|
+
if c:
|
|
362
|
+
v.append(c)
|
|
363
|
+
if not v:
|
|
364
|
+
return '0'
|
|
365
|
+
return ' + '.join(v).replace('+ -', '- ')
|
|
366
|
+
|
|
367
|
+
def _repr_(self):
|
|
368
|
+
"""
|
|
369
|
+
Return string representation of this quaternion.
|
|
370
|
+
|
|
371
|
+
EXAMPLES::
|
|
372
|
+
|
|
373
|
+
sage: R.<x> = Frac(QQ['x'])
|
|
374
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(R,-5*x,-2)
|
|
375
|
+
sage: a = x + i*x^3 + j*x^2 + k*x
|
|
376
|
+
sage: a._repr_()
|
|
377
|
+
'x + x^3*i + x^2*j + x*k'
|
|
378
|
+
sage: a = x+2/3 + i*x^3 + j*(x^2-5/2) + k*x
|
|
379
|
+
sage: a._repr_()
|
|
380
|
+
'x + 2/3 + x^3*i + (x^2 - 5/2)*j + x*k'
|
|
381
|
+
sage: type(a)
|
|
382
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'>
|
|
383
|
+
sage: Q(0)._repr_()
|
|
384
|
+
'0'
|
|
385
|
+
"""
|
|
386
|
+
return self._do_print(self[0], self[1], self[2], self[3])
|
|
387
|
+
|
|
388
|
+
cpdef _richcmp_(self, right, int op):
|
|
389
|
+
"""
|
|
390
|
+
Comparing elements.
|
|
391
|
+
|
|
392
|
+
TESTS::
|
|
393
|
+
|
|
394
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(Frac(QQ['x']),-5,-2)
|
|
395
|
+
sage: a = 1/2 + 2/3*i - 3/4*j + 5/7*k
|
|
396
|
+
sage: a == a
|
|
397
|
+
True
|
|
398
|
+
sage: a == a + 1
|
|
399
|
+
False
|
|
400
|
+
sage: a < a + 1
|
|
401
|
+
True
|
|
402
|
+
|
|
403
|
+
sage: K.<x> = QQ[]; Q.<i,j,k> = QuaternionAlgebra(x, 2*x); a = x + 2*x*i + 3*j + (x-2)*k
|
|
404
|
+
sage: (1/a)*a == 1
|
|
405
|
+
True
|
|
406
|
+
sage: (1/a)*a
|
|
407
|
+
1
|
|
408
|
+
"""
|
|
409
|
+
cdef int i
|
|
410
|
+
for i in range(4):
|
|
411
|
+
res = richcmp_item(self[i], right[i], op)
|
|
412
|
+
if res is not NotImplemented:
|
|
413
|
+
return res
|
|
414
|
+
return rich_to_bool(op, 0)
|
|
415
|
+
|
|
416
|
+
cpdef conjugate(self):
|
|
417
|
+
"""
|
|
418
|
+
Return the conjugate of the quaternion: if `\\theta = x + yi + zj + wk`,
|
|
419
|
+
return `x - yi - zj - wk`; that is, return theta.reduced_trace() - theta.
|
|
420
|
+
|
|
421
|
+
EXAMPLES::
|
|
422
|
+
|
|
423
|
+
sage: A.<i,j,k> = QuaternionAlgebra(QQ,-5,-2)
|
|
424
|
+
sage: a = 3*i - j + 2
|
|
425
|
+
sage: type(a)
|
|
426
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_rational_field'>
|
|
427
|
+
sage: a.conjugate()
|
|
428
|
+
2 - 3*i + j
|
|
429
|
+
|
|
430
|
+
The "universal" test::
|
|
431
|
+
|
|
432
|
+
sage: K.<x,y,z,w,a,b> = QQ[]
|
|
433
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(a,b)
|
|
434
|
+
sage: theta = x+y*i+z*j+w*k
|
|
435
|
+
sage: theta.conjugate()
|
|
436
|
+
x + (-y)*i + (-z)*j + (-w)*k
|
|
437
|
+
"""
|
|
438
|
+
return self.__class__(self._parent, (self[0], -self[1], -self[2], -self[3]), check=False)
|
|
439
|
+
|
|
440
|
+
cpdef reduced_trace(self):
|
|
441
|
+
"""
|
|
442
|
+
Return the reduced trace of self: if `\\theta = x + yi + zj +
|
|
443
|
+
wk`, then `\\theta` has reduced trace `2x`.
|
|
444
|
+
|
|
445
|
+
EXAMPLES::
|
|
446
|
+
|
|
447
|
+
sage: K.<x,y,z,w,a,b> = QQ[]
|
|
448
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(a,b)
|
|
449
|
+
sage: theta = x+y*i+z*j+w*k
|
|
450
|
+
sage: theta.reduced_trace()
|
|
451
|
+
2*x
|
|
452
|
+
"""
|
|
453
|
+
return 2*self[0]
|
|
454
|
+
|
|
455
|
+
cpdef reduced_norm(self):
|
|
456
|
+
"""
|
|
457
|
+
Return the reduced norm of self: if `\\theta = x + yi + zj +
|
|
458
|
+
wk`, then `\\theta` has reduced norm `x^2 - ay^2 - bz^2 +
|
|
459
|
+
abw^2`.
|
|
460
|
+
|
|
461
|
+
EXAMPLES::
|
|
462
|
+
|
|
463
|
+
sage: K.<x,y,z,w,a,b> = QQ[]
|
|
464
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(a,b)
|
|
465
|
+
sage: theta = x+y*i+z*j+w*k
|
|
466
|
+
sage: theta.reduced_norm()
|
|
467
|
+
w^2*a*b - y^2*a - z^2*b + x^2
|
|
468
|
+
"""
|
|
469
|
+
a, b = self._parent._a, self._parent._b
|
|
470
|
+
x, y, z, w = self[0], self[1], self[2], self[3]
|
|
471
|
+
return w*w*a*b - y*y*a - z*z*b + x*x
|
|
472
|
+
|
|
473
|
+
def __invert__(self):
|
|
474
|
+
"""
|
|
475
|
+
Return the inverse of ``self``.
|
|
476
|
+
|
|
477
|
+
EXAMPLES::
|
|
478
|
+
|
|
479
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(QQ,-7,-13)
|
|
480
|
+
sage: theta = 1/3 - 2/3*i + 4/19*j - 17/3*k
|
|
481
|
+
sage: (1/theta) * theta
|
|
482
|
+
1
|
|
483
|
+
sage: type(theta)
|
|
484
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_rational_field'>
|
|
485
|
+
sage: 1/Q(0)
|
|
486
|
+
Traceback (most recent call last):
|
|
487
|
+
...
|
|
488
|
+
ZeroDivisionError: rational division by zero
|
|
489
|
+
|
|
490
|
+
Note that the quaternion algebra need not be a division
|
|
491
|
+
algebra, in which case we can get a ZeroDivisionException::
|
|
492
|
+
|
|
493
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(QQ,4,9)
|
|
494
|
+
sage: theta = 2-i
|
|
495
|
+
sage: theta.reduced_norm()
|
|
496
|
+
0
|
|
497
|
+
sage: 1/theta
|
|
498
|
+
Traceback (most recent call last):
|
|
499
|
+
...
|
|
500
|
+
ZeroDivisionError: rational division by zero
|
|
501
|
+
|
|
502
|
+
The ``universal`` test:
|
|
503
|
+
|
|
504
|
+
sage: K.<x,y,z,w,a,b> = QQ[]
|
|
505
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(a,b)
|
|
506
|
+
sage: theta = x+y*i+z*j+w*k
|
|
507
|
+
sage: 1/theta == theta.conjugate()/theta.reduced_norm()
|
|
508
|
+
True
|
|
509
|
+
"""
|
|
510
|
+
return ~self.reduced_norm() * self.conjugate()
|
|
511
|
+
|
|
512
|
+
cpdef _rmul_(self, Element left):
|
|
513
|
+
"""
|
|
514
|
+
Return left*self, where left is in the base ring.
|
|
515
|
+
|
|
516
|
+
EXAMPLES::
|
|
517
|
+
|
|
518
|
+
sage: x = polygen(QQ, 'x')
|
|
519
|
+
sage: K.<a> = NumberField(x^2-x-1); Q.<i,j,k> = QuaternionAlgebra(K,-1,-1); z=2*i+3*j+4/3*k+5/8
|
|
520
|
+
sage: a*z
|
|
521
|
+
5/8*a + 2*a*i + 3*a*j + 4/3*a*k
|
|
522
|
+
sage: type(z)
|
|
523
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'>
|
|
524
|
+
"""
|
|
525
|
+
return self.__class__(self._parent, (left*self[0], left*self[1], left*self[2], left*self[3]), check=False)
|
|
526
|
+
|
|
527
|
+
cpdef _lmul_(self, Element right):
|
|
528
|
+
"""
|
|
529
|
+
Return self*right, where right is in the base ring.
|
|
530
|
+
|
|
531
|
+
EXAMPLES::
|
|
532
|
+
|
|
533
|
+
sage: x = polygen(QQ, 'x')
|
|
534
|
+
sage: K.<a> = NumberField(x^2-x-1); Q.<i,j,k> = QuaternionAlgebra(K,-1,-1); z=2*i+3*j+4/3*k+5/8
|
|
535
|
+
sage: z*a
|
|
536
|
+
5/8*a + 2*a*i + 3*a*j + 4/3*a*k
|
|
537
|
+
sage: type(z)
|
|
538
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'>
|
|
539
|
+
"""
|
|
540
|
+
return self.__class__(self._parent, (self[0]*right, self[1]*right, self[2]*right, self[3]*right), check=False)
|
|
541
|
+
|
|
542
|
+
cpdef _div_(self, right):
|
|
543
|
+
"""
|
|
544
|
+
Return quotient of ``self`` by ``right``.
|
|
545
|
+
|
|
546
|
+
EXAMPLES::
|
|
547
|
+
|
|
548
|
+
sage: K.<x> = QQ[]; Q.<i,j,k> = QuaternionAlgebra(x, 2*x)
|
|
549
|
+
sage: theta = x + 2*x*i + 3*j + (x-2)*k
|
|
550
|
+
sage: type(theta)
|
|
551
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'>
|
|
552
|
+
sage: theta._div_(theta)
|
|
553
|
+
1
|
|
554
|
+
sage: theta._div_(theta) == 1
|
|
555
|
+
True
|
|
556
|
+
"""
|
|
557
|
+
return self * ~right
|
|
558
|
+
|
|
559
|
+
def reduced_characteristic_polynomial(self, var='x'):
|
|
560
|
+
"""
|
|
561
|
+
Return the reduced characteristic polynomial of this
|
|
562
|
+
quaternion algebra element, which is `X^2 - tX + n`, where `t`
|
|
563
|
+
is the reduced trace and `n` is the reduced norm.
|
|
564
|
+
|
|
565
|
+
INPUT:
|
|
566
|
+
|
|
567
|
+
- ``var`` -- string (default: ``'x'``); indeterminate of characteristic
|
|
568
|
+
polynomial
|
|
569
|
+
|
|
570
|
+
EXAMPLES::
|
|
571
|
+
|
|
572
|
+
sage: A.<i,j,k>=QuaternionAlgebra(-1,-2)
|
|
573
|
+
sage: i.reduced_characteristic_polynomial()
|
|
574
|
+
x^2 + 1
|
|
575
|
+
sage: j.reduced_characteristic_polynomial()
|
|
576
|
+
x^2 + 2
|
|
577
|
+
sage: (i+j).reduced_characteristic_polynomial()
|
|
578
|
+
x^2 + 3
|
|
579
|
+
sage: (2+j+k).reduced_trace()
|
|
580
|
+
4
|
|
581
|
+
sage: (2+j+k).reduced_characteristic_polynomial('T')
|
|
582
|
+
T^2 - 4*T + 8
|
|
583
|
+
"""
|
|
584
|
+
R = PolynomialRing(self.base_ring(), var)
|
|
585
|
+
return R([self.reduced_norm(), -self.reduced_trace(), 1])
|
|
586
|
+
|
|
587
|
+
def matrix(self, action='right'):
|
|
588
|
+
"""
|
|
589
|
+
Return the matrix of right or left multiplication of ``self`` on
|
|
590
|
+
the basis for the ambient quaternion algebra.
|
|
591
|
+
|
|
592
|
+
In particular, if action is ``'right'`` (the default), returns the
|
|
593
|
+
matrix of the mapping sending ``x`` to ``x*self``.
|
|
594
|
+
|
|
595
|
+
INPUT:
|
|
596
|
+
|
|
597
|
+
- ``action`` -- (default: ``'right'``) ``'right'`` or ``'left'``
|
|
598
|
+
|
|
599
|
+
OUTPUT: a matrix
|
|
600
|
+
|
|
601
|
+
EXAMPLES::
|
|
602
|
+
|
|
603
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(-3,-19)
|
|
604
|
+
sage: a = 2/3 -1/2*i + 3/5*j - 4/3*k
|
|
605
|
+
sage: a.matrix()
|
|
606
|
+
[ 2/3 -1/2 3/5 -4/3]
|
|
607
|
+
[ 3/2 2/3 4 3/5]
|
|
608
|
+
[-57/5 -76/3 2/3 1/2]
|
|
609
|
+
[ 76 -57/5 -3/2 2/3]
|
|
610
|
+
sage: a.matrix() == a.matrix(action='right')
|
|
611
|
+
True
|
|
612
|
+
sage: a.matrix(action='left')
|
|
613
|
+
[ 2/3 -1/2 3/5 -4/3]
|
|
614
|
+
[ 3/2 2/3 -4 -3/5]
|
|
615
|
+
[-57/5 76/3 2/3 -1/2]
|
|
616
|
+
[ 76 57/5 3/2 2/3]
|
|
617
|
+
sage: (i*a,j*a,k*a)
|
|
618
|
+
(3/2 + 2/3*i + 4*j + 3/5*k, -57/5 - 76/3*i + 2/3*j + 1/2*k, 76 - 57/5*i - 3/2*j + 2/3*k)
|
|
619
|
+
sage: a.matrix(action='foo')
|
|
620
|
+
Traceback (most recent call last):
|
|
621
|
+
...
|
|
622
|
+
ValueError: action must be either 'left' or 'right'
|
|
623
|
+
|
|
624
|
+
We test over a more generic base field::
|
|
625
|
+
|
|
626
|
+
sage: K.<x> = QQ['x']
|
|
627
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(Frac(K),-5,-2)
|
|
628
|
+
sage: a = 1/2*x^2 + 2/3*x*i - 3/4*j + 5/7*k
|
|
629
|
+
sage: type(a)
|
|
630
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'>
|
|
631
|
+
sage: a.matrix()
|
|
632
|
+
[1/2*x^2 2/3*x -3/4 5/7]
|
|
633
|
+
[-10/3*x 1/2*x^2 -25/7 -3/4]
|
|
634
|
+
[ 3/2 10/7 1/2*x^2 -2/3*x]
|
|
635
|
+
[ -50/7 3/2 10/3*x 1/2*x^2]
|
|
636
|
+
"""
|
|
637
|
+
if action == 'right':
|
|
638
|
+
v = [(a*self).coefficient_tuple() for a in self._parent.basis()]
|
|
639
|
+
elif action == 'left':
|
|
640
|
+
v = [(self*a).coefficient_tuple() for a in self._parent.basis()]
|
|
641
|
+
else:
|
|
642
|
+
raise ValueError("action must be either 'left' or 'right'")
|
|
643
|
+
return matrix(self.base_ring(), 4, v)
|
|
644
|
+
|
|
645
|
+
def coefficient_tuple(self):
|
|
646
|
+
"""
|
|
647
|
+
Return 4-tuple of coefficients of this quaternion.
|
|
648
|
+
|
|
649
|
+
EXAMPLES::
|
|
650
|
+
|
|
651
|
+
sage: K.<x> = QQ['x']
|
|
652
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(Frac(K),-5,-2)
|
|
653
|
+
sage: a = 1/2*x^2 + 2/3*x*i - 3/4*j + 5/7*k
|
|
654
|
+
sage: type(a)
|
|
655
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'>
|
|
656
|
+
sage: a.coefficient_tuple()
|
|
657
|
+
(1/2*x^2, 2/3*x, -3/4, 5/7)
|
|
658
|
+
"""
|
|
659
|
+
return (self[0], self[1], self[2], self[3])
|
|
660
|
+
|
|
661
|
+
def pair(self, right):
|
|
662
|
+
"""
|
|
663
|
+
Return the result of pairing ``self`` and ``right``, which should both
|
|
664
|
+
be elements of a quaternion algebra. The pairing is
|
|
665
|
+
``(x,y) = (x.conjugate()*y).reduced_trace()``.
|
|
666
|
+
|
|
667
|
+
INPUT:
|
|
668
|
+
|
|
669
|
+
- ``right`` -- quaternion
|
|
670
|
+
|
|
671
|
+
EXAMPLES::
|
|
672
|
+
|
|
673
|
+
sage: A.<i,j,k>=QuaternionAlgebra(-1,-2)
|
|
674
|
+
sage: (1+i+j-2*k).pair(2/3+5*i-3*j+k)
|
|
675
|
+
-26/3
|
|
676
|
+
sage: x = 1+i+j-2*k; y = 2/3+5*i-3*j+k
|
|
677
|
+
sage: x.pair(y)
|
|
678
|
+
-26/3
|
|
679
|
+
sage: y.pair(x)
|
|
680
|
+
-26/3
|
|
681
|
+
sage: (x.conjugate()*y).reduced_trace()
|
|
682
|
+
-26/3
|
|
683
|
+
"""
|
|
684
|
+
return (self.conjugate() * right).reduced_trace()
|
|
685
|
+
|
|
686
|
+
def _im_gens_(self, codomain, im_gens, base_map):
|
|
687
|
+
r"""
|
|
688
|
+
Return the image of this quaternion under the morphism
|
|
689
|
+
defined by ``im_gens`` in ``codomain``, where elements
|
|
690
|
+
of the base ring are mapped by ``base_map``.
|
|
691
|
+
|
|
692
|
+
EXAMPLES::
|
|
693
|
+
|
|
694
|
+
sage: Quat.<i,j,k> = QuaternionAlgebra(-1, -19)
|
|
695
|
+
sage: b = 5 + 6*i + 7*j + 8*k
|
|
696
|
+
sage: ims = [~b * g * b for g in Quat.gens()]
|
|
697
|
+
sage: Quat(1)._im_gens_(Quat, ims, None) == 1
|
|
698
|
+
True
|
|
699
|
+
sage: i._im_gens_(Quat, ims, None) == ims[0]
|
|
700
|
+
True
|
|
701
|
+
sage: j._im_gens_(Quat, ims, None) == ims[1]
|
|
702
|
+
True
|
|
703
|
+
sage: k._im_gens_(Quat, ims, None) == ims[2]
|
|
704
|
+
True
|
|
705
|
+
"""
|
|
706
|
+
if base_map is None:
|
|
707
|
+
base_map = lambda v: v
|
|
708
|
+
return sum(base_map(c) * g for c, g in zip(self, [1] + list(im_gens)))
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
cdef class QuaternionAlgebraElement_generic(QuaternionAlgebraElement_abstract):
|
|
712
|
+
"""
|
|
713
|
+
TESTS:
|
|
714
|
+
|
|
715
|
+
Test operations on quaternions over a base ring that is not a field::
|
|
716
|
+
|
|
717
|
+
sage: A.<t> = LaurentPolynomialRing(GF(3))
|
|
718
|
+
sage: B = QuaternionAlgebra(A, -1, t)
|
|
719
|
+
sage: i, j, k = B.gens()
|
|
720
|
+
sage: i*j
|
|
721
|
+
k
|
|
722
|
+
sage: (j + k).reduced_norm()
|
|
723
|
+
t
|
|
724
|
+
|
|
725
|
+
Inverting an element is currently only possible if its reduced
|
|
726
|
+
norm is a unit::
|
|
727
|
+
|
|
728
|
+
sage: ~k
|
|
729
|
+
(t^-1)*k
|
|
730
|
+
sage: ~(i + j)
|
|
731
|
+
Traceback (most recent call last):
|
|
732
|
+
...
|
|
733
|
+
TypeError: unsupported operand parent(s) for *: 'Fraction Field of Univariate Polynomial Ring in t over Finite Field of size 3' and 'Quaternion Algebra (2, t) with base ring Univariate Laurent Polynomial Ring in t over Finite Field of size 3'
|
|
734
|
+
|
|
735
|
+
We test pickling::
|
|
736
|
+
|
|
737
|
+
sage: R.<x> = Frac(QQ['x']); Q.<i,j,k> = QuaternionAlgebra(R,-5*x,-2)
|
|
738
|
+
sage: theta = x + i*x^3 + j*x^2 + k*x
|
|
739
|
+
sage: theta == loads(dumps(theta))
|
|
740
|
+
True
|
|
741
|
+
"""
|
|
742
|
+
def __init__(self, parent, v, bint check=True):
|
|
743
|
+
"""
|
|
744
|
+
Create a quaternion over a general base ring.
|
|
745
|
+
|
|
746
|
+
EXAMPLES::
|
|
747
|
+
|
|
748
|
+
sage: K.<x> = Frac(QQ['x']); Q.<i,j,k> = QuaternionAlgebra(K,-5,-2)
|
|
749
|
+
sage: sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic(Q, (x,1,-7,2/3*x^3))
|
|
750
|
+
x + i + (-7)*j + 2/3*x^3*k
|
|
751
|
+
"""
|
|
752
|
+
self._parent = parent
|
|
753
|
+
if check:
|
|
754
|
+
self.x, self.y, self.z, self.w = to_quaternion(parent._base, v)
|
|
755
|
+
else:
|
|
756
|
+
self.x, self.y, self.z, self.w = v
|
|
757
|
+
|
|
758
|
+
def __getitem__(self, int i):
|
|
759
|
+
"""
|
|
760
|
+
EXAMPLES::
|
|
761
|
+
|
|
762
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(Frac(QQ['x']),-5,-2)
|
|
763
|
+
sage: theta = 1/2 + 2/3*i - 3/4*j + 5/7*k
|
|
764
|
+
sage: type(theta)
|
|
765
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'>
|
|
766
|
+
sage: list(theta)
|
|
767
|
+
[1/2, 2/3, -3/4, 5/7]
|
|
768
|
+
"""
|
|
769
|
+
if i == 0:
|
|
770
|
+
return self.x
|
|
771
|
+
elif i == 1:
|
|
772
|
+
return self.y
|
|
773
|
+
elif i == 2:
|
|
774
|
+
return self.z
|
|
775
|
+
elif i == 3:
|
|
776
|
+
return self.w
|
|
777
|
+
else:
|
|
778
|
+
raise IndexError("quaternion element index out of range")
|
|
779
|
+
|
|
780
|
+
def __reduce__(self):
|
|
781
|
+
"""
|
|
782
|
+
Used for pickling.
|
|
783
|
+
|
|
784
|
+
TESTS::
|
|
785
|
+
|
|
786
|
+
sage: K.<x> = Frac(QQ['x']); Q.<i,j,k> = QuaternionAlgebra(K,-5,-2)
|
|
787
|
+
sage: theta = 1/x + x*i - (x+1)*j + 2/(3*x^3+5)*k
|
|
788
|
+
sage: loads(dumps(theta)) == theta
|
|
789
|
+
True
|
|
790
|
+
sage: type(theta)
|
|
791
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'>
|
|
792
|
+
"""
|
|
793
|
+
return (unpickle_QuaternionAlgebraElement_generic_v0,
|
|
794
|
+
(self._parent, (self.x, self.y, self.z, self.w)))
|
|
795
|
+
|
|
796
|
+
cpdef _add_(self, _right):
|
|
797
|
+
"""
|
|
798
|
+
Return the sum of ``self`` and ``_right``.
|
|
799
|
+
|
|
800
|
+
EXAMPLES::
|
|
801
|
+
|
|
802
|
+
sage: K.<x> = Frac(QQ['x']); Q.<i,j,k> = QuaternionAlgebra(K,-5,-2)
|
|
803
|
+
sage: (x+i+j+x^3*k) + (x-i-j+ (2/3*x^3+x)*k) # indirect doctest
|
|
804
|
+
2*x + (5/3*x^3 + x)*k
|
|
805
|
+
sage: type(i)
|
|
806
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'>
|
|
807
|
+
"""
|
|
808
|
+
cdef QuaternionAlgebraElement_generic right = _right
|
|
809
|
+
# TODO -- make this, etc. use __new__
|
|
810
|
+
return QuaternionAlgebraElement_generic(self._parent, (self.x + right.x, self.y + right.y, self.z + right.z, self.w + right.w), check=False)
|
|
811
|
+
|
|
812
|
+
cpdef _sub_(self, _right):
|
|
813
|
+
"""
|
|
814
|
+
Return the difference of ``self`` and ``_right``.
|
|
815
|
+
|
|
816
|
+
EXAMPLES::
|
|
817
|
+
|
|
818
|
+
sage: K.<x> = Frac(QQ['x']); Q.<i,j,k> = QuaternionAlgebra(K,-5,-2)
|
|
819
|
+
sage: type(i)
|
|
820
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'>
|
|
821
|
+
sage: (x+i+j+x^3*k)._sub_(x-i-j+ (2/3*x^3+x)*k)
|
|
822
|
+
2*i + 2*j + (1/3*x^3 - x)*k
|
|
823
|
+
"""
|
|
824
|
+
cdef QuaternionAlgebraElement_generic right = _right
|
|
825
|
+
return QuaternionAlgebraElement_generic(self._parent, (self.x - right.x, self.y - right.y, self.z - right.z, self.w - right.w), check=False)
|
|
826
|
+
|
|
827
|
+
cpdef _mul_(self, _right):
|
|
828
|
+
"""
|
|
829
|
+
Return the product of ``self`` and ``_right``.
|
|
830
|
+
|
|
831
|
+
EXAMPLES::
|
|
832
|
+
|
|
833
|
+
sage: K.<x> = Frac(QQ['x']); Q.<i,j,k> = QuaternionAlgebra(K,-5,-2)
|
|
834
|
+
sage: type(i)
|
|
835
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'>
|
|
836
|
+
sage: (x+i+j+x^3*k)._mul_(x-i-j+ (2/3*x^3+x)*k)
|
|
837
|
+
-20/3*x^6 - 10*x^4 + x^2 + 7 + (10/3*x^3 + 2*x)*i + (-25/3*x^3 - 5*x)*j + (5/3*x^4 + x^2)*k
|
|
838
|
+
"""
|
|
839
|
+
cdef QuaternionAlgebraElement_generic right = _right
|
|
840
|
+
|
|
841
|
+
a = self._parent._a
|
|
842
|
+
b = self._parent._b
|
|
843
|
+
|
|
844
|
+
x1, y1, z1, w1 = self.x, self.y, self.z, self.w
|
|
845
|
+
x2, y2, z2, w2 = right.x, right.y, right.z, right.w
|
|
846
|
+
|
|
847
|
+
# x = x1*x2 + y1*y2*a + z1*z2*b - w1*w2*a*b
|
|
848
|
+
# y = x1*y2 + y1*x2 - z1*w2*b + w1*z2*b
|
|
849
|
+
# z = x1*z2 + y1*w2 + z1*x2 - w1*y2*a
|
|
850
|
+
# w = x1*w2 + y1*z2 - z1*y2 + w1*x2
|
|
851
|
+
|
|
852
|
+
t1 = x1 * x2
|
|
853
|
+
t2 = y1 * y2
|
|
854
|
+
t3 = z1 * z2
|
|
855
|
+
t4 = w1 * w2
|
|
856
|
+
t5 = x2 * z1
|
|
857
|
+
t6 = y2 * w1
|
|
858
|
+
t7 = x1 * z2
|
|
859
|
+
t8 = y1 * w2
|
|
860
|
+
|
|
861
|
+
x = t1 + a * t2 + b * (t3 - a*t4)
|
|
862
|
+
y = (x1 + y1)*(x2 + y2) - t1 - t2 + b*((z1 + w1)*(z2 - w2) - t3 + t4)
|
|
863
|
+
z = t5 - a*t6 + t7 + a*t8
|
|
864
|
+
w = (x2 - y2)*(z1 + w1) - t5 + t6 + (x1 + y1)*(z2 + w2) - t7 - t8
|
|
865
|
+
|
|
866
|
+
return QuaternionAlgebraElement_generic(self._parent, (x, y, z, w), check=False)
|
|
867
|
+
|
|
868
|
+
def _repr_(self):
|
|
869
|
+
"""
|
|
870
|
+
Print representation of ``self``.
|
|
871
|
+
|
|
872
|
+
EXAMPLES::
|
|
873
|
+
|
|
874
|
+
sage: K.<x> = Frac(QQ['x']); Q.<i,j,k> = QuaternionAlgebra(K,-5,-2)
|
|
875
|
+
sage: theta = 1/x + x*i - (x+1)*j + 2/(3*x^3+5)*k
|
|
876
|
+
sage: type(theta)
|
|
877
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'>
|
|
878
|
+
sage: theta._repr_()
|
|
879
|
+
'1/x + x*i + (-x - 1)*j + (2/3/(x^3 + 5/3))*k'
|
|
880
|
+
"""
|
|
881
|
+
return self._do_print(self.x, self.y, self.z, self.w)
|
|
882
|
+
|
|
883
|
+
|
|
884
|
+
cdef class QuaternionAlgebraElement_rational_field(QuaternionAlgebraElement_abstract):
|
|
885
|
+
"""
|
|
886
|
+
TESTS:
|
|
887
|
+
|
|
888
|
+
We test pickling::
|
|
889
|
+
|
|
890
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(QQ,-5,-2)
|
|
891
|
+
sage: i + j + k == loads(dumps(i+j+k))
|
|
892
|
+
True
|
|
893
|
+
"""
|
|
894
|
+
# Implementation Notes:
|
|
895
|
+
#
|
|
896
|
+
# A Quaternion algebra element (call it a) over Q are implemented as a 4-tuple of
|
|
897
|
+
# integers x, y, z, w and a denominator d, all of type mpz_t, such that
|
|
898
|
+
#
|
|
899
|
+
# theta = (1/d) * (x + y * i + z * j + w * k)
|
|
900
|
+
#
|
|
901
|
+
# (although different letters may be specified instead of i, j, and k, if desired).
|
|
902
|
+
#
|
|
903
|
+
# Inside the element we also store mpz_t integers a and b, where
|
|
904
|
+
#
|
|
905
|
+
# i^2 = a and j^2 = b
|
|
906
|
+
|
|
907
|
+
def __cinit__(self):
|
|
908
|
+
"""
|
|
909
|
+
Initialize C variables.
|
|
910
|
+
|
|
911
|
+
EXAMPLES::
|
|
912
|
+
|
|
913
|
+
sage: QuaternionAlgebra(QQ,-5,-2)([1/2,-1/3,2/3,4/5]) # implicit doctest
|
|
914
|
+
1/2 - 1/3*i + 2/3*j + 4/5*k
|
|
915
|
+
"""
|
|
916
|
+
mpz_init(self.x)
|
|
917
|
+
mpz_init(self.y)
|
|
918
|
+
mpz_init(self.z)
|
|
919
|
+
mpz_init(self.w)
|
|
920
|
+
mpz_init(self.a)
|
|
921
|
+
mpz_init(self.b)
|
|
922
|
+
mpz_init(self.d)
|
|
923
|
+
|
|
924
|
+
def __dealloc__(self):
|
|
925
|
+
mpz_clear(self.x)
|
|
926
|
+
mpz_clear(self.y)
|
|
927
|
+
mpz_clear(self.z)
|
|
928
|
+
mpz_clear(self.w)
|
|
929
|
+
mpz_clear(self.a)
|
|
930
|
+
mpz_clear(self.b)
|
|
931
|
+
mpz_clear(self.d)
|
|
932
|
+
|
|
933
|
+
def _rational_(self):
|
|
934
|
+
"""
|
|
935
|
+
Try to coerce this quaternion to a Rational.
|
|
936
|
+
|
|
937
|
+
EXAMPLES::
|
|
938
|
+
|
|
939
|
+
sage: A.<i,j,k> = QuaternionAlgebra(-1,-2)
|
|
940
|
+
sage: Rational(A(-2/3)) # indirect doctest
|
|
941
|
+
-2/3
|
|
942
|
+
sage: Rational(i)
|
|
943
|
+
Traceback (most recent call last):
|
|
944
|
+
...
|
|
945
|
+
TypeError
|
|
946
|
+
"""
|
|
947
|
+
cdef Rational x = Rational()
|
|
948
|
+
if self.is_constant():
|
|
949
|
+
mpq_set_num(x.value, self.x)
|
|
950
|
+
mpq_set_den(x.value, self.d)
|
|
951
|
+
mpq_canonicalize(x.value)
|
|
952
|
+
return x
|
|
953
|
+
raise TypeError
|
|
954
|
+
|
|
955
|
+
cpdef bint is_constant(self) noexcept:
|
|
956
|
+
"""
|
|
957
|
+
Return ``True`` if this quaternion is constant, i.e., has no `i`, `j`,
|
|
958
|
+
or `k` term.
|
|
959
|
+
|
|
960
|
+
OUTPUT: boolean
|
|
961
|
+
|
|
962
|
+
EXAMPLES::
|
|
963
|
+
|
|
964
|
+
sage: A.<i,j,k>=QuaternionAlgebra(-1,-2)
|
|
965
|
+
sage: A(1/3).is_constant()
|
|
966
|
+
True
|
|
967
|
+
sage: A(-1).is_constant()
|
|
968
|
+
True
|
|
969
|
+
sage: (1+i).is_constant()
|
|
970
|
+
False
|
|
971
|
+
sage: j.is_constant()
|
|
972
|
+
False
|
|
973
|
+
"""
|
|
974
|
+
return not (mpz_sgn(self.y) or mpz_sgn(self.z) or mpz_sgn(self.w))
|
|
975
|
+
|
|
976
|
+
def __bool__(self):
|
|
977
|
+
"""
|
|
978
|
+
Return ``True`` if this quaternion is nonzero.
|
|
979
|
+
|
|
980
|
+
EXAMPLES::
|
|
981
|
+
|
|
982
|
+
sage: A.<i,j,k>=QuaternionAlgebra(-1,-2)
|
|
983
|
+
sage: bool(1+j+k)
|
|
984
|
+
True
|
|
985
|
+
sage: bool(A(0))
|
|
986
|
+
False
|
|
987
|
+
"""
|
|
988
|
+
return bool(mpz_sgn(self.x) or mpz_sgn(self.y) or mpz_sgn(self.z) or mpz_sgn(self.w))
|
|
989
|
+
|
|
990
|
+
cpdef _richcmp_(self, _right, int op):
|
|
991
|
+
"""
|
|
992
|
+
Compare two quaternions.
|
|
993
|
+
|
|
994
|
+
The comparison is fairly arbitrary
|
|
995
|
+
-- first the denominators are compared and if equal then each
|
|
996
|
+
of the other coefficients are compared.
|
|
997
|
+
|
|
998
|
+
TESTS::
|
|
999
|
+
|
|
1000
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(QQ,-5,-2)
|
|
1001
|
+
sage: i < j
|
|
1002
|
+
False
|
|
1003
|
+
sage: -i < j
|
|
1004
|
+
True
|
|
1005
|
+
sage: i == i
|
|
1006
|
+
True
|
|
1007
|
+
sage: Q.one() != -Q.one()
|
|
1008
|
+
True
|
|
1009
|
+
"""
|
|
1010
|
+
cdef QuaternionAlgebraElement_rational_field right = _right
|
|
1011
|
+
cdef int i
|
|
1012
|
+
i = mpz_cmp(self.d, right.d)
|
|
1013
|
+
if i:
|
|
1014
|
+
return rich_to_bool_sgn(op, i)
|
|
1015
|
+
i = mpz_cmp(self.x, right.x)
|
|
1016
|
+
if i:
|
|
1017
|
+
return rich_to_bool_sgn(op, i)
|
|
1018
|
+
i = mpz_cmp(self.y, right.y)
|
|
1019
|
+
if i:
|
|
1020
|
+
return rich_to_bool_sgn(op, i)
|
|
1021
|
+
i = mpz_cmp(self.z, right.z)
|
|
1022
|
+
if i:
|
|
1023
|
+
return rich_to_bool_sgn(op, i)
|
|
1024
|
+
i = mpz_cmp(self.w, right.w)
|
|
1025
|
+
if i:
|
|
1026
|
+
return rich_to_bool_sgn(op, i)
|
|
1027
|
+
return rich_to_bool(op, 0)
|
|
1028
|
+
|
|
1029
|
+
def __init__(self, parent, v, bint check=True):
|
|
1030
|
+
"""
|
|
1031
|
+
Setup element data from parent and coordinates.
|
|
1032
|
+
|
|
1033
|
+
EXAMPLES::
|
|
1034
|
+
|
|
1035
|
+
sage: A.<i,j,k>=QuaternionAlgebra(-4,-5)
|
|
1036
|
+
sage: A(2/3)
|
|
1037
|
+
2/3
|
|
1038
|
+
sage: type(A(2/3))
|
|
1039
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_rational_field'>
|
|
1040
|
+
|
|
1041
|
+
sage: A([-1/2,-10/3,-2/3,-4/5]) # implicit doctest
|
|
1042
|
+
-1/2 - 10/3*i - 2/3*j - 4/5*k
|
|
1043
|
+
sage: A(vector([1,2/3,3/4,4/5]))
|
|
1044
|
+
1 + 2/3*i + 3/4*j + 4/5*k
|
|
1045
|
+
|
|
1046
|
+
::
|
|
1047
|
+
|
|
1048
|
+
sage: QA = QuaternionAlgebra(QQ, -1, -1)
|
|
1049
|
+
sage: foo = QA(3.0); foo
|
|
1050
|
+
3
|
|
1051
|
+
sage: parent(foo)
|
|
1052
|
+
Quaternion Algebra (-1, -1) with base ring Rational Field
|
|
1053
|
+
sage: foo[0]
|
|
1054
|
+
3
|
|
1055
|
+
sage: parent(foo[0])
|
|
1056
|
+
Rational Field
|
|
1057
|
+
"""
|
|
1058
|
+
self._parent = parent
|
|
1059
|
+
|
|
1060
|
+
# cache a and b
|
|
1061
|
+
mpz_set(self.a, (<Integer>parent._a).value)
|
|
1062
|
+
mpz_set(self.b, (<Integer>parent._b).value)
|
|
1063
|
+
|
|
1064
|
+
cdef Rational x, y, z, w
|
|
1065
|
+
cdef mpz_t lcm
|
|
1066
|
+
cdef mpq_t lcm_rat
|
|
1067
|
+
if not isinstance(v, (list, tuple)):
|
|
1068
|
+
try:
|
|
1069
|
+
x = Rational(v)
|
|
1070
|
+
mpz_set(self.x, mpq_numref(x.value))
|
|
1071
|
+
mpz_set_si(self.y, 0)
|
|
1072
|
+
mpz_set_si(self.z, 0)
|
|
1073
|
+
mpz_set_si(self.w, 0)
|
|
1074
|
+
mpz_set(self.d, mpq_denref(x.value))
|
|
1075
|
+
return
|
|
1076
|
+
except TypeError:
|
|
1077
|
+
pass
|
|
1078
|
+
if check:
|
|
1079
|
+
v = tuple(v)
|
|
1080
|
+
# Now v is definitely a list or tuple, and we convert each
|
|
1081
|
+
# entry to a rational, then clear denominators, etc.
|
|
1082
|
+
x = Rational(v[0])
|
|
1083
|
+
y = Rational(v[1])
|
|
1084
|
+
z = Rational(v[2])
|
|
1085
|
+
w = Rational(v[3])
|
|
1086
|
+
else:
|
|
1087
|
+
x, y, z, w = v
|
|
1088
|
+
mpz_init(lcm)
|
|
1089
|
+
mpz_lcm(lcm, mpq_denref(x.value), mpq_denref(y.value))
|
|
1090
|
+
mpz_lcm(lcm, lcm, mpq_denref(z.value))
|
|
1091
|
+
mpz_lcm(lcm, lcm, mpq_denref(w.value))
|
|
1092
|
+
if mpz_cmp_si(lcm, 1):
|
|
1093
|
+
mpz_init_set(mpq_numref(lcm_rat), lcm)
|
|
1094
|
+
mpz_init_set_si(mpq_denref(lcm_rat), 1)
|
|
1095
|
+
mpq_mul(x.value, x.value, lcm_rat)
|
|
1096
|
+
mpq_mul(y.value, y.value, lcm_rat)
|
|
1097
|
+
mpq_mul(z.value, z.value, lcm_rat)
|
|
1098
|
+
mpq_mul(w.value, w.value, lcm_rat)
|
|
1099
|
+
mpq_clear(lcm_rat)
|
|
1100
|
+
mpz_set(self.x, mpq_numref(x.value))
|
|
1101
|
+
mpz_set(self.y, mpq_numref(y.value))
|
|
1102
|
+
mpz_set(self.z, mpq_numref(z.value))
|
|
1103
|
+
mpz_set(self.w, mpq_numref(w.value))
|
|
1104
|
+
mpz_set(self.d, lcm)
|
|
1105
|
+
mpz_clear(lcm)
|
|
1106
|
+
|
|
1107
|
+
def __getitem__(self, int i):
|
|
1108
|
+
"""
|
|
1109
|
+
TESTS::
|
|
1110
|
+
|
|
1111
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(QQ,-5,-2)
|
|
1112
|
+
sage: theta = 1/2 + 2/3*i - 3/4*j + 5/7*k
|
|
1113
|
+
sage: type(theta)
|
|
1114
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_rational_field'>
|
|
1115
|
+
sage: list(theta)
|
|
1116
|
+
[1/2, 2/3, -3/4, 5/7]
|
|
1117
|
+
"""
|
|
1118
|
+
cdef Rational r = Rational()
|
|
1119
|
+
if i == 0:
|
|
1120
|
+
mpq_set_num(r.value, self.x)
|
|
1121
|
+
elif i == 1:
|
|
1122
|
+
mpq_set_num(r.value, self.y)
|
|
1123
|
+
elif i == 2:
|
|
1124
|
+
mpq_set_num(r.value, self.z)
|
|
1125
|
+
elif i == 3:
|
|
1126
|
+
mpq_set_num(r.value, self.w)
|
|
1127
|
+
else:
|
|
1128
|
+
raise IndexError("quaternion element index out of range")
|
|
1129
|
+
mpq_set_den(r.value, self.d)
|
|
1130
|
+
mpq_canonicalize(r.value)
|
|
1131
|
+
return r
|
|
1132
|
+
|
|
1133
|
+
def __reduce__(self):
|
|
1134
|
+
"""
|
|
1135
|
+
Used for pickling.
|
|
1136
|
+
|
|
1137
|
+
TESTS::
|
|
1138
|
+
|
|
1139
|
+
sage: K.<x> = QQ[]
|
|
1140
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(Frac(K),-5,-19)
|
|
1141
|
+
sage: theta = 1/2 + 2/3*i - 3/4*j + 5/7*k
|
|
1142
|
+
sage: type(theta)
|
|
1143
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'>
|
|
1144
|
+
sage: loads(dumps(theta)) == theta
|
|
1145
|
+
True
|
|
1146
|
+
"""
|
|
1147
|
+
return (unpickle_QuaternionAlgebraElement_rational_field_v0,
|
|
1148
|
+
(self._parent, (self[0], self[1], self[2], self[3])))
|
|
1149
|
+
|
|
1150
|
+
cpdef _add_(self, _right):
|
|
1151
|
+
"""
|
|
1152
|
+
EXAMPLES::
|
|
1153
|
+
|
|
1154
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(15)
|
|
1155
|
+
sage: type(i)
|
|
1156
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_rational_field'>
|
|
1157
|
+
sage: (2/3 + 3/4*i + 5/6*j + 7/8*k)._add_(-2/3 - 3/4*i + 5/6*j + 7/8*k)
|
|
1158
|
+
5/3*j + 7/4*k
|
|
1159
|
+
"""
|
|
1160
|
+
|
|
1161
|
+
# Given two quaternion algebra elements
|
|
1162
|
+
# theta = (1/d1)*(x1 + y1 * i + z1 * j + w1 * k)
|
|
1163
|
+
# nu = (1/d2)*(x2 + y2 * i + z2 * j + w2 * k)
|
|
1164
|
+
#
|
|
1165
|
+
# we compute their sum as
|
|
1166
|
+
#
|
|
1167
|
+
# (theta + nu) = (1/d3)*(x3 + y3 * i + z3 * j + w3 * k)
|
|
1168
|
+
#
|
|
1169
|
+
# with d3 = d1 * d2
|
|
1170
|
+
# x3 = d1 * x2 + d2 * x1
|
|
1171
|
+
# y3 = d1 * y2 + d2 * y1
|
|
1172
|
+
# z3 = d1 * z2 + d2 * z1
|
|
1173
|
+
# w3 = d1 * w2 + d2 * w1
|
|
1174
|
+
#
|
|
1175
|
+
# and then we reduce the sum by dividing everything
|
|
1176
|
+
# by the gcd of d3, x3, y3, z3, and w3
|
|
1177
|
+
|
|
1178
|
+
cdef QuaternionAlgebraElement_rational_field right = _right
|
|
1179
|
+
cdef QuaternionAlgebraElement_rational_field result = <QuaternionAlgebraElement_rational_field> QuaternionAlgebraElement_rational_field.__new__(QuaternionAlgebraElement_rational_field)
|
|
1180
|
+
result._parent = self._parent
|
|
1181
|
+
|
|
1182
|
+
mpz_mul(U1, self.x, right.d) # U1 = x1 * d2
|
|
1183
|
+
mpz_mul(U2, right.x, self.d) # U2 = x2 * d1
|
|
1184
|
+
mpz_add(result.x, U1, U2) # x3 = x1 * d2 + x2 * d1
|
|
1185
|
+
|
|
1186
|
+
mpz_mul(U1, self.y, right.d) # U1 = y1 * d2
|
|
1187
|
+
mpz_mul(U2, right.y, self.d) # U2 = y2 * d1
|
|
1188
|
+
mpz_add(result.y, U1, U2) # x3 = y1 * d2 + y2 * d1
|
|
1189
|
+
|
|
1190
|
+
mpz_mul(U1, self.z, right.d) # U1 = z1 * d2
|
|
1191
|
+
mpz_mul(U2, right.z, self.d) # U2 = z2 * d1
|
|
1192
|
+
mpz_add(result.z, U1, U2) # z3 = z1 * d2 + z2 * d1
|
|
1193
|
+
|
|
1194
|
+
mpz_mul(U1, self.w, right.d) # U1 = w1 * d2
|
|
1195
|
+
mpz_mul(U2, right.w, self.d) # U2 = w2 * d1
|
|
1196
|
+
mpz_add(result.w, U1, U2) # w3 = w1 * d2 + w2 * d1
|
|
1197
|
+
|
|
1198
|
+
mpz_mul(result.d, self.d, right.d) # d3 = d1 * d2
|
|
1199
|
+
|
|
1200
|
+
result.canonicalize()
|
|
1201
|
+
|
|
1202
|
+
mpz_set(result.a, self.a)
|
|
1203
|
+
mpz_set(result.b, self.b)
|
|
1204
|
+
return result
|
|
1205
|
+
|
|
1206
|
+
cpdef _sub_(self, _right):
|
|
1207
|
+
"""
|
|
1208
|
+
EXAMPLES::
|
|
1209
|
+
|
|
1210
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(15)
|
|
1211
|
+
sage: type(i)
|
|
1212
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_rational_field'>
|
|
1213
|
+
sage: (2/3 + 3/4*i + 5/6*j + 7/8*k)._sub_(-2/3 - 3/4*i + 5/6*j + 7/8*k)
|
|
1214
|
+
4/3 + 3/2*i
|
|
1215
|
+
"""
|
|
1216
|
+
cdef QuaternionAlgebraElement_rational_field right = _right
|
|
1217
|
+
cdef QuaternionAlgebraElement_rational_field result = <QuaternionAlgebraElement_rational_field> QuaternionAlgebraElement_rational_field.__new__(QuaternionAlgebraElement_rational_field)
|
|
1218
|
+
result._parent = self._parent
|
|
1219
|
+
|
|
1220
|
+
# Implementation Note: To obtain _sub_, we simply replace every occurrence of
|
|
1221
|
+
# "add" in _add_ with "sub"; that is, we s/add/sub to get _sub_
|
|
1222
|
+
|
|
1223
|
+
mpz_mul(U1, self.x, right.d)
|
|
1224
|
+
mpz_mul(U2, right.x, self.d)
|
|
1225
|
+
mpz_sub(result.x, U1, U2)
|
|
1226
|
+
|
|
1227
|
+
mpz_mul(U1, self.y, right.d)
|
|
1228
|
+
mpz_mul(U2, right.y, self.d)
|
|
1229
|
+
mpz_sub(result.y, U1, U2)
|
|
1230
|
+
|
|
1231
|
+
mpz_mul(U1, self.z, right.d)
|
|
1232
|
+
mpz_mul(U2, right.z, self.d)
|
|
1233
|
+
mpz_sub(result.z, U1, U2)
|
|
1234
|
+
|
|
1235
|
+
mpz_mul(U1, self.w, right.d)
|
|
1236
|
+
mpz_mul(U2, right.w, self.d)
|
|
1237
|
+
mpz_sub(result.w, U1, U2)
|
|
1238
|
+
|
|
1239
|
+
mpz_mul(result.d, self.d, right.d)
|
|
1240
|
+
|
|
1241
|
+
result.canonicalize()
|
|
1242
|
+
|
|
1243
|
+
mpz_set(result.a, self.a)
|
|
1244
|
+
mpz_set(result.b, self.b)
|
|
1245
|
+
return result
|
|
1246
|
+
|
|
1247
|
+
cpdef _mul_(self, _right):
|
|
1248
|
+
"""
|
|
1249
|
+
EXAMPLES::
|
|
1250
|
+
|
|
1251
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(15)
|
|
1252
|
+
sage: type(i)
|
|
1253
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_rational_field'>
|
|
1254
|
+
sage: (2/3 + 3/4*i + 5/6*j + 7/8*k)._mul_(-2/3 - 3/4*i + 5/6*j + 7/8*k)
|
|
1255
|
+
9331/576 - i - 63/16*j + 5/4*k
|
|
1256
|
+
"""
|
|
1257
|
+
|
|
1258
|
+
# We use the following formula for multiplication:
|
|
1259
|
+
#
|
|
1260
|
+
# Given two quaternion algebra elements
|
|
1261
|
+
#
|
|
1262
|
+
# theta = (1/d1)*(x1 + y1 * i + z1 * j + w1 * k)
|
|
1263
|
+
# nu = (1/d2)*(x2 + y2 * i + z2 * j + w2 * k)
|
|
1264
|
+
#
|
|
1265
|
+
# we compute their product as
|
|
1266
|
+
#
|
|
1267
|
+
# theta*nu = (1/d3)*(x3 + y3 * i + z3 * j + w3 * k)
|
|
1268
|
+
#
|
|
1269
|
+
# where
|
|
1270
|
+
# x3 = t1 + a * t2 + b * (t3 - a*t4)
|
|
1271
|
+
# y3 = s1*(x2 + y2) - t1 - t2 + b*( s2*(z2 - w2) - t3 + t4)
|
|
1272
|
+
# z3 = t5 - a*t6 + t7 + a*t8
|
|
1273
|
+
# w3 = (x2 - y2)*s2 - t5 + t6 + s1*(z2 + w2) - t7 - t8
|
|
1274
|
+
#
|
|
1275
|
+
# and where
|
|
1276
|
+
# t1 = x1 * x2
|
|
1277
|
+
# t2 = y1 * y2
|
|
1278
|
+
# t3 = z1 * z2
|
|
1279
|
+
# t4 = w1 * w2
|
|
1280
|
+
# t5 = x2 * z1
|
|
1281
|
+
# t6 = y2 * w1
|
|
1282
|
+
# t7 = x1 * z2
|
|
1283
|
+
# t8 = y1 * w2
|
|
1284
|
+
#
|
|
1285
|
+
# s1 = x1 + y1
|
|
1286
|
+
# s2 = z1 + w1
|
|
1287
|
+
#
|
|
1288
|
+
# This takes more integer addition operations but fewer integer multiplication
|
|
1289
|
+
# operations than the "straightforward" multiplication method.
|
|
1290
|
+
#
|
|
1291
|
+
# There might be a way to optimize this formula further.
|
|
1292
|
+
|
|
1293
|
+
cdef QuaternionAlgebraElement_rational_field right = _right
|
|
1294
|
+
cdef QuaternionAlgebraElement_rational_field result = <QuaternionAlgebraElement_rational_field> QuaternionAlgebraElement_rational_field.__new__(QuaternionAlgebraElement_rational_field)
|
|
1295
|
+
result._parent = self._parent
|
|
1296
|
+
|
|
1297
|
+
mpz_set(result.a, self.a)
|
|
1298
|
+
mpz_set(result.b, self.b)
|
|
1299
|
+
|
|
1300
|
+
mpz_mul(T1, self.x, right.x) # t1 = x1 * x2
|
|
1301
|
+
mpz_mul(T2, self.y, right.y) # t2 = y1 * y2
|
|
1302
|
+
mpz_mul(t3, self.z, right.z) # t3 = z1 * z2
|
|
1303
|
+
mpz_mul(t4, self.w, right.w) # t4 = w1 * w2
|
|
1304
|
+
mpz_mul(t5, right.x, self.z) # t5 = x2 * z1
|
|
1305
|
+
mpz_mul(t6, right.y, self.w) # t6 = y2 * w1
|
|
1306
|
+
mpz_mul(t7, self.x, right.z) # t7 = x1 * z2
|
|
1307
|
+
mpz_mul(t8, self.y, right.w) # t8 = y1 * w2
|
|
1308
|
+
|
|
1309
|
+
mpz_add(s1, self.x, self.y) # s1 = x1 + y1
|
|
1310
|
+
mpz_add(s2, self.z, self.w) # s2 = z1 + w1
|
|
1311
|
+
|
|
1312
|
+
# ------------------
|
|
1313
|
+
|
|
1314
|
+
mpz_mul(U1, self.a, t4)
|
|
1315
|
+
mpz_sub(U1, t3, U1)
|
|
1316
|
+
mpz_mul(U1, U1, self.b)
|
|
1317
|
+
mpz_mul(U2, self.a, T2)
|
|
1318
|
+
mpz_add(result.x, T1, U2)
|
|
1319
|
+
mpz_add(result.x, result.x, U1)
|
|
1320
|
+
|
|
1321
|
+
# ------------------
|
|
1322
|
+
|
|
1323
|
+
mpz_sub(U1, right.z, right.w)
|
|
1324
|
+
mpz_mul(U1, U1, s2)
|
|
1325
|
+
mpz_sub(U1, U1, t3)
|
|
1326
|
+
mpz_add(U1, U1, t4)
|
|
1327
|
+
mpz_mul(U1, U1, self.b)
|
|
1328
|
+
mpz_sub(U1, U1, T2)
|
|
1329
|
+
mpz_sub(U1, U1, T1)
|
|
1330
|
+
mpz_add(U2, right.x, right.y)
|
|
1331
|
+
mpz_mul(U2, s1, U2)
|
|
1332
|
+
mpz_add(result.y, U1, U2)
|
|
1333
|
+
|
|
1334
|
+
# ------------------
|
|
1335
|
+
|
|
1336
|
+
mpz_mul(U1, self.a, t8)
|
|
1337
|
+
mpz_add(U1, U1, t7)
|
|
1338
|
+
mpz_mul(U2, self.a, t6)
|
|
1339
|
+
mpz_sub(U1, U1, U2)
|
|
1340
|
+
mpz_add(result.z, U1, t5)
|
|
1341
|
+
|
|
1342
|
+
# ------------------
|
|
1343
|
+
|
|
1344
|
+
mpz_add(U1, right.z, right.w)
|
|
1345
|
+
mpz_mul(U1, U1, s1)
|
|
1346
|
+
mpz_sub(U1, U1, t7)
|
|
1347
|
+
mpz_sub(U1, U1, t8)
|
|
1348
|
+
mpz_add(U1, U1, t6)
|
|
1349
|
+
mpz_sub(U1, U1, t5)
|
|
1350
|
+
mpz_sub(U2, right.x, right.y)
|
|
1351
|
+
mpz_mul(U2, U2, s2)
|
|
1352
|
+
mpz_add(result.w, U1, U2)
|
|
1353
|
+
|
|
1354
|
+
mpz_mul(result.d, self.d, right.d)
|
|
1355
|
+
|
|
1356
|
+
result.canonicalize()
|
|
1357
|
+
|
|
1358
|
+
return result
|
|
1359
|
+
|
|
1360
|
+
cpdef reduced_norm(self):
|
|
1361
|
+
"""
|
|
1362
|
+
Return the reduced norm of ``self``.
|
|
1363
|
+
|
|
1364
|
+
Given a quaternion `x+yi+zj+wk`, this is `x^2 - ay^2 - bz^2 + abw^2`.
|
|
1365
|
+
|
|
1366
|
+
EXAMPLES::
|
|
1367
|
+
|
|
1368
|
+
sage: K.<i,j,k> = QuaternionAlgebra(QQ, -5, -2)
|
|
1369
|
+
sage: i.reduced_norm()
|
|
1370
|
+
5
|
|
1371
|
+
sage: j.reduced_norm()
|
|
1372
|
+
2
|
|
1373
|
+
sage: a = 1/3 + 1/5*i + 1/7*j + k
|
|
1374
|
+
sage: a.reduced_norm()
|
|
1375
|
+
22826/2205
|
|
1376
|
+
"""
|
|
1377
|
+
mpz_mul(U1, self.x, self.x) # U1 = x*x
|
|
1378
|
+
mpz_mul(U2, self.b, self.z) # U2 = b*x
|
|
1379
|
+
mpz_mul(U2, U2, self.z) # U2 = b*z*z
|
|
1380
|
+
mpz_sub(U2, U1, U2) # U2 = -b*z*z + x*x
|
|
1381
|
+
|
|
1382
|
+
mpz_mul(U1, self.y, self.a) # U1 = a*y
|
|
1383
|
+
mpz_mul(U1, U1, self.y) # U1 = a*y*y
|
|
1384
|
+
mpz_sub(U2, U2, U1) # U2 = -a*y*y - b*z*z + x*x
|
|
1385
|
+
|
|
1386
|
+
mpz_mul(U1, self.w, self.w) # U1 = w*w
|
|
1387
|
+
mpz_mul(U1, U1, self.a) # U1 = w*w*a
|
|
1388
|
+
mpz_mul(U1, U1, self.b) # U1 = w*w*a*b
|
|
1389
|
+
|
|
1390
|
+
mpz_add(U1, U1, U2) # U1 = w*w*a*b - a*y*y - b*z*z + x*x
|
|
1391
|
+
|
|
1392
|
+
mpz_mul(U2, self.d, self.d)
|
|
1393
|
+
|
|
1394
|
+
cdef Rational result = Rational.__new__(Rational)
|
|
1395
|
+
mpq_set_num(result.value, U1)
|
|
1396
|
+
mpq_set_den(result.value, U2)
|
|
1397
|
+
mpq_canonicalize(result.value)
|
|
1398
|
+
|
|
1399
|
+
return result
|
|
1400
|
+
|
|
1401
|
+
cpdef conjugate(self):
|
|
1402
|
+
"""
|
|
1403
|
+
Return the conjugate of this quaternion.
|
|
1404
|
+
|
|
1405
|
+
EXAMPLES::
|
|
1406
|
+
|
|
1407
|
+
sage: A.<i,j,k> = QuaternionAlgebra(QQ,-5,-2)
|
|
1408
|
+
sage: a = 3*i - j + 2
|
|
1409
|
+
sage: type(a)
|
|
1410
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_rational_field'>
|
|
1411
|
+
sage: a.conjugate()
|
|
1412
|
+
2 - 3*i + j
|
|
1413
|
+
sage: b = 1 + 1/3*i + 1/5*j - 1/7*k
|
|
1414
|
+
sage: b.conjugate()
|
|
1415
|
+
1 - 1/3*i - 1/5*j + 1/7*k
|
|
1416
|
+
"""
|
|
1417
|
+
|
|
1418
|
+
cdef QuaternionAlgebraElement_rational_field result = <QuaternionAlgebraElement_rational_field> QuaternionAlgebraElement_rational_field.__new__(QuaternionAlgebraElement_rational_field)
|
|
1419
|
+
result._parent = self._parent
|
|
1420
|
+
|
|
1421
|
+
mpz_set(result.a, self.a)
|
|
1422
|
+
mpz_set(result.b, self.b)
|
|
1423
|
+
mpz_set(result.d, self.d)
|
|
1424
|
+
|
|
1425
|
+
mpz_set(result.x, self.x)
|
|
1426
|
+
mpz_mul_si(result.y, self.y, -1)
|
|
1427
|
+
mpz_mul_si(result.z, self.z, -1)
|
|
1428
|
+
mpz_mul_si(result.w, self.w, -1)
|
|
1429
|
+
|
|
1430
|
+
return result
|
|
1431
|
+
|
|
1432
|
+
cpdef reduced_trace(self):
|
|
1433
|
+
"""
|
|
1434
|
+
Return the reduced trace of ``self``.
|
|
1435
|
+
|
|
1436
|
+
This is `2x` if ``self`` is `x+iy+zj+wk`.
|
|
1437
|
+
|
|
1438
|
+
EXAMPLES::
|
|
1439
|
+
|
|
1440
|
+
sage: K.<i,j,k> = QuaternionAlgebra(QQ, -5, -2)
|
|
1441
|
+
sage: i.reduced_trace()
|
|
1442
|
+
0
|
|
1443
|
+
sage: j.reduced_trace()
|
|
1444
|
+
0
|
|
1445
|
+
sage: a = 1/3 + 1/5*i + 1/7*j + k
|
|
1446
|
+
sage: a.reduced_trace()
|
|
1447
|
+
2/3
|
|
1448
|
+
"""
|
|
1449
|
+
mpz_mul_si(U1, self.x, 2)
|
|
1450
|
+
cdef Rational result = Rational.__new__(Rational)
|
|
1451
|
+
mpq_set_num(result.value, U1)
|
|
1452
|
+
mpq_set_den(result.value, self.d)
|
|
1453
|
+
mpq_canonicalize(result.value)
|
|
1454
|
+
return result
|
|
1455
|
+
|
|
1456
|
+
cdef inline canonicalize(self):
|
|
1457
|
+
"""
|
|
1458
|
+
Put the representation of this quaternion element into
|
|
1459
|
+
smallest form. For `a = (1/d)(x + yi + zj + wk)` we
|
|
1460
|
+
divide `a`, `x`, `y`, `z`, and `w` by the gcd of all of them.
|
|
1461
|
+
|
|
1462
|
+
TESTS::
|
|
1463
|
+
|
|
1464
|
+
sage: K.<i,j,k> = QuaternionAlgebra(QQ, -10, -7)
|
|
1465
|
+
sage: (1/4 + 1/2 * i + 1/7 * j + 1/28 * k)*14*i # implicit doctest
|
|
1466
|
+
-70 + 7/2*i + 5*j - 2*k
|
|
1467
|
+
"""
|
|
1468
|
+
|
|
1469
|
+
# Note: this function changes the module-level global variable
|
|
1470
|
+
# U1, so it isn't always safe to use this in the middle of
|
|
1471
|
+
# another function. Normally this function is called
|
|
1472
|
+
# at the end of an arithmetic routine, so this is fine.
|
|
1473
|
+
|
|
1474
|
+
# Implementation-wise, we compute the GCD's one at a time,
|
|
1475
|
+
# and quit if it ever becomes one
|
|
1476
|
+
|
|
1477
|
+
mpz_gcd(U1, self.d, self.x)
|
|
1478
|
+
if mpz_cmp_ui(U1, 1) != 0:
|
|
1479
|
+
mpz_gcd(U1, U1, self.y)
|
|
1480
|
+
if mpz_cmp_ui(U1, 1) != 0:
|
|
1481
|
+
mpz_gcd(U1, U1, self.z)
|
|
1482
|
+
if mpz_cmp_ui(U1, 1) != 0:
|
|
1483
|
+
mpz_gcd(U1, U1, self.w)
|
|
1484
|
+
if mpz_cmp_ui(U1, 1) != 0:
|
|
1485
|
+
# at this point U1 actually contains the gcd of all the terms, and we divide
|
|
1486
|
+
mpz_divexact(self.d, self.d, U1)
|
|
1487
|
+
mpz_divexact(self.x, self.x, U1)
|
|
1488
|
+
mpz_divexact(self.y, self.y, U1)
|
|
1489
|
+
mpz_divexact(self.z, self.z, U1)
|
|
1490
|
+
mpz_divexact(self.w, self.w, U1)
|
|
1491
|
+
|
|
1492
|
+
def denominator(self):
|
|
1493
|
+
"""
|
|
1494
|
+
Return the lowest common multiple of the denominators of the coefficients
|
|
1495
|
+
of i, j and k for this quaternion.
|
|
1496
|
+
|
|
1497
|
+
EXAMPLES::
|
|
1498
|
+
|
|
1499
|
+
sage: A = QuaternionAlgebra(QQ, -1, -1)
|
|
1500
|
+
sage: A.<i,j,k> = QuaternionAlgebra(QQ, -1, -1)
|
|
1501
|
+
sage: a = (1/2) + (1/5)*i + (5/12)*j + (1/13)*k
|
|
1502
|
+
sage: a
|
|
1503
|
+
1/2 + 1/5*i + 5/12*j + 1/13*k
|
|
1504
|
+
sage: a.denominator()
|
|
1505
|
+
780
|
|
1506
|
+
sage: lcm([2, 5, 12, 13])
|
|
1507
|
+
780
|
|
1508
|
+
sage: (a * a).denominator()
|
|
1509
|
+
608400
|
|
1510
|
+
sage: (a + a).denominator()
|
|
1511
|
+
390
|
|
1512
|
+
"""
|
|
1513
|
+
cdef Integer d = Integer()
|
|
1514
|
+
mpz_set(d.value, self.d)
|
|
1515
|
+
return d
|
|
1516
|
+
|
|
1517
|
+
def denominator_and_integer_coefficient_tuple(self):
|
|
1518
|
+
"""
|
|
1519
|
+
Return 5-tuple d, x, y, z, w, where this rational quaternion
|
|
1520
|
+
is equal to `(x + yi + zj + wk)/d` and x, y, z, w do not share
|
|
1521
|
+
a common factor with d.
|
|
1522
|
+
|
|
1523
|
+
OUTPUT: 5-tuple of Integers
|
|
1524
|
+
|
|
1525
|
+
EXAMPLES::
|
|
1526
|
+
|
|
1527
|
+
sage: A.<i,j,k>=QuaternionAlgebra(-1,-2)
|
|
1528
|
+
sage: (2 + 3*i + 4/3*j - 5*k).denominator_and_integer_coefficient_tuple()
|
|
1529
|
+
(3, 6, 9, 4, -15)
|
|
1530
|
+
"""
|
|
1531
|
+
cdef Integer d = Integer()
|
|
1532
|
+
cdef Integer y = Integer()
|
|
1533
|
+
cdef Integer x = Integer()
|
|
1534
|
+
cdef Integer z = Integer()
|
|
1535
|
+
cdef Integer w = Integer()
|
|
1536
|
+
|
|
1537
|
+
mpz_set(d.value, self.d)
|
|
1538
|
+
mpz_set(x.value, self.x)
|
|
1539
|
+
mpz_set(y.value, self.y)
|
|
1540
|
+
mpz_set(z.value, self.z)
|
|
1541
|
+
mpz_set(w.value, self.w)
|
|
1542
|
+
|
|
1543
|
+
return (d, x, y, z, w)
|
|
1544
|
+
|
|
1545
|
+
def integer_coefficient_tuple(self):
|
|
1546
|
+
"""
|
|
1547
|
+
Return the integer part of this quaternion, ignoring the common denominator.
|
|
1548
|
+
|
|
1549
|
+
OUTPUT: 4-tuple of Integers
|
|
1550
|
+
|
|
1551
|
+
EXAMPLES::
|
|
1552
|
+
|
|
1553
|
+
sage: A.<i,j,k>=QuaternionAlgebra(-1,-2)
|
|
1554
|
+
sage: (2 + 3*i + 4/3*j - 5*k).integer_coefficient_tuple()
|
|
1555
|
+
(6, 9, 4, -15)
|
|
1556
|
+
"""
|
|
1557
|
+
cdef Integer y = Integer()
|
|
1558
|
+
cdef Integer x = Integer()
|
|
1559
|
+
cdef Integer z = Integer()
|
|
1560
|
+
cdef Integer w = Integer()
|
|
1561
|
+
|
|
1562
|
+
mpz_set(x.value, self.x)
|
|
1563
|
+
mpz_set(y.value, self.y)
|
|
1564
|
+
mpz_set(z.value, self.z)
|
|
1565
|
+
mpz_set(w.value, self.w)
|
|
1566
|
+
|
|
1567
|
+
return (x, y, z, w)
|
|
1568
|
+
|
|
1569
|
+
def coefficient_tuple(self):
|
|
1570
|
+
"""
|
|
1571
|
+
Return 4-tuple of rational numbers which are the coefficients of this quaternion.
|
|
1572
|
+
|
|
1573
|
+
EXAMPLES::
|
|
1574
|
+
|
|
1575
|
+
sage: A.<i,j,k> = QuaternionAlgebra(-1,-2)
|
|
1576
|
+
sage: (2/3 + 3/5*i + 4/3*j - 5/7*k).coefficient_tuple()
|
|
1577
|
+
(2/3, 3/5, 4/3, -5/7)
|
|
1578
|
+
"""
|
|
1579
|
+
cdef Rational x = Rational()
|
|
1580
|
+
cdef Rational y = Rational()
|
|
1581
|
+
cdef Rational z = Rational()
|
|
1582
|
+
cdef Rational w = Rational()
|
|
1583
|
+
|
|
1584
|
+
mpq_set_num(x.value, self.x)
|
|
1585
|
+
mpq_set_num(y.value, self.y)
|
|
1586
|
+
mpq_set_num(z.value, self.z)
|
|
1587
|
+
mpq_set_num(w.value, self.w)
|
|
1588
|
+
|
|
1589
|
+
mpq_set_den(x.value, self.d)
|
|
1590
|
+
mpq_set_den(y.value, self.d)
|
|
1591
|
+
mpq_set_den(z.value, self.d)
|
|
1592
|
+
mpq_set_den(w.value, self.d)
|
|
1593
|
+
|
|
1594
|
+
mpq_canonicalize(x.value)
|
|
1595
|
+
mpq_canonicalize(y.value)
|
|
1596
|
+
mpq_canonicalize(z.value)
|
|
1597
|
+
mpq_canonicalize(w.value)
|
|
1598
|
+
return (x, y, z, w)
|
|
1599
|
+
|
|
1600
|
+
def _multiply_by_integer(self, Integer n):
|
|
1601
|
+
"""
|
|
1602
|
+
Return the product of ``self`` times the integer `n`.
|
|
1603
|
+
|
|
1604
|
+
EXAMPLES::
|
|
1605
|
+
|
|
1606
|
+
sage: A = QuaternionAlgebra(7)
|
|
1607
|
+
sage: a = A.random_element()
|
|
1608
|
+
sage: 5*a == a._multiply_by_integer(5)
|
|
1609
|
+
True
|
|
1610
|
+
"""
|
|
1611
|
+
cdef QuaternionAlgebraElement_rational_field result = <QuaternionAlgebraElement_rational_field> QuaternionAlgebraElement_rational_field.__new__(QuaternionAlgebraElement_rational_field)
|
|
1612
|
+
result._parent = self._parent
|
|
1613
|
+
|
|
1614
|
+
mpz_set(result.a, self.a)
|
|
1615
|
+
mpz_set(result.b, self.b)
|
|
1616
|
+
|
|
1617
|
+
if mpz_divisible_p(self.d, n.value) != 0:
|
|
1618
|
+
mpz_divexact(result.d, self.d, n.value)
|
|
1619
|
+
mpz_set(result.x, self.x)
|
|
1620
|
+
mpz_set(result.y, self.y)
|
|
1621
|
+
mpz_set(result.z, self.z)
|
|
1622
|
+
mpz_set(result.w, self.w)
|
|
1623
|
+
return result
|
|
1624
|
+
if mpz_divisible_p(n.value, self.d):
|
|
1625
|
+
mpz_divexact(T1, n.value, self.d)
|
|
1626
|
+
else:
|
|
1627
|
+
mpz_set(T1, n.value)
|
|
1628
|
+
|
|
1629
|
+
mpz_set(result.d, self.d)
|
|
1630
|
+
mpz_mul(result.x, self.x, T1)
|
|
1631
|
+
mpz_mul(result.y, self.y, T1)
|
|
1632
|
+
mpz_mul(result.z, self.z, T1)
|
|
1633
|
+
mpz_mul(result.w, self.w, T1)
|
|
1634
|
+
|
|
1635
|
+
return result
|
|
1636
|
+
|
|
1637
|
+
def _divide_by_integer(self, Integer n):
|
|
1638
|
+
"""
|
|
1639
|
+
Return the quotient of ``self`` by the integer `n`.
|
|
1640
|
+
|
|
1641
|
+
EXAMPLES::
|
|
1642
|
+
|
|
1643
|
+
sage: A = QuaternionAlgebra(7)
|
|
1644
|
+
sage: a = A.random_element()
|
|
1645
|
+
sage: a/5 == a._divide_by_integer(5)
|
|
1646
|
+
True
|
|
1647
|
+
sage: a._divide_by_integer(0)
|
|
1648
|
+
Traceback (most recent call last):
|
|
1649
|
+
...
|
|
1650
|
+
ZeroDivisionError
|
|
1651
|
+
"""
|
|
1652
|
+
if mpz_sgn(n.value) == 0:
|
|
1653
|
+
raise ZeroDivisionError
|
|
1654
|
+
|
|
1655
|
+
cdef QuaternionAlgebraElement_rational_field result = <QuaternionAlgebraElement_rational_field> QuaternionAlgebraElement_rational_field.__new__(QuaternionAlgebraElement_rational_field)
|
|
1656
|
+
result._parent = self._parent
|
|
1657
|
+
|
|
1658
|
+
mpz_set(result.a, self.a)
|
|
1659
|
+
mpz_set(result.b, self.b)
|
|
1660
|
+
|
|
1661
|
+
mpz_mul(result.d, self.d, n.value)
|
|
1662
|
+
mpz_set(result.x, self.x)
|
|
1663
|
+
mpz_set(result.y, self.y)
|
|
1664
|
+
mpz_set(result.z, self.z)
|
|
1665
|
+
mpz_set(result.w, self.w)
|
|
1666
|
+
|
|
1667
|
+
result.canonicalize()
|
|
1668
|
+
|
|
1669
|
+
return result
|
|
1670
|
+
|
|
1671
|
+
|
|
1672
|
+
cdef class QuaternionAlgebraElement_number_field(QuaternionAlgebraElement_abstract):
|
|
1673
|
+
def __cinit__(self):
|
|
1674
|
+
"""
|
|
1675
|
+
Allocate memory for this quaternion over a number field.
|
|
1676
|
+
|
|
1677
|
+
EXAMPLES::
|
|
1678
|
+
|
|
1679
|
+
sage: K.<a> = QQ[2^(1/5)]; Q.<i,j,k> = QuaternionAlgebra(K,-a,a*17/3) # needs fpylll sage.symbolic
|
|
1680
|
+
sage: Q([a,-2/3,a^2-1/2,a*2]) # implicit doctest # needs fpylll sage.symbolic
|
|
1681
|
+
a + (-2/3)*i + (a^2 - 1/2)*j + 2*a*k
|
|
1682
|
+
"""
|
|
1683
|
+
fmpz_poly_init(self.x)
|
|
1684
|
+
fmpz_poly_init(self.y)
|
|
1685
|
+
fmpz_poly_init(self.z)
|
|
1686
|
+
fmpz_poly_init(self.w)
|
|
1687
|
+
fmpz_poly_init(self.a)
|
|
1688
|
+
fmpz_poly_init(self.b)
|
|
1689
|
+
fmpz_poly_init(self.modulus)
|
|
1690
|
+
mpz_init(self.d)
|
|
1691
|
+
|
|
1692
|
+
def __dealloc__(self):
|
|
1693
|
+
"""
|
|
1694
|
+
Free memory used by this quaternion over a number field.
|
|
1695
|
+
"""
|
|
1696
|
+
fmpz_poly_clear(self.x)
|
|
1697
|
+
fmpz_poly_clear(self.y)
|
|
1698
|
+
fmpz_poly_clear(self.z)
|
|
1699
|
+
fmpz_poly_clear(self.w)
|
|
1700
|
+
fmpz_poly_clear(self.a)
|
|
1701
|
+
fmpz_poly_clear(self.b)
|
|
1702
|
+
fmpz_poly_clear(self.modulus)
|
|
1703
|
+
mpz_clear(self.d)
|
|
1704
|
+
|
|
1705
|
+
def __init__(self, parent, v, bint check=True):
|
|
1706
|
+
"""
|
|
1707
|
+
EXAMPLES::
|
|
1708
|
+
|
|
1709
|
+
sage: K.<a> = QQ[2^(1/3)]; Q.<i,j,k> = QuaternionAlgebra(K,-a,a+1) # needs fpylll sage.symbolic
|
|
1710
|
+
sage: Q([a,-2/3,a^2-1/2,a*2]) # implicit doctest # needs fpylll sage.symbolic
|
|
1711
|
+
a + (-2/3)*i + (a^2 - 1/2)*j + 2*a*k
|
|
1712
|
+
"""
|
|
1713
|
+
self._parent = parent
|
|
1714
|
+
if check:
|
|
1715
|
+
x, y, z, w = to_quaternion(parent._base, v)
|
|
1716
|
+
else:
|
|
1717
|
+
x, y, z, w = v
|
|
1718
|
+
cdef NumberFieldElement a = <NumberFieldElement>(parent._base(parent._a))
|
|
1719
|
+
cdef NumberFieldElement b = <NumberFieldElement>(parent._base(parent._b))
|
|
1720
|
+
fmpz_poly_set_ZZX(self.x, (<NumberFieldElement>x)._numerator)
|
|
1721
|
+
fmpz_poly_set_ZZX(self.y, (<NumberFieldElement>y)._numerator)
|
|
1722
|
+
fmpz_poly_set_ZZX(self.z, (<NumberFieldElement>z)._numerator)
|
|
1723
|
+
fmpz_poly_set_ZZX(self.w, (<NumberFieldElement>w)._numerator)
|
|
1724
|
+
|
|
1725
|
+
ZZ_to_mpz(T1, &(<NumberFieldElement>x)._denominator)
|
|
1726
|
+
ZZ_to_mpz(T2, &(<NumberFieldElement>y)._denominator)
|
|
1727
|
+
ZZ_to_mpz(t3, &(<NumberFieldElement>z)._denominator)
|
|
1728
|
+
ZZ_to_mpz(t4, &(<NumberFieldElement>w)._denominator)
|
|
1729
|
+
|
|
1730
|
+
mpz_lcm(self.d, T1, T2)
|
|
1731
|
+
mpz_lcm(self.d, self.d, t3)
|
|
1732
|
+
mpz_lcm(self.d, self.d, t4)
|
|
1733
|
+
|
|
1734
|
+
mpz_divexact(T1, self.d, T1)
|
|
1735
|
+
mpz_divexact(T2, self.d, T2)
|
|
1736
|
+
mpz_divexact(t3, self.d, t3)
|
|
1737
|
+
mpz_divexact(t4, self.d, t4)
|
|
1738
|
+
|
|
1739
|
+
fmpz_poly_scalar_mul_mpz(self.x, self.x, T1)
|
|
1740
|
+
fmpz_poly_scalar_mul_mpz(self.y, self.y, T2)
|
|
1741
|
+
fmpz_poly_scalar_mul_mpz(self.z, self.z, t3)
|
|
1742
|
+
fmpz_poly_scalar_mul_mpz(self.w, self.w, t4)
|
|
1743
|
+
|
|
1744
|
+
fmpz_poly_set_ZZX(self.a, a._numerator) # we will assume that the denominator of a and b are 1
|
|
1745
|
+
fmpz_poly_set_ZZX(self.b, b._numerator)
|
|
1746
|
+
|
|
1747
|
+
fmpz_poly_set_ZZX(self.modulus, (<NumberFieldElement>x)._fld_numerator.x) # and same for the modulus
|
|
1748
|
+
|
|
1749
|
+
def __getitem__(self, int i):
|
|
1750
|
+
"""
|
|
1751
|
+
EXAMPLES::
|
|
1752
|
+
|
|
1753
|
+
sage: # needs fpylll sage.symbolic
|
|
1754
|
+
sage: K.<a> = QQ[2^(1/3)]; Q.<i,j,k> = QuaternionAlgebra(K,-a,a+1)
|
|
1755
|
+
sage: Q([a,-2/3,a^2-1/2,a*2])
|
|
1756
|
+
a + (-2/3)*i + (a^2 - 1/2)*j + 2*a*k
|
|
1757
|
+
sage: x = Q([a,-2/3,a^2-1/2,a*2])
|
|
1758
|
+
sage: type(x)
|
|
1759
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_number_field'>
|
|
1760
|
+
sage: x[0]
|
|
1761
|
+
a
|
|
1762
|
+
sage: x[1]
|
|
1763
|
+
-2/3
|
|
1764
|
+
sage: x[2]
|
|
1765
|
+
a^2 - 1/2
|
|
1766
|
+
sage: x[3]
|
|
1767
|
+
2*a
|
|
1768
|
+
sage: list(x)
|
|
1769
|
+
[a, -2/3, a^2 - 1/2, 2*a]
|
|
1770
|
+
"""
|
|
1771
|
+
# general number -- this code assumes that the number field is not quadratic!!
|
|
1772
|
+
|
|
1773
|
+
cdef NumberFieldElement el = <NumberFieldElement>(self._parent.base_ring().an_element())
|
|
1774
|
+
cdef NumberFieldElement item = el._new()
|
|
1775
|
+
|
|
1776
|
+
if i == 0:
|
|
1777
|
+
fmpz_poly_get_ZZX(item._numerator, self.x)
|
|
1778
|
+
elif i == 1:
|
|
1779
|
+
fmpz_poly_get_ZZX(item._numerator, self.y)
|
|
1780
|
+
elif i == 2:
|
|
1781
|
+
fmpz_poly_get_ZZX(item._numerator, self.z)
|
|
1782
|
+
elif i == 3:
|
|
1783
|
+
fmpz_poly_get_ZZX(item._numerator, self.w)
|
|
1784
|
+
else:
|
|
1785
|
+
raise IndexError("quaternion element index out of range")
|
|
1786
|
+
|
|
1787
|
+
mpz_to_ZZ(&item._denominator, self.d)
|
|
1788
|
+
|
|
1789
|
+
return item
|
|
1790
|
+
|
|
1791
|
+
def __reduce__(self):
|
|
1792
|
+
"""
|
|
1793
|
+
EXAMPLES::
|
|
1794
|
+
|
|
1795
|
+
sage: # needs fpylll sage.symbolic
|
|
1796
|
+
sage: K.<a> = QQ[2^(1/3)]; Q.<i,j,k> = QuaternionAlgebra(K, -3, a)
|
|
1797
|
+
sage: z = (i+j+k+a)^2; z
|
|
1798
|
+
a^2 + 4*a - 3 + 2*a*i + 2*a*j + 2*a*k
|
|
1799
|
+
sage: f, t = z.__reduce__()
|
|
1800
|
+
sage: f(*t)
|
|
1801
|
+
a^2 + 4*a - 3 + 2*a*i + 2*a*j + 2*a*k
|
|
1802
|
+
sage: loads(dumps(z)) == z
|
|
1803
|
+
True
|
|
1804
|
+
"""
|
|
1805
|
+
return (unpickle_QuaternionAlgebraElement_number_field_v0,
|
|
1806
|
+
(self._parent, (self[0], self[1], self[2], self[3])))
|
|
1807
|
+
|
|
1808
|
+
cpdef _add_(self, _right):
|
|
1809
|
+
"""
|
|
1810
|
+
Add ``self`` and ``_right``:
|
|
1811
|
+
|
|
1812
|
+
EXAMPLES::
|
|
1813
|
+
|
|
1814
|
+
sage: # needs fpylll sage.symbolic
|
|
1815
|
+
sage: K.<a> = QQ[2^(1/3)]; Q.<i,j,k> = QuaternionAlgebra(K, -3, a)
|
|
1816
|
+
sage: z = a + i + (2/3)*a^3*j + (1+a)*k
|
|
1817
|
+
sage: w = a - i - (2/3)*a^3*j + (1/3+a)*k
|
|
1818
|
+
sage: type(z)
|
|
1819
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_number_field'>
|
|
1820
|
+
sage: z._add_(w)
|
|
1821
|
+
2*a + (2*a + 4/3)*k
|
|
1822
|
+
|
|
1823
|
+
Check that the fix in :issue:`17099` is correct::
|
|
1824
|
+
|
|
1825
|
+
sage: x = polygen(QQ, 'x')
|
|
1826
|
+
sage: K = NumberField(x**3 + x - 1, 'a')
|
|
1827
|
+
sage: D.<i,j,k> = QuaternionAlgebra(K, -1, -3)
|
|
1828
|
+
sage: j/3 + (2*j)/3 == j
|
|
1829
|
+
True
|
|
1830
|
+
"""
|
|
1831
|
+
|
|
1832
|
+
# Given two quaternion algebra elements
|
|
1833
|
+
# a = (1/d1)*(x1 + y1 * i + z1 * j + w1 * k)
|
|
1834
|
+
# b = (1/d2)*(x2 + y2 * i + z2 * j + w2 * k)
|
|
1835
|
+
#
|
|
1836
|
+
# we compute their sum as
|
|
1837
|
+
#
|
|
1838
|
+
# (a + b) = (1/d3)*(x3 + y3 * i + z3 * j + w3 * k)
|
|
1839
|
+
#
|
|
1840
|
+
# with d3 = d1 * d2
|
|
1841
|
+
# x3 = d1 * x2 + d2 * x1
|
|
1842
|
+
# y3 = d1 * y2 + d2 * y1
|
|
1843
|
+
# z3 = d1 * z2 + d2 * z1
|
|
1844
|
+
# w3 = d1 * w2 + d2 * w1
|
|
1845
|
+
#
|
|
1846
|
+
# and then we reduce the sum by calling canonicalize().
|
|
1847
|
+
|
|
1848
|
+
# Note: We are assuming in this routine that the modulus is monic. This shouldn't
|
|
1849
|
+
# currently be an issue because it is impossible to create a number field with
|
|
1850
|
+
# a modulus that is not monic.
|
|
1851
|
+
|
|
1852
|
+
cdef QuaternionAlgebraElement_number_field right = _right
|
|
1853
|
+
cdef QuaternionAlgebraElement_number_field result = <QuaternionAlgebraElement_number_field> QuaternionAlgebraElement_number_field.__new__(QuaternionAlgebraElement_number_field)
|
|
1854
|
+
|
|
1855
|
+
fmpz_poly_set(result.a, self.a)
|
|
1856
|
+
fmpz_poly_set(result.b, self.b)
|
|
1857
|
+
fmpz_poly_set(result.modulus, self.modulus)
|
|
1858
|
+
result._parent = self._parent
|
|
1859
|
+
|
|
1860
|
+
fmpz_poly_scalar_mul_mpz(fU1, self.x, right.d)
|
|
1861
|
+
fmpz_poly_scalar_mul_mpz(fU2, right.x, self.d)
|
|
1862
|
+
fmpz_poly_add(result.x, fU1, fU2)
|
|
1863
|
+
|
|
1864
|
+
fmpz_poly_scalar_mul_mpz(fU1, self.y, right.d)
|
|
1865
|
+
fmpz_poly_scalar_mul_mpz(fU2, right.y, self.d)
|
|
1866
|
+
fmpz_poly_add(result.y, fU1, fU2)
|
|
1867
|
+
|
|
1868
|
+
fmpz_poly_scalar_mul_mpz(fU1, self.w, right.d)
|
|
1869
|
+
fmpz_poly_scalar_mul_mpz(fU2, right.w, self.d)
|
|
1870
|
+
fmpz_poly_add(result.w, fU1, fU2)
|
|
1871
|
+
|
|
1872
|
+
fmpz_poly_scalar_mul_mpz(fU1, self.z, right.d)
|
|
1873
|
+
fmpz_poly_scalar_mul_mpz(fU2, right.z, self.d)
|
|
1874
|
+
fmpz_poly_add(result.z, fU1, fU2)
|
|
1875
|
+
|
|
1876
|
+
mpz_mul(result.d, self.d, right.d)
|
|
1877
|
+
|
|
1878
|
+
result.canonicalize()
|
|
1879
|
+
|
|
1880
|
+
return result
|
|
1881
|
+
|
|
1882
|
+
cpdef _sub_(self, _right):
|
|
1883
|
+
"""
|
|
1884
|
+
Subtract ``_right`` from ``self``.
|
|
1885
|
+
|
|
1886
|
+
EXAMPLES::
|
|
1887
|
+
|
|
1888
|
+
sage: # needs fpylll sage.symbolic
|
|
1889
|
+
sage: K.<a> = QQ[2^(1/3)]; Q.<i,j,k> = QuaternionAlgebra(K, -3, a)
|
|
1890
|
+
sage: z = a + i + (2/3)*a^3*j + (1+a)*k
|
|
1891
|
+
sage: w = a - i - (2/3)*a^3*j + (1/3+a)*k
|
|
1892
|
+
sage: type(z)
|
|
1893
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_number_field'>
|
|
1894
|
+
sage: z._sub_(w)
|
|
1895
|
+
2*i + 8/3*j + 2/3*k
|
|
1896
|
+
"""
|
|
1897
|
+
# Implementation Note: To obtain _sub_, we simply replace every occurrence of
|
|
1898
|
+
# "add" in _add_ with "sub"; that is, we s/add/sub to get _sub_
|
|
1899
|
+
|
|
1900
|
+
# Note: We are assuming in this routine that the modulus is monic. This shouldn't
|
|
1901
|
+
# currently be an issue because it is impossible to create a number field with
|
|
1902
|
+
# a modulus that is not monic.
|
|
1903
|
+
cdef QuaternionAlgebraElement_number_field right = _right
|
|
1904
|
+
cdef QuaternionAlgebraElement_number_field result = <QuaternionAlgebraElement_number_field> QuaternionAlgebraElement_number_field.__new__(QuaternionAlgebraElement_number_field)
|
|
1905
|
+
|
|
1906
|
+
fmpz_poly_set(result.a, self.a)
|
|
1907
|
+
fmpz_poly_set(result.b, self.b)
|
|
1908
|
+
fmpz_poly_set(result.modulus, self.modulus)
|
|
1909
|
+
result._parent = self._parent
|
|
1910
|
+
|
|
1911
|
+
fmpz_poly_scalar_mul_mpz(fU1, self.x, right.d)
|
|
1912
|
+
fmpz_poly_scalar_mul_mpz(fU2, right.x, self.d)
|
|
1913
|
+
fmpz_poly_sub(result.x, fU1, fU2)
|
|
1914
|
+
|
|
1915
|
+
fmpz_poly_scalar_mul_mpz(fU1, self.y, right.d)
|
|
1916
|
+
fmpz_poly_scalar_mul_mpz(fU2, right.y, self.d)
|
|
1917
|
+
fmpz_poly_sub(result.y, fU1, fU2)
|
|
1918
|
+
|
|
1919
|
+
fmpz_poly_scalar_mul_mpz(fU1, self.w, right.d)
|
|
1920
|
+
fmpz_poly_scalar_mul_mpz(fU2, right.w, self.d)
|
|
1921
|
+
fmpz_poly_sub(result.w, fU1, fU2)
|
|
1922
|
+
|
|
1923
|
+
fmpz_poly_scalar_mul_mpz(fU1, self.z, right.d)
|
|
1924
|
+
fmpz_poly_scalar_mul_mpz(fU2, right.z, self.d)
|
|
1925
|
+
fmpz_poly_sub(result.z, fU1, fU2)
|
|
1926
|
+
|
|
1927
|
+
mpz_mul(result.d, self.d, right.d)
|
|
1928
|
+
|
|
1929
|
+
result.canonicalize()
|
|
1930
|
+
|
|
1931
|
+
return result
|
|
1932
|
+
|
|
1933
|
+
cpdef _mul_(self, _right):
|
|
1934
|
+
"""
|
|
1935
|
+
Multiply ``self`` and ``_right``.
|
|
1936
|
+
|
|
1937
|
+
EXAMPLES::
|
|
1938
|
+
|
|
1939
|
+
sage: # needs fpylll sage.symbolic
|
|
1940
|
+
sage: K.<a> = QQ[2^(1/3)]; Q.<i,j,k> = QuaternionAlgebra(K, -3, a)
|
|
1941
|
+
sage: z = a + i + (2/3)*a^3*j + (1+a)*k
|
|
1942
|
+
sage: w = a - i - (2/3)*a^3*j + (1/3+a)*k
|
|
1943
|
+
sage: type(z)
|
|
1944
|
+
<class 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_number_field'>
|
|
1945
|
+
sage: z._mul_(w)
|
|
1946
|
+
5*a^2 - 7/9*a + 9 + (-8/3*a^2 - 16/9*a)*i + (-6*a - 4)*j + (2*a^2 + 4/3*a)*k
|
|
1947
|
+
"""
|
|
1948
|
+
# We use the following formula for multiplication:
|
|
1949
|
+
#
|
|
1950
|
+
# Given two quaternion algebra elements
|
|
1951
|
+
#
|
|
1952
|
+
# a = (1/d1)*(x1 + y1 * i + z1 * j + w1 * k)
|
|
1953
|
+
# b = (1/d2)*(x2 + y2 * i + z2 * j + w2 * k)
|
|
1954
|
+
#
|
|
1955
|
+
# we compute their product as
|
|
1956
|
+
#
|
|
1957
|
+
# ab = (1/d3)*(x3 + y3 * i + z3 * j + w3 * k)
|
|
1958
|
+
#
|
|
1959
|
+
# where
|
|
1960
|
+
# x3 = t1 + a * t2 + b * (t3 - a*t4)
|
|
1961
|
+
# y3 = s1*(x2 + y2) - t1 - t2 + b*( s2*(z2 - w2) - t3 + t4)
|
|
1962
|
+
# z3 = t5 - a*t6 + t7 + a*t8
|
|
1963
|
+
# w3 = (x2 - y2)*s2 - t5 + t6 + s1*(z2 + w2) - t7 - t8
|
|
1964
|
+
#
|
|
1965
|
+
# and where
|
|
1966
|
+
# t1 = x1 * x2
|
|
1967
|
+
# t2 = y1 * y2
|
|
1968
|
+
# t3 = z1 * z2
|
|
1969
|
+
# t4 = w1 * w2
|
|
1970
|
+
# t5 = x2 * z1
|
|
1971
|
+
# t6 = y2 * w1
|
|
1972
|
+
# t7 = x1 * z2
|
|
1973
|
+
# t8 = y1 * w2
|
|
1974
|
+
#
|
|
1975
|
+
# s1 = x1 + y1
|
|
1976
|
+
# s2 = z1 + w1
|
|
1977
|
+
#
|
|
1978
|
+
# This takes more polynomial addition operations but fewer polynomial multiplication
|
|
1979
|
+
# operations than the "straightforward" multiplication method.
|
|
1980
|
+
#
|
|
1981
|
+
# There might be a way to optimize this formula further.
|
|
1982
|
+
|
|
1983
|
+
cdef QuaternionAlgebraElement_number_field right = _right
|
|
1984
|
+
cdef QuaternionAlgebraElement_number_field result = <QuaternionAlgebraElement_number_field> QuaternionAlgebraElement_number_field.__new__(QuaternionAlgebraElement_number_field)
|
|
1985
|
+
|
|
1986
|
+
mpz_set_si(result.d, 1)
|
|
1987
|
+
|
|
1988
|
+
fmpz_poly_set(result.a, self.a)
|
|
1989
|
+
fmpz_poly_set(result.b, self.b)
|
|
1990
|
+
fmpz_poly_set(result.modulus, self.modulus)
|
|
1991
|
+
result._parent = self._parent
|
|
1992
|
+
|
|
1993
|
+
fmpz_poly_mul(fT1, self.x, right.x) # t1 = x1 * x2
|
|
1994
|
+
fmpz_poly_mul(fT2, self.y, right.y) # t2 = y1 * y2
|
|
1995
|
+
fmpz_poly_mul(ft3, self.z, right.z) # t3 = x1 * x2
|
|
1996
|
+
fmpz_poly_mul(ft4, self.w, right.w) # t4 = w1 * w2
|
|
1997
|
+
fmpz_poly_mul(ft5, right.x, self.z) # t5 = x2 * z1
|
|
1998
|
+
fmpz_poly_mul(ft6, right.y, self.w) # t6 = y2 * w1
|
|
1999
|
+
fmpz_poly_mul(ft7, self.x, right.z) # t7 = x1 * z2
|
|
2000
|
+
fmpz_poly_mul(ft8, self.y, right.w) # t8 = y1 * w2
|
|
2001
|
+
|
|
2002
|
+
fmpz_poly_add(fs1, self.x, self.y) # s1 = x1 + y1
|
|
2003
|
+
fmpz_poly_add(fs2, self.z, self.w) # s2 = z1 + w
|
|
2004
|
+
|
|
2005
|
+
# ------------------
|
|
2006
|
+
|
|
2007
|
+
fmpz_poly_mul(fU1, self.a, ft4)
|
|
2008
|
+
fmpz_poly_sub(fU1, ft3, fU1)
|
|
2009
|
+
fmpz_poly_mul(fU1, fU1, self.b)
|
|
2010
|
+
fmpz_poly_mul(fU2, self.a, fT2)
|
|
2011
|
+
fmpz_poly_add(result.x, fT1, fU2)
|
|
2012
|
+
fmpz_poly_add(result.x, result.x, fU1)
|
|
2013
|
+
|
|
2014
|
+
# ------------------
|
|
2015
|
+
|
|
2016
|
+
fmpz_poly_sub(fU1, right.z, right.w)
|
|
2017
|
+
fmpz_poly_mul(fU1, fU1, fs2)
|
|
2018
|
+
fmpz_poly_sub(fU1, fU1, ft3)
|
|
2019
|
+
fmpz_poly_add(fU1, fU1, ft4)
|
|
2020
|
+
fmpz_poly_mul(fU1, fU1, self.b)
|
|
2021
|
+
fmpz_poly_sub(fU1, fU1, fT2)
|
|
2022
|
+
fmpz_poly_sub(fU1, fU1, fT1)
|
|
2023
|
+
fmpz_poly_add(fU2, right.x, right.y)
|
|
2024
|
+
fmpz_poly_mul(fU2, fs1, fU2)
|
|
2025
|
+
fmpz_poly_add(result.y, fU1, fU2)
|
|
2026
|
+
|
|
2027
|
+
# ------------------
|
|
2028
|
+
|
|
2029
|
+
fmpz_poly_mul(fU1, self.a, ft8)
|
|
2030
|
+
fmpz_poly_add(fU1, fU1, ft7)
|
|
2031
|
+
fmpz_poly_mul(fU2, self.a, ft6)
|
|
2032
|
+
fmpz_poly_sub(fU1, fU1, fU2)
|
|
2033
|
+
fmpz_poly_add(result.z, fU1, ft5)
|
|
2034
|
+
|
|
2035
|
+
# ------------------
|
|
2036
|
+
|
|
2037
|
+
fmpz_poly_add(fU1, right.z, right.w)
|
|
2038
|
+
fmpz_poly_mul(fU1, fU1, fs1)
|
|
2039
|
+
fmpz_poly_sub(fU1, fU1, ft7)
|
|
2040
|
+
fmpz_poly_sub(fU1, fU1, ft8)
|
|
2041
|
+
fmpz_poly_add(fU1, fU1, ft6)
|
|
2042
|
+
fmpz_poly_sub(fU1, fU1, ft5)
|
|
2043
|
+
fmpz_poly_sub(fU2, right.x, right.y)
|
|
2044
|
+
fmpz_poly_mul(fU2, fU2, fs2)
|
|
2045
|
+
fmpz_poly_add(result.w, fU1, fU2)
|
|
2046
|
+
|
|
2047
|
+
# At this point we have essentially computed the product, but we still
|
|
2048
|
+
# need to reduce modulo the modulus, which is what the following 12 lines do.
|
|
2049
|
+
#
|
|
2050
|
+
# When this was written, the version of flint in Sage had problems with
|
|
2051
|
+
# fpmz_poly_divrem(). This should be fixed in the newest version of
|
|
2052
|
+
# flint, which also should have some new functions which should do
|
|
2053
|
+
# this faster (Bill Hart sent an email to Bober and William about this).
|
|
2054
|
+
#
|
|
2055
|
+
# This should be fixed in the near future. (I don't know how much
|
|
2056
|
+
# faster it will be when it is updated, but the following code is
|
|
2057
|
+
# currently quite a bottleneck.
|
|
2058
|
+
|
|
2059
|
+
fmpz_poly_div(fT1, result.x, result.modulus)
|
|
2060
|
+
fmpz_poly_mul(fT1, fT1, result.modulus)
|
|
2061
|
+
fmpz_poly_sub(result.x, result.x, fT1)
|
|
2062
|
+
|
|
2063
|
+
fmpz_poly_div(fT1, result.y, result.modulus)
|
|
2064
|
+
fmpz_poly_mul(fT1, fT1, result.modulus)
|
|
2065
|
+
fmpz_poly_sub(result.y, result.y, fT1)
|
|
2066
|
+
|
|
2067
|
+
fmpz_poly_div(fT1, result.z, result.modulus)
|
|
2068
|
+
fmpz_poly_mul(fT1, fT1, result.modulus)
|
|
2069
|
+
fmpz_poly_sub(result.z, result.z, fT1)
|
|
2070
|
+
|
|
2071
|
+
fmpz_poly_div(fT1, result.w, result.modulus)
|
|
2072
|
+
fmpz_poly_mul(fT1, fT1, result.modulus)
|
|
2073
|
+
fmpz_poly_sub(result.w, result.w, fT1)
|
|
2074
|
+
|
|
2075
|
+
mpz_mul(result.d, self.d, right.d)
|
|
2076
|
+
|
|
2077
|
+
result.canonicalize()
|
|
2078
|
+
|
|
2079
|
+
return result
|
|
2080
|
+
|
|
2081
|
+
cdef inline canonicalize(self):
|
|
2082
|
+
"""
|
|
2083
|
+
Put the representation of this quaternion element into
|
|
2084
|
+
smallest form. For a = `(1/d)(x + yi + zj + wk)` we
|
|
2085
|
+
divide `a`, `x`, `y`, `z`, and `w` by the gcd of all of them.
|
|
2086
|
+
|
|
2087
|
+
TESTS::
|
|
2088
|
+
|
|
2089
|
+
sage: # needs fpylll sage.symbolic
|
|
2090
|
+
sage: F = QQ[3^(1/3)]
|
|
2091
|
+
sage: a = F.gen()
|
|
2092
|
+
sage: K.<i,j,k> = QuaternionAlgebra(F, -10 + a, -7 - a)
|
|
2093
|
+
sage: ((1/4 + 1/2 * i + a^3/7 * j + a/28 * k)*14*i)^3 # implicit doctest
|
|
2094
|
+
34503/2*a^2 + 132195/2*a + 791399/4 + (203/8*a^2 - 10591*a + 169225/4)*i
|
|
2095
|
+
+ (-84695/4*a^2 + 483413/8*a + 18591/4)*j + (-87/2*a^2 + 18156*a - 72525)*k
|
|
2096
|
+
"""
|
|
2097
|
+
|
|
2098
|
+
# Note: this function changes the module-level global variables
|
|
2099
|
+
# U1 and U2, so it isn't always safe to use this in the middle of
|
|
2100
|
+
# another function. Normally this function is called
|
|
2101
|
+
# at the end of an arithmetic routine, so this is fine.
|
|
2102
|
+
|
|
2103
|
+
# Implementation-wise, we compute the GCD's one at a time,
|
|
2104
|
+
# and quit if it ever becomes one
|
|
2105
|
+
|
|
2106
|
+
cdef fmpz_t content
|
|
2107
|
+
fmpz_init(content)
|
|
2108
|
+
fmpz_poly_content(content, self.x)
|
|
2109
|
+
fmpz_get_mpz(U1, content)
|
|
2110
|
+
mpz_gcd(U1, self.d, U1)
|
|
2111
|
+
if mpz_cmp_ui(U1, 1) != 0:
|
|
2112
|
+
fmpz_poly_content(content, self.y)
|
|
2113
|
+
fmpz_get_mpz(U2, content)
|
|
2114
|
+
mpz_gcd(U1, U1, U2)
|
|
2115
|
+
if mpz_cmp_ui(U1, 1) != 0:
|
|
2116
|
+
fmpz_poly_content(content, self.z)
|
|
2117
|
+
fmpz_get_mpz(U2, content)
|
|
2118
|
+
mpz_gcd(U1, U1, U2)
|
|
2119
|
+
if mpz_cmp_ui(U1, 1) != 0:
|
|
2120
|
+
fmpz_poly_content(content, self.w)
|
|
2121
|
+
fmpz_get_mpz(U2, content)
|
|
2122
|
+
mpz_gcd(U1, U1, U2)
|
|
2123
|
+
if mpz_cmp_ui(U1, 1) != 0:
|
|
2124
|
+
fmpz_poly_scalar_divexact_mpz(self.x, self.x, U1)
|
|
2125
|
+
fmpz_poly_scalar_divexact_mpz(self.y, self.y, U1)
|
|
2126
|
+
fmpz_poly_scalar_divexact_mpz(self.z, self.z, U1)
|
|
2127
|
+
fmpz_poly_scalar_divexact_mpz(self.w, self.w, U1)
|
|
2128
|
+
mpz_divexact(self.d, self.d, U1)
|
|
2129
|
+
|
|
2130
|
+
fmpz_clear(content)
|
|
2131
|
+
|
|
2132
|
+
|
|
2133
|
+
#######################################################################
|
|
2134
|
+
# Versioned unpickle functions
|
|
2135
|
+
#######################################################################
|
|
2136
|
+
|
|
2137
|
+
def unpickle_QuaternionAlgebraElement_generic_v0(*args):
|
|
2138
|
+
"""
|
|
2139
|
+
EXAMPLES::
|
|
2140
|
+
|
|
2141
|
+
sage: K.<X> = QQ[]
|
|
2142
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(Frac(K), -5,-19); z = 2/3 + i*X - X^2*j + X^3*k
|
|
2143
|
+
sage: f, t = z.__reduce__()
|
|
2144
|
+
sage: sage.algebras.quatalg.quaternion_algebra_element.unpickle_QuaternionAlgebraElement_generic_v0(*t)
|
|
2145
|
+
2/3 + X*i + (-X^2)*j + X^3*k
|
|
2146
|
+
sage: sage.algebras.quatalg.quaternion_algebra_element.unpickle_QuaternionAlgebraElement_generic_v0(*t) == z
|
|
2147
|
+
True
|
|
2148
|
+
"""
|
|
2149
|
+
return QuaternionAlgebraElement_generic(*args, check=False)
|
|
2150
|
+
|
|
2151
|
+
|
|
2152
|
+
def unpickle_QuaternionAlgebraElement_rational_field_v0(*args):
|
|
2153
|
+
"""
|
|
2154
|
+
EXAMPLES::
|
|
2155
|
+
|
|
2156
|
+
sage: Q.<i,j,k> = QuaternionAlgebra(-5,-19); a = 2/3 + i*5/7 - j*2/5 +19/2
|
|
2157
|
+
sage: f, t = a.__reduce__()
|
|
2158
|
+
sage: sage.algebras.quatalg.quaternion_algebra_element.unpickle_QuaternionAlgebraElement_rational_field_v0(*t)
|
|
2159
|
+
61/6 + 5/7*i - 2/5*j
|
|
2160
|
+
"""
|
|
2161
|
+
return QuaternionAlgebraElement_rational_field(*args, check=False)
|
|
2162
|
+
|
|
2163
|
+
|
|
2164
|
+
def unpickle_QuaternionAlgebraElement_number_field_v0(*args):
|
|
2165
|
+
"""
|
|
2166
|
+
EXAMPLES::
|
|
2167
|
+
|
|
2168
|
+
sage: # needs fpylll sage.symbolic
|
|
2169
|
+
sage: K.<a> = QQ[2^(1/3)]; Q.<i,j,k> = QuaternionAlgebra(K, -3, a); z = i + j
|
|
2170
|
+
sage: f, t = z.__reduce__()
|
|
2171
|
+
sage: sage.algebras.quatalg.quaternion_algebra_element.unpickle_QuaternionAlgebraElement_number_field_v0(*t)
|
|
2172
|
+
i + j
|
|
2173
|
+
sage: sage.algebras.quatalg.quaternion_algebra_element.unpickle_QuaternionAlgebraElement_number_field_v0(*t) == z
|
|
2174
|
+
True
|
|
2175
|
+
"""
|
|
2176
|
+
return QuaternionAlgebraElement_number_field(*args, check=False)
|