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/misc/latex.py
ADDED
|
@@ -0,0 +1,2673 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-categories
|
|
2
|
+
"""
|
|
3
|
+
LaTeX printing support
|
|
4
|
+
|
|
5
|
+
In order to support latex formatting, an object should define a
|
|
6
|
+
special method ``_latex_(self)`` that returns a string, which will be typeset
|
|
7
|
+
in a mathematical mode (the exact mode depends on circumstances).
|
|
8
|
+
|
|
9
|
+
This module focuses on using LaTeX for printing. For the use of LaTeX for
|
|
10
|
+
rendering math in HTML by MathJax, see :class:`~sage.misc.html.MathJax` defined in
|
|
11
|
+
:mod:`sage.misc.html`.
|
|
12
|
+
|
|
13
|
+
AUTHORS:
|
|
14
|
+
|
|
15
|
+
- William Stein: original implementation
|
|
16
|
+
|
|
17
|
+
- Joel B. Mohler: latex_variable_name() drastic rewrite and many doc-tests
|
|
18
|
+
"""
|
|
19
|
+
# ****************************************************************************
|
|
20
|
+
# Copyright (C) 2005 William Stein <wstein@gmail.com>
|
|
21
|
+
#
|
|
22
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
23
|
+
# as published by the Free Software Foundation; either version 2 of
|
|
24
|
+
# the License, or (at your option) any later version.
|
|
25
|
+
# https://www.gnu.org/licenses/
|
|
26
|
+
# ****************************************************************************
|
|
27
|
+
|
|
28
|
+
import os
|
|
29
|
+
import random
|
|
30
|
+
import re
|
|
31
|
+
import shutil
|
|
32
|
+
from subprocess import call, run, PIPE
|
|
33
|
+
from tempfile import TemporaryDirectory
|
|
34
|
+
|
|
35
|
+
from sage.misc.cachefunc import cached_function, cached_method
|
|
36
|
+
from sage.misc.lazy_attribute import lazy_attribute
|
|
37
|
+
from sage.structure.sage_object import SageObject
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
COMMON_HEADER = r'''\usepackage{amsmath}
|
|
41
|
+
\usepackage{amssymb}
|
|
42
|
+
\usepackage{amsfonts}
|
|
43
|
+
\usepackage{graphicx}
|
|
44
|
+
\usepackage{mathrsfs}
|
|
45
|
+
\pagestyle{empty}
|
|
46
|
+
\usepackage[utf8]{inputenc}
|
|
47
|
+
\usepackage[T1]{fontenc}
|
|
48
|
+
'''
|
|
49
|
+
|
|
50
|
+
LATEX_HEADER = (r'''\documentclass{article}
|
|
51
|
+
''' + COMMON_HEADER +
|
|
52
|
+
r'''\oddsidemargin 0.0in
|
|
53
|
+
\evensidemargin 0.0in
|
|
54
|
+
\textwidth 6.45in
|
|
55
|
+
\topmargin 0.0in
|
|
56
|
+
\headheight 0.0in
|
|
57
|
+
\headsep 0.0in
|
|
58
|
+
\textheight 9.0in
|
|
59
|
+
''')
|
|
60
|
+
|
|
61
|
+
SLIDE_HEADER = (r'''\documentclass[a0,8pt]{beamer}
|
|
62
|
+
''' + COMMON_HEADER +
|
|
63
|
+
r'''\textwidth=1.1\textwidth
|
|
64
|
+
\textheight=2\textheight
|
|
65
|
+
''')
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def list_function(x):
|
|
69
|
+
r"""
|
|
70
|
+
Return the LaTeX code for a list ``x``.
|
|
71
|
+
|
|
72
|
+
INPUT:
|
|
73
|
+
|
|
74
|
+
- ``x`` -- list
|
|
75
|
+
|
|
76
|
+
EXAMPLES::
|
|
77
|
+
|
|
78
|
+
sage: from sage.misc.latex import list_function
|
|
79
|
+
sage: list_function([1,2,3])
|
|
80
|
+
'\\left[1, 2, 3\\right]'
|
|
81
|
+
sage: latex([1,2,3]) # indirect doctest
|
|
82
|
+
\left[1, 2, 3\right]
|
|
83
|
+
sage: latex([Matrix(ZZ, 3, range(9)), # indirect doctest # needs sage.modules
|
|
84
|
+
....: Matrix(ZZ, 3, range(9))])
|
|
85
|
+
\left[\left(\begin{array}{rrr}
|
|
86
|
+
0 & 1 & 2 \\
|
|
87
|
+
3 & 4 & 5 \\
|
|
88
|
+
6 & 7 & 8
|
|
89
|
+
\end{array}\right), \left(\begin{array}{rrr}
|
|
90
|
+
0 & 1 & 2 \\
|
|
91
|
+
3 & 4 & 5 \\
|
|
92
|
+
6 & 7 & 8
|
|
93
|
+
\end{array}\right)\right]
|
|
94
|
+
"""
|
|
95
|
+
return "\\left[" + ", ".join(latex(v) for v in x) + "\\right]"
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def tuple_function(x, combine_all=False):
|
|
99
|
+
r"""
|
|
100
|
+
Return the LaTeX code for a tuple ``x``.
|
|
101
|
+
|
|
102
|
+
INPUT:
|
|
103
|
+
|
|
104
|
+
- ``x`` -- tuple
|
|
105
|
+
|
|
106
|
+
- ``combine_all`` -- boolean (default: ``False``); if ``combine_all`` is
|
|
107
|
+
``True``, then it does not return a tuple and instead returns a string
|
|
108
|
+
with all the elements separated by a single space. It does not collapse
|
|
109
|
+
tuples which are inside tuples.
|
|
110
|
+
|
|
111
|
+
EXAMPLES::
|
|
112
|
+
|
|
113
|
+
sage: from sage.misc.latex import tuple_function
|
|
114
|
+
sage: tuple_function((1,2,3))
|
|
115
|
+
'\\left(1, 2, 3\\right)'
|
|
116
|
+
|
|
117
|
+
Check that :issue:`11775` is fixed::
|
|
118
|
+
|
|
119
|
+
sage: tuple_function((1,2,3), combine_all=True)
|
|
120
|
+
'1 2 3'
|
|
121
|
+
sage: tuple_function(((1,2),3), combine_all=True)
|
|
122
|
+
'\\left(1, 2\\right) 3'
|
|
123
|
+
"""
|
|
124
|
+
if combine_all:
|
|
125
|
+
return " ".join(latex(v) for v in x)
|
|
126
|
+
return "\\left(" + ", ".join(latex(v) for v in x) + "\\right)"
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def bool_function(x):
|
|
130
|
+
r"""
|
|
131
|
+
Return the LaTeX code for a boolean ``x``.
|
|
132
|
+
|
|
133
|
+
INPUT:
|
|
134
|
+
|
|
135
|
+
- ``x`` -- boolean
|
|
136
|
+
|
|
137
|
+
EXAMPLES::
|
|
138
|
+
|
|
139
|
+
sage: from sage.misc.latex import bool_function
|
|
140
|
+
sage: print(bool_function(2==3))
|
|
141
|
+
\mathrm{False}
|
|
142
|
+
sage: print(bool_function(3==(2+1)))
|
|
143
|
+
\mathrm{True}
|
|
144
|
+
"""
|
|
145
|
+
return r"\mathrm{%s}" % bool(x)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def builtin_constant_function(x):
|
|
149
|
+
r"""
|
|
150
|
+
Return the LaTeX code for a builtin constant ``x``.
|
|
151
|
+
|
|
152
|
+
INPUT:
|
|
153
|
+
|
|
154
|
+
- ``x`` -- builtin constant
|
|
155
|
+
|
|
156
|
+
.. SEEALSO:: Python built-in Constants http://docs.python.org/library/constants.html
|
|
157
|
+
|
|
158
|
+
EXAMPLES::
|
|
159
|
+
|
|
160
|
+
sage: from sage.misc.latex import builtin_constant_function
|
|
161
|
+
sage: builtin_constant_function(True)
|
|
162
|
+
'\\mbox{\\rm True}'
|
|
163
|
+
sage: builtin_constant_function(None)
|
|
164
|
+
'\\mbox{\\rm None}'
|
|
165
|
+
sage: builtin_constant_function(NotImplemented)
|
|
166
|
+
'\\mbox{\\rm NotImplemented}'
|
|
167
|
+
sage: builtin_constant_function(Ellipsis)
|
|
168
|
+
'\\mbox{\\rm Ellipsis}'
|
|
169
|
+
"""
|
|
170
|
+
return "\\mbox{\\rm %s}" % x
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def None_function(x):
|
|
174
|
+
r"""
|
|
175
|
+
Return the LaTeX code for ``None``.
|
|
176
|
+
|
|
177
|
+
INPUT:
|
|
178
|
+
|
|
179
|
+
- ``x`` -- ``None``
|
|
180
|
+
|
|
181
|
+
EXAMPLES::
|
|
182
|
+
|
|
183
|
+
sage: from sage.misc.latex import None_function
|
|
184
|
+
sage: print(None_function(None))
|
|
185
|
+
\mathrm{None}
|
|
186
|
+
"""
|
|
187
|
+
assert x is None
|
|
188
|
+
return r"\mathrm{None}"
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def str_function(x):
|
|
192
|
+
r"""
|
|
193
|
+
Return a LaTeX representation of the string ``x``.
|
|
194
|
+
|
|
195
|
+
The main purpose of this function is to generate LaTeX representation for
|
|
196
|
+
classes that do not provide a customized method.
|
|
197
|
+
|
|
198
|
+
If ``x`` contains only digits with, possibly, a single decimal point and/or
|
|
199
|
+
a sign in front, it is considered to be its own representation. Otherwise
|
|
200
|
+
each line of ``x`` is wrapped in a ``\texttt`` command and these lines are
|
|
201
|
+
assembled in a left-justified array. This gives to complicated strings the
|
|
202
|
+
closest look to their "terminal representation".
|
|
203
|
+
|
|
204
|
+
.. WARNING::
|
|
205
|
+
|
|
206
|
+
Such wrappers **cannot** be used as arguments of LaTeX
|
|
207
|
+
commands or in command definitions. If this causes you any problems,
|
|
208
|
+
they probably can be solved by implementing a suitable ``_latex_``
|
|
209
|
+
method for an appropriate class.
|
|
210
|
+
|
|
211
|
+
INPUT:
|
|
212
|
+
|
|
213
|
+
- ``x`` -- string
|
|
214
|
+
|
|
215
|
+
OUTPUT: string
|
|
216
|
+
|
|
217
|
+
EXAMPLES::
|
|
218
|
+
|
|
219
|
+
sage: from sage.misc.latex import str_function
|
|
220
|
+
sage: str_function('34')
|
|
221
|
+
'34'
|
|
222
|
+
sage: str_function('34.5')
|
|
223
|
+
'34.5'
|
|
224
|
+
sage: str_function('-34.5')
|
|
225
|
+
'-34.5'
|
|
226
|
+
sage: str_function('+34.5')
|
|
227
|
+
'+34.5'
|
|
228
|
+
sage: str_function('hello_world')
|
|
229
|
+
'\\text{\\texttt{hello{\\char`\\_}world}}'
|
|
230
|
+
sage: str_function('-1.00000?') # trac 12178
|
|
231
|
+
'-1.00000?'
|
|
232
|
+
"""
|
|
233
|
+
# Check if x is just a number with a possible sign, and/or decimal
|
|
234
|
+
# point, and/or ends with "?"
|
|
235
|
+
if re.match(r'(\+|-)?[0-9]*\.?[0-9]*\??$', x):
|
|
236
|
+
return x
|
|
237
|
+
# Deal with special characters
|
|
238
|
+
char_wrapper = r"{\char`\%s}"
|
|
239
|
+
x = "".join(char_wrapper % c if c in r"#$%&\^_{}~" else c for c in x)
|
|
240
|
+
# Avoid grouping spaces into one
|
|
241
|
+
x = x.replace(" ", "{ }")
|
|
242
|
+
# And dashes too, since it causes issues for the command line...
|
|
243
|
+
x = x.replace("-", "{-}")
|
|
244
|
+
# Make it work in math mode, but look like typewriter
|
|
245
|
+
line_wrapper = r"\text{\texttt{%s}}"
|
|
246
|
+
x = "\\\\\n".join(line_wrapper % line for line in x.split("\n"))
|
|
247
|
+
# Preserve line breaks
|
|
248
|
+
if "\n" in x:
|
|
249
|
+
x = "\\begin{array}{l}\n%s\n\\end{array}" % x
|
|
250
|
+
return x
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def dict_function(x):
|
|
254
|
+
r"""
|
|
255
|
+
Return the LaTeX code for a dictionary ``x``.
|
|
256
|
+
|
|
257
|
+
INPUT:
|
|
258
|
+
|
|
259
|
+
- ``x`` -- dictionary
|
|
260
|
+
|
|
261
|
+
EXAMPLES::
|
|
262
|
+
|
|
263
|
+
sage: # needs sage.symbolic
|
|
264
|
+
sage: from sage.misc.latex import dict_function
|
|
265
|
+
sage: x,y,z = var('x,y,z')
|
|
266
|
+
sage: print(dict_function({x/2: y^2}))
|
|
267
|
+
\left\{\frac{1}{2} \, x : y^{2}\right\}
|
|
268
|
+
sage: d = {(1,2,x^2): [sin(z^2), y/2]}
|
|
269
|
+
sage: latex(d)
|
|
270
|
+
\left\{\left(1, 2, x^{2}\right) :
|
|
271
|
+
\left[\sin\left(z^{2}\right), \frac{1}{2} \, y\right]\right\}
|
|
272
|
+
"""
|
|
273
|
+
return "".join([r"\left\{",
|
|
274
|
+
", ".join(r"%s : %s" % (latex(key), latex(value))
|
|
275
|
+
for key, value in x.items()),
|
|
276
|
+
r"\right\}"])
|
|
277
|
+
|
|
278
|
+
# One can add to the latex_table in order to install latexing
|
|
279
|
+
# functionality for other types. (Suggested by Robert Kerns of Enthought.)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def float_function(x):
|
|
283
|
+
r"""
|
|
284
|
+
Return the LaTeX code for a python float ``x``.
|
|
285
|
+
|
|
286
|
+
INPUT:
|
|
287
|
+
|
|
288
|
+
- ``x`` -- a python float
|
|
289
|
+
|
|
290
|
+
EXAMPLES::
|
|
291
|
+
|
|
292
|
+
sage: from sage.misc.latex import float_function
|
|
293
|
+
sage: float_function(float(3.14))
|
|
294
|
+
3.14
|
|
295
|
+
sage: float_function(float(1e-10))
|
|
296
|
+
1 \times 10^{-10}
|
|
297
|
+
sage: float_function(float(2e10))
|
|
298
|
+
20000000000.0
|
|
299
|
+
|
|
300
|
+
TESTS:
|
|
301
|
+
|
|
302
|
+
Check that :issue:`7356` is fixed::
|
|
303
|
+
|
|
304
|
+
sage: latex(float(2e-13))
|
|
305
|
+
2 \times 10^{-13}
|
|
306
|
+
"""
|
|
307
|
+
from sage.rings.real_double import RDF
|
|
308
|
+
return latex(RDF(x))
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
latex_table = {
|
|
312
|
+
type(None): None_function,
|
|
313
|
+
bool: bool_function,
|
|
314
|
+
dict: dict_function,
|
|
315
|
+
float: float_function,
|
|
316
|
+
int: str,
|
|
317
|
+
list: list_function,
|
|
318
|
+
str: str_function,
|
|
319
|
+
tuple: tuple_function,
|
|
320
|
+
type(NotImplemented): builtin_constant_function,
|
|
321
|
+
type(Ellipsis): builtin_constant_function
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
class LatexExpr(str):
|
|
326
|
+
r"""
|
|
327
|
+
A class for LaTeX expressions.
|
|
328
|
+
|
|
329
|
+
Normally, objects of this class are created by a :func:`latex` call. It is
|
|
330
|
+
also possible to generate :class:`LatexExpr` directly from a string, which
|
|
331
|
+
must contain valid LaTeX code for typesetting in math mode (without dollar
|
|
332
|
+
signs). In the Jupyter notebook, use
|
|
333
|
+
:func:`~sage.repl.rich_output.pretty_print.pretty_print` to actually see
|
|
334
|
+
the typeset LaTeX code; alternatively, from either the command-line or the
|
|
335
|
+
notebook, use the :func:`view` function.
|
|
336
|
+
|
|
337
|
+
INPUT:
|
|
338
|
+
|
|
339
|
+
- ``str`` -- string with valid math mode LaTeX code (or something
|
|
340
|
+
which can be converted to such a string)
|
|
341
|
+
|
|
342
|
+
OUTPUT: :class:`LatexExpr` wrapping the string representation of the input
|
|
343
|
+
|
|
344
|
+
EXAMPLES::
|
|
345
|
+
|
|
346
|
+
sage: latex(x^20 + 1) # needs sage.symbolic
|
|
347
|
+
x^{20} + 1
|
|
348
|
+
sage: LatexExpr(r"\frac{x^2 + 1}{x - 2}") # needs sage.symbolic
|
|
349
|
+
\frac{x^2 + 1}{x - 2}
|
|
350
|
+
|
|
351
|
+
``LatexExpr`` simply converts to string without doing anything
|
|
352
|
+
extra, it does *not* call :func:`latex`::
|
|
353
|
+
|
|
354
|
+
sage: latex(ZZ)
|
|
355
|
+
\Bold{Z}
|
|
356
|
+
sage: LatexExpr(ZZ)
|
|
357
|
+
Integer Ring
|
|
358
|
+
|
|
359
|
+
The result of :func:`latex` is of type ``LatexExpr``::
|
|
360
|
+
|
|
361
|
+
sage: L = latex(x^20 + 1) # needs sage.symbolic
|
|
362
|
+
sage: L # needs sage.symbolic
|
|
363
|
+
x^{20} + 1
|
|
364
|
+
sage: type(L) # needs sage.symbolic
|
|
365
|
+
<class 'sage.misc.latex.LatexExpr'>
|
|
366
|
+
|
|
367
|
+
A ``LatexExpr`` can be converted to a plain string::
|
|
368
|
+
|
|
369
|
+
sage: str(latex(x^20 + 1)) # needs sage.symbolic
|
|
370
|
+
'x^{20} + 1'
|
|
371
|
+
"""
|
|
372
|
+
def __add__(self, other):
|
|
373
|
+
r"""
|
|
374
|
+
Add a LatexExpr and another LatexExpr (or a string).
|
|
375
|
+
|
|
376
|
+
EXAMPLES::
|
|
377
|
+
|
|
378
|
+
sage: o = LatexExpr(r"\Delta\neq") + LatexExpr(r"\frac{x}{2}"); o
|
|
379
|
+
\Delta\neq \frac{x}{2}
|
|
380
|
+
sage: type(o)
|
|
381
|
+
<class 'sage.misc.latex.LatexExpr'>
|
|
382
|
+
sage: o = LatexExpr(r"\Delta\neq") + r"\frac{x}{2}"; o
|
|
383
|
+
\Delta\neq \frac{x}{2}
|
|
384
|
+
sage: type(o)
|
|
385
|
+
<class 'sage.misc.latex.LatexExpr'>
|
|
386
|
+
|
|
387
|
+
We add extra space only if it was not there yet::
|
|
388
|
+
|
|
389
|
+
sage: LatexExpr("foo ") + LatexExpr("bar")
|
|
390
|
+
foo bar
|
|
391
|
+
sage: LatexExpr("foo") + LatexExpr(" bar")
|
|
392
|
+
foo bar
|
|
393
|
+
sage: str(LatexExpr("") + LatexExpr("bar"))
|
|
394
|
+
'bar'
|
|
395
|
+
sage: str(LatexExpr("foo") + LatexExpr(""))
|
|
396
|
+
'foo'
|
|
397
|
+
"""
|
|
398
|
+
left = str(self)
|
|
399
|
+
right = str(other)
|
|
400
|
+
# Add a space if self ends with a non-space and other starts
|
|
401
|
+
# with a non-space
|
|
402
|
+
try:
|
|
403
|
+
if left[-1] != ' ' and right[0] != ' ':
|
|
404
|
+
left += ' '
|
|
405
|
+
except IndexError:
|
|
406
|
+
pass
|
|
407
|
+
return LatexExpr(left + right)
|
|
408
|
+
|
|
409
|
+
def __radd__(self, other):
|
|
410
|
+
r"""
|
|
411
|
+
Add a string and a LatexExpr.
|
|
412
|
+
|
|
413
|
+
EXAMPLES::
|
|
414
|
+
|
|
415
|
+
sage: o = "a =" + LatexExpr("b")
|
|
416
|
+
sage: o
|
|
417
|
+
a = b
|
|
418
|
+
sage: type(o)
|
|
419
|
+
<class 'sage.misc.latex.LatexExpr'>
|
|
420
|
+
"""
|
|
421
|
+
return LatexExpr(other) + self
|
|
422
|
+
|
|
423
|
+
def __repr__(self):
|
|
424
|
+
"""
|
|
425
|
+
Return a string representation of ``self``.
|
|
426
|
+
|
|
427
|
+
EXAMPLES::
|
|
428
|
+
|
|
429
|
+
sage: LatexExpr("abc").__repr__()
|
|
430
|
+
'abc'
|
|
431
|
+
"""
|
|
432
|
+
return str(self)
|
|
433
|
+
|
|
434
|
+
def _latex_(self):
|
|
435
|
+
"""
|
|
436
|
+
Return a LaTeX representation of ``self``.
|
|
437
|
+
|
|
438
|
+
EXAMPLES::
|
|
439
|
+
|
|
440
|
+
sage: latex(LatexExpr("abc")) # indirect doctest
|
|
441
|
+
abc
|
|
442
|
+
"""
|
|
443
|
+
return str(self)
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def has_latex_attr(x) -> bool:
|
|
447
|
+
"""
|
|
448
|
+
Return ``True`` if ``x`` has a ``_latex_`` attribute, except if ``x``
|
|
449
|
+
is a ``type``, in which case return ``False``.
|
|
450
|
+
|
|
451
|
+
EXAMPLES::
|
|
452
|
+
|
|
453
|
+
sage: from sage.misc.latex import has_latex_attr
|
|
454
|
+
sage: has_latex_attr(identity_matrix(3)) # needs sage.modules
|
|
455
|
+
True
|
|
456
|
+
sage: has_latex_attr("abc") # strings have no _latex_ method
|
|
457
|
+
False
|
|
458
|
+
|
|
459
|
+
Types inherit the ``_latex_`` method of the class to which they refer,
|
|
460
|
+
but calling it is broken::
|
|
461
|
+
|
|
462
|
+
sage: # needs sage.libs.flint sage.modules
|
|
463
|
+
sage: T = type(identity_matrix(3)); T
|
|
464
|
+
<class 'sage.matrix.matrix_integer_dense.Matrix_integer_dense'>
|
|
465
|
+
sage: hasattr(T, '_latex_')
|
|
466
|
+
True
|
|
467
|
+
sage: T._latex_()
|
|
468
|
+
Traceback (most recent call last):
|
|
469
|
+
...
|
|
470
|
+
TypeError: ..._latex_... needs an argument
|
|
471
|
+
sage: has_latex_attr(T)
|
|
472
|
+
False
|
|
473
|
+
"""
|
|
474
|
+
return hasattr(x, '_latex_') and not isinstance(x, type)
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
@cached_function
|
|
478
|
+
def default_engine():
|
|
479
|
+
"""
|
|
480
|
+
Return the default latex engine and the official name of the engine.
|
|
481
|
+
This is determined by availability of the popular engines on the user's
|
|
482
|
+
system. It is assumed that at least latex is available.
|
|
483
|
+
|
|
484
|
+
This function is deprecated as part of the public API. There is
|
|
485
|
+
instead an internal counterpart :func:`_default_engine`, but no
|
|
486
|
+
stability promises are made with regards to its interface.
|
|
487
|
+
|
|
488
|
+
EXAMPLES::
|
|
489
|
+
|
|
490
|
+
sage: from sage.misc.latex import default_engine
|
|
491
|
+
sage: default_engine() # random
|
|
492
|
+
('lualatex', 'LuaLaTeX')
|
|
493
|
+
"""
|
|
494
|
+
from sage.misc.superseded import deprecation
|
|
495
|
+
deprecation(39351, "default_engine is being removed from the public API and replaced with the internal function _default_engine")
|
|
496
|
+
|
|
497
|
+
from sage.features.latex import pdflatex, xelatex, lualatex
|
|
498
|
+
if lualatex().is_present():
|
|
499
|
+
return 'lualatex', 'LuaLaTeX'
|
|
500
|
+
if xelatex().is_present():
|
|
501
|
+
return 'xelatex', 'XeLaTeX'
|
|
502
|
+
if pdflatex().is_present():
|
|
503
|
+
return 'pdflatex', 'pdfLaTeX'
|
|
504
|
+
return 'latex', 'LaTeX'
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
@cached_function
|
|
508
|
+
def _default_engine():
|
|
509
|
+
r"""
|
|
510
|
+
Return the name of the default latex engine.
|
|
511
|
+
|
|
512
|
+
This is determined by availability of the popular engines on the
|
|
513
|
+
user's system. It is assumed that at least "latex" is available.
|
|
514
|
+
|
|
515
|
+
EXAMPLES::
|
|
516
|
+
|
|
517
|
+
sage: from sage.misc.latex import _default_engine
|
|
518
|
+
sage: _default_engine() # random
|
|
519
|
+
'lualatex'
|
|
520
|
+
|
|
521
|
+
TESTS:
|
|
522
|
+
|
|
523
|
+
Ensure that this (expensive) function is not necessary to obtain
|
|
524
|
+
the latex representation of a matrix (doing so probes the latex
|
|
525
|
+
options dict for the delimiters)::
|
|
526
|
+
|
|
527
|
+
sage: import sage.misc.latex
|
|
528
|
+
sage: real_de = sage.misc.latex._default_engine
|
|
529
|
+
sage: def crash():
|
|
530
|
+
....: raise ValueError
|
|
531
|
+
sage: sage.misc.latex._default_engine = crash
|
|
532
|
+
sage: latex(matrix.identity(QQ, 2)) # needs sage.modules
|
|
533
|
+
\left(\begin{array}{rr}
|
|
534
|
+
1 & 0 \\
|
|
535
|
+
0 & 1
|
|
536
|
+
\end{array}\right)
|
|
537
|
+
sage: sage.misc.latex._default_engine = real_de
|
|
538
|
+
"""
|
|
539
|
+
from sage.features.latex import pdflatex, xelatex, lualatex
|
|
540
|
+
if lualatex().is_present():
|
|
541
|
+
return 'lualatex'
|
|
542
|
+
if xelatex().is_present():
|
|
543
|
+
return 'xelatex'
|
|
544
|
+
if pdflatex().is_present():
|
|
545
|
+
return 'pdflatex'
|
|
546
|
+
return 'latex'
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
class _Latex_prefs_object(SageObject):
|
|
550
|
+
"""
|
|
551
|
+
An object that holds LaTeX global preferences.
|
|
552
|
+
"""
|
|
553
|
+
def __init__(self, bb=False, delimiters=["(", ")"],
|
|
554
|
+
matrix_column_alignment='r'):
|
|
555
|
+
"""
|
|
556
|
+
Define an object that holds LaTeX global preferences.
|
|
557
|
+
|
|
558
|
+
EXAMPLES::
|
|
559
|
+
|
|
560
|
+
sage: from sage.misc.latex import _Latex_prefs_object
|
|
561
|
+
sage: latex_prefs = _Latex_prefs_object()
|
|
562
|
+
sage: TestSuite(latex_prefs).run(skip ='_test_pickling')
|
|
563
|
+
"""
|
|
564
|
+
self.__option = {}
|
|
565
|
+
self.__option["blackboard_bold"] = bb
|
|
566
|
+
self.__option["matrix_delimiters"] = list(delimiters)
|
|
567
|
+
self.__option["vector_delimiters"] = list(delimiters)
|
|
568
|
+
self.__option["matrix_column_alignment"] = matrix_column_alignment
|
|
569
|
+
self.__option["macros"] = ""
|
|
570
|
+
self.__option["preamble"] = ""
|
|
571
|
+
|
|
572
|
+
# If None, the _default_engine() will be used.
|
|
573
|
+
self.__option["engine"] = None
|
|
574
|
+
|
|
575
|
+
@lazy_attribute
|
|
576
|
+
def _option(self):
|
|
577
|
+
"""
|
|
578
|
+
This attribute contains the preferences list.
|
|
579
|
+
|
|
580
|
+
EXAMPLES::
|
|
581
|
+
|
|
582
|
+
sage: from sage.misc.latex import _Latex_prefs_object
|
|
583
|
+
sage: sorted(_Latex_prefs_object()._option.items())
|
|
584
|
+
[('blackboard_bold', False),
|
|
585
|
+
('engine', None),
|
|
586
|
+
('macros', ''),
|
|
587
|
+
('matrix_column_alignment', 'r'),
|
|
588
|
+
('matrix_delimiters', ['(', ')']),
|
|
589
|
+
('preamble', ''),
|
|
590
|
+
('vector_delimiters', ['(', ')'])]
|
|
591
|
+
|
|
592
|
+
"""
|
|
593
|
+
return self.__option
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
_Latex_prefs = _Latex_prefs_object()
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
def latex_extra_preamble():
|
|
600
|
+
r"""
|
|
601
|
+
Return the string containing the user-configured preamble,
|
|
602
|
+
``sage_latex_macros``, and any user-configured macros. This is
|
|
603
|
+
used in the :meth:`~Latex.eval` method for the :class:`Latex`
|
|
604
|
+
class, and in :func:`_latex_file_`; it follows either
|
|
605
|
+
``LATEX_HEADER`` or ``SLIDE_HEADER`` (defined at the top of this
|
|
606
|
+
file) which is a string containing the documentclass and standard
|
|
607
|
+
usepackage commands.
|
|
608
|
+
|
|
609
|
+
EXAMPLES::
|
|
610
|
+
|
|
611
|
+
sage: from sage.misc.latex import latex_extra_preamble
|
|
612
|
+
sage: print(latex_extra_preamble())
|
|
613
|
+
<BLANKLINE>
|
|
614
|
+
\newcommand{\ZZ}{\Bold{Z}}
|
|
615
|
+
\newcommand{\NN}{\Bold{N}}
|
|
616
|
+
...
|
|
617
|
+
sage: print(latex_extra_preamble()) # needs sage.all
|
|
618
|
+
<BLANKLINE>
|
|
619
|
+
\newcommand{\ZZ}{\Bold{Z}}
|
|
620
|
+
\newcommand{\NN}{\Bold{N}}
|
|
621
|
+
\newcommand{\RR}{\Bold{R}}
|
|
622
|
+
\newcommand{\CC}{\Bold{C}}
|
|
623
|
+
\newcommand{\QQ}{\Bold{Q}}
|
|
624
|
+
\newcommand{\QQbar}{\overline{\QQ}}
|
|
625
|
+
\newcommand{\GF}[1]{\Bold{F}_{#1}}
|
|
626
|
+
\newcommand{\Zp}[1]{\Bold{Z}_{#1}}
|
|
627
|
+
\newcommand{\Qp}[1]{\Bold{Q}_{#1}}
|
|
628
|
+
\newcommand{\Zmod}[1]{\ZZ/#1\ZZ}
|
|
629
|
+
\newcommand{\CDF}{\Bold{C}}
|
|
630
|
+
\newcommand{\CIF}{\Bold{C}}
|
|
631
|
+
\newcommand{\CLF}{\Bold{C}}
|
|
632
|
+
\newcommand{\RDF}{\Bold{R}}
|
|
633
|
+
\newcommand{\RIF}{\Bold{I} \Bold{R}}
|
|
634
|
+
\newcommand{\RLF}{\Bold{R}}
|
|
635
|
+
\newcommand{\SL}{\mathrm{SL}}
|
|
636
|
+
\newcommand{\PSL}{\mathrm{PSL}}
|
|
637
|
+
\newcommand{\lcm}{\mathop{\operatorname{lcm}}}
|
|
638
|
+
\newcommand{\dist}{\mathrm{dist}}
|
|
639
|
+
\newcommand{\im}{\mathop{\operatorname{im}}}
|
|
640
|
+
\newcommand{\rank}{\mathop{\operatorname{rank}}}
|
|
641
|
+
\newcommand{\PP}{\Bold{P}}
|
|
642
|
+
\newcommand{\OO}{\mathcal{O}}
|
|
643
|
+
\newcommand{\Bold}[1]{\mathbf{#1}}
|
|
644
|
+
<BLANKLINE>
|
|
645
|
+
"""
|
|
646
|
+
from sage.misc.latex_macros import sage_latex_macros
|
|
647
|
+
return "\n".join([_Latex_prefs._option['preamble'],
|
|
648
|
+
"\n".join(sage_latex_macros()),
|
|
649
|
+
_Latex_prefs._option['macros']])
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
def _run_latex_(filename, debug=False, density=150, engine=None, png=False, do_in_background=False):
|
|
653
|
+
"""
|
|
654
|
+
This runs LaTeX on the TeX file "filename.tex". It produces files
|
|
655
|
+
``filename.dvi`` (or ``filename.pdf`` if ``engine`` is either ``'pdflatex'``,
|
|
656
|
+
``'xelatex'``, or ``'lualatex'``) and if ``png`` is ``True``, ``filename.png``.
|
|
657
|
+
If ``png`` is ``True`` and ``dvipng`` cannot convert the dvi file to png
|
|
658
|
+
(because of postscript specials or other issues), then ``dvips`` is called, and
|
|
659
|
+
the PS file is converted to a png file.
|
|
660
|
+
|
|
661
|
+
INPUT:
|
|
662
|
+
|
|
663
|
+
- ``filename`` -- string; file to process, including full path
|
|
664
|
+
|
|
665
|
+
- ``debug`` -- boolean (default: ``False``); whether to print
|
|
666
|
+
verbose debugging output
|
|
667
|
+
|
|
668
|
+
- ``density`` -- integer (default: 150); how big output
|
|
669
|
+
image is
|
|
670
|
+
|
|
671
|
+
- ``engine`` -- string; latex engine to use
|
|
672
|
+
|
|
673
|
+
- ``png`` -- boolean (default: ``False``); whether to produce a
|
|
674
|
+
png file
|
|
675
|
+
|
|
676
|
+
- ``do_in_background`` -- boolean (default: ``False``); unused,
|
|
677
|
+
kept for backwards compatibility
|
|
678
|
+
|
|
679
|
+
OUTPUT:
|
|
680
|
+
|
|
681
|
+
A string which could be a string starting with ``'Error'`` (if there was a
|
|
682
|
+
problem), or it could be ``'pdf'`` or ``'dvi'``. If ``engine`` is
|
|
683
|
+
``'latex'`` or ``None``, then a dvi file is created, but if there appear to
|
|
684
|
+
be problems with it (because of PS special commands, for example), then a
|
|
685
|
+
pdf file is created instead. The function returns ``'dvi'`` or ``'pdf'``
|
|
686
|
+
to indicate which type of file is created. (Detecting problems requires
|
|
687
|
+
that ``dvipng`` be installed; if it is not, then the dvi file is not checked
|
|
688
|
+
for problems and ``'dvi'`` is returned.) If ``engine`` is ``'pdflatex'``,
|
|
689
|
+
``'xelatex'`` or ``'lualatex'`` and there are no errors, then ``'pdf'`` is
|
|
690
|
+
returned.
|
|
691
|
+
|
|
692
|
+
.. WARNING::
|
|
693
|
+
|
|
694
|
+
If ``png`` is ``True``, then when using latex (the default), you
|
|
695
|
+
must have ``dvipng`` (or ``dvips`` and ``convert``) installed on your
|
|
696
|
+
operating system, or this command will not work. When using
|
|
697
|
+
``pdflatex``, ``xelatex`` or ``lualatex``, you must have ``convert`` installed.
|
|
698
|
+
|
|
699
|
+
EXAMPLES::
|
|
700
|
+
|
|
701
|
+
sage: from sage.misc.latex import _run_latex_, _latex_file_
|
|
702
|
+
sage: from tempfile import NamedTemporaryFile
|
|
703
|
+
sage: with NamedTemporaryFile(mode='w+t', suffix='.tex') as f: # random, optional - latex
|
|
704
|
+
....: _ = f.write(_latex_file_([ZZ['x'], RR]))
|
|
705
|
+
....: f.flush()
|
|
706
|
+
....: _run_latex_(f.name)
|
|
707
|
+
'dvi'
|
|
708
|
+
"""
|
|
709
|
+
if engine is None:
|
|
710
|
+
engine = _Latex_prefs._option["engine"]
|
|
711
|
+
if engine is None:
|
|
712
|
+
engine = _default_engine()
|
|
713
|
+
|
|
714
|
+
if not engine or engine == "latex":
|
|
715
|
+
from sage.features.latex import latex
|
|
716
|
+
latex().require()
|
|
717
|
+
command = "latex"
|
|
718
|
+
# 'suffix' is used in the 'convert' command list
|
|
719
|
+
suffix = "ps"
|
|
720
|
+
return_suffix = "dvi"
|
|
721
|
+
elif engine == "pdflatex":
|
|
722
|
+
from sage.features.latex import pdflatex
|
|
723
|
+
pdflatex().require()
|
|
724
|
+
command = "pdflatex"
|
|
725
|
+
suffix = "pdf"
|
|
726
|
+
return_suffix = "pdf"
|
|
727
|
+
elif engine == "xelatex":
|
|
728
|
+
from sage.features.latex import xelatex
|
|
729
|
+
xelatex().require()
|
|
730
|
+
command = "xelatex"
|
|
731
|
+
suffix = "pdf"
|
|
732
|
+
return_suffix = "pdf"
|
|
733
|
+
elif engine == "lualatex":
|
|
734
|
+
from sage.features.latex import lualatex
|
|
735
|
+
lualatex().require()
|
|
736
|
+
command = "lualatex"
|
|
737
|
+
suffix = "pdf"
|
|
738
|
+
return_suffix = "pdf"
|
|
739
|
+
else:
|
|
740
|
+
raise ValueError("Unsupported LaTeX engine.")
|
|
741
|
+
|
|
742
|
+
# if png output + latex, check to see if dvipng or magick/convert is installed.
|
|
743
|
+
from sage.features.imagemagick import ImageMagick
|
|
744
|
+
from sage.features.dvipng import dvipng
|
|
745
|
+
if png:
|
|
746
|
+
if ((not engine or engine == "latex")
|
|
747
|
+
and not (dvipng().is_present() or ImageMagick().is_present())):
|
|
748
|
+
print()
|
|
749
|
+
print("Error: neither dvipng nor magick/convert (from the ImageMagick suite)")
|
|
750
|
+
print("appear to be installed. Displaying LaTeX, PDFLaTeX output")
|
|
751
|
+
print("requires at least one of these programs, so please install")
|
|
752
|
+
print("and try again.")
|
|
753
|
+
print()
|
|
754
|
+
print("Go to http://sourceforge.net/projects/dvipng/ and")
|
|
755
|
+
print("http://www.imagemagick.org to download these programs.")
|
|
756
|
+
return "Error"
|
|
757
|
+
# if png output + [pdf|xe|lua]latex, check to see if magick/convert is installed.
|
|
758
|
+
elif engine in ["pdflatex", "xelatex", "lualatex"]:
|
|
759
|
+
ImageMagick().require()
|
|
760
|
+
# check_validity: check to see if the dvi file is okay by trying
|
|
761
|
+
# to convert to a png file. if this fails, return_suffix will be
|
|
762
|
+
# set to "pdf". return_suffix is the return value for this
|
|
763
|
+
# function.
|
|
764
|
+
#
|
|
765
|
+
# thus if not png output, check validity of dvi output if dvipng
|
|
766
|
+
# or magick/convert is installed.
|
|
767
|
+
else:
|
|
768
|
+
check_validity = dvipng().is_present()
|
|
769
|
+
# set up filenames, other strings:
|
|
770
|
+
base, filename = os.path.split(filename)
|
|
771
|
+
filename = os.path.splitext(filename)[0] # get rid of extension
|
|
772
|
+
if len(filename.split()) > 1:
|
|
773
|
+
raise ValueError("filename must contain no spaces")
|
|
774
|
+
if not debug:
|
|
775
|
+
redirect = PIPE
|
|
776
|
+
else:
|
|
777
|
+
redirect = None
|
|
778
|
+
# if do_in_background:
|
|
779
|
+
# background = ' &'
|
|
780
|
+
# else:
|
|
781
|
+
# background = ''
|
|
782
|
+
|
|
783
|
+
# Define the commands to be used:
|
|
784
|
+
lt = [command, r'\nonstopmode', r'\input{' + filename + '.tex}']
|
|
785
|
+
# dvipng is run with the 'picky' option: this means that if
|
|
786
|
+
# there are warnings, no png file is created.
|
|
787
|
+
dvipng = ['dvipng', '--picky', '-q', '-T', 'tight',
|
|
788
|
+
'-D', str(density), filename + '.dvi', '-o', filename + '.png']
|
|
789
|
+
|
|
790
|
+
dvips = ['dvips', filename + '.dvi']
|
|
791
|
+
|
|
792
|
+
ps2pdf = ['ps2pdf', filename + '.ps']
|
|
793
|
+
|
|
794
|
+
# We seem to need a larger size when using magick/convert compared to
|
|
795
|
+
# when using dvipng:
|
|
796
|
+
density = int(1.4 * density / 1.3)
|
|
797
|
+
from sage.features.imagemagick import Magick
|
|
798
|
+
magick = [Magick().executable, '-density',
|
|
799
|
+
'{0}x{0}'.format(density), '-trim', filename + '.' + suffix,
|
|
800
|
+
filename + '.png']
|
|
801
|
+
|
|
802
|
+
# it is possible to get through the following commands
|
|
803
|
+
# without running a program, so in that case we force error
|
|
804
|
+
e = False
|
|
805
|
+
|
|
806
|
+
# our standard way of calling programs here; change this if we want
|
|
807
|
+
# finer-grained analysis of the return code. Think of the output as
|
|
808
|
+
# a boolean: "the command exited normally"
|
|
809
|
+
def subpcall(x):
|
|
810
|
+
return not call(x, stdout=redirect,
|
|
811
|
+
stderr=redirect, cwd=base)
|
|
812
|
+
if engine in ['pdflatex', 'xelatex', 'lualatex']:
|
|
813
|
+
if debug:
|
|
814
|
+
print(lt)
|
|
815
|
+
if png:
|
|
816
|
+
print(magick)
|
|
817
|
+
e = subpcall(lt)
|
|
818
|
+
if png:
|
|
819
|
+
e = e and subpcall(magick)
|
|
820
|
+
else: # latex
|
|
821
|
+
if (png or check_validity):
|
|
822
|
+
if dvipng().is_present():
|
|
823
|
+
if debug:
|
|
824
|
+
print(lt)
|
|
825
|
+
print(dvipng)
|
|
826
|
+
e = subpcall(lt) and subpcall(dvipng)
|
|
827
|
+
dvipng_error = not os.path.exists(os.path.join(base, filename + '.png'))
|
|
828
|
+
# If there is no png file, then either the latex
|
|
829
|
+
# process failed or dvipng failed. Assume that dvipng
|
|
830
|
+
# failed, and try running dvips and magick/convert. (If the
|
|
831
|
+
# latex process failed, then dvips and magick/convert will
|
|
832
|
+
# fail also, so we'll still catch the error.)
|
|
833
|
+
if dvipng_error:
|
|
834
|
+
if png:
|
|
835
|
+
if ImageMagick().is_present():
|
|
836
|
+
if debug:
|
|
837
|
+
print("'dvipng' failed; trying 'magick/convert' instead...")
|
|
838
|
+
print(dvips)
|
|
839
|
+
print(magick)
|
|
840
|
+
e = subpcall(dvips) and subpcall(magick)
|
|
841
|
+
else:
|
|
842
|
+
print("Error: 'dvipng' failed and 'magick/convert' is not installed.")
|
|
843
|
+
return "Error: dvipng failed."
|
|
844
|
+
else: # not png, i.e., check_validity
|
|
845
|
+
return_suffix = "pdf"
|
|
846
|
+
if debug:
|
|
847
|
+
print("bad dvi file; running dvips and ps2pdf instead...")
|
|
848
|
+
print(dvips)
|
|
849
|
+
print(ps2pdf)
|
|
850
|
+
e = subpcall(dvips) and subpcall(ps2pdf)
|
|
851
|
+
if not e: # error running dvips and/or ps2pdf
|
|
852
|
+
pdflt = lt[:]
|
|
853
|
+
pdflt[1] = 'pdflatex'
|
|
854
|
+
if debug:
|
|
855
|
+
print("error running dvips and ps2pdf; trying pdflatex instead...")
|
|
856
|
+
print(pdflt)
|
|
857
|
+
e = subpcall(pdflt)
|
|
858
|
+
else: # do not have dvipng, so must have magick/convert. run latex, dvips, magick/convert.
|
|
859
|
+
if debug:
|
|
860
|
+
print(lt)
|
|
861
|
+
print(dvips)
|
|
862
|
+
print(magick)
|
|
863
|
+
e = subpcall(lt) and subpcall(dvips) and subpcall(magick)
|
|
864
|
+
if not e:
|
|
865
|
+
print("An error occurred.")
|
|
866
|
+
try:
|
|
867
|
+
with open(base + '/' + filename + '.log') as f:
|
|
868
|
+
print(f.read())
|
|
869
|
+
except OSError:
|
|
870
|
+
pass
|
|
871
|
+
return "Error latexing slide."
|
|
872
|
+
return return_suffix
|
|
873
|
+
|
|
874
|
+
|
|
875
|
+
# -------------------------------------------------------
|
|
876
|
+
# The Latex class is used to make slides and LaTeX output
|
|
877
|
+
# -------------------------------------------------------
|
|
878
|
+
|
|
879
|
+
|
|
880
|
+
class LatexCall:
|
|
881
|
+
r"""
|
|
882
|
+
Typeset Sage objects via a ``__call__`` method to this class,
|
|
883
|
+
typically by calling those objects' ``_latex_`` methods. The
|
|
884
|
+
class :class:`Latex` inherits from this. This class is used in
|
|
885
|
+
:mod:`~sage.misc.latex_macros`, while functions from
|
|
886
|
+
:mod:`~sage.misc.latex_macros` are used in :class:`Latex`, so this
|
|
887
|
+
is here primarily to avoid circular imports.
|
|
888
|
+
|
|
889
|
+
EXAMPLES::
|
|
890
|
+
|
|
891
|
+
sage: from sage.misc.latex import LatexCall
|
|
892
|
+
sage: LatexCall()(ZZ)
|
|
893
|
+
\Bold{Z}
|
|
894
|
+
sage: LatexCall().__call__(ZZ)
|
|
895
|
+
\Bold{Z}
|
|
896
|
+
|
|
897
|
+
This returns an instance of the class :class:`LatexExpr`::
|
|
898
|
+
|
|
899
|
+
sage: type(LatexCall()(ZZ))
|
|
900
|
+
<class 'sage.misc.latex.LatexExpr'>
|
|
901
|
+
"""
|
|
902
|
+
def __call__(self, x, combine_all=False):
|
|
903
|
+
r"""
|
|
904
|
+
Return a :class:`LatexExpr` built out of the argument ``x``.
|
|
905
|
+
|
|
906
|
+
INPUT:
|
|
907
|
+
|
|
908
|
+
- ``x`` -- a Sage object
|
|
909
|
+
|
|
910
|
+
- ``combine_all`` -- boolean (default: ``False``); if ``combine_all``
|
|
911
|
+
is ``True`` and the input is a tuple, then it does not return a
|
|
912
|
+
tuple and instead returns a string with all the elements separated by
|
|
913
|
+
a single space
|
|
914
|
+
|
|
915
|
+
OUTPUT: a :class:`LatexExpr` built from ``x``
|
|
916
|
+
|
|
917
|
+
EXAMPLES::
|
|
918
|
+
|
|
919
|
+
sage: latex(Integer(3)) # indirect doctest
|
|
920
|
+
3
|
|
921
|
+
sage: latex(1==0)
|
|
922
|
+
\mathrm{False}
|
|
923
|
+
sage: print(latex([x, 2])) # needs sage.symbolic
|
|
924
|
+
\left[x, 2\right]
|
|
925
|
+
|
|
926
|
+
Check that :issue:`11775` is fixed::
|
|
927
|
+
|
|
928
|
+
sage: latex((x,2), combine_all=True) # needs sage.symbolic
|
|
929
|
+
x 2
|
|
930
|
+
"""
|
|
931
|
+
if has_latex_attr(x):
|
|
932
|
+
return LatexExpr(x._latex_())
|
|
933
|
+
try:
|
|
934
|
+
f = latex_table[type(x)]
|
|
935
|
+
if isinstance(x, tuple):
|
|
936
|
+
return LatexExpr(f(x, combine_all=combine_all))
|
|
937
|
+
return LatexExpr(f(x))
|
|
938
|
+
except KeyError:
|
|
939
|
+
return LatexExpr(str_function(str(x)))
|
|
940
|
+
|
|
941
|
+
|
|
942
|
+
class Latex(LatexCall):
|
|
943
|
+
r"""nodetex
|
|
944
|
+
Enter, e.g.,
|
|
945
|
+
|
|
946
|
+
::
|
|
947
|
+
|
|
948
|
+
%latex
|
|
949
|
+
The equation $y^2 = x^3 + x$ defines an elliptic curve.
|
|
950
|
+
We have $2006 = \sage{factor(2006)}$.
|
|
951
|
+
|
|
952
|
+
in an input cell in the notebook to get a typeset version. Use
|
|
953
|
+
``%latex_debug`` to get debugging output.
|
|
954
|
+
|
|
955
|
+
Use ``latex(...)`` to typeset a Sage object. Use :class:`LatexExpr`
|
|
956
|
+
to typeset LaTeX code that you create by hand.
|
|
957
|
+
|
|
958
|
+
Use ``%slide`` instead to typeset slides.
|
|
959
|
+
|
|
960
|
+
.. WARNING::
|
|
961
|
+
|
|
962
|
+
You must have dvipng (or dvips and magick/convert) installed
|
|
963
|
+
on your operating system, or this command will not work.
|
|
964
|
+
|
|
965
|
+
EXAMPLES::
|
|
966
|
+
|
|
967
|
+
sage: latex(x^20 + 1) # needs sage.symbolic
|
|
968
|
+
x^{20} + 1
|
|
969
|
+
sage: latex(FiniteField(25,'a')) # needs sage.rings.finite_rings
|
|
970
|
+
\Bold{F}_{5^{2}}
|
|
971
|
+
sage: latex("hello")
|
|
972
|
+
\text{\texttt{hello}}
|
|
973
|
+
sage: LatexExpr(r"\frac{x^2 - 1}{x + 1} = x - 1")
|
|
974
|
+
\frac{x^2 - 1}{x + 1} = x - 1
|
|
975
|
+
|
|
976
|
+
LaTeX expressions can be added; note that a space is automatically
|
|
977
|
+
inserted::
|
|
978
|
+
|
|
979
|
+
sage: LatexExpr(r"y \neq") + latex(x^20 + 1) # needs sage.symbolic
|
|
980
|
+
y \neq x^{20} + 1
|
|
981
|
+
"""
|
|
982
|
+
def __init__(self, debug=False, slide=False, density=150, engine=None):
|
|
983
|
+
"""
|
|
984
|
+
Initialize the latex builder.
|
|
985
|
+
|
|
986
|
+
EXAMPLES::
|
|
987
|
+
|
|
988
|
+
sage: from sage.misc.latex import Latex
|
|
989
|
+
sage: l = Latex()
|
|
990
|
+
sage: TestSuite(l).run(skip ='_test_pickling')
|
|
991
|
+
"""
|
|
992
|
+
self.__debug = debug
|
|
993
|
+
self.__slide = slide
|
|
994
|
+
self.__engine = engine
|
|
995
|
+
self.__density = density
|
|
996
|
+
|
|
997
|
+
def _relation_symbols(self):
|
|
998
|
+
"""
|
|
999
|
+
Return a dictionary whose keys are attributes of the
|
|
1000
|
+
:mod:`operator` module and whose values are the corresponding
|
|
1001
|
+
LaTeX expressions.
|
|
1002
|
+
|
|
1003
|
+
EXAMPLES::
|
|
1004
|
+
|
|
1005
|
+
sage: import operator
|
|
1006
|
+
sage: latex._relation_symbols()[operator.ge]
|
|
1007
|
+
' \\geq '
|
|
1008
|
+
"""
|
|
1009
|
+
import operator
|
|
1010
|
+
return {operator.lt: ' < ', operator.le: ' \\leq ',
|
|
1011
|
+
operator.eq: ' = ', operator.ne: ' \\neq ',
|
|
1012
|
+
operator.ge: ' \\geq ', operator.gt: ' > '}
|
|
1013
|
+
|
|
1014
|
+
def _latex_preparse(self, s, locals):
|
|
1015
|
+
r"""
|
|
1016
|
+
Replace instances of ``'\sage{x}'`` in ``s`` with the LaTeX version of
|
|
1017
|
+
``x`` in the running session.
|
|
1018
|
+
|
|
1019
|
+
EXAMPLES::
|
|
1020
|
+
|
|
1021
|
+
sage: s = 2
|
|
1022
|
+
sage: sage.misc.latex.Latex()._latex_preparse(r'\sage{s}', locals())
|
|
1023
|
+
'2'
|
|
1024
|
+
"""
|
|
1025
|
+
from sage.misc.sage_eval import sage_eval
|
|
1026
|
+
i0 = -1
|
|
1027
|
+
while True:
|
|
1028
|
+
i = s.find('\\sage{')
|
|
1029
|
+
if i == -1 or i == i0:
|
|
1030
|
+
return s
|
|
1031
|
+
i0 = i
|
|
1032
|
+
t = s[i + 6:]
|
|
1033
|
+
j = t.find('}')
|
|
1034
|
+
if j == -1:
|
|
1035
|
+
return s
|
|
1036
|
+
|
|
1037
|
+
var = t[:j]
|
|
1038
|
+
try:
|
|
1039
|
+
k = str(latex(sage_eval(var, locals)))
|
|
1040
|
+
except Exception as msg:
|
|
1041
|
+
print(msg)
|
|
1042
|
+
k = '\\mbox{\\rm [%s undefined]}' % var
|
|
1043
|
+
s = s[:i] + k + t[j + 1:]
|
|
1044
|
+
|
|
1045
|
+
def eval(self, x, globals, strip=False, filename=None, debug=None,
|
|
1046
|
+
density=None, engine=None, locals={}):
|
|
1047
|
+
r"""
|
|
1048
|
+
Compile the formatted tex given by ``x`` as a png and writes the
|
|
1049
|
+
output file to the directory given by ``filename``.
|
|
1050
|
+
|
|
1051
|
+
INPUT:
|
|
1052
|
+
|
|
1053
|
+
- ``globals`` -- a globals dictionary
|
|
1054
|
+
|
|
1055
|
+
- ``x`` -- string to evaluate
|
|
1056
|
+
|
|
1057
|
+
- ``strip`` -- ignored
|
|
1058
|
+
|
|
1059
|
+
- ``filename`` -- output filename
|
|
1060
|
+
|
|
1061
|
+
- ``debug`` -- whether to print verbose debugging output
|
|
1062
|
+
|
|
1063
|
+
- ``density`` -- how big output image is
|
|
1064
|
+
|
|
1065
|
+
- ``engine`` -- latex engine to use. Currently ``'latex'``,
|
|
1066
|
+
``'pdflatex'``, ``'xelatex'`` and ``'lualatex'`` are supported
|
|
1067
|
+
|
|
1068
|
+
- ``locals`` -- extra local variables used when evaluating Sage code in ``x``
|
|
1069
|
+
|
|
1070
|
+
.. WARNING::
|
|
1071
|
+
|
|
1072
|
+
When using ``'latex'`` (the default), you must have ``dvipng`` (or
|
|
1073
|
+
``dvips`` and ``magick/convert``) installed on your operating system, or
|
|
1074
|
+
this command will not work. When using ``'pdflatex'``, ``'xelatex'``
|
|
1075
|
+
or ``'lualatex'``, you must have ``magick/convert`` installed.
|
|
1076
|
+
|
|
1077
|
+
OUTPUT:
|
|
1078
|
+
|
|
1079
|
+
If it compiled successfully, this returns an empty string ``''``,
|
|
1080
|
+
otherwise it returns ``None``.
|
|
1081
|
+
|
|
1082
|
+
EXAMPLES::
|
|
1083
|
+
|
|
1084
|
+
sage: fn = tmp_filename()
|
|
1085
|
+
sage: latex.eval("$\\ZZ[x]$", locals(), filename=fn) # not tested
|
|
1086
|
+
''
|
|
1087
|
+
sage: latex.eval(r"\ThisIsAnInvalidCommand", {}) # optional -- latex ImageMagick
|
|
1088
|
+
An error occurred...
|
|
1089
|
+
"""
|
|
1090
|
+
MACROS = latex_extra_preamble()
|
|
1091
|
+
|
|
1092
|
+
if density is None:
|
|
1093
|
+
density = self.__density
|
|
1094
|
+
if filename is None:
|
|
1095
|
+
filename = 'sage%s' % random.randint(1, 100) # to defeat browser caches
|
|
1096
|
+
else:
|
|
1097
|
+
filename = os.path.splitext(filename)[0] # get rid of extension
|
|
1098
|
+
|
|
1099
|
+
result = None
|
|
1100
|
+
with TemporaryDirectory() as base:
|
|
1101
|
+
orig_base, filename = os.path.split(os.path.abspath(filename))
|
|
1102
|
+
if len(filename.split()) > 1:
|
|
1103
|
+
raise ValueError("filename must contain no spaces")
|
|
1104
|
+
if debug is None:
|
|
1105
|
+
debug = self.__debug
|
|
1106
|
+
x = self._latex_preparse(x, locals)
|
|
1107
|
+
O = open(os.path.join(base, filename + ".tex"), 'w')
|
|
1108
|
+
if self.__slide:
|
|
1109
|
+
O.write(SLIDE_HEADER)
|
|
1110
|
+
O.write(MACROS)
|
|
1111
|
+
O.write('\\begin{document}\n\n')
|
|
1112
|
+
else:
|
|
1113
|
+
O.write(LATEX_HEADER)
|
|
1114
|
+
O.write(MACROS)
|
|
1115
|
+
O.write('\\begin{document}\n')
|
|
1116
|
+
|
|
1117
|
+
O.write(x)
|
|
1118
|
+
if self.__slide:
|
|
1119
|
+
O.write('\n\n\\end{document}')
|
|
1120
|
+
else:
|
|
1121
|
+
O.write('\n\n\\end{document}\n')
|
|
1122
|
+
|
|
1123
|
+
O.close()
|
|
1124
|
+
if engine is None:
|
|
1125
|
+
engine = self.__engine
|
|
1126
|
+
if engine is None:
|
|
1127
|
+
engine = _Latex_prefs._option["engine"]
|
|
1128
|
+
if engine is None:
|
|
1129
|
+
engine = _default_engine()
|
|
1130
|
+
|
|
1131
|
+
e = _run_latex_(os.path.join(base, filename + ".tex"),
|
|
1132
|
+
debug=debug,
|
|
1133
|
+
density=density,
|
|
1134
|
+
engine=engine,
|
|
1135
|
+
png=True)
|
|
1136
|
+
|
|
1137
|
+
if e.find("Error") == -1:
|
|
1138
|
+
shutil.copy(os.path.join(base, filename + ".png"),
|
|
1139
|
+
os.path.join(orig_base, filename + ".png"))
|
|
1140
|
+
result = ''
|
|
1141
|
+
|
|
1142
|
+
return result
|
|
1143
|
+
|
|
1144
|
+
def blackboard_bold(self, t=None):
|
|
1145
|
+
r"""nodetex
|
|
1146
|
+
Controls whether Sage uses blackboard bold or ordinary bold
|
|
1147
|
+
face for typesetting ``ZZ``, ``RR``, etc.
|
|
1148
|
+
|
|
1149
|
+
INPUT:
|
|
1150
|
+
|
|
1151
|
+
- ``t`` -- boolean or ``None``
|
|
1152
|
+
|
|
1153
|
+
OUTPUT:
|
|
1154
|
+
|
|
1155
|
+
If ``t`` is ``None``, return the current setting (``True`` or
|
|
1156
|
+
``False``).
|
|
1157
|
+
|
|
1158
|
+
If ``t`` is ``True``, use blackboard bold (``\mathbb``); otherwise use
|
|
1159
|
+
boldface (``\mathbf``).
|
|
1160
|
+
|
|
1161
|
+
EXAMPLES::
|
|
1162
|
+
|
|
1163
|
+
sage: latex.blackboard_bold()
|
|
1164
|
+
False
|
|
1165
|
+
sage: latex.blackboard_bold(True)
|
|
1166
|
+
sage: latex.blackboard_bold()
|
|
1167
|
+
True
|
|
1168
|
+
sage: latex.blackboard_bold(False)
|
|
1169
|
+
"""
|
|
1170
|
+
if t is None:
|
|
1171
|
+
return _Latex_prefs._option["blackboard_bold"]
|
|
1172
|
+
from .latex_macros import sage_configurable_latex_macros
|
|
1173
|
+
old = _Latex_prefs._option["blackboard_bold"]
|
|
1174
|
+
_Latex_prefs._option["blackboard_bold"] = bool(t)
|
|
1175
|
+
if bool(old) != bool(t):
|
|
1176
|
+
if old:
|
|
1177
|
+
old_macro = "\\newcommand{\\Bold}[1]{\\mathbb{#1}}"
|
|
1178
|
+
else:
|
|
1179
|
+
old_macro = "\\newcommand{\\Bold}[1]{\\mathbf{#1}}"
|
|
1180
|
+
if bool(t):
|
|
1181
|
+
macro = "\\newcommand{\\Bold}[1]{\\mathbb{#1}}"
|
|
1182
|
+
else:
|
|
1183
|
+
macro = "\\newcommand{\\Bold}[1]{\\mathbf{#1}}"
|
|
1184
|
+
sage_configurable_latex_macros.remove(old_macro)
|
|
1185
|
+
sage_configurable_latex_macros.append(macro)
|
|
1186
|
+
|
|
1187
|
+
def matrix_delimiters(self, left=None, right=None):
|
|
1188
|
+
r"""nodetex
|
|
1189
|
+
Change the left and right delimiters for the LaTeX representation
|
|
1190
|
+
of matrices
|
|
1191
|
+
|
|
1192
|
+
INPUT:
|
|
1193
|
+
|
|
1194
|
+
- ``left``, ``right`` -- strings or ``None``
|
|
1195
|
+
|
|
1196
|
+
If both ``left`` and ``right`` are ``None``, then return the
|
|
1197
|
+
current delimiters. Otherwise, set the left and/or right
|
|
1198
|
+
delimiters, whichever are specified.
|
|
1199
|
+
|
|
1200
|
+
Good choices for ``left`` and ``right`` are any delimiters which
|
|
1201
|
+
LaTeX understands and knows how to resize; some examples are:
|
|
1202
|
+
|
|
1203
|
+
- parentheses: ``'('``, ``')'``
|
|
1204
|
+
- brackets: ``'['``, ``']'``
|
|
1205
|
+
- braces: ``'\\{'``, ``'\\}'``
|
|
1206
|
+
- vertical lines: ``'|'``
|
|
1207
|
+
- angle brackets: ``'\\langle'``, ``'\\rangle'``
|
|
1208
|
+
|
|
1209
|
+
.. NOTE::
|
|
1210
|
+
|
|
1211
|
+
Putting aside aesthetics, you may combine these in any way
|
|
1212
|
+
imaginable; for example, you could set ``left`` to be a right-hand
|
|
1213
|
+
bracket ``']'`` and ``right`` to be a right-hand brace ``'\\}'``,
|
|
1214
|
+
and it will be typeset correctly.
|
|
1215
|
+
|
|
1216
|
+
EXAMPLES::
|
|
1217
|
+
|
|
1218
|
+
sage: # needs sage.modules
|
|
1219
|
+
sage: a = matrix(1, 1, [17])
|
|
1220
|
+
sage: latex(a)
|
|
1221
|
+
\left(\begin{array}{r}
|
|
1222
|
+
17
|
|
1223
|
+
\end{array}\right)
|
|
1224
|
+
sage: latex.matrix_delimiters('[', ']')
|
|
1225
|
+
sage: latex(a)
|
|
1226
|
+
\left[\begin{array}{r}
|
|
1227
|
+
17
|
|
1228
|
+
\end{array}\right]
|
|
1229
|
+
sage: latex.matrix_delimiters(left='\\{')
|
|
1230
|
+
sage: latex(a)
|
|
1231
|
+
\left\{\begin{array}{r}
|
|
1232
|
+
17
|
|
1233
|
+
\end{array}\right]
|
|
1234
|
+
sage: latex.matrix_delimiters()
|
|
1235
|
+
['\\{', ']']
|
|
1236
|
+
|
|
1237
|
+
Restore defaults::
|
|
1238
|
+
|
|
1239
|
+
sage: latex.matrix_delimiters('(', ')')
|
|
1240
|
+
"""
|
|
1241
|
+
if left is None and right is None:
|
|
1242
|
+
return _Latex_prefs._option['matrix_delimiters']
|
|
1243
|
+
else:
|
|
1244
|
+
if left is not None:
|
|
1245
|
+
_Latex_prefs._option['matrix_delimiters'][0] = left
|
|
1246
|
+
if right is not None:
|
|
1247
|
+
_Latex_prefs._option['matrix_delimiters'][1] = right
|
|
1248
|
+
|
|
1249
|
+
def vector_delimiters(self, left=None, right=None):
|
|
1250
|
+
r"""nodetex
|
|
1251
|
+
Change the left and right delimiters for the LaTeX representation
|
|
1252
|
+
of vectors
|
|
1253
|
+
|
|
1254
|
+
INPUT:
|
|
1255
|
+
|
|
1256
|
+
- ``left``, ``right`` -- strings or ``None``
|
|
1257
|
+
|
|
1258
|
+
If both ``left`` and ``right`` are ``None``, then return the
|
|
1259
|
+
current delimiters. Otherwise, set the left and/or right
|
|
1260
|
+
delimiters, whichever are specified.
|
|
1261
|
+
|
|
1262
|
+
Good choices for ``left`` and ``right`` are any delimiters which
|
|
1263
|
+
LaTeX understands and knows how to resize; some examples are:
|
|
1264
|
+
|
|
1265
|
+
- parentheses: ``'('``, ``')'``
|
|
1266
|
+
- brackets: ``'['``, ``']'``
|
|
1267
|
+
- braces: ``'\\{'``, ``'\\}'``
|
|
1268
|
+
- vertical lines: ``'|'``
|
|
1269
|
+
- angle brackets: ``'\\langle'``, ``'\\rangle'``
|
|
1270
|
+
|
|
1271
|
+
.. NOTE::
|
|
1272
|
+
|
|
1273
|
+
Putting aside aesthetics, you may combine these in any way
|
|
1274
|
+
imaginable; for example, you could set ``left`` to be a right-hand
|
|
1275
|
+
bracket ``']'`` and ``right`` to be a right-hand brace ``'\\}'``, and it
|
|
1276
|
+
will be typeset correctly.
|
|
1277
|
+
|
|
1278
|
+
EXAMPLES::
|
|
1279
|
+
|
|
1280
|
+
sage: # needs sage.modules
|
|
1281
|
+
sage: a = vector(QQ, [1,2,3])
|
|
1282
|
+
sage: latex(a)
|
|
1283
|
+
\left(1,\,2,\,3\right)
|
|
1284
|
+
sage: latex.vector_delimiters('[', ']')
|
|
1285
|
+
sage: latex(a)
|
|
1286
|
+
\left[1,\,2,\,3\right]
|
|
1287
|
+
sage: latex.vector_delimiters(right='\\}')
|
|
1288
|
+
sage: latex(a)
|
|
1289
|
+
\left[1,\,2,\,3\right\}
|
|
1290
|
+
sage: latex.vector_delimiters()
|
|
1291
|
+
['[', '\\}']
|
|
1292
|
+
|
|
1293
|
+
Restore defaults::
|
|
1294
|
+
|
|
1295
|
+
sage: latex.vector_delimiters('(', ')')
|
|
1296
|
+
"""
|
|
1297
|
+
if left is None and right is None:
|
|
1298
|
+
return _Latex_prefs._option['vector_delimiters']
|
|
1299
|
+
else:
|
|
1300
|
+
if left is not None:
|
|
1301
|
+
_Latex_prefs._option['vector_delimiters'][0] = left
|
|
1302
|
+
if right is not None:
|
|
1303
|
+
_Latex_prefs._option['vector_delimiters'][1] = right
|
|
1304
|
+
|
|
1305
|
+
def matrix_column_alignment(self, align=None):
|
|
1306
|
+
r"""nodetex
|
|
1307
|
+
Changes the column-alignment of the LaTeX representation of
|
|
1308
|
+
matrices.
|
|
1309
|
+
|
|
1310
|
+
INPUT:
|
|
1311
|
+
|
|
1312
|
+
- ``align`` -- string (``'r'`` for right, ``'c'`` for center,
|
|
1313
|
+
``'l'`` for left) or ``None``
|
|
1314
|
+
|
|
1315
|
+
OUTPUT:
|
|
1316
|
+
|
|
1317
|
+
If ``align`` is ``None``, then returns the current
|
|
1318
|
+
alignment-string. Otherwise, set this alignment.
|
|
1319
|
+
|
|
1320
|
+
The input ``align`` can be any string which the LaTeX
|
|
1321
|
+
``array``-environment understands as a parameter for
|
|
1322
|
+
aligning a column.
|
|
1323
|
+
|
|
1324
|
+
EXAMPLES::
|
|
1325
|
+
|
|
1326
|
+
sage: # needs sage.modules
|
|
1327
|
+
sage: a = matrix(1, 1, [42])
|
|
1328
|
+
sage: latex(a)
|
|
1329
|
+
\left(\begin{array}{r}
|
|
1330
|
+
42
|
|
1331
|
+
\end{array}\right)
|
|
1332
|
+
sage: latex.matrix_column_alignment('c')
|
|
1333
|
+
sage: latex(a)
|
|
1334
|
+
\left(\begin{array}{c}
|
|
1335
|
+
42
|
|
1336
|
+
\end{array}\right)
|
|
1337
|
+
sage: latex.matrix_column_alignment('l')
|
|
1338
|
+
sage: latex(a)
|
|
1339
|
+
\left(\begin{array}{l}
|
|
1340
|
+
42
|
|
1341
|
+
\end{array}\right)
|
|
1342
|
+
|
|
1343
|
+
Restore defaults::
|
|
1344
|
+
|
|
1345
|
+
sage: latex.matrix_column_alignment('r')
|
|
1346
|
+
"""
|
|
1347
|
+
if align is None:
|
|
1348
|
+
return _Latex_prefs._option['matrix_column_alignment']
|
|
1349
|
+
else:
|
|
1350
|
+
_Latex_prefs._option['matrix_column_alignment'] = align
|
|
1351
|
+
|
|
1352
|
+
@cached_method
|
|
1353
|
+
def has_file(self, file_name) -> bool:
|
|
1354
|
+
"""
|
|
1355
|
+
INPUT:
|
|
1356
|
+
|
|
1357
|
+
- ``file_name`` -- string
|
|
1358
|
+
|
|
1359
|
+
Tests whether the local LaTeX installation includes ``file_name``.
|
|
1360
|
+
|
|
1361
|
+
EXAMPLES::
|
|
1362
|
+
|
|
1363
|
+
sage: latex.has_file("article.cls") # optional - latex
|
|
1364
|
+
True
|
|
1365
|
+
sage: latex.has_file("some_inexistent_file.sty")
|
|
1366
|
+
False
|
|
1367
|
+
"""
|
|
1368
|
+
assert isinstance(file_name, str)
|
|
1369
|
+
try:
|
|
1370
|
+
retcode = call("kpsewhich %s" % file_name, shell=True,
|
|
1371
|
+
stdout=PIPE, stderr=PIPE)
|
|
1372
|
+
return (retcode == 0)
|
|
1373
|
+
except OSError:
|
|
1374
|
+
return False
|
|
1375
|
+
|
|
1376
|
+
@cached_method
|
|
1377
|
+
def check_file(self, file_name, more_info=""):
|
|
1378
|
+
"""
|
|
1379
|
+
INPUT:
|
|
1380
|
+
|
|
1381
|
+
- ``file_name`` -- string
|
|
1382
|
+
|
|
1383
|
+
- ``more_info`` -- string (default: ``''``)
|
|
1384
|
+
|
|
1385
|
+
Emit a warning if the local LaTeX installation does not
|
|
1386
|
+
include ``file_name``. The string ``more_info`` is appended
|
|
1387
|
+
to the warning message. The warning is only emitted the first
|
|
1388
|
+
time this method is called.
|
|
1389
|
+
|
|
1390
|
+
EXAMPLES::
|
|
1391
|
+
|
|
1392
|
+
sage: latex.check_file("article.cls") # optional - latex
|
|
1393
|
+
sage: latex.check_file("some_inexistent_file.sty")
|
|
1394
|
+
Warning: `some_inexistent_file.sty` is not part of this computer's TeX installation.
|
|
1395
|
+
sage: latex.check_file("some_inexistent_file.sty")
|
|
1396
|
+
sage: latex.check_file("some_inexistent_file.sty", "This file is required for blah. It can be downloaded from: http://blah.org/")
|
|
1397
|
+
Warning: `some_inexistent_file.sty` is not part of this computer's TeX installation.
|
|
1398
|
+
This file is required for blah. It can be downloaded from: http://blah.org/
|
|
1399
|
+
|
|
1400
|
+
This test checks that the bug in :issue:`9091` is fixed::
|
|
1401
|
+
|
|
1402
|
+
sage: latex.check_file("article.cls", "The article class is really critical.") # optional - latex
|
|
1403
|
+
"""
|
|
1404
|
+
assert isinstance(file_name, str)
|
|
1405
|
+
if not self.has_file(file_name):
|
|
1406
|
+
print("""
|
|
1407
|
+
Warning: `{}` is not part of this computer's TeX installation.""".format(file_name))
|
|
1408
|
+
if more_info:
|
|
1409
|
+
print(more_info)
|
|
1410
|
+
|
|
1411
|
+
def extra_macros(self, macros=None):
|
|
1412
|
+
r"""nodetex
|
|
1413
|
+
String containing extra LaTeX macros to use with ``%latex`` and ``%html``.
|
|
1414
|
+
|
|
1415
|
+
INPUT:
|
|
1416
|
+
|
|
1417
|
+
- ``macros`` -- string (default: ``None``)
|
|
1418
|
+
|
|
1419
|
+
If ``macros`` is ``None``, return the current string. Otherwise,
|
|
1420
|
+
set it to ``macros``. If you want to *append* to the string
|
|
1421
|
+
of macros instead of replacing it, use
|
|
1422
|
+
:meth:`latex.add_macro <Latex.add_macro>`.
|
|
1423
|
+
|
|
1424
|
+
EXAMPLES::
|
|
1425
|
+
|
|
1426
|
+
sage: latex.extra_macros("\\newcommand{\\foo}{bar}")
|
|
1427
|
+
sage: latex.extra_macros()
|
|
1428
|
+
'\\newcommand{\\foo}{bar}'
|
|
1429
|
+
sage: latex.extra_macros("")
|
|
1430
|
+
sage: latex.extra_macros()
|
|
1431
|
+
''
|
|
1432
|
+
"""
|
|
1433
|
+
if macros is None:
|
|
1434
|
+
return _Latex_prefs._option['macros']
|
|
1435
|
+
else:
|
|
1436
|
+
_Latex_prefs._option['macros'] = macros
|
|
1437
|
+
|
|
1438
|
+
def add_macro(self, macro):
|
|
1439
|
+
r"""nodetex
|
|
1440
|
+
Append to the string of extra LaTeX macros, for use with %latex and %html.
|
|
1441
|
+
|
|
1442
|
+
INPUT:
|
|
1443
|
+
|
|
1444
|
+
- ``macro`` -- string
|
|
1445
|
+
|
|
1446
|
+
EXAMPLES::
|
|
1447
|
+
|
|
1448
|
+
sage: latex.extra_macros()
|
|
1449
|
+
''
|
|
1450
|
+
sage: latex.add_macro("\\newcommand{\\foo}{bar}")
|
|
1451
|
+
sage: latex.extra_macros()
|
|
1452
|
+
'\\newcommand{\\foo}{bar}'
|
|
1453
|
+
sage: latex.extra_macros("") # restore to default
|
|
1454
|
+
"""
|
|
1455
|
+
current = latex.extra_macros()
|
|
1456
|
+
if current.find(macro) == -1:
|
|
1457
|
+
_Latex_prefs._option['macros'] += macro
|
|
1458
|
+
|
|
1459
|
+
def extra_preamble(self, s=None):
|
|
1460
|
+
r"""nodetex
|
|
1461
|
+
String containing extra preamble to be used with %latex.
|
|
1462
|
+
|
|
1463
|
+
INPUT:
|
|
1464
|
+
|
|
1465
|
+
- ``s`` -- string or ``None``
|
|
1466
|
+
|
|
1467
|
+
If ``s`` is ``None``, return the current preamble. Otherwise, set
|
|
1468
|
+
it to ``s``. If you want to *append* to the current extra
|
|
1469
|
+
preamble instead of replacing it, use
|
|
1470
|
+
:meth:`latex.add_to_preamble <Latex.add_to_preamble>`.
|
|
1471
|
+
|
|
1472
|
+
You will almost certainly need to use this when using the
|
|
1473
|
+
XeLaTeX engine; see below or the documentation for
|
|
1474
|
+
:func:`engine` for a suggested preamble.
|
|
1475
|
+
|
|
1476
|
+
EXAMPLES::
|
|
1477
|
+
|
|
1478
|
+
sage: latex.extra_preamble("\\DeclareMathOperator{\\Ext}{Ext}")
|
|
1479
|
+
sage: latex.extra_preamble()
|
|
1480
|
+
'\\DeclareMathOperator{\\Ext}{Ext}'
|
|
1481
|
+
sage: latex.extra_preamble("\\"+r"usepackage{fontspec,xunicode,xltxtra}\setmainfont[Mapping=tex-text]{UnBatang}\setmonofont[Mapping=tex-text]{UnDotum}")
|
|
1482
|
+
sage: latex.extra_preamble()
|
|
1483
|
+
'\\usepackage{fontspec,xunicode,xltxtra}\\setmainfont[Mapping=tex-text]{UnBatang}\\setmonofont[Mapping=tex-text]{UnDotum}'
|
|
1484
|
+
sage: latex.extra_preamble("")
|
|
1485
|
+
sage: latex.extra_preamble()
|
|
1486
|
+
''
|
|
1487
|
+
"""
|
|
1488
|
+
if s is None:
|
|
1489
|
+
return _Latex_prefs._option['preamble']
|
|
1490
|
+
else:
|
|
1491
|
+
_Latex_prefs._option['preamble'] = s
|
|
1492
|
+
|
|
1493
|
+
def add_to_preamble(self, s):
|
|
1494
|
+
r"""nodetex
|
|
1495
|
+
Append to the string ``s`` of extra LaTeX macros, for use with
|
|
1496
|
+
``%latex``.
|
|
1497
|
+
|
|
1498
|
+
EXAMPLES::
|
|
1499
|
+
|
|
1500
|
+
sage: latex.extra_preamble()
|
|
1501
|
+
''
|
|
1502
|
+
sage: latex.add_to_preamble("\\DeclareMathOperator{\\Ext}{Ext}")
|
|
1503
|
+
|
|
1504
|
+
At this point, a notebook cell containing
|
|
1505
|
+
|
|
1506
|
+
::
|
|
1507
|
+
|
|
1508
|
+
%latex
|
|
1509
|
+
$\Ext_A^*(\GF{2}, \GF{2}) \Rightarrow \pi_*^s*(S^0)$
|
|
1510
|
+
|
|
1511
|
+
will be typeset correctly.
|
|
1512
|
+
|
|
1513
|
+
::
|
|
1514
|
+
|
|
1515
|
+
sage: latex.add_to_preamble("\\usepackage{xypic}")
|
|
1516
|
+
sage: latex.extra_preamble()
|
|
1517
|
+
'\\DeclareMathOperator{\\Ext}{Ext}\\usepackage{xypic}'
|
|
1518
|
+
|
|
1519
|
+
Now one can put various xypic diagrams into a ``%latex`` cell, such as
|
|
1520
|
+
|
|
1521
|
+
::
|
|
1522
|
+
|
|
1523
|
+
%latex
|
|
1524
|
+
\[ \xymatrix{ \circ \ar `r[d]^{a} `[rr]^{b} `/4pt[rr]^{c} `[rrr]^{d}
|
|
1525
|
+
`_dl[drrr]^{e} [drrr]^{f} & \circ & \circ & \circ \\ \circ & \circ &
|
|
1526
|
+
\circ & \circ } \]
|
|
1527
|
+
|
|
1528
|
+
Reset the preamble to its default, the empty string::
|
|
1529
|
+
|
|
1530
|
+
sage: latex.extra_preamble('')
|
|
1531
|
+
sage: latex.extra_preamble()
|
|
1532
|
+
''
|
|
1533
|
+
"""
|
|
1534
|
+
current = latex.extra_preamble()
|
|
1535
|
+
if current.find(s) == -1:
|
|
1536
|
+
_Latex_prefs._option['preamble'] += s
|
|
1537
|
+
|
|
1538
|
+
def add_package_to_preamble_if_available(self, package_name):
|
|
1539
|
+
r"""
|
|
1540
|
+
Add a ``\usepackage{package_name}`` instruction to the latex
|
|
1541
|
+
preamble if not yet present there, and if ``package_name.sty``
|
|
1542
|
+
is available in the LaTeX installation.
|
|
1543
|
+
|
|
1544
|
+
INPUT:
|
|
1545
|
+
|
|
1546
|
+
- ``package_name`` -- string
|
|
1547
|
+
|
|
1548
|
+
.. SEEALSO::
|
|
1549
|
+
|
|
1550
|
+
- :meth:`add_to_preamble`
|
|
1551
|
+
- :meth:`has_file`.
|
|
1552
|
+
|
|
1553
|
+
TESTS::
|
|
1554
|
+
|
|
1555
|
+
sage: latex.add_package_to_preamble_if_available("tkz-graph")
|
|
1556
|
+
sage: latex.add_package_to_preamble_if_available("nonexistent_package")
|
|
1557
|
+
sage: latex.extra_preamble() # optional - latex latex_package_tkz_graph
|
|
1558
|
+
'\\usepackage{tkz-graph}\n'
|
|
1559
|
+
sage: latex.extra_preamble('')
|
|
1560
|
+
"""
|
|
1561
|
+
assert isinstance(package_name, str)
|
|
1562
|
+
if self.has_file(package_name + ".sty"):
|
|
1563
|
+
self.add_to_preamble("\\usepackage{%s}\n" % package_name)
|
|
1564
|
+
|
|
1565
|
+
def engine(self, e=None):
|
|
1566
|
+
r"""
|
|
1567
|
+
Set Sage to use ``e`` as latex engine when typesetting with
|
|
1568
|
+
:func:`view`, in ``%latex`` cells, etc.
|
|
1569
|
+
|
|
1570
|
+
INPUT:
|
|
1571
|
+
|
|
1572
|
+
- ``e`` -- ``'latex'``, ``'pdflatex'``, ``'xelatex'``, ``'lualatex'`` or ``None``
|
|
1573
|
+
|
|
1574
|
+
If ``e`` is ``None``, return the current engine.
|
|
1575
|
+
|
|
1576
|
+
If using the XeLaTeX engine, it will almost always be necessary
|
|
1577
|
+
to set the proper preamble with :func:`extra_preamble` or
|
|
1578
|
+
:func:`add_to_preamble`. For example::
|
|
1579
|
+
|
|
1580
|
+
latex.extra_preamble(r'''\usepackage{fontspec,xunicode,xltxtra}
|
|
1581
|
+
\setmainfont[Mapping=tex-text]{some font here}
|
|
1582
|
+
\setmonofont[Mapping=tex-text]{another font here}''')
|
|
1583
|
+
|
|
1584
|
+
EXAMPLES::
|
|
1585
|
+
|
|
1586
|
+
sage: latex.engine() # random
|
|
1587
|
+
'lualatex'
|
|
1588
|
+
sage: latex.engine("latex")
|
|
1589
|
+
sage: latex.engine()
|
|
1590
|
+
'latex'
|
|
1591
|
+
sage: latex.engine("pdflatex")
|
|
1592
|
+
sage: latex.engine()
|
|
1593
|
+
'pdflatex'
|
|
1594
|
+
"""
|
|
1595
|
+
if e is None:
|
|
1596
|
+
e = _Latex_prefs._option["engine"]
|
|
1597
|
+
if e is None:
|
|
1598
|
+
return _default_engine()
|
|
1599
|
+
else:
|
|
1600
|
+
return e
|
|
1601
|
+
|
|
1602
|
+
if e not in ["latex", "pdflatex", "xelatex", "luatex"]:
|
|
1603
|
+
raise ValueError("%s is not a supported LaTeX engine. Use latex, pdflatex, xelatex, or lualatex" % e)
|
|
1604
|
+
|
|
1605
|
+
_Latex_prefs._option["engine"] = e
|
|
1606
|
+
|
|
1607
|
+
|
|
1608
|
+
# Note: latex used to be a separate function, which by default was
|
|
1609
|
+
# only loaded in command-line mode: in the old notebook,
|
|
1610
|
+
# latex was defined by 'latex = Latex(density=130)'.
|
|
1611
|
+
# Meanwhile, the __call__ method for Latex used to call the latex
|
|
1612
|
+
# function. This has been changed around so that the contents of the
|
|
1613
|
+
# old latex function are now in Latex.__call__; thus the following
|
|
1614
|
+
# assignment.
|
|
1615
|
+
latex = Latex()
|
|
1616
|
+
# Ensure that latex appear in the sphinx doc as a function
|
|
1617
|
+
# so that the link :func:`latex` is correctly set up.
|
|
1618
|
+
latex.__doc__ = Latex.__call__.__doc__
|
|
1619
|
+
|
|
1620
|
+
|
|
1621
|
+
def _latex_file_(objects, title='SAGE', debug=False,
|
|
1622
|
+
sep='', tiny=False, math_left='\\[',
|
|
1623
|
+
math_right='\\]',
|
|
1624
|
+
extra_preamble=''):
|
|
1625
|
+
r"""nodetex
|
|
1626
|
+
Produce a string to be used as a LaTeX file, containing a
|
|
1627
|
+
representation of each object in objects.
|
|
1628
|
+
|
|
1629
|
+
INPUT:
|
|
1630
|
+
|
|
1631
|
+
- ``objects`` -- list (or object)
|
|
1632
|
+
|
|
1633
|
+
- ``title`` -- string (default: ``'Sage'``); title for the document
|
|
1634
|
+
|
|
1635
|
+
- ``math_left`` -- string (default: ``'\\['``); left delimiter for math mode
|
|
1636
|
+
|
|
1637
|
+
- ``math_right`` -- string (default: ``'\\]'``); right delimiter for math mode
|
|
1638
|
+
|
|
1639
|
+
- ``debug`` -- boolean (default: ``False``); print verbose output
|
|
1640
|
+
|
|
1641
|
+
- ``sep`` -- string (default: ``''``); separator between math objects
|
|
1642
|
+
|
|
1643
|
+
- ``tiny`` -- boolean (default: ``False``); use 'tiny' font
|
|
1644
|
+
|
|
1645
|
+
- ``extra_preamble`` -- string (default: ``''``); extra LaTeX commands;
|
|
1646
|
+
inserted before ``'\\begin{document}'``
|
|
1647
|
+
|
|
1648
|
+
This creates a string intended to be a LaTeX file containing the
|
|
1649
|
+
LaTeX representations of objects. It contains the following:
|
|
1650
|
+
|
|
1651
|
+
- a header (with documentclass and usepackage commands)
|
|
1652
|
+
|
|
1653
|
+
- ``extra_preamble``
|
|
1654
|
+
|
|
1655
|
+
- the title (centered)
|
|
1656
|
+
|
|
1657
|
+
- a size specification if ``tiny`` is ``True``
|
|
1658
|
+
|
|
1659
|
+
- LaTeX representation of the first element of ``objects``,
|
|
1660
|
+
surrounded by ``math_left`` and ``math_right``
|
|
1661
|
+
|
|
1662
|
+
Then if ``objects`` contains more than one element, for each
|
|
1663
|
+
remaining element:
|
|
1664
|
+
|
|
1665
|
+
- the string ``sep``; you can use this, for example, to add
|
|
1666
|
+
vertical space between objects with ``sep='\\vspace{15mm}'``,
|
|
1667
|
+
or to add a horizontal line between objects with
|
|
1668
|
+
``sep='\\hrule'``, or to insert a page break between objects
|
|
1669
|
+
with ``sep='\\newpage'``.
|
|
1670
|
+
|
|
1671
|
+
- the LaTeX representation of the element
|
|
1672
|
+
|
|
1673
|
+
The string ends with ``'\\end{document}'``.
|
|
1674
|
+
|
|
1675
|
+
EXAMPLES::
|
|
1676
|
+
|
|
1677
|
+
sage: from sage.misc.latex import _latex_file_
|
|
1678
|
+
sage: _latex_file_(3, title="The number three")
|
|
1679
|
+
'\\documentclass{article}...\\begin{document}\n\\begin{center}{\\Large\\bf The number three}\\end{center}\n\\vspace{40mm}\\[3\\]\n\\end{document}'
|
|
1680
|
+
sage: _latex_file_([7, 8, 9], title="Why was six afraid of seven?", sep='\\vfill\\hrule\\vfill')
|
|
1681
|
+
'\\documentclass{article}...\\begin{document}\n\\begin{center}{\\Large\\bf Why was six afraid of seven?}\\end{center}\n\\vspace{40mm}\\[7\\]\n\n\\vfill\\hrule\\vfill\n\n\\[8\\]\n\n\\vfill\\hrule\\vfill\n\n\\[9\\]\n\\end{document}'
|
|
1682
|
+
|
|
1683
|
+
TESTS:
|
|
1684
|
+
|
|
1685
|
+
This makes sure that latex is called only once on an object::
|
|
1686
|
+
|
|
1687
|
+
sage: class blah():
|
|
1688
|
+
....: def _latex_(x):
|
|
1689
|
+
....: print("coucou")
|
|
1690
|
+
....: return "x"
|
|
1691
|
+
sage: latex(blah())
|
|
1692
|
+
coucou
|
|
1693
|
+
x
|
|
1694
|
+
sage: s = sage.misc.latex._latex_file_(blah())
|
|
1695
|
+
coucou
|
|
1696
|
+
"""
|
|
1697
|
+
process = True
|
|
1698
|
+
if has_latex_attr(objects):
|
|
1699
|
+
objects = [objects]
|
|
1700
|
+
|
|
1701
|
+
if not isinstance(objects, list):
|
|
1702
|
+
objects = [objects]
|
|
1703
|
+
|
|
1704
|
+
if tiny:
|
|
1705
|
+
size = '\\tiny\n'
|
|
1706
|
+
else:
|
|
1707
|
+
size = ''
|
|
1708
|
+
|
|
1709
|
+
formatted_title = "\n\\begin{center}{\\Large\\bf %s}\\end{center}\n" % str(title) if title else ""
|
|
1710
|
+
s = '%s\n\\begin{document}%s%s' % (extra_preamble, formatted_title, size)
|
|
1711
|
+
|
|
1712
|
+
if title:
|
|
1713
|
+
s += '\\vspace{40mm}'
|
|
1714
|
+
if process:
|
|
1715
|
+
for i in range(len(objects)):
|
|
1716
|
+
x = objects[i]
|
|
1717
|
+
L = latex(x)
|
|
1718
|
+
if '\\begin{pgfpicture}' in L:
|
|
1719
|
+
# Resize the pgf figure to the text width if larger.
|
|
1720
|
+
s += r'\begingroup\makeatletter\@ifundefined{pgffigure}{\newsavebox{\pgffigure}}{}\makeatother\endgroup'
|
|
1721
|
+
s += r'\begin{lrbox}{\pgffigure}' + '\n'
|
|
1722
|
+
s += '%s' % L
|
|
1723
|
+
s += r'\end{lrbox}'
|
|
1724
|
+
s += r'\resizebox{\ifdim\width>\textwidth\textwidth\else\width\fi}{!}{\usebox{\pgffigure}}' + '\n'
|
|
1725
|
+
elif '\\begin{verbatim}' not in L:
|
|
1726
|
+
s += '%s%s%s' % (math_left, L, math_right)
|
|
1727
|
+
else:
|
|
1728
|
+
s += '%s' % L
|
|
1729
|
+
if i < len(objects) - 1:
|
|
1730
|
+
s += '\n\n%s\n\n' % sep
|
|
1731
|
+
else:
|
|
1732
|
+
s += "\n\n".join(str(x) for x in objects)
|
|
1733
|
+
|
|
1734
|
+
# latex_extra_preamble() is called here and not before because some objects
|
|
1735
|
+
# may require additional packages to be displayed in LaTeX. Hence, the call
|
|
1736
|
+
# to latex(x) in the previous loop may change the result of
|
|
1737
|
+
# latex_extra_preamble()
|
|
1738
|
+
MACROS = latex_extra_preamble()
|
|
1739
|
+
s = LATEX_HEADER + '\n' + MACROS + s + '\n\\end{document}'
|
|
1740
|
+
|
|
1741
|
+
if debug:
|
|
1742
|
+
print('----')
|
|
1743
|
+
print(s)
|
|
1744
|
+
print('----')
|
|
1745
|
+
|
|
1746
|
+
return s
|
|
1747
|
+
|
|
1748
|
+
|
|
1749
|
+
def view(objects, title='Sage', debug=False, sep='', tiny=False,
|
|
1750
|
+
engine=None, viewer=None, tightpage=True, margin=None,
|
|
1751
|
+
mode='inline', combine_all=False, **kwds):
|
|
1752
|
+
r"""nodetex
|
|
1753
|
+
Compute a latex representation of each object in objects, compile,
|
|
1754
|
+
and display typeset. If used from the command line, this requires
|
|
1755
|
+
that latex be installed.
|
|
1756
|
+
|
|
1757
|
+
INPUT:
|
|
1758
|
+
|
|
1759
|
+
- ``objects`` -- list (or object)
|
|
1760
|
+
|
|
1761
|
+
- ``title`` -- string (default: ``'Sage'``); title for the
|
|
1762
|
+
document
|
|
1763
|
+
|
|
1764
|
+
- ``debug`` -- boolean (default: ``False``); print verbose
|
|
1765
|
+
output
|
|
1766
|
+
|
|
1767
|
+
- ``sep`` -- string (default: ``''``); separator between
|
|
1768
|
+
math objects
|
|
1769
|
+
|
|
1770
|
+
- ``tiny`` -- boolean (default: ``False``); use tiny font
|
|
1771
|
+
|
|
1772
|
+
- ``engine`` -- string or ``None`` (default: ``None``); can take the
|
|
1773
|
+
following values:
|
|
1774
|
+
|
|
1775
|
+
- ``None`` -- the value defined in the LaTeX global preferences
|
|
1776
|
+
``latex.engine()`` is used
|
|
1777
|
+
|
|
1778
|
+
- ``'pdflatex'`` -- compilation does ``tex`` -> ``pdf``
|
|
1779
|
+
|
|
1780
|
+
- ``'xelatex'`` -- compilation does ``tex`` -> ``pdf``
|
|
1781
|
+
|
|
1782
|
+
- ``'lualatex'`` -- compilation does ``tex`` -> ``pdf``
|
|
1783
|
+
|
|
1784
|
+
- ``'latex'`` -- compilation first tries ``tex`` -> ``dvi`` -> ``png`` and if an
|
|
1785
|
+
error occurs then tries ``dvi`` -> ``ps`` -> ``pdf``. This is slower than
|
|
1786
|
+
``'pdflatex'`` and known to be broken when overfull hboxes are detected.
|
|
1787
|
+
|
|
1788
|
+
- ``viewer`` -- string or ``None`` (default: ``None``); specify a viewer
|
|
1789
|
+
to use; currently the only options are ``None`` and ``'pdf'``
|
|
1790
|
+
|
|
1791
|
+
- ``tightpage`` -- boolean (default: ``True``); use the LaTeX package
|
|
1792
|
+
``preview`` with the 'tightpage' option
|
|
1793
|
+
|
|
1794
|
+
- ``margin`` -- float or ``None`` (default: ``None``); adds a margin
|
|
1795
|
+
of ``margin`` mm. Has no affect if the option ``tightpage`` is ``False``.
|
|
1796
|
+
|
|
1797
|
+
- ``mode`` -- string (default: ``'inline'``); ``'display'`` for
|
|
1798
|
+
displaymath or ``'inline'`` for inline math
|
|
1799
|
+
|
|
1800
|
+
- ``combine_all`` -- boolean (default: ``False``); if ``combine_all`` is
|
|
1801
|
+
``True`` and the input is a tuple, then it does not return a tuple and
|
|
1802
|
+
instead returns a string with all the elements separated by a single
|
|
1803
|
+
space
|
|
1804
|
+
|
|
1805
|
+
OUTPUT: display typeset objects
|
|
1806
|
+
|
|
1807
|
+
The output is displayed in a separate viewer displaying a dvi (or pdf)
|
|
1808
|
+
file, with the following: the title string is printed, centered, at the
|
|
1809
|
+
top. Beneath that, each object in ``objects`` is typeset on its own line,
|
|
1810
|
+
with the string ``sep`` inserted between these lines.
|
|
1811
|
+
|
|
1812
|
+
The value of ``sep`` is inserted between each element of the list
|
|
1813
|
+
``objects``; you can, for example, add vertical space between
|
|
1814
|
+
objects with ``sep='\\vspace{15mm}'``, while ``sep='\\hrule'``
|
|
1815
|
+
adds a horizontal line between objects, and ``sep='\\newpage'``
|
|
1816
|
+
inserts a page break between objects.
|
|
1817
|
+
|
|
1818
|
+
If the ``engine`` is either ``'pdflatex'``, ``'xelatex'``, or ``'lualatex'``,
|
|
1819
|
+
it produces a pdf file. Otherwise, it produces a dvi file, and if the program
|
|
1820
|
+
``dvipng`` is installed, it checks the dvi file by trying to convert it to a
|
|
1821
|
+
png file. If this conversion fails, the dvi file probably contains some
|
|
1822
|
+
postscript special commands or it has other issues which might make
|
|
1823
|
+
displaying it a problem; in this case, the file is converted to a pdf file,
|
|
1824
|
+
which is then displayed.
|
|
1825
|
+
|
|
1826
|
+
Setting ``viewer`` to ``'pdf'`` forces the use of a separate
|
|
1827
|
+
viewer, even in notebook mode. This also sets the latex engine to be
|
|
1828
|
+
``pdflatex`` if the current engine is ``latex``.
|
|
1829
|
+
|
|
1830
|
+
Setting the option ``tightpage`` to ``True`` (this is the default setting)
|
|
1831
|
+
tells LaTeX to use the package 'preview' with the 'tightpage' option.
|
|
1832
|
+
Then, each object is typeset in its own page, and that page is cropped to
|
|
1833
|
+
exactly the size of the object. This is typically useful for very
|
|
1834
|
+
large pictures (like graphs) generated with tikz. This only works
|
|
1835
|
+
when using a separate viewer. Note that the object are currently
|
|
1836
|
+
typeset in plain math mode rather than displaymath, because the
|
|
1837
|
+
latter imposes a limit on the width of the picture. Technically,
|
|
1838
|
+
``tightpage`` adds ::
|
|
1839
|
+
|
|
1840
|
+
\\usepackage[tightpage,active]{preview}
|
|
1841
|
+
\\PreviewEnvironment{page}
|
|
1842
|
+
|
|
1843
|
+
to the LaTeX preamble, and replaces the ``\\[`` and ``\\]`` around
|
|
1844
|
+
each object by ``\\begin{page}$`` and ``$\\end{page}``.
|
|
1845
|
+
Setting ``tightpage`` to ``False`` turns off this behavior and provides
|
|
1846
|
+
the latex output as a full page. If ``tightpage`` is set to ``True``,
|
|
1847
|
+
the ``Title`` is ignored.
|
|
1848
|
+
|
|
1849
|
+
TESTS::
|
|
1850
|
+
|
|
1851
|
+
sage: from sage.misc.latex import _run_latex_, _latex_file_
|
|
1852
|
+
sage: from tempfile import NamedTemporaryFile
|
|
1853
|
+
sage: g = sage.misc.latex.latex_examples.graph()
|
|
1854
|
+
sage: latex.add_to_preamble(r"\usepackage{tkz-graph}") # optional - latex_package_tkz_graph
|
|
1855
|
+
sage: with NamedTemporaryFile(mode='w+t', suffix='.tex') as f: # optional - latex latex_package_tkz_graph
|
|
1856
|
+
....: _ = f.write(_latex_file_(g))
|
|
1857
|
+
....: f.flush()
|
|
1858
|
+
....: _run_latex_(f.name, engine='pdflatex')
|
|
1859
|
+
'pdf'
|
|
1860
|
+
|
|
1861
|
+
sage: view(4, margin=5, debug=True) # not tested
|
|
1862
|
+
\documentclass{article}
|
|
1863
|
+
...
|
|
1864
|
+
\usepackage[tightpage,active]{preview}
|
|
1865
|
+
\PreviewEnvironment{page}
|
|
1866
|
+
\setlength\PreviewBorder{5.000000mm}
|
|
1867
|
+
\begin{document}\begin{page}$4$\end{page}
|
|
1868
|
+
\end{document}
|
|
1869
|
+
...
|
|
1870
|
+
|
|
1871
|
+
sage: view(4, debug=True) # not tested
|
|
1872
|
+
\documentclass{article}
|
|
1873
|
+
...
|
|
1874
|
+
\usepackage[tightpage,active]{preview}
|
|
1875
|
+
\PreviewEnvironment{page}
|
|
1876
|
+
\begin{document}\begin{page}$4$\end{page}
|
|
1877
|
+
\end{document}
|
|
1878
|
+
...
|
|
1879
|
+
|
|
1880
|
+
|
|
1881
|
+
sage: latex.extra_preamble('') # reset the preamble
|
|
1882
|
+
|
|
1883
|
+
sage: view(4, engine='garbage')
|
|
1884
|
+
Traceback (most recent call last):
|
|
1885
|
+
...
|
|
1886
|
+
ValueError: Unsupported LaTeX engine.
|
|
1887
|
+
"""
|
|
1888
|
+
if tightpage:
|
|
1889
|
+
if margin is None:
|
|
1890
|
+
margin_str = ""
|
|
1891
|
+
else:
|
|
1892
|
+
margin_str = '\n\\setlength\\PreviewBorder{%fmm}' % margin
|
|
1893
|
+
latex_options = {'extra_preamble':
|
|
1894
|
+
'\\usepackage[tightpage,active]{preview}\n' +
|
|
1895
|
+
'\\PreviewEnvironment{page}%s' % margin_str,
|
|
1896
|
+
'math_left': '\\begin{page}$',
|
|
1897
|
+
'math_right': '$\\end{page}'}
|
|
1898
|
+
title = None
|
|
1899
|
+
else:
|
|
1900
|
+
latex_options = {}
|
|
1901
|
+
|
|
1902
|
+
s = _latex_file_(objects, title=title, sep=sep, tiny=tiny, debug=debug, **latex_options)
|
|
1903
|
+
if engine is None:
|
|
1904
|
+
engine = _Latex_prefs._option["engine"]
|
|
1905
|
+
if engine is None:
|
|
1906
|
+
engine = _default_engine()
|
|
1907
|
+
|
|
1908
|
+
if viewer == "pdf" and engine == "latex":
|
|
1909
|
+
engine = "pdflatex"
|
|
1910
|
+
# command line or notebook with viewer
|
|
1911
|
+
|
|
1912
|
+
# We can't automatically delete the temporary file in this case
|
|
1913
|
+
# because we need it to live at least long enough for the viewer
|
|
1914
|
+
# to open it. Since we're launching the viewer asynchronously,
|
|
1915
|
+
# that would be tricky.
|
|
1916
|
+
tmp = TemporaryDirectory()
|
|
1917
|
+
|
|
1918
|
+
tex_file = os.path.join(tmp.name, "sage.tex")
|
|
1919
|
+
with open(tex_file, 'w') as file:
|
|
1920
|
+
file.write(s)
|
|
1921
|
+
suffix = _run_latex_(tex_file, debug=debug, engine=engine, png=False)
|
|
1922
|
+
if suffix == "pdf":
|
|
1923
|
+
from sage.misc.viewer import pdf_viewer
|
|
1924
|
+
viewer = pdf_viewer()
|
|
1925
|
+
elif suffix == "dvi":
|
|
1926
|
+
from sage.misc.viewer import dvi_viewer
|
|
1927
|
+
viewer = dvi_viewer()
|
|
1928
|
+
else:
|
|
1929
|
+
print("Latex error")
|
|
1930
|
+
tmp.cleanup()
|
|
1931
|
+
return
|
|
1932
|
+
output_file = os.path.join(tmp.name, "sage." + suffix)
|
|
1933
|
+
|
|
1934
|
+
if debug:
|
|
1935
|
+
print(f'temporary file: "{output_file}"')
|
|
1936
|
+
print(f'viewer: "{viewer}"')
|
|
1937
|
+
|
|
1938
|
+
# Return immediately but only clean up the temporary file after
|
|
1939
|
+
# the viewer has closed. This function is synchronous and waits
|
|
1940
|
+
# for the process to complete...
|
|
1941
|
+
def run_viewer():
|
|
1942
|
+
run([*viewer.split(), output_file], capture_output=True)
|
|
1943
|
+
tmp.cleanup()
|
|
1944
|
+
|
|
1945
|
+
# ...but we execute it asynchronously so that view() completes
|
|
1946
|
+
# immediately. The "daemon" flag is important because, without it,
|
|
1947
|
+
# sage won't quit until the viewer does.
|
|
1948
|
+
from threading import Thread
|
|
1949
|
+
t = Thread(target=run_viewer)
|
|
1950
|
+
t.daemon = True
|
|
1951
|
+
t.start()
|
|
1952
|
+
|
|
1953
|
+
|
|
1954
|
+
def pdf(x, filename, tiny=False, tightpage=True, margin=None, engine=None, debug=False):
|
|
1955
|
+
"""
|
|
1956
|
+
Create an image from the latex representation of ``x`` and save it as a pdf
|
|
1957
|
+
file with the given filename.
|
|
1958
|
+
|
|
1959
|
+
INPUT:
|
|
1960
|
+
|
|
1961
|
+
- ``x`` -- a Sage object
|
|
1962
|
+
|
|
1963
|
+
- ``filename`` -- the filename with which to save the image
|
|
1964
|
+
|
|
1965
|
+
- ``tiny`` -- boolean (default: ``False``); if ``True``, use a tiny font
|
|
1966
|
+
|
|
1967
|
+
- ``tightpage`` -- boolean (default: ``True``); use the LaTeX package
|
|
1968
|
+
``preview`` with the 'tightpage' option
|
|
1969
|
+
|
|
1970
|
+
- ``margin`` -- float (default: no margin); width of border, only effective
|
|
1971
|
+
with 'tight page'
|
|
1972
|
+
|
|
1973
|
+
- ``engine`` -- (default: ``None``) ``'latex'``, ``'pdflatex'``,
|
|
1974
|
+
``'xelatex'`` or ``'lualatex'``; if ``None``, the value defined in the
|
|
1975
|
+
LaTeX global preferences ``latex.engine()`` is used
|
|
1976
|
+
|
|
1977
|
+
- ``debug`` -- boolean (default: ``False``); if ``True``, print verbose output
|
|
1978
|
+
|
|
1979
|
+
EXAMPLES::
|
|
1980
|
+
|
|
1981
|
+
sage: # optional - latex
|
|
1982
|
+
sage: from sage.misc.latex import pdf
|
|
1983
|
+
sage: import tempfile
|
|
1984
|
+
sage: with tempfile.NamedTemporaryFile(suffix=".pdf") as f: # random
|
|
1985
|
+
....: pdf(ZZ[x], f.name)
|
|
1986
|
+
"""
|
|
1987
|
+
from sage.plot.graphics import Graphics
|
|
1988
|
+
if isinstance(x, Graphics):
|
|
1989
|
+
x.save(filename)
|
|
1990
|
+
return
|
|
1991
|
+
|
|
1992
|
+
if tightpage:
|
|
1993
|
+
if margin is None:
|
|
1994
|
+
margin_str = ""
|
|
1995
|
+
else:
|
|
1996
|
+
margin_str = '\n\\setlength\\PreviewBorder{%fmm}' % margin
|
|
1997
|
+
latex_options = {'extra_preamble':
|
|
1998
|
+
'\\usepackage[tightpage,active]{preview}\n' +
|
|
1999
|
+
'\\PreviewEnvironment{page}%s' % margin_str,
|
|
2000
|
+
'math_left': '\\begin{page}$',
|
|
2001
|
+
'math_right': '$\\end{page}'}
|
|
2002
|
+
else:
|
|
2003
|
+
latex_options = {}
|
|
2004
|
+
|
|
2005
|
+
# create a string of latex code to write in a file
|
|
2006
|
+
s = _latex_file_([x], title='', tiny=tiny, debug=debug, **latex_options)
|
|
2007
|
+
if engine is None:
|
|
2008
|
+
engine = _Latex_prefs._option["engine"]
|
|
2009
|
+
if engine is None:
|
|
2010
|
+
engine = _default_engine()
|
|
2011
|
+
|
|
2012
|
+
# path name for permanent pdf output
|
|
2013
|
+
abs_path_to_pdf = os.path.abspath(filename)
|
|
2014
|
+
# temporary directory to store stuff
|
|
2015
|
+
with TemporaryDirectory() as tmp:
|
|
2016
|
+
tex_file = os.path.join(tmp, "sage.tex")
|
|
2017
|
+
pdf_file = os.path.join(tmp, "sage.pdf")
|
|
2018
|
+
# write latex string to file
|
|
2019
|
+
with open(tex_file, 'w') as file:
|
|
2020
|
+
file.write(s)
|
|
2021
|
+
# run latex on the file
|
|
2022
|
+
e = _run_latex_(tex_file, debug=debug, engine=engine)
|
|
2023
|
+
if e == 'pdf':
|
|
2024
|
+
# if no errors, copy pdf_file to the appropriate place
|
|
2025
|
+
shutil.copy(pdf_file, abs_path_to_pdf)
|
|
2026
|
+
else:
|
|
2027
|
+
print("Latex error or no pdf was generated.")
|
|
2028
|
+
|
|
2029
|
+
|
|
2030
|
+
def png(x, filename, density=150, debug=False,
|
|
2031
|
+
do_in_background=False, tiny=False, engine=None):
|
|
2032
|
+
"""
|
|
2033
|
+
Create a png image representation of ``x`` and save to the given
|
|
2034
|
+
filename.
|
|
2035
|
+
|
|
2036
|
+
INPUT:
|
|
2037
|
+
|
|
2038
|
+
- ``x`` -- object to be displayed
|
|
2039
|
+
|
|
2040
|
+
- ``filename`` -- file in which to save the image
|
|
2041
|
+
|
|
2042
|
+
- ``density`` -- integer (default: 150)
|
|
2043
|
+
|
|
2044
|
+
- ``debug`` -- boolean (default: ``False``); print verbose output
|
|
2045
|
+
|
|
2046
|
+
- ``do_in_background`` -- boolean (default: ``False``); unused, kept for
|
|
2047
|
+
backwards compatibility
|
|
2048
|
+
|
|
2049
|
+
- ``tiny`` -- boolean (default: ``False``); use tiny font
|
|
2050
|
+
|
|
2051
|
+
- ``engine`` -- (default: ``None``) ``'latex'``, ``'pdflatex'``,
|
|
2052
|
+
``'xelatex'`` or ``'lualatex'``
|
|
2053
|
+
|
|
2054
|
+
EXAMPLES::
|
|
2055
|
+
|
|
2056
|
+
sage: # optional - imagemagick latex, needs sage.plot
|
|
2057
|
+
sage: from sage.misc.latex import png
|
|
2058
|
+
sage: import tempfile
|
|
2059
|
+
sage: with tempfile.NamedTemporaryFile(suffix='.png') as f: # random
|
|
2060
|
+
....: png(ZZ[x], f.name)
|
|
2061
|
+
"""
|
|
2062
|
+
import sage.plot.all
|
|
2063
|
+
if isinstance(x, sage.plot.graphics.Graphics):
|
|
2064
|
+
x.save(filename)
|
|
2065
|
+
return
|
|
2066
|
+
# if not graphics: create a string of latex code to write in a file
|
|
2067
|
+
s = _latex_file_([x], math_left='$\\displaystyle', math_right='$', title='',
|
|
2068
|
+
debug=debug, tiny=tiny,
|
|
2069
|
+
extra_preamble='\\textheight=2\\textheight')
|
|
2070
|
+
if engine is None:
|
|
2071
|
+
engine = _Latex_prefs._option["engine"]
|
|
2072
|
+
if engine is None:
|
|
2073
|
+
engine = _default_engine()
|
|
2074
|
+
|
|
2075
|
+
# path name for permanent png output
|
|
2076
|
+
abs_path_to_png = os.path.abspath(filename)
|
|
2077
|
+
# temporary directory to store stuff
|
|
2078
|
+
with TemporaryDirectory() as tmp:
|
|
2079
|
+
tex_file = os.path.join(tmp, "sage.tex")
|
|
2080
|
+
png_file = os.path.join(tmp, "sage.png")
|
|
2081
|
+
# write latex string to file
|
|
2082
|
+
with open(tex_file, 'w') as file:
|
|
2083
|
+
file.write(s)
|
|
2084
|
+
# run latex on the file, producing png output to png_file
|
|
2085
|
+
e = _run_latex_(tex_file, density=density, debug=debug,
|
|
2086
|
+
png=True, engine=engine)
|
|
2087
|
+
if e.find("Error") == -1:
|
|
2088
|
+
# if no errors, copy png_file to the appropriate place
|
|
2089
|
+
shutil.copy(png_file, abs_path_to_png)
|
|
2090
|
+
else:
|
|
2091
|
+
print("Latex error")
|
|
2092
|
+
|
|
2093
|
+
if debug:
|
|
2094
|
+
return s
|
|
2095
|
+
return
|
|
2096
|
+
|
|
2097
|
+
|
|
2098
|
+
def coeff_repr(c):
|
|
2099
|
+
r"""
|
|
2100
|
+
LaTeX string representing coefficients in a linear combination.
|
|
2101
|
+
|
|
2102
|
+
INPUT:
|
|
2103
|
+
|
|
2104
|
+
- ``c`` -- a coefficient (i.e., an element of a ring)
|
|
2105
|
+
|
|
2106
|
+
OUTPUT: string
|
|
2107
|
+
|
|
2108
|
+
EXAMPLES::
|
|
2109
|
+
|
|
2110
|
+
sage: from sage.misc.latex import coeff_repr
|
|
2111
|
+
sage: coeff_repr(QQ(1/2))
|
|
2112
|
+
'\\frac{1}{2}'
|
|
2113
|
+
sage: coeff_repr(-x^2) # needs sage.symbolic
|
|
2114
|
+
'\\left(-x^{2}\\right)'
|
|
2115
|
+
"""
|
|
2116
|
+
try:
|
|
2117
|
+
return c._latex_coeff_repr()
|
|
2118
|
+
except AttributeError:
|
|
2119
|
+
pass
|
|
2120
|
+
if isinstance(c, (int, float)):
|
|
2121
|
+
return str(c)
|
|
2122
|
+
s = latex(c)
|
|
2123
|
+
if s.find("+") != -1 or s.find("-") != -1:
|
|
2124
|
+
return "(%s)" % s
|
|
2125
|
+
return s
|
|
2126
|
+
|
|
2127
|
+
|
|
2128
|
+
def repr_lincomb(symbols, coeffs):
|
|
2129
|
+
r"""
|
|
2130
|
+
Compute a latex representation of a linear combination of some
|
|
2131
|
+
formal symbols.
|
|
2132
|
+
|
|
2133
|
+
INPUT:
|
|
2134
|
+
|
|
2135
|
+
- ``symbols`` -- list of symbols
|
|
2136
|
+
|
|
2137
|
+
- ``coeffs`` -- list of coefficients of the symbols
|
|
2138
|
+
|
|
2139
|
+
OUTPUT: string
|
|
2140
|
+
|
|
2141
|
+
EXAMPLES::
|
|
2142
|
+
|
|
2143
|
+
sage: t = PolynomialRing(QQ, 't').0
|
|
2144
|
+
sage: from sage.misc.latex import repr_lincomb
|
|
2145
|
+
sage: repr_lincomb(['a', 's', ''], [-t, t - 2, t^12 + 2])
|
|
2146
|
+
'-t\\text{\\texttt{a}} + \\left(t - 2\\right)\\text{\\texttt{s}} + \\left(t^{12} + 2\\right)'
|
|
2147
|
+
sage: repr_lincomb(['a', 'b'], [1,1])
|
|
2148
|
+
'\\text{\\texttt{a}} + \\text{\\texttt{b}}'
|
|
2149
|
+
|
|
2150
|
+
Verify that a certain corner case works (see :issue:`5707` and
|
|
2151
|
+
:issue:`5766`)::
|
|
2152
|
+
|
|
2153
|
+
sage: repr_lincomb([1,5,-3],[2,8/9,7])
|
|
2154
|
+
'2\\cdot 1 + \\frac{8}{9}\\cdot 5 + 7\\cdot -3'
|
|
2155
|
+
|
|
2156
|
+
Verify that :issue:`17299` (latex representation of modular symbols)
|
|
2157
|
+
is fixed::
|
|
2158
|
+
|
|
2159
|
+
sage: x = EllipticCurve('64a1').modular_symbol_space(sign=1).basis()[0] # needs database_cremona_mini_ellcurve sage.schemes
|
|
2160
|
+
sage: from sage.misc.latex import repr_lincomb
|
|
2161
|
+
sage: latex(x.modular_symbol_rep()) # needs database_cremona_mini_ellcurve sage.schemes
|
|
2162
|
+
\left\{\frac{-3}{11}, \frac{-1}{4}\right\} - \left\{\frac{3}{13}, \frac{1}{4}\right\}
|
|
2163
|
+
|
|
2164
|
+
Verify that it works when the symbols are numbers::
|
|
2165
|
+
|
|
2166
|
+
sage: x = FormalSum([(1,2),(3,4)])
|
|
2167
|
+
sage: latex(x)
|
|
2168
|
+
2 + 3\cdot 4
|
|
2169
|
+
|
|
2170
|
+
Verify that it works when ``bv in CC`` raises an error::
|
|
2171
|
+
|
|
2172
|
+
sage: x = FormalSum([(1,'x'),(2,'y')])
|
|
2173
|
+
sage: latex(x)
|
|
2174
|
+
\text{\texttt{x}} + 2\text{\texttt{y}}
|
|
2175
|
+
"""
|
|
2176
|
+
s = ""
|
|
2177
|
+
first = True
|
|
2178
|
+
i = 0
|
|
2179
|
+
|
|
2180
|
+
from sage.rings.cc import CC
|
|
2181
|
+
|
|
2182
|
+
for c in coeffs:
|
|
2183
|
+
bv = symbols[i]
|
|
2184
|
+
b = latex(bv)
|
|
2185
|
+
if c != 0:
|
|
2186
|
+
if c == 1:
|
|
2187
|
+
if first:
|
|
2188
|
+
s += b
|
|
2189
|
+
else:
|
|
2190
|
+
s += " + %s" % b
|
|
2191
|
+
else:
|
|
2192
|
+
coeff = coeff_repr(c)
|
|
2193
|
+
if coeff == "-1":
|
|
2194
|
+
coeff = "-"
|
|
2195
|
+
if first:
|
|
2196
|
+
coeff = str(coeff)
|
|
2197
|
+
else:
|
|
2198
|
+
coeff = " + %s" % coeff
|
|
2199
|
+
# this is a hack: i want to say that if the symbol
|
|
2200
|
+
# happens to be a number, then we should put a
|
|
2201
|
+
# multiplication sign in
|
|
2202
|
+
try:
|
|
2203
|
+
if bv in CC:
|
|
2204
|
+
s += r"%s\cdot %s" % (coeff, b)
|
|
2205
|
+
else:
|
|
2206
|
+
s += "%s%s" % (coeff, b)
|
|
2207
|
+
except Exception:
|
|
2208
|
+
s += "%s%s" % (coeff, b)
|
|
2209
|
+
first = False
|
|
2210
|
+
i += 1
|
|
2211
|
+
if first:
|
|
2212
|
+
s = "0"
|
|
2213
|
+
s = s.replace("+ -", "- ")
|
|
2214
|
+
return s
|
|
2215
|
+
|
|
2216
|
+
|
|
2217
|
+
common_varnames = ['alpha',
|
|
2218
|
+
'beta',
|
|
2219
|
+
'gamma',
|
|
2220
|
+
'Gamma',
|
|
2221
|
+
'delta',
|
|
2222
|
+
'Delta',
|
|
2223
|
+
'epsilon',
|
|
2224
|
+
'zeta',
|
|
2225
|
+
'eta',
|
|
2226
|
+
'theta',
|
|
2227
|
+
'Theta',
|
|
2228
|
+
'iota',
|
|
2229
|
+
'kappa',
|
|
2230
|
+
'lambda',
|
|
2231
|
+
'Lambda',
|
|
2232
|
+
'mu',
|
|
2233
|
+
'nu',
|
|
2234
|
+
'xi',
|
|
2235
|
+
'Xi',
|
|
2236
|
+
'pi',
|
|
2237
|
+
'Pi',
|
|
2238
|
+
'rho',
|
|
2239
|
+
'sigma',
|
|
2240
|
+
'Sigma',
|
|
2241
|
+
'tau',
|
|
2242
|
+
'upsilon',
|
|
2243
|
+
'phi',
|
|
2244
|
+
'Phi',
|
|
2245
|
+
'varphi',
|
|
2246
|
+
'chi',
|
|
2247
|
+
'psi',
|
|
2248
|
+
'Psi',
|
|
2249
|
+
'omega',
|
|
2250
|
+
'Omega',
|
|
2251
|
+
'ast',
|
|
2252
|
+
'bullet',
|
|
2253
|
+
'circ',
|
|
2254
|
+
'times',
|
|
2255
|
+
'star']
|
|
2256
|
+
|
|
2257
|
+
|
|
2258
|
+
def latex_varify(a, is_fname=False):
|
|
2259
|
+
r"""
|
|
2260
|
+
Convert a string ``a`` to a LaTeX string: if it's an element of
|
|
2261
|
+
``common_varnames``, then prepend a backslash. If ``a`` consists
|
|
2262
|
+
of a single letter, then return it. Otherwise, return
|
|
2263
|
+
either "{\\rm a}" or "\\mbox{a}" if "is_fname" flag is ``True``
|
|
2264
|
+
or ``False``.
|
|
2265
|
+
|
|
2266
|
+
INPUT:
|
|
2267
|
+
|
|
2268
|
+
- ``a`` -- string
|
|
2269
|
+
|
|
2270
|
+
OUTPUT: string
|
|
2271
|
+
|
|
2272
|
+
EXAMPLES::
|
|
2273
|
+
|
|
2274
|
+
sage: from sage.misc.latex import latex_varify
|
|
2275
|
+
sage: latex_varify('w')
|
|
2276
|
+
'w'
|
|
2277
|
+
sage: latex_varify('aleph')
|
|
2278
|
+
'\\mathit{aleph}'
|
|
2279
|
+
sage: latex_varify('aleph', is_fname=True)
|
|
2280
|
+
'{\\rm aleph}'
|
|
2281
|
+
sage: latex_varify('alpha')
|
|
2282
|
+
'\\alpha'
|
|
2283
|
+
sage: latex_varify('ast')
|
|
2284
|
+
'\\ast'
|
|
2285
|
+
|
|
2286
|
+
TESTS:
|
|
2287
|
+
|
|
2288
|
+
sage: abc = var('abc') # needs sage.symbolic
|
|
2289
|
+
sage: latex((abc/(abc+1)+42)/(abc-1)) # trac #15870 # needs sage.symbolic
|
|
2290
|
+
\frac{\frac{\mathit{abc}}{\mathit{abc} + 1} + 42}{\mathit{abc} - 1}
|
|
2291
|
+
"""
|
|
2292
|
+
if a in common_varnames:
|
|
2293
|
+
return "\\" + a
|
|
2294
|
+
elif len(a) == 0:
|
|
2295
|
+
return ''
|
|
2296
|
+
elif len(a) == 1:
|
|
2297
|
+
return a
|
|
2298
|
+
elif is_fname is True:
|
|
2299
|
+
return '{\\rm %s}' % a
|
|
2300
|
+
else:
|
|
2301
|
+
return '\\mathit{%s}' % a
|
|
2302
|
+
|
|
2303
|
+
|
|
2304
|
+
def latex_variable_name(x, is_fname=False):
|
|
2305
|
+
r"""
|
|
2306
|
+
Return latex version of a variable name.
|
|
2307
|
+
|
|
2308
|
+
Here are some guiding principles for usage of this function:
|
|
2309
|
+
|
|
2310
|
+
1. If the variable is a single letter, that is the latex version.
|
|
2311
|
+
|
|
2312
|
+
2. If the variable name is suffixed by a number, we put the number
|
|
2313
|
+
in the subscript.
|
|
2314
|
+
|
|
2315
|
+
3. If the variable name contains an ``'_'`` we start the subscript at
|
|
2316
|
+
the underscore. Note that #3 trumps rule #2.
|
|
2317
|
+
|
|
2318
|
+
4. If a component of the variable is a Greek letter, escape it
|
|
2319
|
+
properly.
|
|
2320
|
+
|
|
2321
|
+
5. Recurse nicely with subscripts.
|
|
2322
|
+
|
|
2323
|
+
Refer to the examples section for how these rules might play out in
|
|
2324
|
+
practice.
|
|
2325
|
+
|
|
2326
|
+
EXAMPLES::
|
|
2327
|
+
|
|
2328
|
+
sage: from sage.misc.latex import latex_variable_name
|
|
2329
|
+
sage: latex_variable_name('a')
|
|
2330
|
+
'a'
|
|
2331
|
+
sage: latex_variable_name('abc')
|
|
2332
|
+
'\\mathit{abc}'
|
|
2333
|
+
sage: latex_variable_name('sigma')
|
|
2334
|
+
'\\sigma'
|
|
2335
|
+
sage: latex_variable_name('sigma_k')
|
|
2336
|
+
'\\sigma_{k}'
|
|
2337
|
+
sage: latex_variable_name('sigma389')
|
|
2338
|
+
'\\sigma_{389}'
|
|
2339
|
+
sage: latex_variable_name('beta_00')
|
|
2340
|
+
'\\beta_{00}'
|
|
2341
|
+
sage: latex_variable_name('Omega84')
|
|
2342
|
+
'\\Omega_{84}'
|
|
2343
|
+
sage: latex_variable_name('sigma_alpha')
|
|
2344
|
+
'\\sigma_{\\alpha}'
|
|
2345
|
+
sage: latex_variable_name('nothing1')
|
|
2346
|
+
'\\mathit{nothing}_{1}'
|
|
2347
|
+
sage: latex_variable_name('nothing1', is_fname=True)
|
|
2348
|
+
'{\\rm nothing}_{1}'
|
|
2349
|
+
sage: latex_variable_name('nothing_abc')
|
|
2350
|
+
'\\mathit{nothing}_{\\mathit{abc}}'
|
|
2351
|
+
sage: latex_variable_name('nothing_abc', is_fname=True)
|
|
2352
|
+
'{\\rm nothing}_{{\\rm abc}}'
|
|
2353
|
+
sage: latex_variable_name('alpha_beta_gamma12')
|
|
2354
|
+
'\\alpha_{\\beta_{\\gamma_{12}}}'
|
|
2355
|
+
sage: latex_variable_name('x_ast')
|
|
2356
|
+
'x_{\\ast}'
|
|
2357
|
+
|
|
2358
|
+
TESTS::
|
|
2359
|
+
|
|
2360
|
+
sage: latex_variable_name('_C') # trac #16007 # needs sage.symbolic
|
|
2361
|
+
'C'
|
|
2362
|
+
sage: latex_variable_name('_K1') # needs sage.symbolic
|
|
2363
|
+
'K_{1}'
|
|
2364
|
+
|
|
2365
|
+
sage: latex_variable_name('5') # needs sage.symbolic
|
|
2366
|
+
'5'
|
|
2367
|
+
"""
|
|
2368
|
+
# if x is an integer (it might be the case for padics), we return x
|
|
2369
|
+
if re.match(r'\d+$', x):
|
|
2370
|
+
return x
|
|
2371
|
+
underscore = x.find("_")
|
|
2372
|
+
if underscore == -1:
|
|
2373
|
+
# * The "\d|[.,]" means "decimal digit" or period or comma
|
|
2374
|
+
# * The "+" means "1 or more"
|
|
2375
|
+
# * The "$" means "at the end of the line"
|
|
2376
|
+
m = re.search(r'(\d|[.,])+$', x)
|
|
2377
|
+
if m is None:
|
|
2378
|
+
prefix = x
|
|
2379
|
+
suffix = None
|
|
2380
|
+
else:
|
|
2381
|
+
prefix = x[:m.start()]
|
|
2382
|
+
suffix = x[m.start():]
|
|
2383
|
+
else:
|
|
2384
|
+
prefix = x[:underscore]
|
|
2385
|
+
suffix = x[underscore + 1:]
|
|
2386
|
+
if prefix == '':
|
|
2387
|
+
from sage.calculus.calculus import symtable
|
|
2388
|
+
for sym in symtable.values():
|
|
2389
|
+
if sym[0] == '_' and sym[1:] == suffix:
|
|
2390
|
+
return latex_variable_name(suffix)
|
|
2391
|
+
if suffix and len(suffix):
|
|
2392
|
+
# handle the suffix specially because it very well might be numeric
|
|
2393
|
+
# I use strip to avoid using regex's -- It makes it a bit faster (and the code is more comprehensible to non-regex'ed people)
|
|
2394
|
+
if suffix.strip("1234567890") != "":
|
|
2395
|
+
suffix = latex_variable_name(suffix, is_fname) # recurse to deal with recursive subscripts
|
|
2396
|
+
return '%s_{%s}' % (latex_varify(prefix, is_fname), suffix)
|
|
2397
|
+
else:
|
|
2398
|
+
return latex_varify(prefix, is_fname)
|
|
2399
|
+
|
|
2400
|
+
|
|
2401
|
+
class LatexExamples:
|
|
2402
|
+
r"""
|
|
2403
|
+
A catalogue of Sage objects with complicated ``_latex_`` methods.
|
|
2404
|
+
Use these for testing :func:`latex`, :func:`view`, the Typeset
|
|
2405
|
+
button in the notebook, etc.
|
|
2406
|
+
|
|
2407
|
+
The classes here only have ``__init__``, ``_repr_``, and ``_latex_``
|
|
2408
|
+
methods.
|
|
2409
|
+
|
|
2410
|
+
EXAMPLES::
|
|
2411
|
+
|
|
2412
|
+
sage: from sage.misc.latex import latex_examples
|
|
2413
|
+
sage: K = latex_examples.knot()
|
|
2414
|
+
sage: K
|
|
2415
|
+
LaTeX example for testing display of a knot produced by xypic...
|
|
2416
|
+
sage: latex(K)
|
|
2417
|
+
\vtop{\vbox{\xygraph{!{0;/r1.5pc/:}
|
|
2418
|
+
[u] !{\vloop<(-.005)\khole||\vcrossneg \vunder- }
|
|
2419
|
+
[] !{\ar @{-}@'{p-(1,0)@+}+(-1,1)}
|
|
2420
|
+
[ul] !{\vcap[3]>\khole}
|
|
2421
|
+
[rrr] !{\ar @{-}@'{p-(0,1)@+}-(1,1)}
|
|
2422
|
+
}}}
|
|
2423
|
+
"""
|
|
2424
|
+
class graph(SageObject):
|
|
2425
|
+
"""
|
|
2426
|
+
LaTeX example for testing display of graphs. See its string
|
|
2427
|
+
representation for details.
|
|
2428
|
+
|
|
2429
|
+
EXAMPLES::
|
|
2430
|
+
|
|
2431
|
+
sage: from sage.misc.latex import latex_examples
|
|
2432
|
+
sage: G = latex_examples.graph()
|
|
2433
|
+
sage: G
|
|
2434
|
+
LaTeX example for testing display of graphs...
|
|
2435
|
+
"""
|
|
2436
|
+
|
|
2437
|
+
def _repr_(self):
|
|
2438
|
+
"""
|
|
2439
|
+
String representation.
|
|
2440
|
+
|
|
2441
|
+
EXAMPLES::
|
|
2442
|
+
|
|
2443
|
+
sage: from sage.misc.latex import latex_examples
|
|
2444
|
+
sage: G = latex_examples.graph()
|
|
2445
|
+
sage: len(G._repr_()) > 300
|
|
2446
|
+
True
|
|
2447
|
+
"""
|
|
2448
|
+
return r"""LaTeX example for testing display of graphs.
|
|
2449
|
+
|
|
2450
|
+
To use, first try calling 'view' on this object -- it will not work.
|
|
2451
|
+
Now, make sure that you have the most recent version of the TeX
|
|
2452
|
+
package pgf installed, along with the LaTeX package tkz-graph. Run
|
|
2453
|
+
'latex.add_to_preamble("\\usepackage{tkz-graph}")', and try viewing it
|
|
2454
|
+
again. From the command line, this should pop open a nice window with
|
|
2455
|
+
a picture of a graph.
|
|
2456
|
+
|
|
2457
|
+
(LaTeX code taken from the documentation of the LaTeX package tkz-graph
|
|
2458
|
+
https://www.ctan.org/pkg/tkz-graph)
|
|
2459
|
+
"""
|
|
2460
|
+
|
|
2461
|
+
def _latex_(self):
|
|
2462
|
+
"""
|
|
2463
|
+
LaTeX representation.
|
|
2464
|
+
|
|
2465
|
+
EXAMPLES::
|
|
2466
|
+
|
|
2467
|
+
sage: from sage.misc.latex import latex_examples
|
|
2468
|
+
sage: len(latex_examples.graph()._latex_()) > 500
|
|
2469
|
+
True
|
|
2470
|
+
sage: len(latex_examples.graph()._latex_()) > 600
|
|
2471
|
+
False
|
|
2472
|
+
"""
|
|
2473
|
+
return r"""\begin{tikzpicture}[node distance = 4 cm]
|
|
2474
|
+
\GraphInit[vstyle=Shade]
|
|
2475
|
+
\tikzset{LabelStyle/.style = {draw,
|
|
2476
|
+
fill = yellow,
|
|
2477
|
+
text = red}}
|
|
2478
|
+
\Vertex{A}
|
|
2479
|
+
\EA(A){B}
|
|
2480
|
+
\EA(B){C}
|
|
2481
|
+
\tikzset{node distance = 8 cm}
|
|
2482
|
+
\NO(B){D}
|
|
2483
|
+
\Edge[label=1](B)(D)
|
|
2484
|
+
\tikzset{EdgeStyle/.append style = {bend left}}
|
|
2485
|
+
\Edge[label=4](A)(B)
|
|
2486
|
+
\Edge[label=5](B)(A)
|
|
2487
|
+
\Edge[label=6](B)(C)
|
|
2488
|
+
\Edge[label=7](C)(B)
|
|
2489
|
+
\Edge[label=2](A)(D)
|
|
2490
|
+
\Edge[label=3](D)(C)
|
|
2491
|
+
\end{tikzpicture}"""
|
|
2492
|
+
|
|
2493
|
+
class pstricks(SageObject):
|
|
2494
|
+
"""
|
|
2495
|
+
LaTeX example for testing display of pstricks output. See its
|
|
2496
|
+
string representation for details.
|
|
2497
|
+
|
|
2498
|
+
EXAMPLES::
|
|
2499
|
+
|
|
2500
|
+
sage: from sage.misc.latex import latex_examples
|
|
2501
|
+
sage: PS = latex_examples.pstricks()
|
|
2502
|
+
sage: PS
|
|
2503
|
+
LaTeX example for testing display of pstricks...
|
|
2504
|
+
"""
|
|
2505
|
+
|
|
2506
|
+
def _repr_(self):
|
|
2507
|
+
"""
|
|
2508
|
+
String representation.
|
|
2509
|
+
|
|
2510
|
+
EXAMPLES::
|
|
2511
|
+
|
|
2512
|
+
sage: from sage.misc.latex import latex_examples
|
|
2513
|
+
sage: len(latex_examples.pstricks()._repr_()) > 300
|
|
2514
|
+
True
|
|
2515
|
+
"""
|
|
2516
|
+
return """LaTeX example for testing display of pstricks output.
|
|
2517
|
+
|
|
2518
|
+
To use, first try calling 'view' on this object -- it will not work. Now,
|
|
2519
|
+
make sure that you have the most recent version of the TeX package
|
|
2520
|
+
pstricks installed. Run 'latex.add_to_preamble("\\usepackage{pstricks}")'
|
|
2521
|
+
and try viewing it again. Call 'view' with the option `engine='latex'`
|
|
2522
|
+
-- the default behavior is to use lualatex, which does not work with
|
|
2523
|
+
pstricks. From the command line, this should pop open a nice window
|
|
2524
|
+
with a picture of forces acting on a mass on a pendulum."""
|
|
2525
|
+
|
|
2526
|
+
def _latex_(self):
|
|
2527
|
+
"""
|
|
2528
|
+
LaTeX representation.
|
|
2529
|
+
|
|
2530
|
+
EXAMPLES::
|
|
2531
|
+
|
|
2532
|
+
sage: from sage.misc.latex import latex_examples
|
|
2533
|
+
sage: len(latex_examples.pstricks()._latex_()) > 250
|
|
2534
|
+
True
|
|
2535
|
+
"""
|
|
2536
|
+
return r"""\begin{pspicture}(0,-4)(14,0)
|
|
2537
|
+
\psline{-}(0,0)(0,-4)
|
|
2538
|
+
\psline[linewidth=2pt]{-}(0,0)(1,-3)
|
|
2539
|
+
\qdisk(1,-3){3pt}
|
|
2540
|
+
\psarc{-}(0,0){0.6}{270}{292}
|
|
2541
|
+
\psline{->}(1,-3.3)(1,-4)
|
|
2542
|
+
\psline{->}(1.1,-2.7)(0.85,-1.95)
|
|
2543
|
+
\psline{-}(5,0)(5,-4)
|
|
2544
|
+
\psline[linewidth=2pt]{-}(5,0)(6,-3)
|
|
2545
|
+
\qdisk(6,-3){3pt}
|
|
2546
|
+
\psarc{-}(5,0){0.6}{270}{292}
|
|
2547
|
+
\psarc{-}(5,0){3.2}{270}{290}
|
|
2548
|
+
\end{pspicture}"""
|
|
2549
|
+
|
|
2550
|
+
class knot(SageObject):
|
|
2551
|
+
"""
|
|
2552
|
+
LaTeX example for testing display of knots. See its string
|
|
2553
|
+
representation for details.
|
|
2554
|
+
|
|
2555
|
+
EXAMPLES::
|
|
2556
|
+
|
|
2557
|
+
sage: from sage.misc.latex import latex_examples
|
|
2558
|
+
sage: K = latex_examples.knot()
|
|
2559
|
+
sage: K
|
|
2560
|
+
LaTeX example for testing display of a knot...
|
|
2561
|
+
"""
|
|
2562
|
+
|
|
2563
|
+
def _repr_(self):
|
|
2564
|
+
"""
|
|
2565
|
+
String representation.
|
|
2566
|
+
|
|
2567
|
+
EXAMPLES::
|
|
2568
|
+
|
|
2569
|
+
sage: from sage.misc.latex import latex_examples
|
|
2570
|
+
sage: len(latex_examples.knot()._repr_()) > 250
|
|
2571
|
+
True
|
|
2572
|
+
"""
|
|
2573
|
+
return r"""LaTeX example for testing display of a knot produced by xypic.
|
|
2574
|
+
|
|
2575
|
+
To use, try to view this object -- it will not work. Now try
|
|
2576
|
+
'latex.add_to_preamble("\\usepackage[graph,knot,poly,curve]{xypic}")',
|
|
2577
|
+
and try viewing again.
|
|
2578
|
+
|
|
2579
|
+
(LaTeX code taken from the xypic manual)
|
|
2580
|
+
"""
|
|
2581
|
+
|
|
2582
|
+
def _latex_(self):
|
|
2583
|
+
"""
|
|
2584
|
+
LaTeX representation.
|
|
2585
|
+
|
|
2586
|
+
EXAMPLES::
|
|
2587
|
+
|
|
2588
|
+
sage: from sage.misc.latex import latex_examples
|
|
2589
|
+
sage: len(latex_examples.knot()._latex_()) > 180
|
|
2590
|
+
True
|
|
2591
|
+
"""
|
|
2592
|
+
return r"""\vtop{\vbox{\xygraph{!{0;/r1.5pc/:}
|
|
2593
|
+
[u] !{\vloop<(-.005)\khole||\vcrossneg \vunder- }
|
|
2594
|
+
[] !{\ar @{-}@'{p-(1,0)@+}+(-1,1)}
|
|
2595
|
+
[ul] !{\vcap[3]>\khole}
|
|
2596
|
+
[rrr] !{\ar @{-}@'{p-(0,1)@+}-(1,1)}
|
|
2597
|
+
}}}"""
|
|
2598
|
+
|
|
2599
|
+
class diagram(SageObject):
|
|
2600
|
+
"""
|
|
2601
|
+
LaTeX example for testing display of commutative diagrams.
|
|
2602
|
+
See its string representation for details.
|
|
2603
|
+
|
|
2604
|
+
EXAMPLES::
|
|
2605
|
+
|
|
2606
|
+
sage: from sage.misc.latex import latex_examples
|
|
2607
|
+
sage: CD = latex_examples.diagram()
|
|
2608
|
+
sage: CD
|
|
2609
|
+
LaTeX example for testing display of a commutative diagram...
|
|
2610
|
+
"""
|
|
2611
|
+
|
|
2612
|
+
def _repr_(self):
|
|
2613
|
+
"""
|
|
2614
|
+
String representation.
|
|
2615
|
+
|
|
2616
|
+
EXAMPLES::
|
|
2617
|
+
|
|
2618
|
+
sage: from sage.misc.latex import latex_examples
|
|
2619
|
+
sage: len(latex_examples.diagram()._repr_()) > 300
|
|
2620
|
+
True
|
|
2621
|
+
"""
|
|
2622
|
+
return r"""LaTeX example for testing display of a commutative diagram produced
|
|
2623
|
+
by xypic.
|
|
2624
|
+
|
|
2625
|
+
To use, try to view this object -- it will not work. Now try
|
|
2626
|
+
'latex.add_to_preamble("\\usepackage[matrix,arrow,curve,cmtip]{xy}")',
|
|
2627
|
+
and try viewing again. You should get a picture (a part of the diagram arising
|
|
2628
|
+
from a filtered chain complex)."""
|
|
2629
|
+
|
|
2630
|
+
def _latex_(self):
|
|
2631
|
+
"""
|
|
2632
|
+
LaTeX representation.
|
|
2633
|
+
|
|
2634
|
+
EXAMPLES::
|
|
2635
|
+
|
|
2636
|
+
sage: from sage.misc.latex import latex_examples
|
|
2637
|
+
sage: len(latex_examples.diagram()._latex_()) > 1000
|
|
2638
|
+
True
|
|
2639
|
+
"""
|
|
2640
|
+
return r"""\xymatrix{
|
|
2641
|
+
& {} \ar[d] & & \ar[d] & & \ar[d] \\
|
|
2642
|
+
\ldots \ar[r] & H_{p+q}(K^{p-2}) \ar[r] \ar[d] &
|
|
2643
|
+
H_{p+q}(K^{p-2}/K^{p-3}) \ar[r] & H_{p+q-1}(K^{p-3}) \ar[r] \ar[d] &
|
|
2644
|
+
H_{p+q-1}(K^{p-3}/K^{p-4}) \ar[r] & H_{p+q-2}(K^{p-4}) \ar[r] \ar[d] &
|
|
2645
|
+
\ldots \\
|
|
2646
|
+
\ldots \ar[r]^{k \quad \quad } & H_{p+q}(K^{p-1}) \ar[r] \ar[d]^{i} &
|
|
2647
|
+
H_{p+q}(K^{p-1}/K^{p-2}) \ar[r] & H_{p+q-1}(K^{p-2}) \ar[r] \ar[d] &
|
|
2648
|
+
H_{p+q-1}(K^{p-2}/K^{p-3}) \ar[r] & H_{p+q-2}(K^{p-3}) \ar[r] \ar[d] &
|
|
2649
|
+
\ldots \\
|
|
2650
|
+
\ldots \ar[r] & H_{p+q}(K^{p}) \ar[r]^{j} \ar[d] &
|
|
2651
|
+
H_{p+q}(K^{p}/K^{p-1}) \ar[r]^{k} & H_{p+q-1}(K^{p-1}) \ar[r] \ar[d]^{i} &
|
|
2652
|
+
H_{p+q-1}(K^{p-1}/K^{p-2}) \ar[r] & H_{p+q-2}(K^{p-2}) \ar[r] \ar[d] &
|
|
2653
|
+
\ldots \\
|
|
2654
|
+
\ldots \ar[r] & H_{p+q}(K^{p+1}) \ar[r] \ar[d] &
|
|
2655
|
+
H_{p+q}(K^{p+1}/K^{p}) \ar[r] & H_{p+q-1}(K^{p}) \ar[r]^{j} \ar[d] &
|
|
2656
|
+
H_{p+q-1}(K^{p}/K^{p-1}) \ar[r]^{k} & H_{p+q-2}(K^{p-1}) \ar[r] \ar[d]^{i} &
|
|
2657
|
+
\ldots \\
|
|
2658
|
+
& {} & {} & {} & {} & {} \\
|
|
2659
|
+
{} \\
|
|
2660
|
+
\save "3,1"+DL \PATH ~={**@{-}}
|
|
2661
|
+
'+<0pc,-1pc> '+<4pc,0pc> '+<0pc,-4pc> '+<16pc,0pc>
|
|
2662
|
+
'+<0pc,-3pc> '+<19pc,0pc>
|
|
2663
|
+
'+<0pc,-1pc>
|
|
2664
|
+
\restore
|
|
2665
|
+
\save "3,1"+DL \PATH ~={**@{-}}
|
|
2666
|
+
'+<0pc,2pc> '+<9pc,0pc> '+<0pc,-3pc> '+<18pc,0pc>
|
|
2667
|
+
'+<0pc,-4pc> '+<18pc,0pc>
|
|
2668
|
+
'+<0pc,-4pc>
|
|
2669
|
+
\restore
|
|
2670
|
+
}"""
|
|
2671
|
+
|
|
2672
|
+
|
|
2673
|
+
latex_examples = LatexExamples()
|