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.

Files changed (490) hide show
  1. PySingular.cpython-314-aarch64-linux-gnu.so +0 -0
  2. passagemath_singular-10.6.31rc3.dist-info/METADATA +183 -0
  3. passagemath_singular-10.6.31rc3.dist-info/RECORD +490 -0
  4. passagemath_singular-10.6.31rc3.dist-info/WHEEL +6 -0
  5. passagemath_singular-10.6.31rc3.dist-info/top_level.txt +3 -0
  6. passagemath_singular.libs/libSingular-4-6a2a8666.4.1.so +0 -0
  7. passagemath_singular.libs/libcddgmp-ac579979.so.0.1.3 +0 -0
  8. passagemath_singular.libs/libfactory-4-66e33516.4.1.so +0 -0
  9. passagemath_singular.libs/libflint-81de1160.so.21.0.0 +0 -0
  10. passagemath_singular.libs/libgf2x-fbd36f80.so.3.0.0 +0 -0
  11. passagemath_singular.libs/libgfortran-e1b7dfc8.so.5.0.0 +0 -0
  12. passagemath_singular.libs/libgmp-93ebf16a.so.10.5.0 +0 -0
  13. passagemath_singular.libs/libgsl-e3525837.so.28.0.0 +0 -0
  14. passagemath_singular.libs/libmpfr-e0f11cf3.so.6.2.1 +0 -0
  15. passagemath_singular.libs/libntl-0043a3a2.so.44.0.1 +0 -0
  16. passagemath_singular.libs/libomalloc-0-06512335.9.6.so +0 -0
  17. passagemath_singular.libs/libopenblasp-r0-4c5b64b1.3.29.so +0 -0
  18. passagemath_singular.libs/libpolys-4-cb7246b5.4.1.so +0 -0
  19. passagemath_singular.libs/libreadline-28330744.so.8.2 +0 -0
  20. passagemath_singular.libs/libsingular_resources-4-8c425241.4.1.so +0 -0
  21. passagemath_singular.libs/libtinfo-f81c2d16.so.6.3 +0 -0
  22. sage/algebras/all__sagemath_singular.py +3 -0
  23. sage/algebras/fusion_rings/all.py +19 -0
  24. sage/algebras/fusion_rings/f_matrix.py +2448 -0
  25. sage/algebras/fusion_rings/fast_parallel_fmats_methods.cpython-314-aarch64-linux-gnu.so +0 -0
  26. sage/algebras/fusion_rings/fast_parallel_fmats_methods.pxd +5 -0
  27. sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx +538 -0
  28. sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.cpython-314-aarch64-linux-gnu.so +0 -0
  29. sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pxd +3 -0
  30. sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx +331 -0
  31. sage/algebras/fusion_rings/fusion_double.py +899 -0
  32. sage/algebras/fusion_rings/fusion_ring.py +1580 -0
  33. sage/algebras/fusion_rings/poly_tup_engine.cpython-314-aarch64-linux-gnu.so +0 -0
  34. sage/algebras/fusion_rings/poly_tup_engine.pxd +24 -0
  35. sage/algebras/fusion_rings/poly_tup_engine.pyx +579 -0
  36. sage/algebras/fusion_rings/shm_managers.cpython-314-aarch64-linux-gnu.so +0 -0
  37. sage/algebras/fusion_rings/shm_managers.pxd +24 -0
  38. sage/algebras/fusion_rings/shm_managers.pyx +780 -0
  39. sage/algebras/letterplace/all.py +1 -0
  40. sage/algebras/letterplace/free_algebra_element_letterplace.cpython-314-aarch64-linux-gnu.so +0 -0
  41. sage/algebras/letterplace/free_algebra_element_letterplace.pxd +18 -0
  42. sage/algebras/letterplace/free_algebra_element_letterplace.pyx +755 -0
  43. sage/algebras/letterplace/free_algebra_letterplace.cpython-314-aarch64-linux-gnu.so +0 -0
  44. sage/algebras/letterplace/free_algebra_letterplace.pxd +35 -0
  45. sage/algebras/letterplace/free_algebra_letterplace.pyx +914 -0
  46. sage/algebras/letterplace/letterplace_ideal.cpython-314-aarch64-linux-gnu.so +0 -0
  47. sage/algebras/letterplace/letterplace_ideal.pyx +408 -0
  48. sage/algebras/quatalg/all.py +2 -0
  49. sage/algebras/quatalg/quaternion_algebra.py +4778 -0
  50. sage/algebras/quatalg/quaternion_algebra_cython.cpython-314-aarch64-linux-gnu.so +0 -0
  51. sage/algebras/quatalg/quaternion_algebra_cython.pyx +261 -0
  52. sage/algebras/quatalg/quaternion_algebra_element.cpython-314-aarch64-linux-gnu.so +0 -0
  53. sage/algebras/quatalg/quaternion_algebra_element.pxd +29 -0
  54. sage/algebras/quatalg/quaternion_algebra_element.pyx +2176 -0
  55. sage/all__sagemath_singular.py +11 -0
  56. sage/ext_data/all__sagemath_singular.py +1 -0
  57. sage/ext_data/singular/function_field/core.lib +98 -0
  58. sage/interfaces/all__sagemath_singular.py +1 -0
  59. sage/interfaces/singular.py +2835 -0
  60. sage/libs/all__sagemath_singular.py +1 -0
  61. sage/libs/singular/__init__.py +1 -0
  62. sage/libs/singular/decl.pxd +1168 -0
  63. sage/libs/singular/function.cpython-314-aarch64-linux-gnu.so +0 -0
  64. sage/libs/singular/function.pxd +87 -0
  65. sage/libs/singular/function.pyx +1901 -0
  66. sage/libs/singular/function_factory.py +61 -0
  67. sage/libs/singular/groebner_strategy.cpython-314-aarch64-linux-gnu.so +0 -0
  68. sage/libs/singular/groebner_strategy.pxd +22 -0
  69. sage/libs/singular/groebner_strategy.pyx +582 -0
  70. sage/libs/singular/option.cpython-314-aarch64-linux-gnu.so +0 -0
  71. sage/libs/singular/option.pyx +671 -0
  72. sage/libs/singular/polynomial.cpython-314-aarch64-linux-gnu.so +0 -0
  73. sage/libs/singular/polynomial.pxd +39 -0
  74. sage/libs/singular/polynomial.pyx +661 -0
  75. sage/libs/singular/ring.cpython-314-aarch64-linux-gnu.so +0 -0
  76. sage/libs/singular/ring.pxd +58 -0
  77. sage/libs/singular/ring.pyx +893 -0
  78. sage/libs/singular/singular.cpython-314-aarch64-linux-gnu.so +0 -0
  79. sage/libs/singular/singular.pxd +72 -0
  80. sage/libs/singular/singular.pyx +1944 -0
  81. sage/libs/singular/standard_options.py +145 -0
  82. sage/matrix/all__sagemath_singular.py +1 -0
  83. sage/matrix/matrix_mpolynomial_dense.cpython-314-aarch64-linux-gnu.so +0 -0
  84. sage/matrix/matrix_mpolynomial_dense.pxd +7 -0
  85. sage/matrix/matrix_mpolynomial_dense.pyx +615 -0
  86. sage/rings/all__sagemath_singular.py +1 -0
  87. sage/rings/function_field/all__sagemath_singular.py +1 -0
  88. sage/rings/function_field/derivations_polymod.py +911 -0
  89. sage/rings/function_field/element_polymod.cpython-314-aarch64-linux-gnu.so +0 -0
  90. sage/rings/function_field/element_polymod.pyx +406 -0
  91. sage/rings/function_field/function_field_polymod.py +2611 -0
  92. sage/rings/function_field/ideal_polymod.py +1775 -0
  93. sage/rings/function_field/order_polymod.py +1475 -0
  94. sage/rings/function_field/place_polymod.py +681 -0
  95. sage/rings/polynomial/all__sagemath_singular.py +1 -0
  96. sage/rings/polynomial/multi_polynomial_ideal_libsingular.cpython-314-aarch64-linux-gnu.so +0 -0
  97. sage/rings/polynomial/multi_polynomial_ideal_libsingular.pxd +5 -0
  98. sage/rings/polynomial/multi_polynomial_ideal_libsingular.pyx +339 -0
  99. sage/rings/polynomial/multi_polynomial_libsingular.cpython-314-aarch64-linux-gnu.so +0 -0
  100. sage/rings/polynomial/multi_polynomial_libsingular.pxd +30 -0
  101. sage/rings/polynomial/multi_polynomial_libsingular.pyx +6277 -0
  102. sage/rings/polynomial/plural.cpython-314-aarch64-linux-gnu.so +0 -0
  103. sage/rings/polynomial/plural.pxd +48 -0
  104. sage/rings/polynomial/plural.pyx +3171 -0
  105. sage/symbolic/all__sagemath_singular.py +1 -0
  106. sage/symbolic/comparison_impl.pxi +428 -0
  107. sage/symbolic/constants_c_impl.pxi +178 -0
  108. sage/symbolic/expression.cpython-314-aarch64-linux-gnu.so +0 -0
  109. sage/symbolic/expression.pxd +7 -0
  110. sage/symbolic/expression.pyx +14200 -0
  111. sage/symbolic/getitem_impl.pxi +202 -0
  112. sage/symbolic/pynac.pxi +572 -0
  113. sage/symbolic/pynac_constant_impl.pxi +133 -0
  114. sage/symbolic/pynac_function_impl.pxi +206 -0
  115. sage/symbolic/pynac_impl.pxi +2576 -0
  116. sage/symbolic/pynac_wrap.h +124 -0
  117. sage/symbolic/series_impl.pxi +272 -0
  118. sage/symbolic/substitution_map_impl.pxi +94 -0
  119. sage_wheels/bin/ESingular +0 -0
  120. sage_wheels/bin/Singular +0 -0
  121. sage_wheels/bin/TSingular +0 -0
  122. sage_wheels/lib/singular/MOD/cohomo.la +41 -0
  123. sage_wheels/lib/singular/MOD/cohomo.so +0 -0
  124. sage_wheels/lib/singular/MOD/customstd.la +41 -0
  125. sage_wheels/lib/singular/MOD/customstd.so +0 -0
  126. sage_wheels/lib/singular/MOD/freealgebra.la +41 -0
  127. sage_wheels/lib/singular/MOD/freealgebra.so +0 -0
  128. sage_wheels/lib/singular/MOD/gfanlib.la +41 -0
  129. sage_wheels/lib/singular/MOD/gfanlib.so +0 -0
  130. sage_wheels/lib/singular/MOD/gitfan.la +41 -0
  131. sage_wheels/lib/singular/MOD/gitfan.so +0 -0
  132. sage_wheels/lib/singular/MOD/interval.la +41 -0
  133. sage_wheels/lib/singular/MOD/interval.so +0 -0
  134. sage_wheels/lib/singular/MOD/loctriv.la +41 -0
  135. sage_wheels/lib/singular/MOD/loctriv.so +0 -0
  136. sage_wheels/lib/singular/MOD/machinelearning.la +41 -0
  137. sage_wheels/lib/singular/MOD/machinelearning.so +0 -0
  138. sage_wheels/lib/singular/MOD/p_Procs_FieldGeneral.la +41 -0
  139. sage_wheels/lib/singular/MOD/p_Procs_FieldGeneral.so +0 -0
  140. sage_wheels/lib/singular/MOD/p_Procs_FieldIndep.la +41 -0
  141. sage_wheels/lib/singular/MOD/p_Procs_FieldIndep.so +0 -0
  142. sage_wheels/lib/singular/MOD/p_Procs_FieldQ.la +41 -0
  143. sage_wheels/lib/singular/MOD/p_Procs_FieldQ.so +0 -0
  144. sage_wheels/lib/singular/MOD/p_Procs_FieldZp.la +41 -0
  145. sage_wheels/lib/singular/MOD/p_Procs_FieldZp.so +0 -0
  146. sage_wheels/lib/singular/MOD/partialgb.la +41 -0
  147. sage_wheels/lib/singular/MOD/partialgb.so +0 -0
  148. sage_wheels/lib/singular/MOD/pyobject.la +41 -0
  149. sage_wheels/lib/singular/MOD/pyobject.so +0 -0
  150. sage_wheels/lib/singular/MOD/singmathic.la +41 -0
  151. sage_wheels/lib/singular/MOD/singmathic.so +0 -0
  152. sage_wheels/lib/singular/MOD/sispasm.la +41 -0
  153. sage_wheels/lib/singular/MOD/sispasm.so +0 -0
  154. sage_wheels/lib/singular/MOD/subsets.la +41 -0
  155. sage_wheels/lib/singular/MOD/subsets.so +0 -0
  156. sage_wheels/lib/singular/MOD/systhreads.la +41 -0
  157. sage_wheels/lib/singular/MOD/systhreads.so +0 -0
  158. sage_wheels/lib/singular/MOD/syzextra.la +41 -0
  159. sage_wheels/lib/singular/MOD/syzextra.so +0 -0
  160. sage_wheels/libexec/singular/MOD/change_cost +0 -0
  161. sage_wheels/libexec/singular/MOD/singularsurf +11 -0
  162. sage_wheels/libexec/singular/MOD/singularsurf_jupyter +9 -0
  163. sage_wheels/libexec/singular/MOD/singularsurf_win +10 -0
  164. sage_wheels/libexec/singular/MOD/solve_IP +0 -0
  165. sage_wheels/libexec/singular/MOD/surfex +16 -0
  166. sage_wheels/libexec/singular/MOD/toric_ideal +0 -0
  167. sage_wheels/share/factory/gftables/10201 +342 -0
  168. sage_wheels/share/factory/gftables/1024 +37 -0
  169. sage_wheels/share/factory/gftables/10609 +356 -0
  170. sage_wheels/share/factory/gftables/11449 +384 -0
  171. sage_wheels/share/factory/gftables/11881 +398 -0
  172. sage_wheels/share/factory/gftables/121 +6 -0
  173. sage_wheels/share/factory/gftables/12167 +408 -0
  174. sage_wheels/share/factory/gftables/125 +7 -0
  175. sage_wheels/share/factory/gftables/12769 +428 -0
  176. sage_wheels/share/factory/gftables/128 +7 -0
  177. sage_wheels/share/factory/gftables/1331 +47 -0
  178. sage_wheels/share/factory/gftables/1369 +48 -0
  179. sage_wheels/share/factory/gftables/14641 +490 -0
  180. sage_wheels/share/factory/gftables/15625 +523 -0
  181. sage_wheels/share/factory/gftables/16 +3 -0
  182. sage_wheels/share/factory/gftables/16129 +540 -0
  183. sage_wheels/share/factory/gftables/16384 +549 -0
  184. sage_wheels/share/factory/gftables/16807 +563 -0
  185. sage_wheels/share/factory/gftables/1681 +58 -0
  186. sage_wheels/share/factory/gftables/169 +8 -0
  187. sage_wheels/share/factory/gftables/17161 +574 -0
  188. sage_wheels/share/factory/gftables/1849 +64 -0
  189. sage_wheels/share/factory/gftables/18769 +628 -0
  190. sage_wheels/share/factory/gftables/19321 +646 -0
  191. sage_wheels/share/factory/gftables/19683 +659 -0
  192. sage_wheels/share/factory/gftables/2048 +71 -0
  193. sage_wheels/share/factory/gftables/2187 +75 -0
  194. sage_wheels/share/factory/gftables/2197 +76 -0
  195. sage_wheels/share/factory/gftables/2209 +76 -0
  196. sage_wheels/share/factory/gftables/22201 +742 -0
  197. sage_wheels/share/factory/gftables/22801 +762 -0
  198. sage_wheels/share/factory/gftables/2401 +82 -0
  199. sage_wheels/share/factory/gftables/243 +11 -0
  200. sage_wheels/share/factory/gftables/24389 +815 -0
  201. sage_wheels/share/factory/gftables/24649 +824 -0
  202. sage_wheels/share/factory/gftables/25 +3 -0
  203. sage_wheels/share/factory/gftables/256 +11 -0
  204. sage_wheels/share/factory/gftables/26569 +888 -0
  205. sage_wheels/share/factory/gftables/27 +3 -0
  206. sage_wheels/share/factory/gftables/27889 +932 -0
  207. sage_wheels/share/factory/gftables/2809 +96 -0
  208. sage_wheels/share/factory/gftables/28561 +954 -0
  209. sage_wheels/share/factory/gftables/289 +12 -0
  210. sage_wheels/share/factory/gftables/29791 +995 -0
  211. sage_wheels/share/factory/gftables/29929 +1000 -0
  212. sage_wheels/share/factory/gftables/3125 +107 -0
  213. sage_wheels/share/factory/gftables/32 +4 -0
  214. sage_wheels/share/factory/gftables/32041 +1070 -0
  215. sage_wheels/share/factory/gftables/32761 +1094 -0
  216. sage_wheels/share/factory/gftables/32768 +1095 -0
  217. sage_wheels/share/factory/gftables/343 +14 -0
  218. sage_wheels/share/factory/gftables/3481 +118 -0
  219. sage_wheels/share/factory/gftables/361 +14 -0
  220. sage_wheels/share/factory/gftables/36481 +1218 -0
  221. sage_wheels/share/factory/gftables/3721 +126 -0
  222. sage_wheels/share/factory/gftables/37249 +1244 -0
  223. sage_wheels/share/factory/gftables/38809 +1296 -0
  224. sage_wheels/share/factory/gftables/39601 +1322 -0
  225. sage_wheels/share/factory/gftables/4 +3 -0
  226. sage_wheels/share/factory/gftables/4096 +139 -0
  227. sage_wheels/share/factory/gftables/44521 +1486 -0
  228. sage_wheels/share/factory/gftables/4489 +152 -0
  229. sage_wheels/share/factory/gftables/49 +4 -0
  230. sage_wheels/share/factory/gftables/4913 +166 -0
  231. sage_wheels/share/factory/gftables/49729 +1660 -0
  232. sage_wheels/share/factory/gftables/5041 +170 -0
  233. sage_wheels/share/factory/gftables/50653 +1691 -0
  234. sage_wheels/share/factory/gftables/512 +20 -0
  235. sage_wheels/share/factory/gftables/51529 +1720 -0
  236. sage_wheels/share/factory/gftables/52441 +1750 -0
  237. sage_wheels/share/factory/gftables/529 +20 -0
  238. sage_wheels/share/factory/gftables/5329 +180 -0
  239. sage_wheels/share/factory/gftables/54289 +1812 -0
  240. sage_wheels/share/factory/gftables/57121 +1906 -0
  241. sage_wheels/share/factory/gftables/58081 +1938 -0
  242. sage_wheels/share/factory/gftables/59049 +1971 -0
  243. sage_wheels/share/factory/gftables/6241 +210 -0
  244. sage_wheels/share/factory/gftables/625 +23 -0
  245. sage_wheels/share/factory/gftables/63001 +2102 -0
  246. sage_wheels/share/factory/gftables/64 +5 -0
  247. sage_wheels/share/factory/gftables/6561 +221 -0
  248. sage_wheels/share/factory/gftables/6859 +231 -0
  249. sage_wheels/share/factory/gftables/6889 +232 -0
  250. sage_wheels/share/factory/gftables/729 +27 -0
  251. sage_wheels/share/factory/gftables/7921 +266 -0
  252. sage_wheels/share/factory/gftables/8 +3 -0
  253. sage_wheels/share/factory/gftables/81 +5 -0
  254. sage_wheels/share/factory/gftables/8192 +276 -0
  255. sage_wheels/share/factory/gftables/841 +30 -0
  256. sage_wheels/share/factory/gftables/9 +3 -0
  257. sage_wheels/share/factory/gftables/9409 +316 -0
  258. sage_wheels/share/factory/gftables/961 +34 -0
  259. sage_wheels/share/info/singular.info +191898 -0
  260. sage_wheels/share/singular/LIB/GND.lib +1359 -0
  261. sage_wheels/share/singular/LIB/JMBTest.lib +976 -0
  262. sage_wheels/share/singular/LIB/JMSConst.lib +1363 -0
  263. sage_wheels/share/singular/LIB/KVequiv.lib +699 -0
  264. sage_wheels/share/singular/LIB/SingularityDBM.lib +491 -0
  265. sage_wheels/share/singular/LIB/VecField.lib +1542 -0
  266. sage_wheels/share/singular/LIB/absfact.lib +959 -0
  267. sage_wheels/share/singular/LIB/ainvar.lib +730 -0
  268. sage_wheels/share/singular/LIB/aksaka.lib +419 -0
  269. sage_wheels/share/singular/LIB/alexpoly.lib +2542 -0
  270. sage_wheels/share/singular/LIB/algebra.lib +1193 -0
  271. sage_wheels/share/singular/LIB/all.lib +136 -0
  272. sage_wheels/share/singular/LIB/arcpoint.lib +514 -0
  273. sage_wheels/share/singular/LIB/arnold.lib +4553 -0
  274. sage_wheels/share/singular/LIB/arnoldclassify.lib +2058 -0
  275. sage_wheels/share/singular/LIB/arr.lib +3486 -0
  276. sage_wheels/share/singular/LIB/assprimeszerodim.lib +755 -0
  277. sage_wheels/share/singular/LIB/autgradalg.lib +3361 -0
  278. sage_wheels/share/singular/LIB/bfun.lib +1964 -0
  279. sage_wheels/share/singular/LIB/bimodules.lib +774 -0
  280. sage_wheels/share/singular/LIB/brillnoether.lib +226 -0
  281. sage_wheels/share/singular/LIB/brnoeth.lib +5017 -0
  282. sage_wheels/share/singular/LIB/central.lib +2169 -0
  283. sage_wheels/share/singular/LIB/chern.lib +4162 -0
  284. sage_wheels/share/singular/LIB/cimonom.lib +571 -0
  285. sage_wheels/share/singular/LIB/cisimplicial.lib +1835 -0
  286. sage_wheels/share/singular/LIB/classify.lib +3239 -0
  287. sage_wheels/share/singular/LIB/classify2.lib +1462 -0
  288. sage_wheels/share/singular/LIB/classifyMapGerms.lib +1515 -0
  289. sage_wheels/share/singular/LIB/classify_aeq.lib +3253 -0
  290. sage_wheels/share/singular/LIB/classifyceq.lib +2092 -0
  291. sage_wheels/share/singular/LIB/classifyci.lib +1133 -0
  292. sage_wheels/share/singular/LIB/combinat.lib +91 -0
  293. sage_wheels/share/singular/LIB/compregb.lib +276 -0
  294. sage_wheels/share/singular/LIB/control.lib +1636 -0
  295. sage_wheels/share/singular/LIB/crypto.lib +3795 -0
  296. sage_wheels/share/singular/LIB/curveInv.lib +667 -0
  297. sage_wheels/share/singular/LIB/curvepar.lib +1817 -0
  298. sage_wheels/share/singular/LIB/customstd.lib +100 -0
  299. sage_wheels/share/singular/LIB/deRham.lib +5979 -0
  300. sage_wheels/share/singular/LIB/decodegb.lib +2134 -0
  301. sage_wheels/share/singular/LIB/decomp.lib +1655 -0
  302. sage_wheels/share/singular/LIB/deflation.lib +872 -0
  303. sage_wheels/share/singular/LIB/deform.lib +925 -0
  304. sage_wheels/share/singular/LIB/difform.lib +3055 -0
  305. sage_wheels/share/singular/LIB/divisors.lib +750 -0
  306. sage_wheels/share/singular/LIB/dmod.lib +5817 -0
  307. sage_wheels/share/singular/LIB/dmodapp.lib +3269 -0
  308. sage_wheels/share/singular/LIB/dmodideal.lib +1211 -0
  309. sage_wheels/share/singular/LIB/dmodloc.lib +2645 -0
  310. sage_wheels/share/singular/LIB/dmodvar.lib +818 -0
  311. sage_wheels/share/singular/LIB/dummy.lib +17 -0
  312. sage_wheels/share/singular/LIB/elim.lib +1009 -0
  313. sage_wheels/share/singular/LIB/ellipticcovers.lib +548 -0
  314. sage_wheels/share/singular/LIB/enumpoints.lib +146 -0
  315. sage_wheels/share/singular/LIB/equising.lib +2127 -0
  316. sage_wheels/share/singular/LIB/ffmodstd.lib +2384 -0
  317. sage_wheels/share/singular/LIB/ffsolve.lib +1289 -0
  318. sage_wheels/share/singular/LIB/findifs.lib +778 -0
  319. sage_wheels/share/singular/LIB/finitediff.lib +1768 -0
  320. sage_wheels/share/singular/LIB/finvar.lib +7989 -0
  321. sage_wheels/share/singular/LIB/fpadim.lib +2429 -0
  322. sage_wheels/share/singular/LIB/fpalgebras.lib +1666 -0
  323. sage_wheels/share/singular/LIB/fpaprops.lib +1462 -0
  324. sage_wheels/share/singular/LIB/freegb.lib +3853 -0
  325. sage_wheels/share/singular/LIB/general.lib +1350 -0
  326. sage_wheels/share/singular/LIB/gfan.lib +1768 -0
  327. sage_wheels/share/singular/LIB/gitfan.lib +3130 -0
  328. sage_wheels/share/singular/LIB/gkdim.lib +99 -0
  329. sage_wheels/share/singular/LIB/gmspoly.lib +589 -0
  330. sage_wheels/share/singular/LIB/gmssing.lib +1739 -0
  331. sage_wheels/share/singular/LIB/goettsche.lib +909 -0
  332. sage_wheels/share/singular/LIB/graal.lib +1366 -0
  333. sage_wheels/share/singular/LIB/gradedModules.lib +2541 -0
  334. sage_wheels/share/singular/LIB/graphics.lib +360 -0
  335. sage_wheels/share/singular/LIB/grobcov.lib +7706 -0
  336. sage_wheels/share/singular/LIB/groups.lib +1123 -0
  337. sage_wheels/share/singular/LIB/grwalk.lib +507 -0
  338. sage_wheels/share/singular/LIB/hdepth.lib +194 -0
  339. sage_wheels/share/singular/LIB/help.cnf +57 -0
  340. sage_wheels/share/singular/LIB/hess.lib +1946 -0
  341. sage_wheels/share/singular/LIB/hnoether.lib +4292 -0
  342. sage_wheels/share/singular/LIB/hodge.lib +400 -0
  343. sage_wheels/share/singular/LIB/homolog.lib +1965 -0
  344. sage_wheels/share/singular/LIB/hyperel.lib +975 -0
  345. sage_wheels/share/singular/LIB/inout.lib +679 -0
  346. sage_wheels/share/singular/LIB/integralbasis.lib +6224 -0
  347. sage_wheels/share/singular/LIB/interval.lib +1418 -0
  348. sage_wheels/share/singular/LIB/intprog.lib +778 -0
  349. sage_wheels/share/singular/LIB/invar.lib +443 -0
  350. sage_wheels/share/singular/LIB/involut.lib +980 -0
  351. sage_wheels/share/singular/LIB/jacobson.lib +1215 -0
  352. sage_wheels/share/singular/LIB/kskernel.lib +534 -0
  353. sage_wheels/share/singular/LIB/latex.lib +3146 -0
  354. sage_wheels/share/singular/LIB/lejeune.lib +651 -0
  355. sage_wheels/share/singular/LIB/linalg.lib +2040 -0
  356. sage_wheels/share/singular/LIB/locnormal.lib +212 -0
  357. sage_wheels/share/singular/LIB/lrcalc.lib +526 -0
  358. sage_wheels/share/singular/LIB/makedbm.lib +294 -0
  359. sage_wheels/share/singular/LIB/mathml.lib +813 -0
  360. sage_wheels/share/singular/LIB/matrix.lib +1372 -0
  361. sage_wheels/share/singular/LIB/maxlike.lib +1132 -0
  362. sage_wheels/share/singular/LIB/methods.lib +212 -0
  363. sage_wheels/share/singular/LIB/moddiq.lib +322 -0
  364. sage_wheels/share/singular/LIB/modfinduni.lib +181 -0
  365. sage_wheels/share/singular/LIB/modnormal.lib +218 -0
  366. sage_wheels/share/singular/LIB/modprimdec.lib +1278 -0
  367. sage_wheels/share/singular/LIB/modquotient.lib +269 -0
  368. sage_wheels/share/singular/LIB/modstd.lib +1024 -0
  369. sage_wheels/share/singular/LIB/modular.lib +545 -0
  370. sage_wheels/share/singular/LIB/modules.lib +2561 -0
  371. sage_wheels/share/singular/LIB/modwalk.lib +609 -0
  372. sage_wheels/share/singular/LIB/mondromy.lib +1016 -0
  373. sage_wheels/share/singular/LIB/monomialideal.lib +3851 -0
  374. sage_wheels/share/singular/LIB/mprimdec.lib +2353 -0
  375. sage_wheels/share/singular/LIB/mregular.lib +1863 -0
  376. sage_wheels/share/singular/LIB/multigrading.lib +5629 -0
  377. sage_wheels/share/singular/LIB/ncHilb.lib +777 -0
  378. sage_wheels/share/singular/LIB/ncModslimgb.lib +791 -0
  379. sage_wheels/share/singular/LIB/ncalg.lib +16311 -0
  380. sage_wheels/share/singular/LIB/ncall.lib +31 -0
  381. sage_wheels/share/singular/LIB/ncdecomp.lib +468 -0
  382. sage_wheels/share/singular/LIB/ncfactor.lib +13371 -0
  383. sage_wheels/share/singular/LIB/ncfrac.lib +1023 -0
  384. sage_wheels/share/singular/LIB/nchilbert.lib +448 -0
  385. sage_wheels/share/singular/LIB/nchomolog.lib +759 -0
  386. sage_wheels/share/singular/LIB/ncloc.lib +361 -0
  387. sage_wheels/share/singular/LIB/ncpreim.lib +795 -0
  388. sage_wheels/share/singular/LIB/ncrat.lib +2849 -0
  389. sage_wheels/share/singular/LIB/nctools.lib +1887 -0
  390. sage_wheels/share/singular/LIB/nets.lib +1456 -0
  391. sage_wheels/share/singular/LIB/nfmodstd.lib +1000 -0
  392. sage_wheels/share/singular/LIB/nfmodsyz.lib +732 -0
  393. sage_wheels/share/singular/LIB/noether.lib +1106 -0
  394. sage_wheels/share/singular/LIB/normal.lib +8700 -0
  395. sage_wheels/share/singular/LIB/normaliz.lib +2226 -0
  396. sage_wheels/share/singular/LIB/ntsolve.lib +362 -0
  397. sage_wheels/share/singular/LIB/numerAlg.lib +560 -0
  398. sage_wheels/share/singular/LIB/numerDecom.lib +2261 -0
  399. sage_wheels/share/singular/LIB/olga.lib +1933 -0
  400. sage_wheels/share/singular/LIB/orbitparam.lib +351 -0
  401. sage_wheels/share/singular/LIB/parallel.lib +319 -0
  402. sage_wheels/share/singular/LIB/paraplanecurves.lib +3110 -0
  403. sage_wheels/share/singular/LIB/perron.lib +202 -0
  404. sage_wheels/share/singular/LIB/pfd.lib +2223 -0
  405. sage_wheels/share/singular/LIB/phindex.lib +642 -0
  406. sage_wheels/share/singular/LIB/pointid.lib +673 -0
  407. sage_wheels/share/singular/LIB/polybori.lib +1430 -0
  408. sage_wheels/share/singular/LIB/polyclass.lib +525 -0
  409. sage_wheels/share/singular/LIB/polylib.lib +1174 -0
  410. sage_wheels/share/singular/LIB/polymake.lib +1902 -0
  411. sage_wheels/share/singular/LIB/presolve.lib +1533 -0
  412. sage_wheels/share/singular/LIB/primdec.lib +9576 -0
  413. sage_wheels/share/singular/LIB/primdecint.lib +1782 -0
  414. sage_wheels/share/singular/LIB/primitiv.lib +401 -0
  415. sage_wheels/share/singular/LIB/puiseuxexpansions.lib +1631 -0
  416. sage_wheels/share/singular/LIB/purityfiltration.lib +960 -0
  417. sage_wheels/share/singular/LIB/qhmoduli.lib +1561 -0
  418. sage_wheels/share/singular/LIB/qmatrix.lib +293 -0
  419. sage_wheels/share/singular/LIB/random.lib +455 -0
  420. sage_wheels/share/singular/LIB/ratgb.lib +489 -0
  421. sage_wheels/share/singular/LIB/realclassify.lib +5759 -0
  422. sage_wheels/share/singular/LIB/realizationMatroids.lib +772 -0
  423. sage_wheels/share/singular/LIB/realrad.lib +1197 -0
  424. sage_wheels/share/singular/LIB/recover.lib +2628 -0
  425. sage_wheels/share/singular/LIB/redcgs.lib +3984 -0
  426. sage_wheels/share/singular/LIB/reesclos.lib +465 -0
  427. sage_wheels/share/singular/LIB/resbinomial.lib +2802 -0
  428. sage_wheels/share/singular/LIB/resgraph.lib +789 -0
  429. sage_wheels/share/singular/LIB/resjung.lib +820 -0
  430. sage_wheels/share/singular/LIB/resolve.lib +5110 -0
  431. sage_wheels/share/singular/LIB/resources.lib +170 -0
  432. sage_wheels/share/singular/LIB/reszeta.lib +5473 -0
  433. sage_wheels/share/singular/LIB/ring.lib +1328 -0
  434. sage_wheels/share/singular/LIB/ringgb.lib +343 -0
  435. sage_wheels/share/singular/LIB/rinvar.lib +1153 -0
  436. sage_wheels/share/singular/LIB/rootisolation.lib +1481 -0
  437. sage_wheels/share/singular/LIB/rootsmr.lib +709 -0
  438. sage_wheels/share/singular/LIB/rootsur.lib +886 -0
  439. sage_wheels/share/singular/LIB/rstandard.lib +607 -0
  440. sage_wheels/share/singular/LIB/rwalk.lib +336 -0
  441. sage_wheels/share/singular/LIB/sagbi.lib +1353 -0
  442. sage_wheels/share/singular/LIB/sagbiNormaliz.lib +1622 -0
  443. sage_wheels/share/singular/LIB/sagbiNormaliz0.lib +1498 -0
  444. sage_wheels/share/singular/LIB/sagbigrob.lib +449 -0
  445. sage_wheels/share/singular/LIB/schreyer.lib +321 -0
  446. sage_wheels/share/singular/LIB/schubert.lib +2551 -0
  447. sage_wheels/share/singular/LIB/sets.lib +524 -0
  448. sage_wheels/share/singular/LIB/sheafcoh.lib +1663 -0
  449. sage_wheels/share/singular/LIB/signcond.lib +437 -0
  450. sage_wheels/share/singular/LIB/sing.lib +1094 -0
  451. sage_wheels/share/singular/LIB/sing4ti2.lib +419 -0
  452. sage_wheels/share/singular/LIB/solve.lib +2243 -0
  453. sage_wheels/share/singular/LIB/spcurve.lib +1077 -0
  454. sage_wheels/share/singular/LIB/spectrum.lib +62 -0
  455. sage_wheels/share/singular/LIB/sresext.lib +757 -0
  456. sage_wheels/share/singular/LIB/ssi.lib +143 -0
  457. sage_wheels/share/singular/LIB/standard.lib +2769 -0
  458. sage_wheels/share/singular/LIB/stanleyreisner.lib +473 -0
  459. sage_wheels/share/singular/LIB/stdmodule.lib +547 -0
  460. sage_wheels/share/singular/LIB/stratify.lib +1070 -0
  461. sage_wheels/share/singular/LIB/surf.lib +506 -0
  462. sage_wheels/share/singular/LIB/surf_jupyter.lib +223 -0
  463. sage_wheels/share/singular/LIB/surfacesignature.lib +522 -0
  464. sage_wheels/share/singular/LIB/surfex.lib +1462 -0
  465. sage_wheels/share/singular/LIB/swalk.lib +877 -0
  466. sage_wheels/share/singular/LIB/symodstd.lib +1570 -0
  467. sage_wheels/share/singular/LIB/systhreads.lib +74 -0
  468. sage_wheels/share/singular/LIB/tasks.lib +1324 -0
  469. sage_wheels/share/singular/LIB/tateProdCplxNegGrad.lib +2412 -0
  470. sage_wheels/share/singular/LIB/teachstd.lib +858 -0
  471. sage_wheels/share/singular/LIB/template.lib +116 -0
  472. sage_wheels/share/singular/LIB/toric.lib +1119 -0
  473. sage_wheels/share/singular/LIB/transformation.lib +116 -0
  474. sage_wheels/share/singular/LIB/triang.lib +1197 -0
  475. sage_wheels/share/singular/LIB/tropical.lib +8741 -0
  476. sage_wheels/share/singular/LIB/tropicalEllipticCovers.lib +2922 -0
  477. sage_wheels/share/singular/LIB/tropicalNewton.lib +1128 -0
  478. sage_wheels/share/singular/LIB/tst.lib +1108 -0
  479. sage_wheels/share/singular/LIB/weierstr.lib +241 -0
  480. sage_wheels/share/singular/LIB/zeroset.lib +1478 -0
  481. sage_wheels/share/singular/emacs/.emacs-general +184 -0
  482. sage_wheels/share/singular/emacs/.emacs-singular +234 -0
  483. sage_wheels/share/singular/emacs/COPYING +44 -0
  484. sage_wheels/share/singular/emacs/cmd-cmpl.el +241 -0
  485. sage_wheels/share/singular/emacs/ex-cmpl.el +1681 -0
  486. sage_wheels/share/singular/emacs/hlp-cmpl.el +4318 -0
  487. sage_wheels/share/singular/emacs/lib-cmpl.el +179 -0
  488. sage_wheels/share/singular/emacs/singular.el +4273 -0
  489. sage_wheels/share/singular/emacs/singular.xpm +39 -0
  490. 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)