passagemath-categories 10.6.32__cp314-cp314t-musllinux_1_2_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.
- passagemath_categories-10.6.32.dist-info/METADATA +156 -0
- passagemath_categories-10.6.32.dist-info/RECORD +719 -0
- passagemath_categories-10.6.32.dist-info/WHEEL +5 -0
- passagemath_categories-10.6.32.dist-info/top_level.txt +2 -0
- passagemath_categories.libs/libgcc_s-2d945d6c.so.1 +0 -0
- passagemath_categories.libs/libgmp-28992bcb.so.10.5.0 +0 -0
- passagemath_categories.libs/libstdc++-85f2cd6d.so.6.0.33 +0 -0
- sage/all__sagemath_categories.py +28 -0
- sage/arith/all.py +38 -0
- sage/arith/constants.pxd +27 -0
- sage/arith/functions.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/arith/functions.pxd +4 -0
- sage/arith/functions.pyx +221 -0
- sage/arith/misc.py +6552 -0
- sage/arith/multi_modular.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/arith/multi_modular.pxd +39 -0
- sage/arith/multi_modular.pyx +994 -0
- sage/arith/rational_reconstruction.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/arith/rational_reconstruction.pxd +4 -0
- sage/arith/rational_reconstruction.pyx +115 -0
- sage/arith/srange.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/arith/srange.pyx +571 -0
- sage/calculus/all__sagemath_categories.py +2 -0
- sage/calculus/functional.py +481 -0
- sage/calculus/functions.py +151 -0
- sage/categories/additive_groups.py +73 -0
- sage/categories/additive_magmas.py +1044 -0
- sage/categories/additive_monoids.py +114 -0
- sage/categories/additive_semigroups.py +184 -0
- sage/categories/affine_weyl_groups.py +238 -0
- sage/categories/algebra_ideals.py +95 -0
- sage/categories/algebra_modules.py +96 -0
- sage/categories/algebras.py +349 -0
- sage/categories/algebras_with_basis.py +377 -0
- sage/categories/all.py +160 -0
- sage/categories/aperiodic_semigroups.py +29 -0
- sage/categories/associative_algebras.py +47 -0
- sage/categories/bialgebras.py +101 -0
- sage/categories/bialgebras_with_basis.py +414 -0
- sage/categories/bimodules.py +206 -0
- sage/categories/chain_complexes.py +268 -0
- sage/categories/classical_crystals.py +480 -0
- sage/categories/coalgebras.py +405 -0
- sage/categories/coalgebras_with_basis.py +232 -0
- sage/categories/coercion_methods.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/categories/coercion_methods.pyx +52 -0
- sage/categories/commutative_additive_groups.py +104 -0
- sage/categories/commutative_additive_monoids.py +45 -0
- sage/categories/commutative_additive_semigroups.py +48 -0
- sage/categories/commutative_algebra_ideals.py +87 -0
- sage/categories/commutative_algebras.py +94 -0
- sage/categories/commutative_ring_ideals.py +58 -0
- sage/categories/commutative_rings.py +736 -0
- sage/categories/complete_discrete_valuation.py +293 -0
- sage/categories/complex_reflection_groups.py +145 -0
- sage/categories/complex_reflection_or_generalized_coxeter_groups.py +1249 -0
- sage/categories/coxeter_group_algebras.py +186 -0
- sage/categories/coxeter_groups.py +3402 -0
- sage/categories/crystals.py +2628 -0
- sage/categories/cw_complexes.py +216 -0
- sage/categories/dedekind_domains.py +137 -0
- sage/categories/discrete_valuation.py +325 -0
- sage/categories/distributive_magmas_and_additive_magmas.py +100 -0
- sage/categories/division_rings.py +114 -0
- sage/categories/domains.py +95 -0
- sage/categories/drinfeld_modules.py +789 -0
- sage/categories/dual.py +42 -0
- sage/categories/enumerated_sets.py +1146 -0
- sage/categories/euclidean_domains.py +271 -0
- sage/categories/examples/algebras_with_basis.py +102 -0
- sage/categories/examples/all.py +1 -0
- sage/categories/examples/commutative_additive_monoids.py +130 -0
- sage/categories/examples/commutative_additive_semigroups.py +199 -0
- sage/categories/examples/coxeter_groups.py +8 -0
- sage/categories/examples/crystals.py +236 -0
- sage/categories/examples/cw_complexes.py +163 -0
- sage/categories/examples/facade_sets.py +187 -0
- sage/categories/examples/filtered_algebras_with_basis.py +204 -0
- sage/categories/examples/filtered_modules_with_basis.py +154 -0
- sage/categories/examples/finite_coxeter_groups.py +252 -0
- sage/categories/examples/finite_dimensional_algebras_with_basis.py +148 -0
- sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py +495 -0
- sage/categories/examples/finite_enumerated_sets.py +208 -0
- sage/categories/examples/finite_monoids.py +150 -0
- sage/categories/examples/finite_semigroups.py +190 -0
- sage/categories/examples/finite_weyl_groups.py +191 -0
- sage/categories/examples/graded_connected_hopf_algebras_with_basis.py +152 -0
- sage/categories/examples/graded_modules_with_basis.py +168 -0
- sage/categories/examples/graphs.py +122 -0
- sage/categories/examples/hopf_algebras_with_basis.py +145 -0
- sage/categories/examples/infinite_enumerated_sets.py +190 -0
- sage/categories/examples/lie_algebras.py +352 -0
- sage/categories/examples/lie_algebras_with_basis.py +196 -0
- sage/categories/examples/magmas.py +162 -0
- sage/categories/examples/manifolds.py +94 -0
- sage/categories/examples/monoids.py +144 -0
- sage/categories/examples/posets.py +178 -0
- sage/categories/examples/semigroups.py +580 -0
- sage/categories/examples/semigroups_cython.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/categories/examples/semigroups_cython.pyx +221 -0
- sage/categories/examples/semirings.py +249 -0
- sage/categories/examples/sets_cat.py +706 -0
- sage/categories/examples/sets_with_grading.py +101 -0
- sage/categories/examples/with_realizations.py +542 -0
- sage/categories/fields.py +991 -0
- sage/categories/filtered_algebras.py +63 -0
- sage/categories/filtered_algebras_with_basis.py +548 -0
- sage/categories/filtered_hopf_algebras_with_basis.py +138 -0
- sage/categories/filtered_modules.py +210 -0
- sage/categories/filtered_modules_with_basis.py +1209 -0
- sage/categories/finite_complex_reflection_groups.py +1506 -0
- sage/categories/finite_coxeter_groups.py +1138 -0
- sage/categories/finite_crystals.py +103 -0
- sage/categories/finite_dimensional_algebras_with_basis.py +1860 -0
- sage/categories/finite_dimensional_bialgebras_with_basis.py +33 -0
- sage/categories/finite_dimensional_coalgebras_with_basis.py +33 -0
- sage/categories/finite_dimensional_graded_lie_algebras_with_basis.py +231 -0
- sage/categories/finite_dimensional_hopf_algebras_with_basis.py +38 -0
- sage/categories/finite_dimensional_lie_algebras_with_basis.py +2774 -0
- sage/categories/finite_dimensional_modules_with_basis.py +1407 -0
- sage/categories/finite_dimensional_nilpotent_lie_algebras_with_basis.py +167 -0
- sage/categories/finite_dimensional_semisimple_algebras_with_basis.py +270 -0
- sage/categories/finite_enumerated_sets.py +769 -0
- sage/categories/finite_fields.py +252 -0
- sage/categories/finite_groups.py +256 -0
- sage/categories/finite_lattice_posets.py +242 -0
- sage/categories/finite_monoids.py +316 -0
- sage/categories/finite_permutation_groups.py +339 -0
- sage/categories/finite_posets.py +1994 -0
- sage/categories/finite_semigroups.py +136 -0
- sage/categories/finite_sets.py +93 -0
- sage/categories/finite_weyl_groups.py +39 -0
- sage/categories/finitely_generated_lambda_bracket_algebras.py +112 -0
- sage/categories/finitely_generated_lie_conformal_algebras.py +114 -0
- sage/categories/finitely_generated_magmas.py +57 -0
- sage/categories/finitely_generated_semigroups.py +214 -0
- sage/categories/function_fields.py +76 -0
- sage/categories/g_sets.py +77 -0
- sage/categories/gcd_domains.py +65 -0
- sage/categories/generalized_coxeter_groups.py +94 -0
- sage/categories/graded_algebras.py +85 -0
- sage/categories/graded_algebras_with_basis.py +258 -0
- sage/categories/graded_bialgebras.py +32 -0
- sage/categories/graded_bialgebras_with_basis.py +32 -0
- sage/categories/graded_coalgebras.py +65 -0
- sage/categories/graded_coalgebras_with_basis.py +51 -0
- sage/categories/graded_hopf_algebras.py +41 -0
- sage/categories/graded_hopf_algebras_with_basis.py +169 -0
- sage/categories/graded_lie_algebras.py +91 -0
- sage/categories/graded_lie_algebras_with_basis.py +44 -0
- sage/categories/graded_lie_conformal_algebras.py +74 -0
- sage/categories/graded_modules.py +133 -0
- sage/categories/graded_modules_with_basis.py +329 -0
- sage/categories/graphs.py +138 -0
- sage/categories/group_algebras.py +430 -0
- sage/categories/groupoid.py +94 -0
- sage/categories/groups.py +667 -0
- sage/categories/h_trivial_semigroups.py +64 -0
- sage/categories/hecke_modules.py +185 -0
- sage/categories/highest_weight_crystals.py +980 -0
- sage/categories/hopf_algebras.py +219 -0
- sage/categories/hopf_algebras_with_basis.py +309 -0
- sage/categories/infinite_enumerated_sets.py +115 -0
- sage/categories/integral_domains.py +203 -0
- sage/categories/j_trivial_semigroups.py +29 -0
- sage/categories/kac_moody_algebras.py +82 -0
- sage/categories/kahler_algebras.py +203 -0
- sage/categories/l_trivial_semigroups.py +63 -0
- sage/categories/lambda_bracket_algebras.py +280 -0
- sage/categories/lambda_bracket_algebras_with_basis.py +107 -0
- sage/categories/lattice_posets.py +89 -0
- sage/categories/left_modules.py +49 -0
- sage/categories/lie_algebras.py +1070 -0
- sage/categories/lie_algebras_with_basis.py +261 -0
- sage/categories/lie_conformal_algebras.py +350 -0
- sage/categories/lie_conformal_algebras_with_basis.py +147 -0
- sage/categories/lie_groups.py +73 -0
- sage/categories/loop_crystals.py +1290 -0
- sage/categories/magmas.py +1189 -0
- sage/categories/magmas_and_additive_magmas.py +149 -0
- sage/categories/magmatic_algebras.py +365 -0
- sage/categories/manifolds.py +352 -0
- sage/categories/matrix_algebras.py +40 -0
- sage/categories/metric_spaces.py +387 -0
- sage/categories/modular_abelian_varieties.py +78 -0
- sage/categories/modules.py +989 -0
- sage/categories/modules_with_basis.py +2794 -0
- sage/categories/monoid_algebras.py +38 -0
- sage/categories/monoids.py +739 -0
- sage/categories/noetherian_rings.py +87 -0
- sage/categories/number_fields.py +242 -0
- sage/categories/ore_modules.py +189 -0
- sage/categories/partially_ordered_monoids.py +49 -0
- sage/categories/permutation_groups.py +63 -0
- sage/categories/pointed_sets.py +42 -0
- sage/categories/polyhedra.py +74 -0
- sage/categories/poor_man_map.py +270 -0
- sage/categories/posets.py +722 -0
- sage/categories/principal_ideal_domains.py +270 -0
- sage/categories/quantum_group_representations.py +543 -0
- sage/categories/quotient_fields.py +728 -0
- sage/categories/r_trivial_semigroups.py +45 -0
- sage/categories/regular_crystals.py +898 -0
- sage/categories/regular_supercrystals.py +170 -0
- sage/categories/right_modules.py +49 -0
- sage/categories/ring_ideals.py +74 -0
- sage/categories/rings.py +1904 -0
- sage/categories/rngs.py +175 -0
- sage/categories/schemes.py +393 -0
- sage/categories/semigroups.py +1060 -0
- sage/categories/semirings.py +71 -0
- sage/categories/semisimple_algebras.py +114 -0
- sage/categories/sets_with_grading.py +235 -0
- sage/categories/shephard_groups.py +43 -0
- sage/categories/signed_tensor.py +120 -0
- sage/categories/simplicial_complexes.py +134 -0
- sage/categories/simplicial_sets.py +1206 -0
- sage/categories/super_algebras.py +149 -0
- sage/categories/super_algebras_with_basis.py +144 -0
- sage/categories/super_hopf_algebras_with_basis.py +126 -0
- sage/categories/super_lie_conformal_algebras.py +193 -0
- sage/categories/super_modules.py +229 -0
- sage/categories/super_modules_with_basis.py +193 -0
- sage/categories/supercommutative_algebras.py +99 -0
- sage/categories/supercrystals.py +406 -0
- sage/categories/tensor.py +110 -0
- sage/categories/topological_spaces.py +170 -0
- sage/categories/triangular_kac_moody_algebras.py +439 -0
- sage/categories/tutorial.py +58 -0
- sage/categories/unique_factorization_domains.py +318 -0
- sage/categories/unital_algebras.py +426 -0
- sage/categories/vector_bundles.py +159 -0
- sage/categories/vector_spaces.py +357 -0
- sage/categories/weyl_groups.py +853 -0
- sage/combinat/all__sagemath_categories.py +34 -0
- sage/combinat/backtrack.py +180 -0
- sage/combinat/combinat.py +2269 -0
- sage/combinat/combinat_cython.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/combinat/combinat_cython.pxd +6 -0
- sage/combinat/combinat_cython.pyx +390 -0
- sage/combinat/combination.py +796 -0
- sage/combinat/combinatorial_map.py +416 -0
- sage/combinat/composition.py +2192 -0
- sage/combinat/dlx.py +510 -0
- sage/combinat/integer_lists/__init__.py +7 -0
- sage/combinat/integer_lists/base.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/combinat/integer_lists/base.pxd +16 -0
- sage/combinat/integer_lists/base.pyx +713 -0
- sage/combinat/integer_lists/invlex.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/combinat/integer_lists/invlex.pxd +4 -0
- sage/combinat/integer_lists/invlex.pyx +1650 -0
- sage/combinat/integer_lists/lists.py +328 -0
- sage/combinat/integer_lists/nn.py +48 -0
- sage/combinat/integer_vector.py +1818 -0
- sage/combinat/integer_vector_weighted.py +413 -0
- sage/combinat/matrices/all__sagemath_categories.py +5 -0
- sage/combinat/matrices/dancing_links.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/combinat/matrices/dancing_links.pyx +1159 -0
- sage/combinat/matrices/dancing_links_c.h +380 -0
- sage/combinat/matrices/dlxcpp.py +136 -0
- sage/combinat/partition.py +10070 -0
- sage/combinat/partitions.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/combinat/partitions.pyx +743 -0
- sage/combinat/permutation.py +10168 -0
- sage/combinat/permutation_cython.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/combinat/permutation_cython.pxd +11 -0
- sage/combinat/permutation_cython.pyx +407 -0
- sage/combinat/q_analogues.py +1090 -0
- sage/combinat/ranker.py +268 -0
- sage/combinat/subset.py +1561 -0
- sage/combinat/subsets_hereditary.py +202 -0
- sage/combinat/subsets_pairwise.py +184 -0
- sage/combinat/tools.py +63 -0
- sage/combinat/tuple.py +348 -0
- sage/data_structures/all.py +2 -0
- sage/data_structures/all__sagemath_categories.py +2 -0
- sage/data_structures/binary_matrix.pxd +138 -0
- sage/data_structures/binary_search.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/data_structures/binary_search.pxd +3 -0
- sage/data_structures/binary_search.pyx +66 -0
- sage/data_structures/bitset.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/data_structures/bitset.pxd +40 -0
- sage/data_structures/bitset.pyx +2385 -0
- sage/data_structures/bitset_base.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/data_structures/bitset_base.pxd +926 -0
- sage/data_structures/bitset_base.pyx +117 -0
- sage/data_structures/bitset_intrinsics.h +487 -0
- sage/data_structures/blas_dict.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/data_structures/blas_dict.pxd +12 -0
- sage/data_structures/blas_dict.pyx +469 -0
- sage/data_structures/list_of_pairs.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/data_structures/list_of_pairs.pxd +16 -0
- sage/data_structures/list_of_pairs.pyx +122 -0
- sage/data_structures/mutable_poset.py +3312 -0
- sage/data_structures/pairing_heap.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/data_structures/pairing_heap.h +346 -0
- sage/data_structures/pairing_heap.pxd +88 -0
- sage/data_structures/pairing_heap.pyx +1464 -0
- sage/data_structures/sparse_bitset.pxd +62 -0
- sage/data_structures/stream.py +5070 -0
- sage/databases/all__sagemath_categories.py +7 -0
- sage/databases/sql_db.py +2236 -0
- sage/ext/all__sagemath_categories.py +3 -0
- sage/ext/fast_callable.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/ext/fast_callable.pxd +4 -0
- sage/ext/fast_callable.pyx +2746 -0
- sage/ext/fast_eval.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/ext/fast_eval.pxd +1 -0
- sage/ext/fast_eval.pyx +102 -0
- sage/ext/interpreters/__init__.py +1 -0
- sage/ext/interpreters/all__sagemath_categories.py +2 -0
- sage/ext/interpreters/wrapper_el.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/ext/interpreters/wrapper_el.pxd +18 -0
- sage/ext/interpreters/wrapper_el.pyx +148 -0
- sage/ext/interpreters/wrapper_py.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/ext/interpreters/wrapper_py.pxd +17 -0
- sage/ext/interpreters/wrapper_py.pyx +133 -0
- sage/functions/airy.py +937 -0
- sage/functions/all.py +97 -0
- sage/functions/bessel.py +2102 -0
- sage/functions/error.py +784 -0
- sage/functions/exp_integral.py +1529 -0
- sage/functions/gamma.py +1087 -0
- sage/functions/generalized.py +672 -0
- sage/functions/hyperbolic.py +747 -0
- sage/functions/hypergeometric.py +1156 -0
- sage/functions/jacobi.py +1705 -0
- sage/functions/log.py +1402 -0
- sage/functions/min_max.py +338 -0
- sage/functions/orthogonal_polys.py +3106 -0
- sage/functions/other.py +2303 -0
- sage/functions/piecewise.py +1505 -0
- sage/functions/prime_pi.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/functions/prime_pi.pyx +262 -0
- sage/functions/special.py +1212 -0
- sage/functions/spike_function.py +278 -0
- sage/functions/transcendental.py +690 -0
- sage/functions/trig.py +1062 -0
- sage/functions/wigner.py +726 -0
- sage/geometry/abc.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/geometry/abc.pyx +82 -0
- sage/geometry/all__sagemath_categories.py +1 -0
- sage/groups/all__sagemath_categories.py +11 -0
- sage/groups/generic.py +1733 -0
- sage/groups/groups_catalog.py +113 -0
- sage/groups/perm_gps/all__sagemath_categories.py +1 -0
- sage/groups/perm_gps/partn_ref/all.py +1 -0
- sage/groups/perm_gps/partn_ref/all__sagemath_categories.py +1 -0
- sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.pxd +52 -0
- sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.pyx +906 -0
- sage/groups/perm_gps/partn_ref/canonical_augmentation.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/groups/perm_gps/partn_ref/canonical_augmentation.pxd +85 -0
- sage/groups/perm_gps/partn_ref/canonical_augmentation.pyx +534 -0
- sage/groups/perm_gps/partn_ref/data_structures.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/groups/perm_gps/partn_ref/data_structures.pxd +576 -0
- sage/groups/perm_gps/partn_ref/data_structures.pyx +1792 -0
- sage/groups/perm_gps/partn_ref/double_coset.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/groups/perm_gps/partn_ref/double_coset.pxd +45 -0
- sage/groups/perm_gps/partn_ref/double_coset.pyx +739 -0
- sage/groups/perm_gps/partn_ref/refinement_lists.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/groups/perm_gps/partn_ref/refinement_lists.pxd +18 -0
- sage/groups/perm_gps/partn_ref/refinement_lists.pyx +82 -0
- sage/groups/perm_gps/partn_ref/refinement_python.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/groups/perm_gps/partn_ref/refinement_python.pxd +16 -0
- sage/groups/perm_gps/partn_ref/refinement_python.pyx +564 -0
- sage/groups/perm_gps/partn_ref/refinement_sets.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/groups/perm_gps/partn_ref/refinement_sets.pxd +60 -0
- sage/groups/perm_gps/partn_ref/refinement_sets.pyx +858 -0
- sage/interfaces/abc.py +140 -0
- sage/interfaces/all.py +58 -0
- sage/interfaces/all__sagemath_categories.py +1 -0
- sage/interfaces/expect.py +1643 -0
- sage/interfaces/interface.py +1682 -0
- sage/interfaces/process.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/interfaces/process.pxd +5 -0
- sage/interfaces/process.pyx +288 -0
- sage/interfaces/quit.py +167 -0
- sage/interfaces/sage0.py +604 -0
- sage/interfaces/sagespawn.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/interfaces/sagespawn.pyx +308 -0
- sage/interfaces/tab_completion.py +101 -0
- sage/misc/all__sagemath_categories.py +78 -0
- sage/misc/allocator.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/misc/allocator.pxd +6 -0
- sage/misc/allocator.pyx +47 -0
- sage/misc/binary_tree.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/misc/binary_tree.pxd +29 -0
- sage/misc/binary_tree.pyx +537 -0
- sage/misc/callable_dict.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/misc/callable_dict.pyx +89 -0
- sage/misc/citation.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/misc/citation.pyx +159 -0
- sage/misc/converting_dict.py +293 -0
- sage/misc/defaults.py +129 -0
- sage/misc/derivative.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/misc/derivative.pyx +223 -0
- sage/misc/functional.py +2005 -0
- sage/misc/html.py +589 -0
- sage/misc/latex.py +2673 -0
- sage/misc/latex_macros.py +236 -0
- sage/misc/latex_standalone.py +1833 -0
- sage/misc/map_threaded.py +38 -0
- sage/misc/mathml.py +76 -0
- sage/misc/method_decorator.py +88 -0
- sage/misc/mrange.py +755 -0
- sage/misc/multireplace.py +41 -0
- sage/misc/object_multiplexer.py +92 -0
- sage/misc/parser.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/misc/parser.pyx +1107 -0
- sage/misc/random_testing.py +264 -0
- sage/misc/rest_index_of_methods.py +377 -0
- sage/misc/search.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/misc/search.pxd +2 -0
- sage/misc/search.pyx +68 -0
- sage/misc/stopgap.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/misc/stopgap.pyx +95 -0
- sage/misc/table.py +853 -0
- sage/monoids/all__sagemath_categories.py +1 -0
- sage/monoids/indexed_free_monoid.py +1071 -0
- sage/monoids/monoid.py +82 -0
- sage/numerical/all__sagemath_categories.py +1 -0
- sage/numerical/backends/all__sagemath_categories.py +1 -0
- sage/numerical/backends/generic_backend.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/numerical/backends/generic_backend.pxd +61 -0
- sage/numerical/backends/generic_backend.pyx +1893 -0
- sage/numerical/backends/generic_sdp_backend.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/numerical/backends/generic_sdp_backend.pxd +38 -0
- sage/numerical/backends/generic_sdp_backend.pyx +755 -0
- sage/parallel/all.py +6 -0
- sage/parallel/decorate.py +575 -0
- sage/parallel/map_reduce.py +1997 -0
- sage/parallel/multiprocessing_sage.py +76 -0
- sage/parallel/ncpus.py +35 -0
- sage/parallel/parallelism.py +364 -0
- sage/parallel/reference.py +47 -0
- sage/parallel/use_fork.py +333 -0
- sage/rings/abc.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/abc.pxd +31 -0
- sage/rings/abc.pyx +526 -0
- sage/rings/algebraic_closure_finite_field.py +1154 -0
- sage/rings/all__sagemath_categories.py +91 -0
- sage/rings/big_oh.py +227 -0
- sage/rings/continued_fraction.py +2754 -0
- sage/rings/continued_fraction_gosper.py +220 -0
- sage/rings/factorint.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/factorint.pyx +295 -0
- sage/rings/fast_arith.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/fast_arith.pxd +21 -0
- sage/rings/fast_arith.pyx +535 -0
- sage/rings/finite_rings/all__sagemath_categories.py +9 -0
- sage/rings/finite_rings/conway_polynomials.py +542 -0
- sage/rings/finite_rings/element_base.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/finite_rings/element_base.pxd +12 -0
- sage/rings/finite_rings/element_base.pyx +1176 -0
- sage/rings/finite_rings/finite_field_base.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/finite_rings/finite_field_base.pxd +7 -0
- sage/rings/finite_rings/finite_field_base.pyx +2171 -0
- sage/rings/finite_rings/finite_field_constructor.py +827 -0
- sage/rings/finite_rings/finite_field_prime_modn.py +372 -0
- sage/rings/finite_rings/galois_group.py +154 -0
- sage/rings/finite_rings/hom_finite_field.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/finite_rings/hom_finite_field.pxd +23 -0
- sage/rings/finite_rings/hom_finite_field.pyx +856 -0
- sage/rings/finite_rings/hom_prime_finite_field.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/finite_rings/hom_prime_finite_field.pxd +15 -0
- sage/rings/finite_rings/hom_prime_finite_field.pyx +164 -0
- sage/rings/finite_rings/homset.py +357 -0
- sage/rings/finite_rings/integer_mod.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/finite_rings/integer_mod.pxd +56 -0
- sage/rings/finite_rings/integer_mod.pyx +4586 -0
- sage/rings/finite_rings/integer_mod_limits.h +11 -0
- sage/rings/finite_rings/integer_mod_ring.py +2044 -0
- sage/rings/finite_rings/residue_field.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/finite_rings/residue_field.pxd +30 -0
- sage/rings/finite_rings/residue_field.pyx +1811 -0
- sage/rings/finite_rings/stdint.pxd +19 -0
- sage/rings/fraction_field.py +1452 -0
- sage/rings/fraction_field_element.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/fraction_field_element.pyx +1357 -0
- sage/rings/function_field/all.py +7 -0
- sage/rings/function_field/all__sagemath_categories.py +2 -0
- sage/rings/function_field/constructor.py +218 -0
- sage/rings/function_field/element.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/function_field/element.pxd +11 -0
- sage/rings/function_field/element.pyx +1008 -0
- sage/rings/function_field/element_rational.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/function_field/element_rational.pyx +513 -0
- sage/rings/function_field/extensions.py +230 -0
- sage/rings/function_field/function_field.py +1468 -0
- sage/rings/function_field/function_field_rational.py +1005 -0
- sage/rings/function_field/ideal.py +1155 -0
- sage/rings/function_field/ideal_rational.py +629 -0
- sage/rings/function_field/jacobian_base.py +826 -0
- sage/rings/function_field/jacobian_hess.py +1053 -0
- sage/rings/function_field/jacobian_khuri_makdisi.py +1027 -0
- sage/rings/function_field/maps.py +1039 -0
- sage/rings/function_field/order.py +281 -0
- sage/rings/function_field/order_basis.py +586 -0
- sage/rings/function_field/order_rational.py +576 -0
- sage/rings/function_field/place.py +426 -0
- sage/rings/function_field/place_rational.py +181 -0
- sage/rings/generic.py +320 -0
- sage/rings/homset.py +332 -0
- sage/rings/ideal.py +1885 -0
- sage/rings/ideal_monoid.py +215 -0
- sage/rings/infinity.py +1890 -0
- sage/rings/integer.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/integer.pxd +45 -0
- sage/rings/integer.pyx +7874 -0
- sage/rings/integer_ring.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/integer_ring.pxd +8 -0
- sage/rings/integer_ring.pyx +1693 -0
- sage/rings/laurent_series_ring.py +931 -0
- sage/rings/laurent_series_ring_element.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/laurent_series_ring_element.pxd +11 -0
- sage/rings/laurent_series_ring_element.pyx +1927 -0
- sage/rings/lazy_series.py +7815 -0
- sage/rings/lazy_series_ring.py +4356 -0
- sage/rings/localization.py +1043 -0
- sage/rings/morphism.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/morphism.pxd +39 -0
- sage/rings/morphism.pyx +3299 -0
- sage/rings/multi_power_series_ring.py +1145 -0
- sage/rings/multi_power_series_ring_element.py +2184 -0
- sage/rings/noncommutative_ideals.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/noncommutative_ideals.pyx +423 -0
- sage/rings/number_field/all__sagemath_categories.py +1 -0
- sage/rings/number_field/number_field_base.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/number_field/number_field_base.pxd +8 -0
- sage/rings/number_field/number_field_base.pyx +507 -0
- sage/rings/number_field/number_field_element_base.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/number_field/number_field_element_base.pxd +6 -0
- sage/rings/number_field/number_field_element_base.pyx +36 -0
- sage/rings/number_field/number_field_ideal.py +3550 -0
- sage/rings/padics/all__sagemath_categories.py +4 -0
- sage/rings/padics/local_generic.py +1670 -0
- sage/rings/padics/local_generic_element.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/padics/local_generic_element.pxd +5 -0
- sage/rings/padics/local_generic_element.pyx +1017 -0
- sage/rings/padics/misc.py +256 -0
- sage/rings/padics/padic_generic.py +1911 -0
- sage/rings/padics/pow_computer.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/padics/pow_computer.pxd +38 -0
- sage/rings/padics/pow_computer.pyx +671 -0
- sage/rings/padics/precision_error.py +24 -0
- sage/rings/polynomial/all__sagemath_categories.py +25 -0
- sage/rings/polynomial/commutative_polynomial.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/polynomial/commutative_polynomial.pxd +6 -0
- sage/rings/polynomial/commutative_polynomial.pyx +24 -0
- sage/rings/polynomial/cyclotomic.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/polynomial/cyclotomic.pyx +404 -0
- sage/rings/polynomial/flatten.py +711 -0
- sage/rings/polynomial/ideal.py +102 -0
- sage/rings/polynomial/infinite_polynomial_element.py +1768 -0
- sage/rings/polynomial/infinite_polynomial_ring.py +1653 -0
- sage/rings/polynomial/laurent_polynomial.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/polynomial/laurent_polynomial.pxd +18 -0
- sage/rings/polynomial/laurent_polynomial.pyx +2190 -0
- sage/rings/polynomial/laurent_polynomial_ideal.py +590 -0
- sage/rings/polynomial/laurent_polynomial_ring.py +832 -0
- sage/rings/polynomial/laurent_polynomial_ring_base.py +708 -0
- sage/rings/polynomial/multi_polynomial.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/polynomial/multi_polynomial.pxd +12 -0
- sage/rings/polynomial/multi_polynomial.pyx +3082 -0
- sage/rings/polynomial/multi_polynomial_element.py +2570 -0
- sage/rings/polynomial/multi_polynomial_ideal.py +5771 -0
- sage/rings/polynomial/multi_polynomial_ring.py +947 -0
- sage/rings/polynomial/multi_polynomial_ring_base.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/polynomial/multi_polynomial_ring_base.pxd +15 -0
- sage/rings/polynomial/multi_polynomial_ring_base.pyx +1855 -0
- sage/rings/polynomial/multi_polynomial_sequence.py +2204 -0
- sage/rings/polynomial/polydict.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/polynomial/polydict.pxd +45 -0
- sage/rings/polynomial/polydict.pyx +2701 -0
- sage/rings/polynomial/polynomial_compiled.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/polynomial/polynomial_compiled.pxd +59 -0
- sage/rings/polynomial/polynomial_compiled.pyx +509 -0
- sage/rings/polynomial/polynomial_element.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/polynomial/polynomial_element.pxd +64 -0
- sage/rings/polynomial/polynomial_element.pyx +13255 -0
- sage/rings/polynomial/polynomial_element_generic.py +1637 -0
- sage/rings/polynomial/polynomial_fateman.py +97 -0
- sage/rings/polynomial/polynomial_quotient_ring.py +2465 -0
- sage/rings/polynomial/polynomial_quotient_ring_element.py +779 -0
- sage/rings/polynomial/polynomial_ring.py +3784 -0
- sage/rings/polynomial/polynomial_ring_constructor.py +1051 -0
- sage/rings/polynomial/polynomial_ring_homomorphism.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/polynomial/polynomial_ring_homomorphism.pxd +5 -0
- sage/rings/polynomial/polynomial_ring_homomorphism.pyx +121 -0
- sage/rings/polynomial/polynomial_singular_interface.py +549 -0
- sage/rings/polynomial/symmetric_ideal.py +989 -0
- sage/rings/polynomial/symmetric_reduction.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/polynomial/symmetric_reduction.pxd +8 -0
- sage/rings/polynomial/symmetric_reduction.pyx +669 -0
- sage/rings/polynomial/term_order.py +2279 -0
- sage/rings/polynomial/toy_buchberger.py +449 -0
- sage/rings/polynomial/toy_d_basis.py +387 -0
- sage/rings/polynomial/toy_variety.py +362 -0
- sage/rings/power_series_mpoly.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/power_series_mpoly.pxd +9 -0
- sage/rings/power_series_mpoly.pyx +161 -0
- sage/rings/power_series_poly.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/power_series_poly.pxd +10 -0
- sage/rings/power_series_poly.pyx +1317 -0
- sage/rings/power_series_ring.py +1441 -0
- sage/rings/power_series_ring_element.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/power_series_ring_element.pxd +12 -0
- sage/rings/power_series_ring_element.pyx +3028 -0
- sage/rings/puiseux_series_ring.py +487 -0
- sage/rings/puiseux_series_ring_element.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/puiseux_series_ring_element.pxd +7 -0
- sage/rings/puiseux_series_ring_element.pyx +1055 -0
- sage/rings/qqbar_decorators.py +167 -0
- sage/rings/quotient_ring.py +1598 -0
- sage/rings/quotient_ring_element.py +979 -0
- sage/rings/rational.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/rational.pxd +20 -0
- sage/rings/rational.pyx +4284 -0
- sage/rings/rational_field.py +1730 -0
- sage/rings/real_double.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/real_double.pxd +16 -0
- sage/rings/real_double.pyx +2218 -0
- sage/rings/real_lazy.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/real_lazy.pxd +30 -0
- sage/rings/real_lazy.pyx +1773 -0
- sage/rings/ring.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/ring.pxd +30 -0
- sage/rings/ring.pyx +850 -0
- sage/rings/semirings/all.py +3 -0
- sage/rings/semirings/non_negative_integer_semiring.py +107 -0
- sage/rings/semirings/tropical_mpolynomial.py +972 -0
- sage/rings/semirings/tropical_polynomial.py +997 -0
- sage/rings/semirings/tropical_semiring.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/semirings/tropical_semiring.pyx +676 -0
- sage/rings/semirings/tropical_variety.py +1701 -0
- sage/rings/sum_of_squares.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/rings/sum_of_squares.pxd +3 -0
- sage/rings/sum_of_squares.pyx +336 -0
- sage/rings/tests.py +504 -0
- sage/schemes/affine/affine_homset.py +508 -0
- sage/schemes/affine/affine_morphism.py +1574 -0
- sage/schemes/affine/affine_point.py +460 -0
- sage/schemes/affine/affine_rational_point.py +308 -0
- sage/schemes/affine/affine_space.py +1264 -0
- sage/schemes/affine/affine_subscheme.py +592 -0
- sage/schemes/affine/all.py +25 -0
- sage/schemes/all__sagemath_categories.py +5 -0
- sage/schemes/generic/algebraic_scheme.py +2092 -0
- sage/schemes/generic/all.py +5 -0
- sage/schemes/generic/ambient_space.py +400 -0
- sage/schemes/generic/divisor.py +465 -0
- sage/schemes/generic/divisor_group.py +313 -0
- sage/schemes/generic/glue.py +84 -0
- sage/schemes/generic/homset.py +820 -0
- sage/schemes/generic/hypersurface.py +234 -0
- sage/schemes/generic/morphism.py +2107 -0
- sage/schemes/generic/point.py +237 -0
- sage/schemes/generic/scheme.py +1190 -0
- sage/schemes/generic/spec.py +199 -0
- sage/schemes/product_projective/all.py +6 -0
- sage/schemes/product_projective/homset.py +236 -0
- sage/schemes/product_projective/morphism.py +517 -0
- sage/schemes/product_projective/point.py +568 -0
- sage/schemes/product_projective/rational_point.py +550 -0
- sage/schemes/product_projective/space.py +1301 -0
- sage/schemes/product_projective/subscheme.py +466 -0
- sage/schemes/projective/all.py +24 -0
- sage/schemes/projective/proj_bdd_height.py +453 -0
- sage/schemes/projective/projective_homset.py +718 -0
- sage/schemes/projective/projective_morphism.py +2792 -0
- sage/schemes/projective/projective_point.py +1484 -0
- sage/schemes/projective/projective_rational_point.py +569 -0
- sage/schemes/projective/projective_space.py +2571 -0
- sage/schemes/projective/projective_subscheme.py +1574 -0
- sage/sets/all.py +17 -0
- sage/sets/cartesian_product.py +376 -0
- sage/sets/condition_set.py +525 -0
- sage/sets/disjoint_set.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/sets/disjoint_set.pxd +36 -0
- sage/sets/disjoint_set.pyx +998 -0
- sage/sets/disjoint_union_enumerated_sets.py +625 -0
- sage/sets/family.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/sets/family.pxd +12 -0
- sage/sets/family.pyx +1556 -0
- sage/sets/finite_enumerated_set.py +406 -0
- sage/sets/finite_set_map_cy.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/sets/finite_set_map_cy.pxd +34 -0
- sage/sets/finite_set_map_cy.pyx +708 -0
- sage/sets/finite_set_maps.py +591 -0
- sage/sets/image_set.py +448 -0
- sage/sets/integer_range.py +829 -0
- sage/sets/non_negative_integers.py +241 -0
- sage/sets/positive_integers.py +93 -0
- sage/sets/primes.py +188 -0
- sage/sets/real_set.py +2760 -0
- sage/sets/recursively_enumerated_set.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/sets/recursively_enumerated_set.pxd +31 -0
- sage/sets/recursively_enumerated_set.pyx +2082 -0
- sage/sets/set.py +2083 -0
- sage/sets/set_from_iterator.py +1021 -0
- sage/sets/totally_ordered_finite_set.py +329 -0
- sage/symbolic/all__sagemath_categories.py +1 -0
- sage/symbolic/function.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/symbolic/function.pxd +29 -0
- sage/symbolic/function.pyx +1488 -0
- sage/symbolic/symbols.py +56 -0
- sage/tests/all__sagemath_categories.py +1 -0
- sage/tests/cython.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/tests/cython.pyx +37 -0
- sage/tests/stl_vector.cpython-314t-aarch64-linux-musl.so +0 -0
- sage/tests/stl_vector.pyx +171 -0
- sage/typeset/all.py +6 -0
- sage/typeset/ascii_art.py +295 -0
- sage/typeset/character_art.py +789 -0
- sage/typeset/character_art_factory.py +572 -0
- sage/typeset/symbols.py +334 -0
- sage/typeset/unicode_art.py +183 -0
- sage/typeset/unicode_characters.py +101 -0
sage/groups/generic.py
ADDED
|
@@ -0,0 +1,1733 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-categories
|
|
2
|
+
"""
|
|
3
|
+
Miscellaneous generic functions
|
|
4
|
+
|
|
5
|
+
A collection of functions implementing generic algorithms in arbitrary
|
|
6
|
+
groups, including additive and multiplicative groups.
|
|
7
|
+
|
|
8
|
+
In all cases the group operation is specified by a parameter
|
|
9
|
+
``operation``, which is a string either one of the set of
|
|
10
|
+
``multiplication_names`` or ``addition_names`` specified below, or other.
|
|
11
|
+
In the latter case, the caller must provide an identity, ``inverse()`` and
|
|
12
|
+
``op()`` functions.
|
|
13
|
+
|
|
14
|
+
::
|
|
15
|
+
|
|
16
|
+
multiplication_names = ('multiplication', 'times', 'product', '*')
|
|
17
|
+
addition_names = ('addition', 'plus', 'sum', '+')
|
|
18
|
+
|
|
19
|
+
Also included are a generic function for computing multiples (or
|
|
20
|
+
powers), and an iterator for general multiples and powers.
|
|
21
|
+
|
|
22
|
+
EXAMPLES:
|
|
23
|
+
|
|
24
|
+
Some examples in the multiplicative group of a finite field:
|
|
25
|
+
|
|
26
|
+
- Discrete logs::
|
|
27
|
+
|
|
28
|
+
sage: # needs sage.rings.finite_rings
|
|
29
|
+
sage: K = GF(3^6,'b')
|
|
30
|
+
sage: b = K.gen()
|
|
31
|
+
sage: a = b^210
|
|
32
|
+
sage: discrete_log(a, b, K.order() - 1)
|
|
33
|
+
210
|
|
34
|
+
|
|
35
|
+
- Linear relation finder::
|
|
36
|
+
|
|
37
|
+
sage: # needs sage.rings.finite_rings
|
|
38
|
+
sage: F.<a> = GF(3^6,'a')
|
|
39
|
+
sage: a.multiplicative_order().factor()
|
|
40
|
+
2^3 * 7 * 13
|
|
41
|
+
sage: b = a^7
|
|
42
|
+
sage: c = a^13
|
|
43
|
+
sage: linear_relation(b,c,'*')
|
|
44
|
+
(13, 7)
|
|
45
|
+
sage: b^13 == c^7
|
|
46
|
+
True
|
|
47
|
+
|
|
48
|
+
- Orders of elements::
|
|
49
|
+
|
|
50
|
+
sage: # needs sage.rings.finite_rings
|
|
51
|
+
sage: from sage.groups.generic import order_from_multiple, order_from_bounds
|
|
52
|
+
sage: k.<a> = GF(5^5)
|
|
53
|
+
sage: b = a^4
|
|
54
|
+
sage: order_from_multiple(b, 5^5 - 1, operation='*')
|
|
55
|
+
781
|
|
56
|
+
sage: order_from_bounds(b, (5^4, 5^5), operation='*')
|
|
57
|
+
781
|
|
58
|
+
|
|
59
|
+
Some examples in the group of points of an elliptic curve over a finite field:
|
|
60
|
+
|
|
61
|
+
- Discrete logs::
|
|
62
|
+
|
|
63
|
+
sage: # needs sage.libs.gap sage.rings.finite_rings sage.schemes
|
|
64
|
+
sage: F = GF(37^2,'a')
|
|
65
|
+
sage: E = EllipticCurve(F,[1,1])
|
|
66
|
+
sage: F.<a> = GF(37^2,'a')
|
|
67
|
+
sage: E = EllipticCurve(F,[1,1])
|
|
68
|
+
sage: P = E(25*a + 16 , 15*a + 7 )
|
|
69
|
+
sage: P.order()
|
|
70
|
+
672
|
|
71
|
+
sage: Q = 39*P; Q
|
|
72
|
+
(36*a + 32 : 5*a + 12 : 1)
|
|
73
|
+
sage: discrete_log(Q, P, P.order(), operation='+')
|
|
74
|
+
39
|
|
75
|
+
|
|
76
|
+
- Linear relation finder::
|
|
77
|
+
|
|
78
|
+
sage: # needs sage.libs.gap sage.rings.finite_rings sage.schemes
|
|
79
|
+
sage: F.<a> = GF(3^6,'a')
|
|
80
|
+
sage: E = EllipticCurve([a^5 + 2*a^3 + 2*a^2 + 2*a, a^4 + a^3 + 2*a + 1])
|
|
81
|
+
sage: P = E(a^5 + a^4 + a^3 + a^2 + a + 2 , 0)
|
|
82
|
+
sage: Q = E(2*a^3 + 2*a^2 + 2*a , a^3 + 2*a^2 + 1)
|
|
83
|
+
sage: linear_relation(P,Q,'+')
|
|
84
|
+
(1, 2)
|
|
85
|
+
sage: P == 2*Q
|
|
86
|
+
True
|
|
87
|
+
|
|
88
|
+
- Orders of elements::
|
|
89
|
+
|
|
90
|
+
sage: # needs sage.libs.gap sage.rings.finite_rings sage.schemes
|
|
91
|
+
sage: from sage.groups.generic import order_from_multiple, order_from_bounds
|
|
92
|
+
sage: k.<a> = GF(5^5)
|
|
93
|
+
sage: E = EllipticCurve(k,[2,4])
|
|
94
|
+
sage: P = E(3*a^4 + 3*a, 2*a + 1)
|
|
95
|
+
sage: M = E.cardinality(); M
|
|
96
|
+
3227
|
|
97
|
+
sage: plist = M.prime_factors()
|
|
98
|
+
sage: order_from_multiple(P, M, plist, operation='+')
|
|
99
|
+
3227
|
|
100
|
+
sage: Q = E(0,2)
|
|
101
|
+
sage: order_from_multiple(Q, M, plist, operation='+')
|
|
102
|
+
7
|
|
103
|
+
sage: order_from_bounds(Q, Hasse_bounds(5^5), operation='+')
|
|
104
|
+
7
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
# ****************************************************************************
|
|
108
|
+
# Copyright (C) 2008 William Stein <wstein@gmail.com>
|
|
109
|
+
# John Cremona <john.cremona@gmail.com>
|
|
110
|
+
#
|
|
111
|
+
# This program is free software: you can redistribute it and/or modify
|
|
112
|
+
# it under the terms of the GNU General Public License as published by
|
|
113
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
114
|
+
# (at your option) any later version.
|
|
115
|
+
# https://www.gnu.org/licenses/
|
|
116
|
+
# ****************************************************************************
|
|
117
|
+
|
|
118
|
+
from copy import copy
|
|
119
|
+
|
|
120
|
+
from sage.arith.misc import integer_ceil, integer_floor, xlcm
|
|
121
|
+
from sage.arith.srange import xsrange
|
|
122
|
+
from sage.misc.misc_c import prod
|
|
123
|
+
import sage.rings.integer_ring as integer_ring
|
|
124
|
+
import sage.rings.integer
|
|
125
|
+
|
|
126
|
+
#
|
|
127
|
+
# Lists of names (as strings) which the user may use to identify one
|
|
128
|
+
# of the standard operations:
|
|
129
|
+
#
|
|
130
|
+
multiplication_names = ('multiplication', 'times', 'product', '*')
|
|
131
|
+
addition_names = ('addition', 'plus', 'sum', '+')
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def multiple(a, n, operation='*', identity=None, inverse=None, op=None):
|
|
135
|
+
r"""
|
|
136
|
+
Return either `na` or `a^n`, where `n` is any integer and `a` is
|
|
137
|
+
a Python object on which a group operation such as addition or
|
|
138
|
+
multiplication is defined. Uses the standard binary algorithm.
|
|
139
|
+
|
|
140
|
+
INPUT: See the documentation for ``discrete_logarithm()``.
|
|
141
|
+
|
|
142
|
+
EXAMPLES::
|
|
143
|
+
|
|
144
|
+
sage: multiple(2, 5)
|
|
145
|
+
32
|
|
146
|
+
sage: multiple(RealField()('2.5'), 4) # needs sage.rings.real_mpfr
|
|
147
|
+
39.0625000000000
|
|
148
|
+
sage: multiple(2, -3)
|
|
149
|
+
1/8
|
|
150
|
+
sage: multiple(2, 100, '+') == 100*2
|
|
151
|
+
True
|
|
152
|
+
sage: multiple(2, 100) == 2**100
|
|
153
|
+
True
|
|
154
|
+
sage: multiple(2, -100,) == 2**-100
|
|
155
|
+
True
|
|
156
|
+
sage: R.<x> = ZZ[]
|
|
157
|
+
sage: multiple(x, 100)
|
|
158
|
+
x^100
|
|
159
|
+
sage: multiple(x, 100, '+')
|
|
160
|
+
100*x
|
|
161
|
+
sage: multiple(x, -10)
|
|
162
|
+
1/x^10
|
|
163
|
+
|
|
164
|
+
Idempotence is detected, making the following fast::
|
|
165
|
+
|
|
166
|
+
sage: multiple(1, 10^1000)
|
|
167
|
+
1
|
|
168
|
+
|
|
169
|
+
sage: # needs database_cremona_mini_ellcurve sage.schemes
|
|
170
|
+
sage: E = EllipticCurve('389a1')
|
|
171
|
+
sage: P = E(-1,1)
|
|
172
|
+
sage: multiple(P, 10, '+')
|
|
173
|
+
(645656132358737542773209599489/22817025904944891235367494656 :
|
|
174
|
+
525532176124281192881231818644174845702936831/3446581505217248068297884384990762467229696 : 1)
|
|
175
|
+
sage: multiple(P, -10, '+')
|
|
176
|
+
(645656132358737542773209599489/22817025904944891235367494656 :
|
|
177
|
+
-528978757629498440949529703029165608170166527/3446581505217248068297884384990762467229696 : 1)
|
|
178
|
+
"""
|
|
179
|
+
from operator import inv, mul, neg, add
|
|
180
|
+
|
|
181
|
+
if operation in multiplication_names:
|
|
182
|
+
identity = a.parent().one()
|
|
183
|
+
inverse = inv
|
|
184
|
+
op = mul
|
|
185
|
+
elif operation in addition_names:
|
|
186
|
+
identity = a.parent().zero()
|
|
187
|
+
inverse = neg
|
|
188
|
+
op = add
|
|
189
|
+
else:
|
|
190
|
+
if identity is None or inverse is None or op is None:
|
|
191
|
+
raise ValueError("identity, inverse and operation must all be specified")
|
|
192
|
+
|
|
193
|
+
if n == 0:
|
|
194
|
+
return identity
|
|
195
|
+
|
|
196
|
+
if n < 0:
|
|
197
|
+
n = -n
|
|
198
|
+
a = inverse(a)
|
|
199
|
+
|
|
200
|
+
if n == 1:
|
|
201
|
+
return a
|
|
202
|
+
|
|
203
|
+
# check for idempotence, and store the result otherwise
|
|
204
|
+
aa = op(a, a)
|
|
205
|
+
if aa == a:
|
|
206
|
+
return a
|
|
207
|
+
|
|
208
|
+
if n == 2:
|
|
209
|
+
return aa
|
|
210
|
+
|
|
211
|
+
if n == 3:
|
|
212
|
+
return op(aa, a)
|
|
213
|
+
|
|
214
|
+
if n == 4:
|
|
215
|
+
return op(aa, aa)
|
|
216
|
+
|
|
217
|
+
# since we've computed a^2, let's start squaring there
|
|
218
|
+
# so, let's keep the least-significant bit around, just
|
|
219
|
+
# in case.
|
|
220
|
+
m = n & 1
|
|
221
|
+
n = n >> 1
|
|
222
|
+
|
|
223
|
+
# One multiplication can be saved by starting with
|
|
224
|
+
# the second-smallest power needed rather than with 1
|
|
225
|
+
# we've already squared a, so let's start there.
|
|
226
|
+
apow = aa
|
|
227
|
+
while n & 1 == 0:
|
|
228
|
+
apow = op(apow, apow)
|
|
229
|
+
n = n >> 1
|
|
230
|
+
power = apow
|
|
231
|
+
n = n >> 1
|
|
232
|
+
|
|
233
|
+
# now multiply that least-significant bit in...
|
|
234
|
+
if m:
|
|
235
|
+
power = op(power, a)
|
|
236
|
+
|
|
237
|
+
# and this is straight from the book.
|
|
238
|
+
while n != 0:
|
|
239
|
+
apow = op(apow, apow)
|
|
240
|
+
if n & 1 != 0:
|
|
241
|
+
power = op(power, apow)
|
|
242
|
+
n = n >> 1
|
|
243
|
+
|
|
244
|
+
return power
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
#
|
|
248
|
+
# Generic iterator for looping through multiples or powers
|
|
249
|
+
#
|
|
250
|
+
|
|
251
|
+
class multiples:
|
|
252
|
+
r"""
|
|
253
|
+
Return an iterator which runs through ``P0+i*P`` for ``i`` in ``range(n)``.
|
|
254
|
+
|
|
255
|
+
``P`` and ``P0`` must be Sage objects in some group; if the operation is
|
|
256
|
+
multiplication then the returned values are instead ``P0*P**i``.
|
|
257
|
+
|
|
258
|
+
EXAMPLES::
|
|
259
|
+
|
|
260
|
+
sage: list(multiples(1, 10))
|
|
261
|
+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
262
|
+
sage: list(multiples(1, 10, 100))
|
|
263
|
+
[100, 101, 102, 103, 104, 105, 106, 107, 108, 109]
|
|
264
|
+
|
|
265
|
+
sage: # needs database_cremona_mini_ellcurve sage.schemes
|
|
266
|
+
sage: E = EllipticCurve('389a1')
|
|
267
|
+
sage: P = E(-1,1)
|
|
268
|
+
sage: for Q in multiples(P, 5): print((Q, Q.height()/P.height()))
|
|
269
|
+
((0 : 1 : 0), 0.000000000000000)
|
|
270
|
+
((-1 : 1 : 1), 1.00000000000000)
|
|
271
|
+
((10/9 : -35/27 : 1), 4.00000000000000)
|
|
272
|
+
((26/361 : -5720/6859 : 1), 9.00000000000000)
|
|
273
|
+
((47503/16641 : 9862190/2146689 : 1), 16.0000000000000)
|
|
274
|
+
|
|
275
|
+
sage: R.<x> = ZZ[]
|
|
276
|
+
sage: list(multiples(x, 5))
|
|
277
|
+
[0, x, 2*x, 3*x, 4*x]
|
|
278
|
+
sage: list(multiples(x, 5, operation='*'))
|
|
279
|
+
[1, x, x^2, x^3, x^4]
|
|
280
|
+
sage: list(multiples(x, 5, indexed=True))
|
|
281
|
+
[(0, 0), (1, x), (2, 2*x), (3, 3*x), (4, 4*x)]
|
|
282
|
+
sage: list(multiples(x, 5, indexed=True, operation='*'))
|
|
283
|
+
[(0, 1), (1, x), (2, x^2), (3, x^3), (4, x^4)]
|
|
284
|
+
sage: for i,y in multiples(x, 5, indexed=True): print("%s times %s = %s"%(i,x,y))
|
|
285
|
+
0 times x = 0
|
|
286
|
+
1 times x = x
|
|
287
|
+
2 times x = 2*x
|
|
288
|
+
3 times x = 3*x
|
|
289
|
+
4 times x = 4*x
|
|
290
|
+
|
|
291
|
+
sage: for i,n in multiples(3, 5, indexed=True, operation='*'):
|
|
292
|
+
....: print("3 to the power %s = %s" % (i,n))
|
|
293
|
+
3 to the power 0 = 1
|
|
294
|
+
3 to the power 1 = 3
|
|
295
|
+
3 to the power 2 = 9
|
|
296
|
+
3 to the power 3 = 27
|
|
297
|
+
3 to the power 4 = 81
|
|
298
|
+
"""
|
|
299
|
+
def __init__(self, P, n, P0=None, indexed=False, operation='+', op=None):
|
|
300
|
+
"""
|
|
301
|
+
Create a multiples iterator.
|
|
302
|
+
|
|
303
|
+
INPUT:
|
|
304
|
+
|
|
305
|
+
- ``P`` -- step value; any Sage object on which a binary operation is defined
|
|
306
|
+
- ``n`` -- number of multiples; nonnegative integer
|
|
307
|
+
- ``P0`` -- offset (default: 0); Sage object which can be 'added' to P
|
|
308
|
+
- ``indexed`` -- boolean (default: ``False``)
|
|
309
|
+
|
|
310
|
+
If ``indexed==False``, then the iterator delivers ``P0+i*P``
|
|
311
|
+
(if ``operation=='+'``) or ``P0*P**i`` (if
|
|
312
|
+
``operation=='*'``), for ``i`` in ``range(n)``.
|
|
313
|
+
|
|
314
|
+
If ``indexed==True`` then the iterator delivers tuples
|
|
315
|
+
``(i, P0+i*P)`` or ``(i, P0*P**i)``.
|
|
316
|
+
|
|
317
|
+
- ``operation`` -- string: ``'+'`` (the default) or ``'*'`` or other.
|
|
318
|
+
|
|
319
|
+
If other, a function ``op()`` must be supplied (a function
|
|
320
|
+
of 2 arguments) defining the group binary operation; also
|
|
321
|
+
``P0`` must be supplied.
|
|
322
|
+
"""
|
|
323
|
+
if n < 0:
|
|
324
|
+
raise ValueError('n cannot be negative in multiples')
|
|
325
|
+
|
|
326
|
+
from operator import mul, add
|
|
327
|
+
|
|
328
|
+
if operation in multiplication_names:
|
|
329
|
+
if P0 is None:
|
|
330
|
+
P0 = P.parent().one()
|
|
331
|
+
self.op = mul
|
|
332
|
+
elif operation in addition_names:
|
|
333
|
+
if P0 is None:
|
|
334
|
+
P0 = P.parent().zero()
|
|
335
|
+
self.op = add
|
|
336
|
+
else:
|
|
337
|
+
if P0 is None:
|
|
338
|
+
raise ValueError("P0 must be supplied when operation is neither addition nor multiplication")
|
|
339
|
+
if op is None:
|
|
340
|
+
raise ValueError("op() must both be supplied when operation is neither addition nor multiplication")
|
|
341
|
+
self.op = op
|
|
342
|
+
|
|
343
|
+
self.P = copy(P)
|
|
344
|
+
self.Q = copy(P0)
|
|
345
|
+
if self.P is None or self.Q is None:
|
|
346
|
+
raise ValueError("P and Q must not be None")
|
|
347
|
+
self.i = 0
|
|
348
|
+
self.bound = n
|
|
349
|
+
self.indexed = indexed
|
|
350
|
+
|
|
351
|
+
def __next__(self):
|
|
352
|
+
"""
|
|
353
|
+
Return the next item in this multiples iterator.
|
|
354
|
+
"""
|
|
355
|
+
if self.i >= self.bound:
|
|
356
|
+
raise StopIteration
|
|
357
|
+
i = self.i
|
|
358
|
+
val = self.Q
|
|
359
|
+
self.i += 1
|
|
360
|
+
self.Q = self.op(self.Q, self.P)
|
|
361
|
+
if self.indexed:
|
|
362
|
+
return (i, val)
|
|
363
|
+
else:
|
|
364
|
+
return val
|
|
365
|
+
|
|
366
|
+
next = __next__
|
|
367
|
+
|
|
368
|
+
def __iter__(self):
|
|
369
|
+
"""
|
|
370
|
+
Standard member function making this class an iterator.
|
|
371
|
+
"""
|
|
372
|
+
return self
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def bsgs(a, b, bounds, operation='*', identity=None, inverse=None, op=None):
|
|
376
|
+
r"""
|
|
377
|
+
Totally generic discrete baby-step giant-step function.
|
|
378
|
+
|
|
379
|
+
Solves `na=b` (or `a^n=b`) with `lb\le n\le ub` where ``bounds==(lb,ub)``,
|
|
380
|
+
raising an error if no such `n` exists.
|
|
381
|
+
|
|
382
|
+
`a` and `b` must be elements of some group with given identity,
|
|
383
|
+
inverse of ``x`` given by ``inverse(x)``, and group operation on
|
|
384
|
+
``x``, ``y`` by ``op(x,y)``.
|
|
385
|
+
|
|
386
|
+
If operation is '*' or '+' then the other
|
|
387
|
+
arguments are provided automatically; otherwise they must be
|
|
388
|
+
provided by the caller.
|
|
389
|
+
|
|
390
|
+
INPUT:
|
|
391
|
+
|
|
392
|
+
- ``a`` -- group element
|
|
393
|
+
- ``b`` -- group element
|
|
394
|
+
- ``bounds`` -- a 2-tuple of integers ``(lower,upper)`` with ``0<=lower<=upper``
|
|
395
|
+
- ``operation`` -- string: ``'*'``, ``'+'``, other
|
|
396
|
+
- ``identity`` -- the identity element of the group
|
|
397
|
+
- ``inverse`` -- function of 1 argument ``x``, returning inverse of ``x``
|
|
398
|
+
- ``op`` -- function of 2 arguments ``x``, ``y`` returning ``x*y`` in the group
|
|
399
|
+
|
|
400
|
+
OUTPUT:
|
|
401
|
+
|
|
402
|
+
An integer `n` such that `a^n = b` (or `na = b`). If no
|
|
403
|
+
such `n` exists, this function raises a :exc:`ValueError` exception.
|
|
404
|
+
|
|
405
|
+
.. NOTE::
|
|
406
|
+
|
|
407
|
+
This is a generalization of discrete logarithm. One
|
|
408
|
+
situation where this version is useful is to find the order of
|
|
409
|
+
an element in a group where we only have bounds on the group
|
|
410
|
+
order (see the elliptic curve example below).
|
|
411
|
+
|
|
412
|
+
ALGORITHM: Baby step giant step. Time and space are soft
|
|
413
|
+
`O(\sqrt{n})` where `n` is the difference between upper and lower
|
|
414
|
+
bounds.
|
|
415
|
+
|
|
416
|
+
EXAMPLES::
|
|
417
|
+
|
|
418
|
+
sage: from sage.groups.generic import bsgs
|
|
419
|
+
sage: b = Mod(2,37); a = b^20
|
|
420
|
+
sage: bsgs(b, a, (0, 36))
|
|
421
|
+
20
|
|
422
|
+
|
|
423
|
+
sage: p = next_prime(10^20) # needs sage.libs.pari
|
|
424
|
+
sage: a = Mod(2,p); b = a^(10^25) # needs sage.libs.pari
|
|
425
|
+
sage: bsgs(a, b, (10^25 - 10^6, 10^25 + 10^6)) == 10^25 # needs sage.libs.pari
|
|
426
|
+
True
|
|
427
|
+
|
|
428
|
+
sage: # needs sage.rings.finite_rings
|
|
429
|
+
sage: K = GF(3^6,'b')
|
|
430
|
+
sage: a = K.gen()
|
|
431
|
+
sage: b = a^210
|
|
432
|
+
sage: bsgs(a, b, (0, K.order() - 1))
|
|
433
|
+
210
|
|
434
|
+
|
|
435
|
+
sage: K.<z> = CyclotomicField(230) # needs sage.rings.number_field
|
|
436
|
+
sage: w = z^500 # needs sage.rings.number_field
|
|
437
|
+
sage: bsgs(z, w, (0, 229)) # needs sage.rings.number_field
|
|
438
|
+
40
|
|
439
|
+
|
|
440
|
+
An additive example in an elliptic curve group::
|
|
441
|
+
|
|
442
|
+
sage: # needs sage.rings.finite_rings sage.schemes
|
|
443
|
+
sage: F.<a> = GF(37^5)
|
|
444
|
+
sage: E = EllipticCurve(F, [1,1])
|
|
445
|
+
sage: P = E.lift_x(a); P
|
|
446
|
+
(a : 28*a^4 + 15*a^3 + 14*a^2 + 7 : 1)
|
|
447
|
+
|
|
448
|
+
This will return a multiple of the order of P::
|
|
449
|
+
|
|
450
|
+
sage: bsgs(P, P.parent().zero(), Hasse_bounds(F.order()), operation='+') # needs sage.rings.finite_rings sage.schemes
|
|
451
|
+
69327408
|
|
452
|
+
|
|
453
|
+
AUTHOR:
|
|
454
|
+
|
|
455
|
+
- John Cremona (2008-03-15)
|
|
456
|
+
"""
|
|
457
|
+
Z = integer_ring.ZZ
|
|
458
|
+
|
|
459
|
+
from operator import inv, mul, neg, add
|
|
460
|
+
|
|
461
|
+
if operation in multiplication_names:
|
|
462
|
+
identity = a.parent().one()
|
|
463
|
+
inverse = inv
|
|
464
|
+
op = mul
|
|
465
|
+
elif operation in addition_names:
|
|
466
|
+
# Should this be replaced with .zero()? With an extra AttributeError handler?
|
|
467
|
+
identity = a.parent().zero()
|
|
468
|
+
inverse = neg
|
|
469
|
+
op = add
|
|
470
|
+
else:
|
|
471
|
+
if identity is None or inverse is None or op is None:
|
|
472
|
+
raise ValueError("identity, inverse and operation must be given")
|
|
473
|
+
|
|
474
|
+
lb, ub = bounds
|
|
475
|
+
if lb < 0 or ub < lb:
|
|
476
|
+
raise ValueError("bsgs() requires 0<=lb<=ub")
|
|
477
|
+
|
|
478
|
+
if a == identity and b != identity:
|
|
479
|
+
raise ValueError("no solution in bsgs()")
|
|
480
|
+
|
|
481
|
+
ran = 1 + ub - lb # the length of the interval
|
|
482
|
+
|
|
483
|
+
mult = lambda x, y: multiple(x, y, operation=operation, identity=identity, inverse=inverse, op=op)
|
|
484
|
+
c = op(inverse(b), mult(a, lb))
|
|
485
|
+
|
|
486
|
+
if ran < 30: # use simple search for small ranges
|
|
487
|
+
d = c
|
|
488
|
+
# for i,d in multiples(a,ran,c,indexed=True,operation=operation):
|
|
489
|
+
for i0 in range(ran):
|
|
490
|
+
i = lb + i0
|
|
491
|
+
if identity == d: # identity == b^(-1)*a^i, so return i
|
|
492
|
+
return Z(i)
|
|
493
|
+
d = op(a, d)
|
|
494
|
+
raise ValueError("no solution in bsgs()")
|
|
495
|
+
|
|
496
|
+
m = ran.isqrt() + 1 # we need sqrt(ran) rounded up
|
|
497
|
+
table = {} # will hold pairs (a^(lb+i),lb+i) for i in range(m)
|
|
498
|
+
|
|
499
|
+
d = c
|
|
500
|
+
for i0 in xsrange(m):
|
|
501
|
+
i = lb + i0
|
|
502
|
+
if identity == d: # identity == b^(-1)*a^i, so return i
|
|
503
|
+
return Z(i)
|
|
504
|
+
table[d] = i
|
|
505
|
+
d = op(d, a)
|
|
506
|
+
|
|
507
|
+
c = op(c, inverse(d)) # this is now a**(-m)
|
|
508
|
+
d = identity
|
|
509
|
+
for i in xsrange(m):
|
|
510
|
+
j = table.get(d)
|
|
511
|
+
if j is not None: # then d == b*a**(-i*m) == a**j
|
|
512
|
+
return Z(i * m + j)
|
|
513
|
+
d = op(c, d)
|
|
514
|
+
|
|
515
|
+
raise ValueError("log of %s to the base %s does not exist in %s" % (b, a, bounds))
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
def discrete_log_rho(a, base, ord=None, operation='*', identity=None, inverse=None, op=None, hash_function=hash):
|
|
519
|
+
"""
|
|
520
|
+
Pollard Rho algorithm for computing discrete logarithm in cyclic
|
|
521
|
+
group of prime order.
|
|
522
|
+
If the group order is very small it falls back to the baby step giant step
|
|
523
|
+
algorithm.
|
|
524
|
+
|
|
525
|
+
INPUT:
|
|
526
|
+
|
|
527
|
+
- ``a`` -- a group element
|
|
528
|
+
- ``base`` -- a group element
|
|
529
|
+
- ``ord`` -- the order of ``base`` or ``None``, in this case we try
|
|
530
|
+
to compute it
|
|
531
|
+
- ``operation`` -- string (default: ``'*'``); denoting whether we
|
|
532
|
+
are in an additive group or a multiplicative one
|
|
533
|
+
- ``identity`` -- the group's identity
|
|
534
|
+
- ``inverse`` -- function of 1 argument ``x``, returning inverse of ``x``
|
|
535
|
+
- ``op`` -- function of 2 arguments ``x``, ``y``, returning ``x*y`` in the group
|
|
536
|
+
- ``hash_function`` -- having an efficient hash function is critical
|
|
537
|
+
for this algorithm (see examples)
|
|
538
|
+
|
|
539
|
+
OUTPUT: integer `n` such that `a = base^n` (or `a = n*base`)
|
|
540
|
+
|
|
541
|
+
ALGORITHM: Pollard rho for discrete logarithm, adapted from the
|
|
542
|
+
article of Edlyn Teske, 'A space efficient algorithm for group
|
|
543
|
+
structure computation'.
|
|
544
|
+
|
|
545
|
+
EXAMPLES::
|
|
546
|
+
|
|
547
|
+
sage: F.<a> = GF(2^13) # needs sage.rings.finite_rings
|
|
548
|
+
sage: g = F.gen() # needs sage.rings.finite_rings
|
|
549
|
+
sage: discrete_log_rho(g^1234, g) # needs sage.rings.finite_rings
|
|
550
|
+
1234
|
|
551
|
+
|
|
552
|
+
sage: # needs sage.rings.finite_rings sage.schemes
|
|
553
|
+
sage: F.<a> = GF(37^5)
|
|
554
|
+
sage: E = EllipticCurve(F, [1,1])
|
|
555
|
+
sage: G = (3*31*2^4)*E.lift_x(a)
|
|
556
|
+
sage: discrete_log_rho(12345*G, G, ord=46591, operation='+')
|
|
557
|
+
12345
|
|
558
|
+
|
|
559
|
+
It also works with matrices::
|
|
560
|
+
|
|
561
|
+
sage: A = matrix(GF(50021), [[10577, 23999, 28893], # needs sage.modules sage.rings.finite_rings
|
|
562
|
+
....: [14601, 41019, 30188],
|
|
563
|
+
....: [3081, 736, 27092]])
|
|
564
|
+
sage: discrete_log_rho(A^1234567, A) # needs sage.modules sage.rings.finite_rings
|
|
565
|
+
1234567
|
|
566
|
+
|
|
567
|
+
Beware, the order must be prime::
|
|
568
|
+
|
|
569
|
+
sage: I = IntegerModRing(171980)
|
|
570
|
+
sage: discrete_log_rho(I(2), I(3)) # needs sage.libs.pari
|
|
571
|
+
Traceback (most recent call last):
|
|
572
|
+
...
|
|
573
|
+
ValueError: for Pollard rho algorithm the order of the group must be prime
|
|
574
|
+
|
|
575
|
+
If it fails to find a suitable logarithm, it raises a :exc:`ValueError`::
|
|
576
|
+
|
|
577
|
+
sage: I = IntegerModRing(171980)
|
|
578
|
+
sage: discrete_log_rho(I(31002), I(15501)) # needs sage.libs.pari
|
|
579
|
+
Traceback (most recent call last):
|
|
580
|
+
...
|
|
581
|
+
ValueError: Pollard rho algorithm failed to find a logarithm
|
|
582
|
+
|
|
583
|
+
The main limitation on the hash function is that we don't want to have
|
|
584
|
+
``hash(x*y) == hash(x) + hash(y)``::
|
|
585
|
+
|
|
586
|
+
sage: # needs sage.libs.pari
|
|
587
|
+
sage: I = IntegerModRing(next_prime(2^23))
|
|
588
|
+
sage: def test():
|
|
589
|
+
....: try:
|
|
590
|
+
....: discrete_log_rho(I(123456), I(1), operation='+')
|
|
591
|
+
....: except Exception:
|
|
592
|
+
....: print("FAILURE")
|
|
593
|
+
sage: test() # random failure
|
|
594
|
+
FAILURE
|
|
595
|
+
|
|
596
|
+
If this happens, we can provide a better hash function::
|
|
597
|
+
|
|
598
|
+
sage: discrete_log_rho(I(123456), I(1), operation='+', # needs sage.libs.pari
|
|
599
|
+
....: hash_function=lambda x: hash(x*x))
|
|
600
|
+
123456
|
|
601
|
+
|
|
602
|
+
AUTHOR:
|
|
603
|
+
|
|
604
|
+
- Yann Laigle-Chapuy (2009-09-05)
|
|
605
|
+
"""
|
|
606
|
+
from sage.rings.integer import Integer
|
|
607
|
+
from sage.rings.finite_rings.integer_mod_ring import IntegerModRing
|
|
608
|
+
from operator import mul, add, pow
|
|
609
|
+
|
|
610
|
+
# should be reasonable choices
|
|
611
|
+
partition_size = 20
|
|
612
|
+
memory_size = 4
|
|
613
|
+
mult = op
|
|
614
|
+
power = lambda x, y: multiple(x, y, operation=operation, identity=identity, inverse=inverse, op=op)
|
|
615
|
+
if operation in addition_names:
|
|
616
|
+
mult = add
|
|
617
|
+
power = mul
|
|
618
|
+
if ord is None:
|
|
619
|
+
ord = base.additive_order()
|
|
620
|
+
elif operation in multiplication_names:
|
|
621
|
+
mult = mul
|
|
622
|
+
power = pow
|
|
623
|
+
if ord is None:
|
|
624
|
+
ord = base.multiplicative_order()
|
|
625
|
+
elif ord is None or inverse is None or identity is None or op is None:
|
|
626
|
+
raise ValueError
|
|
627
|
+
|
|
628
|
+
ord = Integer(ord)
|
|
629
|
+
if not ord.is_prime():
|
|
630
|
+
raise ValueError("for Pollard rho algorithm the order of the group must be prime")
|
|
631
|
+
|
|
632
|
+
# check if we need to set immutable before hashing
|
|
633
|
+
mut = hasattr(base, 'set_immutable')
|
|
634
|
+
|
|
635
|
+
isqrtord = ord.isqrt()
|
|
636
|
+
|
|
637
|
+
if isqrtord < partition_size: # setup to costly, use bsgs
|
|
638
|
+
return bsgs(base, a, bounds=(0, ord), identity=identity, inverse=inverse, op=op, operation=operation)
|
|
639
|
+
|
|
640
|
+
reset_bound = 8 * isqrtord # we take some margin
|
|
641
|
+
|
|
642
|
+
I = IntegerModRing(ord)
|
|
643
|
+
|
|
644
|
+
for s in range(10): # to avoid infinite loops
|
|
645
|
+
# random walk function setup
|
|
646
|
+
m = [I.random_element() for i in range(partition_size)]
|
|
647
|
+
n = [I.random_element() for i in range(partition_size)]
|
|
648
|
+
M = [mult(power(base, Integer(m[i])), power(a, Integer(n[i])))
|
|
649
|
+
for i in range(partition_size)]
|
|
650
|
+
|
|
651
|
+
ax = I.random_element()
|
|
652
|
+
x = power(base, Integer(ax))
|
|
653
|
+
if mut:
|
|
654
|
+
x.set_immutable()
|
|
655
|
+
|
|
656
|
+
bx = I(0)
|
|
657
|
+
|
|
658
|
+
sigma = [(0, None)] * memory_size
|
|
659
|
+
H = {} # memory
|
|
660
|
+
i0 = 0
|
|
661
|
+
nextsigma = 0
|
|
662
|
+
for i in range(reset_bound):
|
|
663
|
+
# random walk, we need an efficient hash
|
|
664
|
+
s = hash_function(x) % partition_size
|
|
665
|
+
x, ax, bx = (mult(M[s], x), ax + m[s], bx + n[s])
|
|
666
|
+
if mut:
|
|
667
|
+
x.set_immutable()
|
|
668
|
+
# look for collisions
|
|
669
|
+
if x in H:
|
|
670
|
+
ay, by = H[x]
|
|
671
|
+
if bx == by:
|
|
672
|
+
break
|
|
673
|
+
else:
|
|
674
|
+
res = sage.rings.integer.Integer((ay - ax) / (bx - by))
|
|
675
|
+
if power(base, res) == a:
|
|
676
|
+
return res
|
|
677
|
+
else:
|
|
678
|
+
break
|
|
679
|
+
# should we remember this value?
|
|
680
|
+
elif i >= nextsigma:
|
|
681
|
+
if sigma[i0][1] is not None:
|
|
682
|
+
H.pop(sigma[i0][1])
|
|
683
|
+
sigma[i0] = (i, x)
|
|
684
|
+
i0 = (i0 + 1) % memory_size
|
|
685
|
+
nextsigma = 3 * sigma[i0][0] # 3 seems a good choice
|
|
686
|
+
H[x] = (ax, bx)
|
|
687
|
+
|
|
688
|
+
raise ValueError("Pollard rho algorithm failed to find a logarithm")
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
def discrete_log(a, base, ord=None, bounds=None, operation='*', identity=None, inverse=None, op=None, algorithm='bsgs', *, verify=True):
|
|
692
|
+
r"""
|
|
693
|
+
Totally generic discrete log function.
|
|
694
|
+
|
|
695
|
+
INPUT:
|
|
696
|
+
|
|
697
|
+
- ``a`` -- group element
|
|
698
|
+
- ``base`` -- group element (the base)
|
|
699
|
+
- ``ord`` -- integer (multiple of order of base, or ``None``)
|
|
700
|
+
- ``bounds`` -- a priori bounds on the log
|
|
701
|
+
- ``operation`` -- string: ``'*'``, ``'+'``, other
|
|
702
|
+
- ``identity`` -- the group's identity
|
|
703
|
+
- ``inverse`` -- function of 1 argument ``x``, returning inverse of ``x``
|
|
704
|
+
- ``op`` -- function of 2 arguments ``x``, ``y``, returning ``x*y`` in the group
|
|
705
|
+
- ``algorithm`` -- string denoting what algorithm to use for prime-order
|
|
706
|
+
logarithms: ``'bsgs'``, ``'rho'``, ``'lambda'``
|
|
707
|
+
- ``verify`` -- boolean (default: ``True``); whether to verify that output is
|
|
708
|
+
correct before returning it.
|
|
709
|
+
|
|
710
|
+
``a`` and ``base`` must be elements of some group with identity
|
|
711
|
+
given by ``identity``, inverse of ``x`` by ``inverse(x)``, and group
|
|
712
|
+
operation on ``x``, ``y`` by ``op(x,y)``.
|
|
713
|
+
|
|
714
|
+
If operation is ``'*'`` or ``'+'``, then the other
|
|
715
|
+
arguments are provided automatically; otherwise they must be
|
|
716
|
+
provided by the caller.
|
|
717
|
+
|
|
718
|
+
OUTPUT:
|
|
719
|
+
|
|
720
|
+
This returns an integer `n` such that `b^n = a` (or `nb = a`),
|
|
721
|
+
assuming that ``ord`` is a multiple of the order of the base `b`.
|
|
722
|
+
If ``ord`` is not specified, an attempt is made to compute it.
|
|
723
|
+
|
|
724
|
+
If no such `n` exists, this function raises a :exc:`ValueError` exception.
|
|
725
|
+
|
|
726
|
+
.. WARNING::
|
|
727
|
+
|
|
728
|
+
If ``x`` has a ``log`` method, it is likely to be vastly faster
|
|
729
|
+
than using this function. E.g., if ``x`` is an integer modulo
|
|
730
|
+
`n`, use its ``log`` method instead!
|
|
731
|
+
|
|
732
|
+
ALGORITHM: Pohlig-Hellman, Baby step giant step, Pollard's lambda/kangaroo, and Pollard's rho.
|
|
733
|
+
|
|
734
|
+
EXAMPLES::
|
|
735
|
+
|
|
736
|
+
sage: b = Mod(2,37); a = b^20
|
|
737
|
+
sage: discrete_log(a, b)
|
|
738
|
+
20
|
|
739
|
+
sage: b = Mod(3,2017); a = b^20
|
|
740
|
+
sage: discrete_log(a, b, bounds=(10, 100))
|
|
741
|
+
20
|
|
742
|
+
|
|
743
|
+
sage: # needs sage.rings.finite_rings
|
|
744
|
+
sage: K = GF(3^6, 'b')
|
|
745
|
+
sage: b = K.gen()
|
|
746
|
+
sage: a = b^210
|
|
747
|
+
sage: discrete_log(a, b, K.order() - 1)
|
|
748
|
+
210
|
|
749
|
+
|
|
750
|
+
sage: b = Mod(1,37); x = Mod(2,37)
|
|
751
|
+
sage: discrete_log(x, b)
|
|
752
|
+
Traceback (most recent call last):
|
|
753
|
+
...
|
|
754
|
+
ValueError: no discrete log of 2 found to base 1
|
|
755
|
+
sage: b = Mod(1,997); x = Mod(2,997)
|
|
756
|
+
sage: discrete_log(x, b)
|
|
757
|
+
Traceback (most recent call last):
|
|
758
|
+
...
|
|
759
|
+
ValueError: no discrete log of 2 found to base 1
|
|
760
|
+
|
|
761
|
+
See :issue:`2356`::
|
|
762
|
+
|
|
763
|
+
sage: F.<w> = GF(121) # needs sage.rings.finite_rings
|
|
764
|
+
sage: v = w^120 # needs sage.rings.finite_rings
|
|
765
|
+
sage: v.log(w) # needs sage.rings.finite_rings
|
|
766
|
+
0
|
|
767
|
+
|
|
768
|
+
sage: K.<z> = CyclotomicField(230) # needs sage.rings.number_field
|
|
769
|
+
sage: w = z^50 # needs sage.rings.number_field
|
|
770
|
+
sage: discrete_log(w, z) # needs sage.rings.number_field
|
|
771
|
+
50
|
|
772
|
+
|
|
773
|
+
An example where the order is infinite: note that we must give
|
|
774
|
+
an upper bound here::
|
|
775
|
+
|
|
776
|
+
sage: # needs sage.rings.number_field
|
|
777
|
+
sage: K.<a> = QuadraticField(23)
|
|
778
|
+
sage: eps = 5*a - 24 # a fundamental unit
|
|
779
|
+
sage: eps.multiplicative_order()
|
|
780
|
+
+Infinity
|
|
781
|
+
sage: eta = eps^100
|
|
782
|
+
sage: discrete_log(eta, eps, bounds=(0,1000))
|
|
783
|
+
100
|
|
784
|
+
|
|
785
|
+
In this case we cannot detect negative powers::
|
|
786
|
+
|
|
787
|
+
sage: eta = eps^(-3) # needs sage.rings.number_field
|
|
788
|
+
sage: discrete_log(eta,eps,bounds=(0,100)) # needs sage.rings.number_field
|
|
789
|
+
Traceback (most recent call last):
|
|
790
|
+
...
|
|
791
|
+
ValueError: no discrete log of -11515*a - 55224 found to base 5*a - 24 with bounds (0, 100)
|
|
792
|
+
|
|
793
|
+
But we can invert the base (and negate the result) instead::
|
|
794
|
+
|
|
795
|
+
sage: -discrete_log(eta^-1, eps, bounds=(0,100)) # needs sage.rings.number_field
|
|
796
|
+
-3
|
|
797
|
+
|
|
798
|
+
An additive example: elliptic curve DLOG::
|
|
799
|
+
|
|
800
|
+
sage: # needs sage.libs.gap sage.rings.finite_rings sage.schemes
|
|
801
|
+
sage: F = GF(37^2,'a')
|
|
802
|
+
sage: E = EllipticCurve(F, [1,1])
|
|
803
|
+
sage: F.<a> = GF(37^2,'a')
|
|
804
|
+
sage: E = EllipticCurve(F, [1,1])
|
|
805
|
+
sage: P = E(25*a + 16, 15*a + 7)
|
|
806
|
+
sage: P.order()
|
|
807
|
+
672
|
|
808
|
+
sage: Q = 39*P; Q
|
|
809
|
+
(36*a + 32 : 5*a + 12 : 1)
|
|
810
|
+
sage: discrete_log(Q, P, P.order(), operation='+')
|
|
811
|
+
39
|
|
812
|
+
|
|
813
|
+
An example of big smooth group::
|
|
814
|
+
|
|
815
|
+
sage: # needs sage.rings.finite_rings
|
|
816
|
+
sage: F.<a> = GF(2^63)
|
|
817
|
+
sage: g = F.gen()
|
|
818
|
+
sage: u = g**123456789
|
|
819
|
+
sage: discrete_log(u,g)
|
|
820
|
+
123456789
|
|
821
|
+
|
|
822
|
+
The above examples also work when the ``'rho'`` and ``'lambda'`` algorithms are used::
|
|
823
|
+
|
|
824
|
+
sage: b = Mod(2,37); a = b^20
|
|
825
|
+
sage: discrete_log(a, b, algorithm='rho')
|
|
826
|
+
20
|
|
827
|
+
sage: b = Mod(3,2017); a = b^20
|
|
828
|
+
sage: discrete_log(a, b, algorithm='lambda', bounds=(10, 100))
|
|
829
|
+
20
|
|
830
|
+
|
|
831
|
+
sage: # needs sage.rings.finite_rings
|
|
832
|
+
sage: K = GF(3^6,'b')
|
|
833
|
+
sage: b = K.gen()
|
|
834
|
+
sage: a = b^210
|
|
835
|
+
sage: discrete_log(a, b, K.order()-1, algorithm='rho')
|
|
836
|
+
210
|
|
837
|
+
|
|
838
|
+
sage: b = Mod(1,37); x = Mod(2,37)
|
|
839
|
+
sage: discrete_log(x, b, algorithm='lambda')
|
|
840
|
+
Traceback (most recent call last):
|
|
841
|
+
...
|
|
842
|
+
ValueError: no discrete log of 2 found to base 1
|
|
843
|
+
sage: b = Mod(1,997); x = Mod(2,997)
|
|
844
|
+
sage: discrete_log(x, b, algorithm='rho')
|
|
845
|
+
Traceback (most recent call last):
|
|
846
|
+
...
|
|
847
|
+
ValueError: no discrete log of 2 found to base 1
|
|
848
|
+
|
|
849
|
+
sage: # needs sage.libs.gap sage.rings.finite_rings sage.schemes
|
|
850
|
+
sage: F = GF(37^2,'a')
|
|
851
|
+
sage: E = EllipticCurve(F, [1,1])
|
|
852
|
+
sage: F.<a> = GF(37^2,'a')
|
|
853
|
+
sage: E = EllipticCurve(F, [1,1])
|
|
854
|
+
sage: P = E(25*a + 16, 15*a + 7)
|
|
855
|
+
sage: P.order()
|
|
856
|
+
672
|
|
857
|
+
sage: Q = 39*P; Q
|
|
858
|
+
(36*a + 32 : 5*a + 12 : 1)
|
|
859
|
+
sage: discrete_log(Q, P, P.order(), operation='+', algorithm='lambda')
|
|
860
|
+
39
|
|
861
|
+
|
|
862
|
+
sage: # needs sage.rings.finite_rings
|
|
863
|
+
sage: F.<a> = GF(2^63)
|
|
864
|
+
sage: g = F.gen()
|
|
865
|
+
sage: u = g**123456789
|
|
866
|
+
sage: discrete_log(u, g, algorithm='rho')
|
|
867
|
+
123456789
|
|
868
|
+
|
|
869
|
+
TESTS:
|
|
870
|
+
|
|
871
|
+
Random testing::
|
|
872
|
+
|
|
873
|
+
sage: G = Zmod(randrange(1, 1000))
|
|
874
|
+
sage: base = G.random_element()
|
|
875
|
+
sage: order = choice([base.additive_order(), G.order()])
|
|
876
|
+
sage: assert order.divides(G.cardinality())
|
|
877
|
+
sage: sol = randrange(base.additive_order())
|
|
878
|
+
sage: elem = sol * base
|
|
879
|
+
sage: args = (elem, base, order)
|
|
880
|
+
sage: kwargs = {'operation': '+'}
|
|
881
|
+
sage: kwargs['algorithm'] = choice(['bsgs', 'rho', 'lambda'])
|
|
882
|
+
sage: if randrange(2):
|
|
883
|
+
....: lo = randrange(-order, sol + 1)
|
|
884
|
+
....: hi = randrange(sol + 1, 2*order)
|
|
885
|
+
....: assert lo <= sol <= hi
|
|
886
|
+
....: kwargs['bounds'] = (lo, hi)
|
|
887
|
+
sage: try:
|
|
888
|
+
....: res = discrete_log(*args, **kwargs)
|
|
889
|
+
....: except ValueError:
|
|
890
|
+
....: # lambda can fail randomly
|
|
891
|
+
....: assert kwargs['algorithm'] == 'lambda'
|
|
892
|
+
....: else:
|
|
893
|
+
....: assert res == sol
|
|
894
|
+
|
|
895
|
+
Verify that :issue:`38316` is fixed::
|
|
896
|
+
|
|
897
|
+
sage: F = GF(5)
|
|
898
|
+
sage: base = F(3)
|
|
899
|
+
sage: a = F(1)
|
|
900
|
+
sage: discrete_log(a, base, bounds=(1,2), operation="*")
|
|
901
|
+
Traceback (most recent call last):
|
|
902
|
+
...
|
|
903
|
+
ValueError: no discrete log of 1 found to base 3 with bounds (1, 2)
|
|
904
|
+
|
|
905
|
+
AUTHORS:
|
|
906
|
+
|
|
907
|
+
- William Stein and David Joyner (2005-01-05)
|
|
908
|
+
- John Cremona (2008-02-29) rewrite using ``dict()`` and make generic
|
|
909
|
+
- Julien Grijalva (2022-08-09) rewrite to make more generic, more algorithm options, and more effective use of bounds
|
|
910
|
+
"""
|
|
911
|
+
from operator import mul, add, pow
|
|
912
|
+
power = mul if operation in addition_names else pow
|
|
913
|
+
mult = add if operation in addition_names else mul
|
|
914
|
+
original_a = a # Store the original value of a so we can verify the answer
|
|
915
|
+
if op:
|
|
916
|
+
mult = op
|
|
917
|
+
power = lambda x, y: multiple(x, y, operation=operation, identity=identity, inverse=inverse, op=op)
|
|
918
|
+
if bounds:
|
|
919
|
+
lb, ub = map(integer_ring.ZZ, bounds)
|
|
920
|
+
if (op is None or identity is None or inverse is None or ord is None) and operation not in addition_names + multiplication_names:
|
|
921
|
+
raise ValueError("ord, op, identity, and inverse must all be specified for this operation")
|
|
922
|
+
if ord is None:
|
|
923
|
+
if operation in multiplication_names:
|
|
924
|
+
try:
|
|
925
|
+
ord = base.multiplicative_order()
|
|
926
|
+
except Exception:
|
|
927
|
+
ord = base.order()
|
|
928
|
+
else:
|
|
929
|
+
try:
|
|
930
|
+
ord = base.additive_order()
|
|
931
|
+
except Exception:
|
|
932
|
+
ord = base.order()
|
|
933
|
+
else:
|
|
934
|
+
ord = integer_ring.ZZ(ord)
|
|
935
|
+
try:
|
|
936
|
+
from sage.rings.infinity import Infinity
|
|
937
|
+
if ord == +Infinity:
|
|
938
|
+
return bsgs(base, a, bounds, identity=identity, inverse=inverse, op=op, operation=operation)
|
|
939
|
+
if base == power(base, 0) and a != base:
|
|
940
|
+
raise ValueError
|
|
941
|
+
f = ord.factor()
|
|
942
|
+
l = [0] * len(f)
|
|
943
|
+
mods = []
|
|
944
|
+
running_mod = 1
|
|
945
|
+
offset = 0
|
|
946
|
+
if bounds:
|
|
947
|
+
a = mult(a, power(base, -lb))
|
|
948
|
+
offset = lb
|
|
949
|
+
bound = ub - lb
|
|
950
|
+
i = -1 # this corrects a bug in which the loop is never entered and i never gets assigned a value
|
|
951
|
+
for i, (pi, ri) in enumerate(f):
|
|
952
|
+
gamma = power(base, ord // pi)
|
|
953
|
+
# pohlig-hellman doesn't work with an incorrect order, and the user might have provided a bad parameter
|
|
954
|
+
while gamma == power(gamma, 0) and ri > 0: # identity might be None
|
|
955
|
+
ord //= pi
|
|
956
|
+
ri -= 1
|
|
957
|
+
gamma = power(base, ord // pi)
|
|
958
|
+
if not bounds:
|
|
959
|
+
bound = ord - 1
|
|
960
|
+
running_bound = min(bound, pi**ri - 1)
|
|
961
|
+
j = -1
|
|
962
|
+
for j in range(ri):
|
|
963
|
+
temp_bound = min(running_bound, pi - 1)
|
|
964
|
+
h = power(mult(a, power(base, -l[i])), ord // pi**(j + 1))
|
|
965
|
+
if algorithm == 'bsgs':
|
|
966
|
+
c = bsgs(gamma, h, (0, temp_bound), inverse=inverse, identity=identity, op=op, operation=operation)
|
|
967
|
+
elif algorithm == 'rho':
|
|
968
|
+
c = discrete_log_rho(h, gamma, ord=pi, inverse=inverse, identity=identity, op=op, operation=operation)
|
|
969
|
+
elif algorithm == 'lambda':
|
|
970
|
+
c = discrete_log_lambda(h, gamma, (0, temp_bound), inverse=inverse, identity=identity, op=op, operation=operation)
|
|
971
|
+
l[i] += c * (pi**j)
|
|
972
|
+
running_bound //= pi
|
|
973
|
+
running_mod *= pi
|
|
974
|
+
if running_mod > bound:
|
|
975
|
+
break
|
|
976
|
+
mods.append(pi ** (j+1))
|
|
977
|
+
if running_mod > bound:
|
|
978
|
+
break # we have log%running_mod. if we know that log<running_mod, then we have the value of log.
|
|
979
|
+
l = l[:i + 1]
|
|
980
|
+
from sage.arith.misc import CRT_list
|
|
981
|
+
result = (CRT_list(l, mods) + offset) % ord
|
|
982
|
+
if (verify and power(base, result) != original_a):
|
|
983
|
+
raise ValueError
|
|
984
|
+
return result
|
|
985
|
+
except ValueError:
|
|
986
|
+
with_bounds = f" with bounds {bounds}" if bounds else ""
|
|
987
|
+
raise ValueError(f"no discrete log of {original_a} found to base {base}{with_bounds}")
|
|
988
|
+
|
|
989
|
+
|
|
990
|
+
def discrete_log_generic(a, base, ord=None, bounds=None, operation='*', identity=None, inverse=None, op=None, algorithm='bsgs'):
|
|
991
|
+
"""
|
|
992
|
+
Alias for ``discrete_log``.
|
|
993
|
+
"""
|
|
994
|
+
return discrete_log(a, base, ord=ord, bounds=bounds, operation=operation, identity=identity, inverse=inverse, op=op, algorithm=algorithm)
|
|
995
|
+
|
|
996
|
+
|
|
997
|
+
def discrete_log_lambda(a, base, bounds, operation='*', identity=None, inverse=None, op=None, hash_function=hash):
|
|
998
|
+
"""
|
|
999
|
+
Pollard Lambda algorithm for computing discrete logarithms. It uses
|
|
1000
|
+
only a logarithmic amount of memory. It's useful if you have
|
|
1001
|
+
bounds on the logarithm. If you are computing logarithms in a
|
|
1002
|
+
whole finite group, you should use Pollard Rho algorithm.
|
|
1003
|
+
|
|
1004
|
+
INPUT:
|
|
1005
|
+
|
|
1006
|
+
- ``a`` -- a group element
|
|
1007
|
+
- ``base`` -- a group element
|
|
1008
|
+
- ``bounds`` -- a couple (lb,ub) representing the range where we look for a logarithm
|
|
1009
|
+
- ``operation`` -- string: '+', '*' or 'other'
|
|
1010
|
+
- ``identity`` -- the identity element of the group
|
|
1011
|
+
- ``inverse`` -- function of 1 argument ``x`` returning inverse of ``x``
|
|
1012
|
+
- ``op`` -- function of 2 arguments ``x``, ``y`` returning ``x*y`` in the group
|
|
1013
|
+
- ``hash_function`` -- having an efficient hash function is critical for this algorithm
|
|
1014
|
+
|
|
1015
|
+
OUTPUT: integer `n` such that `a=base^n` (or `a=n*base`)
|
|
1016
|
+
|
|
1017
|
+
ALGORITHM: Pollard Lambda, if bounds are (lb,ub) it has time complexity
|
|
1018
|
+
O(sqrt(ub-lb)) and space complexity O(log(ub-lb))
|
|
1019
|
+
|
|
1020
|
+
EXAMPLES::
|
|
1021
|
+
|
|
1022
|
+
sage: F.<a> = GF(2^63) # needs sage.rings.finite_rings
|
|
1023
|
+
sage: discrete_log_lambda(a^1234567, a, (1200000,1250000)) # needs sage.rings.finite_rings
|
|
1024
|
+
1234567
|
|
1025
|
+
|
|
1026
|
+
sage: # needs sage.rings.finite_rings sage.schemes
|
|
1027
|
+
sage: F.<a> = GF(37^5)
|
|
1028
|
+
sage: E = EllipticCurve(F, [1,1])
|
|
1029
|
+
sage: P = E.lift_x(a); P
|
|
1030
|
+
(a : 28*a^4 + 15*a^3 + 14*a^2 + 7 : 1)
|
|
1031
|
+
|
|
1032
|
+
This will return a multiple of the order of P::
|
|
1033
|
+
|
|
1034
|
+
sage: discrete_log_lambda(P.parent().zero(), P, Hasse_bounds(F.order()), # needs sage.rings.finite_rings sage.schemes
|
|
1035
|
+
....: operation='+')
|
|
1036
|
+
69327408
|
|
1037
|
+
|
|
1038
|
+
sage: K.<a> = GF(89**5) # needs sage.rings.finite_rings
|
|
1039
|
+
sage: hs = lambda x: hash(x) + 15
|
|
1040
|
+
sage: discrete_log_lambda(a**(89**3 - 3), # long time (10s on sage.math, 2011), needs sage.rings.finite_rings
|
|
1041
|
+
....: a, (89**2, 89**4), operation='*', hash_function=hs)
|
|
1042
|
+
704966
|
|
1043
|
+
|
|
1044
|
+
AUTHOR:
|
|
1045
|
+
|
|
1046
|
+
-- Yann Laigle-Chapuy (2009-01-25)
|
|
1047
|
+
"""
|
|
1048
|
+
from sage.rings.integer import Integer
|
|
1049
|
+
from operator import mul, add
|
|
1050
|
+
|
|
1051
|
+
mult = op
|
|
1052
|
+
if operation in addition_names:
|
|
1053
|
+
mult = add
|
|
1054
|
+
elif operation in multiplication_names:
|
|
1055
|
+
mult = mul
|
|
1056
|
+
power = lambda x, y: multiple(x, y, operation=operation, identity=identity, inverse=inverse, op=op)
|
|
1057
|
+
|
|
1058
|
+
lb, ub = bounds
|
|
1059
|
+
if lb < 0 or ub < lb:
|
|
1060
|
+
raise ValueError("discrete_log_lambda() requires 0<=lb<=ub")
|
|
1061
|
+
|
|
1062
|
+
# check for mutability
|
|
1063
|
+
mut = hasattr(base, 'set_immutable')
|
|
1064
|
+
|
|
1065
|
+
width = Integer(ub - lb)
|
|
1066
|
+
N = width.isqrt() + 1
|
|
1067
|
+
|
|
1068
|
+
M = {}
|
|
1069
|
+
for s in range(10): # to avoid infinite loops
|
|
1070
|
+
# random walk function setup
|
|
1071
|
+
k = 0
|
|
1072
|
+
while 2**k < N:
|
|
1073
|
+
r = sage.misc.prandom.randrange(1, N)
|
|
1074
|
+
M[k] = (r, power(base, r))
|
|
1075
|
+
k += 1
|
|
1076
|
+
# first random walk
|
|
1077
|
+
H = power(base, ub)
|
|
1078
|
+
c = ub
|
|
1079
|
+
for i in range(N):
|
|
1080
|
+
if mut:
|
|
1081
|
+
H.set_immutable()
|
|
1082
|
+
r, e = M[hash_function(H) % k]
|
|
1083
|
+
H = mult(H, e)
|
|
1084
|
+
c += r
|
|
1085
|
+
if mut:
|
|
1086
|
+
H.set_immutable()
|
|
1087
|
+
mem = {H}
|
|
1088
|
+
# second random walk
|
|
1089
|
+
H = a
|
|
1090
|
+
d = 0
|
|
1091
|
+
while c - d >= lb:
|
|
1092
|
+
if mut:
|
|
1093
|
+
H.set_immutable()
|
|
1094
|
+
if ub >= c - d and H in mem:
|
|
1095
|
+
return c - d
|
|
1096
|
+
r, e = M[hash_function(H) % k]
|
|
1097
|
+
H = mult(H, e)
|
|
1098
|
+
d += r
|
|
1099
|
+
|
|
1100
|
+
raise ValueError("Pollard Lambda failed to find a log")
|
|
1101
|
+
|
|
1102
|
+
|
|
1103
|
+
################################################################
|
|
1104
|
+
#
|
|
1105
|
+
# Generic linear relation finder
|
|
1106
|
+
#
|
|
1107
|
+
################################################################
|
|
1108
|
+
|
|
1109
|
+
|
|
1110
|
+
def linear_relation(P, Q, operation='+', identity=None, inverse=None, op=None):
|
|
1111
|
+
r"""
|
|
1112
|
+
Function which solves the equation ``a*P=m*Q`` or ``P^a=Q^m``.
|
|
1113
|
+
|
|
1114
|
+
Additive version: returns `(a,m)` with minimal `m>0` such that
|
|
1115
|
+
`aP=mQ`. Special case: if `\left<P\right>` and `\left<Q\right>`
|
|
1116
|
+
intersect only in `\{0\}` then `(a,m)=(0,n)` where `n` is
|
|
1117
|
+
``Q.additive_order()``.
|
|
1118
|
+
|
|
1119
|
+
Multiplicative version: returns `(a,m)` with minimal `m>0` such
|
|
1120
|
+
that `P^a=Q^m`. Special case: if `\left<P\right>` and
|
|
1121
|
+
`\left<Q\right>` intersect only in `\{1\}` then `(a,m)=(0,n)`
|
|
1122
|
+
where `n` is ``Q.multiplicative_order()``.
|
|
1123
|
+
|
|
1124
|
+
ALGORITHM:
|
|
1125
|
+
|
|
1126
|
+
Uses the generic ``bsgs()`` function, and so works in general
|
|
1127
|
+
finite abelian groups.
|
|
1128
|
+
|
|
1129
|
+
EXAMPLES:
|
|
1130
|
+
|
|
1131
|
+
An additive example (in an elliptic curve group)::
|
|
1132
|
+
|
|
1133
|
+
sage: # needs sage.rings.finite_rings sage.schemes
|
|
1134
|
+
sage: F.<a> = GF(3^6,'a')
|
|
1135
|
+
sage: E = EllipticCurve([a^5 + 2*a^3 + 2*a^2 + 2*a, a^4 + a^3 + 2*a + 1])
|
|
1136
|
+
sage: P = E(a^5 + a^4 + a^3 + a^2 + a + 2, 0)
|
|
1137
|
+
sage: Q = E(2*a^3 + 2*a^2 + 2*a, a^3 + 2*a^2 + 1)
|
|
1138
|
+
sage: linear_relation(P, Q, '+')
|
|
1139
|
+
(1, 2)
|
|
1140
|
+
sage: P == 2*Q
|
|
1141
|
+
True
|
|
1142
|
+
|
|
1143
|
+
A multiplicative example (in a finite field's multiplicative group)::
|
|
1144
|
+
|
|
1145
|
+
sage: # needs sage.rings.finite_rings
|
|
1146
|
+
sage: F.<a> = GF(3^6,'a')
|
|
1147
|
+
sage: a.multiplicative_order().factor()
|
|
1148
|
+
2^3 * 7 * 13
|
|
1149
|
+
sage: b = a^7
|
|
1150
|
+
sage: c = a^13
|
|
1151
|
+
sage: linear_relation(b, c, '*')
|
|
1152
|
+
(13, 7)
|
|
1153
|
+
sage: b^13 == c^7
|
|
1154
|
+
True
|
|
1155
|
+
"""
|
|
1156
|
+
Z = integer_ring.ZZ
|
|
1157
|
+
|
|
1158
|
+
if operation in multiplication_names:
|
|
1159
|
+
try:
|
|
1160
|
+
n = P.multiplicative_order()
|
|
1161
|
+
m = Q.multiplicative_order()
|
|
1162
|
+
except Exception:
|
|
1163
|
+
n = P.order()
|
|
1164
|
+
m = Q.order()
|
|
1165
|
+
elif operation in addition_names:
|
|
1166
|
+
try:
|
|
1167
|
+
n = P.additive_order()
|
|
1168
|
+
m = Q.additive_order()
|
|
1169
|
+
except Exception:
|
|
1170
|
+
n = P.order()
|
|
1171
|
+
m = Q.order()
|
|
1172
|
+
else:
|
|
1173
|
+
if op is None:
|
|
1174
|
+
raise ValueError("operation must be specified")
|
|
1175
|
+
n = P.order()
|
|
1176
|
+
m = Q.order()
|
|
1177
|
+
|
|
1178
|
+
g = n.gcd(m)
|
|
1179
|
+
if g == 1:
|
|
1180
|
+
return (m, Z.zero())
|
|
1181
|
+
n1 = n // g
|
|
1182
|
+
m1 = m // g
|
|
1183
|
+
P1 = multiple(P, n1, operation=operation) # has exact order g
|
|
1184
|
+
Q1 = multiple(Q, m1, operation=operation) # has exact order g
|
|
1185
|
+
|
|
1186
|
+
# now see if Q1 is a multiple of P1; the only multiples we
|
|
1187
|
+
# need check are h*Q1 where h divides g
|
|
1188
|
+
for h in g.divisors(): # positive divisors!
|
|
1189
|
+
try:
|
|
1190
|
+
Q2 = multiple(Q1, h, operation=operation)
|
|
1191
|
+
return (n1 * bsgs(P1, Q2, (0, g - 1), operation=operation),
|
|
1192
|
+
m1 * h)
|
|
1193
|
+
except ValueError:
|
|
1194
|
+
pass # to next h
|
|
1195
|
+
raise ValueError("no solution found in linear_relation")
|
|
1196
|
+
|
|
1197
|
+
################################################################
|
|
1198
|
+
#
|
|
1199
|
+
# Generic functions to find orders of elements
|
|
1200
|
+
#
|
|
1201
|
+
# 1. order_from_multiple: finds the order given a multiple of the order
|
|
1202
|
+
#
|
|
1203
|
+
# 2. order_from_bounds: finds the order given an interval containing a
|
|
1204
|
+
# multiple of the order
|
|
1205
|
+
#
|
|
1206
|
+
# 3. has_order: check if the order is exactly equal to a given integer
|
|
1207
|
+
#
|
|
1208
|
+
################################################################
|
|
1209
|
+
|
|
1210
|
+
|
|
1211
|
+
def order_from_multiple(P, m, plist=None, factorization=None, check=True,
|
|
1212
|
+
operation='+', identity=None, inverse=None, op=None):
|
|
1213
|
+
r"""
|
|
1214
|
+
Generic function to find order of a group element given a multiple
|
|
1215
|
+
of its order.
|
|
1216
|
+
|
|
1217
|
+
See :meth:`bsgs` for full explanation of the inputs.
|
|
1218
|
+
|
|
1219
|
+
INPUT:
|
|
1220
|
+
|
|
1221
|
+
- ``P`` -- a Sage object which is a group element
|
|
1222
|
+
- ``m`` -- Sage integer which is a multiple of the order of ``P``,
|
|
1223
|
+
i.e. we require that ``m*P=0`` (or ``P**m=1``)
|
|
1224
|
+
- ``check`` -- a Boolean (default: ``True``), indicating whether we check if ``m``
|
|
1225
|
+
really is a multiple of the order
|
|
1226
|
+
- ``factorization`` -- the factorization of ``m``, or ``None`` in which
|
|
1227
|
+
case this function will need to factor ``m``
|
|
1228
|
+
- ``plist`` -- list of the prime factors of ``m``, or ``None``. Kept for compatibility only,
|
|
1229
|
+
prefer the use of ``factorization``
|
|
1230
|
+
- ``operation`` -- string: ``'+'`` (default), ``'*'`` or ``None``
|
|
1231
|
+
- ``identity`` -- the identity element of the group
|
|
1232
|
+
- ``inverse`` -- function of 1 argument ``x``, returning inverse of ``x``
|
|
1233
|
+
- ``op`` -- function of 2 arguments ``x``, ``y`` returning ``x*y`` in the group
|
|
1234
|
+
|
|
1235
|
+
.. NOTE::
|
|
1236
|
+
|
|
1237
|
+
It is more efficient for the caller to factor ``m`` and cache
|
|
1238
|
+
the factors for subsequent calls.
|
|
1239
|
+
|
|
1240
|
+
EXAMPLES::
|
|
1241
|
+
|
|
1242
|
+
sage: from sage.groups.generic import order_from_multiple
|
|
1243
|
+
|
|
1244
|
+
sage: # needs sage.rings.finite_rings
|
|
1245
|
+
sage: k.<a> = GF(5^5)
|
|
1246
|
+
sage: b = a^4
|
|
1247
|
+
sage: order_from_multiple(b, 5^5 - 1, operation='*')
|
|
1248
|
+
781
|
|
1249
|
+
|
|
1250
|
+
sage: # needs sage.rings.finite_rings sage.schemes
|
|
1251
|
+
sage: E = EllipticCurve(k, [2,4])
|
|
1252
|
+
sage: P = E(3*a^4 + 3*a, 2*a + 1)
|
|
1253
|
+
sage: M = E.cardinality(); M
|
|
1254
|
+
3227
|
|
1255
|
+
sage: F = M.factor()
|
|
1256
|
+
sage: order_from_multiple(P, M, factorization=F, operation='+')
|
|
1257
|
+
3227
|
|
1258
|
+
sage: Q = E(0,2)
|
|
1259
|
+
sage: order_from_multiple(Q, M, factorization=F, operation='+')
|
|
1260
|
+
7
|
|
1261
|
+
|
|
1262
|
+
sage: # needs sage.rings.number_field
|
|
1263
|
+
sage: K.<z> = CyclotomicField(230)
|
|
1264
|
+
sage: w = z^50
|
|
1265
|
+
sage: order_from_multiple(w, 230, operation='*')
|
|
1266
|
+
23
|
|
1267
|
+
|
|
1268
|
+
sage: # needs sage.modules sage.rings.finite_rings
|
|
1269
|
+
sage: F = GF(2^1279,'a')
|
|
1270
|
+
sage: n = F.cardinality() - 1 # Mersenne prime
|
|
1271
|
+
sage: order_from_multiple(F.random_element(), n,
|
|
1272
|
+
....: factorization=[(n,1)], operation='*') == n
|
|
1273
|
+
True
|
|
1274
|
+
|
|
1275
|
+
sage: # needs sage.rings.finite_rings
|
|
1276
|
+
sage: K.<a> = GF(3^60)
|
|
1277
|
+
sage: order_from_multiple(a, 3^60 - 1, operation='*', check=False)
|
|
1278
|
+
42391158275216203514294433200
|
|
1279
|
+
|
|
1280
|
+
TESTS:
|
|
1281
|
+
|
|
1282
|
+
Check that :issue:`38489` is fixed::
|
|
1283
|
+
|
|
1284
|
+
sage: from sage.groups.generic import order_from_multiple
|
|
1285
|
+
sage: plist = [43, 257, 547, 881]
|
|
1286
|
+
sage: m = prod(plist[:-1])
|
|
1287
|
+
sage: elt = Zmod(m)(plist[-1])
|
|
1288
|
+
sage: order_from_multiple(elt, m, plist=plist)
|
|
1289
|
+
6044897
|
|
1290
|
+
"""
|
|
1291
|
+
Z = integer_ring.ZZ
|
|
1292
|
+
|
|
1293
|
+
if operation in multiplication_names:
|
|
1294
|
+
identity = P.parent().one()
|
|
1295
|
+
elif operation in addition_names:
|
|
1296
|
+
identity = P.parent().zero()
|
|
1297
|
+
else:
|
|
1298
|
+
if identity is None or inverse is None or op is None:
|
|
1299
|
+
raise ValueError("identity, inverse and operation must all be specified")
|
|
1300
|
+
|
|
1301
|
+
def _multiple(A, B):
|
|
1302
|
+
return multiple(A,
|
|
1303
|
+
B,
|
|
1304
|
+
operation=operation,
|
|
1305
|
+
identity=identity,
|
|
1306
|
+
inverse=inverse,
|
|
1307
|
+
op=op)
|
|
1308
|
+
|
|
1309
|
+
if P == identity:
|
|
1310
|
+
return Z.one()
|
|
1311
|
+
|
|
1312
|
+
M = Z(m)
|
|
1313
|
+
if check and _multiple(P, M) != identity:
|
|
1314
|
+
raise ValueError(f"The order of P(={P}) does not divide {M}")
|
|
1315
|
+
|
|
1316
|
+
if factorization:
|
|
1317
|
+
F = factorization
|
|
1318
|
+
elif plist:
|
|
1319
|
+
F = [(p, M.valuation(p)) for p in plist]
|
|
1320
|
+
else:
|
|
1321
|
+
F = M.factor()
|
|
1322
|
+
|
|
1323
|
+
if len(F) == 1 and list(F) == [(M, 1)]:
|
|
1324
|
+
return M
|
|
1325
|
+
|
|
1326
|
+
# Efficiency improvement (2009-10-27, implemented by Yann Laigle-Chapuy):
|
|
1327
|
+
# we use an internal recursive function to avoid unnecessary computations.
|
|
1328
|
+
def _order_from_multiple_helper(Q, L, S):
|
|
1329
|
+
"""
|
|
1330
|
+
For internal use, to minimize the number of group operations.
|
|
1331
|
+
"""
|
|
1332
|
+
l = len(L)
|
|
1333
|
+
if l == 1:
|
|
1334
|
+
# we determine the power of p dividing the order,
|
|
1335
|
+
|
|
1336
|
+
# Efficiency improvement (2009-04-01, suggested by Ryan Hinton,
|
|
1337
|
+
# implemented by John Cremona): avoid the last multiplication by p.
|
|
1338
|
+
# For example, if M itself is prime the code used to compute M*P
|
|
1339
|
+
# twice (unless P=0), now it does it once.
|
|
1340
|
+
p, e = L[0]
|
|
1341
|
+
e0 = 0
|
|
1342
|
+
while (Q != identity) and (e0 < e - 1):
|
|
1343
|
+
Q = _multiple(Q, p)
|
|
1344
|
+
e0 += 1
|
|
1345
|
+
if Q != identity:
|
|
1346
|
+
e0 += 1
|
|
1347
|
+
return p**e0
|
|
1348
|
+
else:
|
|
1349
|
+
# try to split the list wisely
|
|
1350
|
+
sum_left = 0
|
|
1351
|
+
for k in range(l):
|
|
1352
|
+
p, e = L[k]
|
|
1353
|
+
# multiplying by p**e require roughly 'e log_2(p) / 2' additions
|
|
1354
|
+
v = e * sage.functions.log.log(float(p))
|
|
1355
|
+
if abs(sum_left + v - (S / 2)) > abs(sum_left - (S / 2)):
|
|
1356
|
+
break
|
|
1357
|
+
sum_left += v
|
|
1358
|
+
if not 0 < k < l:
|
|
1359
|
+
k = l // 2
|
|
1360
|
+
L1 = L[:k]
|
|
1361
|
+
L2 = L[k:]
|
|
1362
|
+
# recursive calls
|
|
1363
|
+
o1 = _order_from_multiple_helper(
|
|
1364
|
+
_multiple(Q, prod([p**e for p, e in L2])), L1, sum_left)
|
|
1365
|
+
o2 = _order_from_multiple_helper(_multiple(Q, o1), L2, S - sum_left)
|
|
1366
|
+
return o1 * o2
|
|
1367
|
+
|
|
1368
|
+
return _order_from_multiple_helper(P, F, sage.functions.log.log(float(M)))
|
|
1369
|
+
|
|
1370
|
+
|
|
1371
|
+
def order_from_bounds(P, bounds, d=None, operation='+',
|
|
1372
|
+
identity=None, inverse=None, op=None):
|
|
1373
|
+
r"""
|
|
1374
|
+
Generic function to find order of a group element, given only
|
|
1375
|
+
upper and lower bounds for a multiple of the order (e.g. bounds on
|
|
1376
|
+
the order of the group of which it is an element)
|
|
1377
|
+
|
|
1378
|
+
INPUT:
|
|
1379
|
+
|
|
1380
|
+
- ``P`` -- a Sage object which is a group element
|
|
1381
|
+
|
|
1382
|
+
- ``bounds`` -- a 2-tuple ``(lb,ub)`` such that ``m*P=0`` (or
|
|
1383
|
+
``P**m=1``) for some ``m`` with ``lb<=m<=ub``
|
|
1384
|
+
|
|
1385
|
+
- ``d`` -- (optional) a positive integer; only ``m`` which are
|
|
1386
|
+
multiples of this will be considered
|
|
1387
|
+
|
|
1388
|
+
- ``operation`` -- string; ``'+'`` (default) or ``'*'`` or other.
|
|
1389
|
+
If other, the following must be supplied:
|
|
1390
|
+
|
|
1391
|
+
- ``identity`` -- the identity element for the group
|
|
1392
|
+
- ``inverse`` -- a function of one argument giving the inverse
|
|
1393
|
+
of a group element
|
|
1394
|
+
- ``op`` -- a function of 2 arguments defining the group binary
|
|
1395
|
+
operation
|
|
1396
|
+
|
|
1397
|
+
.. NOTE::
|
|
1398
|
+
|
|
1399
|
+
Typically ``lb`` and ``ub`` will be bounds on the group order,
|
|
1400
|
+
and from previous calculation we know that the group order is
|
|
1401
|
+
divisible by ``d``.
|
|
1402
|
+
|
|
1403
|
+
EXAMPLES::
|
|
1404
|
+
|
|
1405
|
+
sage: from sage.groups.generic import order_from_bounds
|
|
1406
|
+
|
|
1407
|
+
sage: # needs sage.rings.finite_rings
|
|
1408
|
+
sage: k.<a> = GF(5^5)
|
|
1409
|
+
sage: b = a^4
|
|
1410
|
+
sage: order_from_bounds(b, (5^4, 5^5), operation='*')
|
|
1411
|
+
781
|
|
1412
|
+
|
|
1413
|
+
sage: # needs sage.rings.finite_rings sage.schemes
|
|
1414
|
+
sage: E = EllipticCurve(k, [2,4])
|
|
1415
|
+
sage: P = E(3*a^4 + 3*a, 2*a + 1)
|
|
1416
|
+
sage: bounds = Hasse_bounds(5^5)
|
|
1417
|
+
sage: Q = E(0,2)
|
|
1418
|
+
sage: order_from_bounds(Q, bounds, operation='+')
|
|
1419
|
+
7
|
|
1420
|
+
sage: order_from_bounds(P, bounds, 7, operation='+')
|
|
1421
|
+
3227
|
|
1422
|
+
|
|
1423
|
+
sage: # needs sage.rings.number_field
|
|
1424
|
+
sage: K.<z> = CyclotomicField(230)
|
|
1425
|
+
sage: w = z^50
|
|
1426
|
+
sage: order_from_bounds(w, (200, 250), operation='*')
|
|
1427
|
+
23
|
|
1428
|
+
"""
|
|
1429
|
+
from operator import mul, add
|
|
1430
|
+
|
|
1431
|
+
if operation in multiplication_names:
|
|
1432
|
+
op = mul
|
|
1433
|
+
identity = P.parent().one()
|
|
1434
|
+
elif operation in addition_names:
|
|
1435
|
+
op = add
|
|
1436
|
+
identity = P.parent().zero()
|
|
1437
|
+
else:
|
|
1438
|
+
if op is None:
|
|
1439
|
+
raise ValueError("operation and identity must be specified")
|
|
1440
|
+
|
|
1441
|
+
Q = P
|
|
1442
|
+
if d is None:
|
|
1443
|
+
d = 1
|
|
1444
|
+
if d > 1:
|
|
1445
|
+
Q = multiple(P, d, operation=operation)
|
|
1446
|
+
lb, ub = bounds
|
|
1447
|
+
bounds = (integer_ceil(lb / d), integer_floor(ub / d))
|
|
1448
|
+
|
|
1449
|
+
# Use generic bsgs to find n=d*m with lb<=n<=ub and n*P=0
|
|
1450
|
+
|
|
1451
|
+
m = d * bsgs(Q, identity, bounds, operation=operation)
|
|
1452
|
+
|
|
1453
|
+
# Now use the order_from_multiple() function to finish the job:
|
|
1454
|
+
|
|
1455
|
+
return order_from_multiple(P, m, operation=operation, check=False)
|
|
1456
|
+
|
|
1457
|
+
|
|
1458
|
+
def has_order(P, n, operation='+') -> bool:
|
|
1459
|
+
r"""
|
|
1460
|
+
Generic function to test if a group element `P` has order
|
|
1461
|
+
exactly equal to a given positive integer `n`.
|
|
1462
|
+
|
|
1463
|
+
INPUT:
|
|
1464
|
+
|
|
1465
|
+
- ``P`` -- group element with respect to the specified ``operation``
|
|
1466
|
+
- ``n`` -- positive integer, or its :class:`~sage.structure.factorization.Factorization`
|
|
1467
|
+
- ``operation`` -- string, either ``'+'`` (default) or ``'*'``
|
|
1468
|
+
|
|
1469
|
+
EXAMPLES::
|
|
1470
|
+
|
|
1471
|
+
sage: from sage.groups.generic import has_order
|
|
1472
|
+
|
|
1473
|
+
sage: # needs sage.schemes
|
|
1474
|
+
sage: E.<P> = EllipticCurve(GF(71), [5,5])
|
|
1475
|
+
sage: P.order()
|
|
1476
|
+
57
|
|
1477
|
+
sage: has_order(P, 57)
|
|
1478
|
+
True
|
|
1479
|
+
sage: has_order(P, factor(57))
|
|
1480
|
+
True
|
|
1481
|
+
sage: has_order(P, 19)
|
|
1482
|
+
False
|
|
1483
|
+
sage: has_order(3*P, 19)
|
|
1484
|
+
True
|
|
1485
|
+
sage: has_order(3*P, 57)
|
|
1486
|
+
False
|
|
1487
|
+
|
|
1488
|
+
::
|
|
1489
|
+
|
|
1490
|
+
sage: R = Zmod(14981)
|
|
1491
|
+
sage: g = R(321)
|
|
1492
|
+
sage: g.multiplicative_order() # needs sage.libs.pari
|
|
1493
|
+
42
|
|
1494
|
+
sage: has_order(g, 42, operation='*')
|
|
1495
|
+
True
|
|
1496
|
+
sage: has_order(g, factor(42), operation='*')
|
|
1497
|
+
True
|
|
1498
|
+
sage: has_order(g, 70, operation='*')
|
|
1499
|
+
False
|
|
1500
|
+
|
|
1501
|
+
TESTS::
|
|
1502
|
+
|
|
1503
|
+
sage: # needs sage.libs.pari sage.modules
|
|
1504
|
+
sage: ns = [randrange(1,10**5) for _ in range(randrange(1,5))]
|
|
1505
|
+
sage: A = AdditiveAbelianGroup(ns)
|
|
1506
|
+
sage: from sage.groups.generic import has_order
|
|
1507
|
+
sage: el = A.random_element()
|
|
1508
|
+
sage: o = el.order()
|
|
1509
|
+
sage: has_order(el, o)
|
|
1510
|
+
True
|
|
1511
|
+
sage: has_order(el, o.factor())
|
|
1512
|
+
True
|
|
1513
|
+
sage: not_o = ZZ(randrange(100*o))
|
|
1514
|
+
sage: not_o += (not_o == o)
|
|
1515
|
+
sage: has_order(el, not_o)
|
|
1516
|
+
False
|
|
1517
|
+
|
|
1518
|
+
Check for :issue:`37102`::
|
|
1519
|
+
|
|
1520
|
+
sage: from sage.groups.generic import has_order
|
|
1521
|
+
sage: x = Mod(9, 24)
|
|
1522
|
+
sage: has_order(x, 0)
|
|
1523
|
+
False
|
|
1524
|
+
sage: has_order(x, -8)
|
|
1525
|
+
False
|
|
1526
|
+
|
|
1527
|
+
Check for :issue:`38708`::
|
|
1528
|
+
|
|
1529
|
+
sage: has_order(Mod(2,3), int(2), operation='*')
|
|
1530
|
+
True
|
|
1531
|
+
|
|
1532
|
+
.. NOTE::
|
|
1533
|
+
|
|
1534
|
+
In some cases, order *testing* can be much faster than
|
|
1535
|
+
*computing* the order using :func:`order_from_multiple`.
|
|
1536
|
+
"""
|
|
1537
|
+
if not isinstance(n, sage.structure.factorization.Factorization):
|
|
1538
|
+
n = integer_ring.ZZ(n)
|
|
1539
|
+
if n <= 0:
|
|
1540
|
+
return False
|
|
1541
|
+
n = n.factor()
|
|
1542
|
+
|
|
1543
|
+
if operation in addition_names:
|
|
1544
|
+
isid = lambda el: not el
|
|
1545
|
+
mult = lambda el, n: multiple(el, n, operation='+')
|
|
1546
|
+
elif operation in multiplication_names:
|
|
1547
|
+
isid = lambda el: el.is_one()
|
|
1548
|
+
mult = multiple
|
|
1549
|
+
else:
|
|
1550
|
+
raise ValueError('unknown group operation')
|
|
1551
|
+
|
|
1552
|
+
def _rec(Q, fn) -> bool:
|
|
1553
|
+
if not fn:
|
|
1554
|
+
return isid(Q)
|
|
1555
|
+
|
|
1556
|
+
if len(fn) == 1:
|
|
1557
|
+
p, k = fn[0]
|
|
1558
|
+
for _ in range(k):
|
|
1559
|
+
if isid(Q):
|
|
1560
|
+
return False
|
|
1561
|
+
Q = mult(Q, p)
|
|
1562
|
+
return isid(Q)
|
|
1563
|
+
|
|
1564
|
+
fl = fn[::2]
|
|
1565
|
+
fr = fn[1::2]
|
|
1566
|
+
l = prod(p**k for p, k in fl)
|
|
1567
|
+
r = prod(p**k for p, k in fr)
|
|
1568
|
+
L, R = mult(Q, r), mult(Q, l)
|
|
1569
|
+
return _rec(L, fl) and _rec(R, fr)
|
|
1570
|
+
|
|
1571
|
+
return _rec(P, n)
|
|
1572
|
+
|
|
1573
|
+
|
|
1574
|
+
def merge_points(P1, P2, operation='+',
|
|
1575
|
+
identity=None, inverse=None, op=None, check=True):
|
|
1576
|
+
r"""
|
|
1577
|
+
Return a group element whose order is the lcm of the given elements.
|
|
1578
|
+
|
|
1579
|
+
INPUT:
|
|
1580
|
+
|
|
1581
|
+
- ``P1`` -- a pair `(g_1,n_1)` where `g_1` is a group element of order `n_1`
|
|
1582
|
+
- ``P2`` -- a pair `(g_2,n_2)` where `g_2` is a group element of order `n_2`
|
|
1583
|
+
- ``operation`` -- string; ``'+'`` (default) or ``'*'`` or other. If
|
|
1584
|
+
other, the following must be supplied:
|
|
1585
|
+
|
|
1586
|
+
- ``identity`` -- the identity element for the group
|
|
1587
|
+
- ``inverse`` -- a function of one argument giving the inverse
|
|
1588
|
+
of a group element
|
|
1589
|
+
- ``op`` -- a function of 2 arguments defining the group
|
|
1590
|
+
binary operation
|
|
1591
|
+
|
|
1592
|
+
OUTPUT: a pair `(g_3,n_3)` where `g_3` has order `n_3=\hbox{lcm}(n_1,n_2)`
|
|
1593
|
+
|
|
1594
|
+
EXAMPLES::
|
|
1595
|
+
|
|
1596
|
+
sage: # needs sage.rings.finite_rings
|
|
1597
|
+
sage: from sage.groups.generic import merge_points
|
|
1598
|
+
sage: F.<a> = GF(3^6,'a')
|
|
1599
|
+
sage: b = a^7
|
|
1600
|
+
sage: c = a^13
|
|
1601
|
+
sage: ob = (3^6-1)//7
|
|
1602
|
+
sage: oc = (3^6-1)//13
|
|
1603
|
+
sage: merge_points((b,ob), (c,oc), operation='*')
|
|
1604
|
+
(a^4 + 2*a^3 + 2*a^2, 728)
|
|
1605
|
+
sage: d, od = merge_points((b,ob), (c,oc), operation='*')
|
|
1606
|
+
sage: od == d.multiplicative_order()
|
|
1607
|
+
True
|
|
1608
|
+
sage: od == lcm(ob, oc)
|
|
1609
|
+
True
|
|
1610
|
+
|
|
1611
|
+
sage: # needs sage.libs.gap sage.rings.finite_rings sage.schemes
|
|
1612
|
+
sage: E = EllipticCurve([a^5 + 2*a^3 + 2*a^2 + 2*a, a^4 + a^3 + 2*a + 1])
|
|
1613
|
+
sage: P = E(2*a^5 + 2*a^4 + a^3 + 2, a^4 + a^3 + a^2 + 2*a + 2)
|
|
1614
|
+
sage: P.order()
|
|
1615
|
+
7
|
|
1616
|
+
sage: Q = E(2*a^5 + 2*a^4 + 1, a^5 + 2*a^3 + 2*a + 2)
|
|
1617
|
+
sage: Q.order()
|
|
1618
|
+
4
|
|
1619
|
+
sage: R, m = merge_points((P,7), (Q,4), operation='+')
|
|
1620
|
+
sage: R.order() == m
|
|
1621
|
+
True
|
|
1622
|
+
sage: m == lcm(7,4)
|
|
1623
|
+
True
|
|
1624
|
+
"""
|
|
1625
|
+
from operator import mul, add
|
|
1626
|
+
|
|
1627
|
+
g1, n1 = P1
|
|
1628
|
+
g2, n2 = P2
|
|
1629
|
+
|
|
1630
|
+
if operation in multiplication_names:
|
|
1631
|
+
op = mul
|
|
1632
|
+
identity = g1.parent().one()
|
|
1633
|
+
elif operation in addition_names:
|
|
1634
|
+
op = add
|
|
1635
|
+
identity = g1.parent().zero()
|
|
1636
|
+
else:
|
|
1637
|
+
if op is None:
|
|
1638
|
+
raise ValueError("operation and identity must be specified")
|
|
1639
|
+
|
|
1640
|
+
if check:
|
|
1641
|
+
if multiple(g1, n1, operation=operation) != identity or multiple(g2, n2, operation=operation) != identity:
|
|
1642
|
+
raise ValueError("the orders provided do not divide the orders of the points provided")
|
|
1643
|
+
|
|
1644
|
+
# trivial cases
|
|
1645
|
+
if n1.divides(n2):
|
|
1646
|
+
return (g2, n2)
|
|
1647
|
+
if n2.divides(n1):
|
|
1648
|
+
return (g1, n1)
|
|
1649
|
+
|
|
1650
|
+
m, k1, k2 = xlcm(n1, n2)
|
|
1651
|
+
m1 = n1 // k1
|
|
1652
|
+
m2 = n2 // k2
|
|
1653
|
+
g1 = multiple(g1, m1, operation=operation)
|
|
1654
|
+
g2 = multiple(g2, m2, operation=operation)
|
|
1655
|
+
return (op(g1, g2), m)
|
|
1656
|
+
|
|
1657
|
+
|
|
1658
|
+
def structure_description(G, latex=False):
|
|
1659
|
+
r"""
|
|
1660
|
+
Return a string that tries to describe the structure of ``G``.
|
|
1661
|
+
|
|
1662
|
+
This methods wraps GAP's ``StructureDescription`` method.
|
|
1663
|
+
|
|
1664
|
+
For full details, including the form of the returned string and the
|
|
1665
|
+
algorithm to build it, see :gap:`GAP's documentation <chap39>`.
|
|
1666
|
+
|
|
1667
|
+
INPUT:
|
|
1668
|
+
|
|
1669
|
+
- ``latex`` -- boolean (default: ``False``); if ``True``, return a
|
|
1670
|
+
LaTeX formatted string
|
|
1671
|
+
|
|
1672
|
+
OUTPUT: string
|
|
1673
|
+
|
|
1674
|
+
.. WARNING::
|
|
1675
|
+
|
|
1676
|
+
From GAP's documentation: The string returned by
|
|
1677
|
+
``StructureDescription`` is **not** an isomorphism invariant:
|
|
1678
|
+
non-isomorphic groups can have the same string value, and two
|
|
1679
|
+
isomorphic groups in different representations can produce different
|
|
1680
|
+
strings.
|
|
1681
|
+
|
|
1682
|
+
EXAMPLES::
|
|
1683
|
+
|
|
1684
|
+
sage: # needs sage.groups
|
|
1685
|
+
sage: G = CyclicPermutationGroup(6)
|
|
1686
|
+
sage: G.structure_description()
|
|
1687
|
+
'C6'
|
|
1688
|
+
sage: G.structure_description(latex=True)
|
|
1689
|
+
'C_{6}'
|
|
1690
|
+
sage: G2 = G.direct_product(G, maps=False)
|
|
1691
|
+
sage: LatexExpr(G2.structure_description(latex=True))
|
|
1692
|
+
C_{6} \times C_{6}
|
|
1693
|
+
|
|
1694
|
+
This method is mainly intended for small groups or groups with few
|
|
1695
|
+
normal subgroups. Even then there are some surprises::
|
|
1696
|
+
|
|
1697
|
+
sage: D3 = DihedralGroup(3) # needs sage.groups
|
|
1698
|
+
sage: D3.structure_description() # needs sage.groups
|
|
1699
|
+
'S3'
|
|
1700
|
+
|
|
1701
|
+
We use the Sage notation for the degree of dihedral groups::
|
|
1702
|
+
|
|
1703
|
+
sage: D4 = DihedralGroup(4) # needs sage.groups
|
|
1704
|
+
sage: D4.structure_description() # needs sage.groups
|
|
1705
|
+
'D4'
|
|
1706
|
+
|
|
1707
|
+
Works for finitely presented groups (:issue:`17573`)::
|
|
1708
|
+
|
|
1709
|
+
sage: F.<x, y> = FreeGroup() # needs sage.combinat sage.groups
|
|
1710
|
+
sage: G = F / [x^2*y^-1, x^3*y^2, x*y*x^-1*y^-1] # needs sage.combinat sage.groups
|
|
1711
|
+
sage: G.structure_description() # needs sage.combinat sage.groups
|
|
1712
|
+
'C7'
|
|
1713
|
+
|
|
1714
|
+
And matrix groups (:issue:`17573`)::
|
|
1715
|
+
|
|
1716
|
+
sage: groups.matrix.GL(4,2).structure_description() # needs sage.libs.gap sage.modules
|
|
1717
|
+
'A8'
|
|
1718
|
+
"""
|
|
1719
|
+
import re
|
|
1720
|
+
|
|
1721
|
+
def correct_dihedral_degree(match):
|
|
1722
|
+
return "%sD%d" % (match.group(1), int(match.group(2)) // 2)
|
|
1723
|
+
|
|
1724
|
+
description = str(G._gap_().StructureDescription())
|
|
1725
|
+
|
|
1726
|
+
description = re.sub(r"(\A|\W)D(\d+)", correct_dihedral_degree, description)
|
|
1727
|
+
if not latex:
|
|
1728
|
+
return description
|
|
1729
|
+
description = description.replace("x", r"\times").replace(":", r"\rtimes")
|
|
1730
|
+
description = re.sub(r"([A-Za-z]+)([0-9]+)", r"\g<1>_{\g<2>}", description)
|
|
1731
|
+
description = re.sub(r"O([+-])", r"O^{\g<1>}", description)
|
|
1732
|
+
|
|
1733
|
+
return description
|