=head1 NAME Math::Symbolic::Custom::LaTeXDumper - Math::Symbolic LaTeX output =head1 SYNOPSIS use Math::Symbolic qw/parse_from_string/; use Math::Symbolic::Custom::LaTeXDumper; $term = parse_from_string(...); print $term->to_latex(...); =head1 DESCRIPTION This class provides the C method for all L trees. It is a rewrite of the C method that was supplied by C prior to version 0.201. For details on how the custom method delegation model works, please have a look at the C and C classes. =head2 EXPORT Please see the docs for C for details, but you should not try to use the standard Exporter semantics with this class. =head1 SUBROUTINES =cut package Math::Symbolic::Custom::LaTeXDumper; use 5.006; use strict; use warnings; no warnings 'recursion'; our $VERSION = '0.206'; use Math::Symbolic::Custom::Base; BEGIN { *import = \&Math::Symbolic::Custom::Base::aggregate_import } use Math::Symbolic::ExportConstants qw/:all/; use Carp; # Class Data: Special variable required by Math::Symbolic::Custom # importing/exporting functionality. # All subroutines that are to be exported to the Math::Symbolic::Custom # namespace should be listed here. our $Aggregate_Export = [ qw/ to_latex / ]; =head2 to_latex It returns a LaTeX representation of the Math::Symbolic tree it is called on. The LaTeX is meant to be included in a LaTeX source document. It does not include an enclosing math environment. The method uses named parameter passing style. Valid parameters are: =over 4 =item implicit_multiplication Used to turn on the '.' (C<\cdot>) operators for all multiplications. Defaults to true, that is, the multiplication operators are not present. =item no_fractions Use '/' division operator instead of fractions. Defaults to false, that is, use fractions. See also: C parameter. =item max_fractions Setting this parameter to a positive integer results in a limitation of the number of nested fractions. It defaults to C<2>. Setting this to C<0> results in arbitrarily nested fractions. To disable fractions altogether, set the C parameter. =item exclude_signature By default, the method includes all variables' signatures in parenthesis if present. Set this to true to omit variable signatures. =item replace_default_greek By default, all variable names are outputted as LaTeX in a way that makes them show up exactly as they did in your code. If you set this option to true, Math::Symbolic will try to replace as many greek character names with the appropriates symbols as possible. Valid LaTeX symbols that are matched are: Lower case letters: alpha, beta, gamma, delta, epsilon, zeta, eta, theta, iota, kappa, lambda, mu, nu, xi, pi, rho, sigma, tau, upsilon, phi, chi, psi, omega Variant forms of small letters: varepsilon, vartheta, varpi, varrho, varsigma, varphi Upper case letters: Gamma, Delta, Theta, Lambda, Xi, Pi, Sigma, Upsilon, Phi, Psi, Omega =item variable_mappings Because not all variable names come out as you might want them to, you may use the 'variable_mappings' option to replace variable names in the output LaTeX stream with custom LaTeX. For example, the variable x_i should probably indicate an 'x' with a subscripted i. The argument to variable_mappings needs to be a hash reference which contains variable name / LaTeX mapping pairs. If a variable is replaced in the above fashion, other options that modify the outcome of the conversion of variable names to LaTeX are ignored. =item subscript Set this option to a true value to have all underscores in variable names interpreted as subscripts. Once an underscore is encountered, the rest of the variable name is treated as subscripted. If multiple underscores are found, this mechanism works recursively. Defaults to C. Set to a false value to turn this off. This is automatically turned off for variables that are mapped to custom LaTeX by the C parameter. =item no_sqrt By default, the dumper tries to convert exponents of 1/2 (or 0.5) or anything numeric that ends up being 1/2 to a square root. If this gives inconvenient results, set this option to a true value to turn the heuristics off. =back =cut sub to_latex { my $self = shift; my %config = @_; $config{implicit_multiplication} = 1 unless defined $config{implicit_multiplication}; $config{no_fractions} = 0 unless defined $config{no_fractions}; $config{max_fractions} = 2 if not exists $config{max_fractions}; $config{exclude_signature} = 0 unless defined $config{exclude_signature}; $config{no_sqrt} = 0 unless defined $config{no_sqrt}; $config{replace_default_greek} = 0 unless defined $config{replace_default_greek}; $config{subscript} = 1 if not exists $config{subscript}; $config{variable_mappings} = {} if not exists $config{variable_mappings} or not ref( $config{variable_mappings} ) eq 'HASH'; my $default_greek = qr/(?($1).'}'/e if $config{subscript}; return $s; }; my $precedence = [ 1, # B_SUM 1, # B_DIFFERENCE 5, # B_PRODUCT 5, # B_DIVISION 15, # U_MINUS 20, # U_P_DERIVATIVE 20, # U_T_DERIVATIVE 25, # B_EXP 50, # B_LOG 50, # U_SINE 50, # U_COSINE 50, # U_TANGENT 50, # U_COTANGENT 50, # U_ARCSINE 50, # U_ARCCOSINE 50, # U_ARCTANGENT 50, # U_ARCCOTANGENT 50, # U_SINE_H 50, # U_COSINE_H 50, # U_AREASINE_H 50, # U_AREACOSINE_H 50, # B_ARCTANGENT_TWO ]; my $op_to_tex = [ # B_SUM sub { "$_[0] + $_[1]" }, # B_DIFFERENCE sub { "$_[0] - $_[1]" }, # B_PRODUCT sub { $config{implicit_multiplication} ? "$_[0] $_[1]" : "$_[0] \\cdot $_[1]"; }, # B_DIVISION sub { if ( $config{no_fractions} ) { "$_[0] / $_[1]" } elsif (!$config{max_fractions} or $config{max_fractions} > $_[2]) { "\\frac{$_[0]}{$_[1]}" } else { "$_[0] / $_[1]" } }, # U_MINUS sub { "-$_[0]" }, # U_P_DERIVATIVE sub { "\\frac{\\partial $_[0]}{\\partial $_[1]}" }, # U_T_DERIVATIVE sub { "\\frac{d $_[0]}{d $_[1]}" }, # B_EXP sub { if (!$config{no_sqrt} and length($_[1]) > 2 and $_[1] !~ /\{|\}|\^|_|\-|\\|[A-DF-Za-df-z]/ and $_[1] - 0.5 < 1e-28) { return "\\sqrt{$_[0]}"; } length($_[1]) == 1 ? "$_[0]^$_[1]" : "$_[0]^{$_[1]}" }, # B_LOG sub { "\\log_{$_[0]}$_[1]" }, # U_SINE sub { "\\sin{$_[0]}" }, # U_COSINE sub { "\\cos{$_[0]}" }, # U_TANGENT sub { "\\tan{$_[0]}" }, # U_COTANGENT sub { "\\cot{$_[0]}" }, # U_ARCSINE sub { "\\arcsin{$_[0]}" }, # U_ARCCOSINE sub { "\\arccos{$_[0]}" }, # U_ARCTANGENT sub { "\\arctan{$_[0]}" }, # U_ARCCOTANGENT sub { "\\mathrm{cosec}{$_[0]}" }, # U_SINE_H sub { "\\sinh{$_[0]}" }, # U_COSINE_H sub { "\\cosh{$_[0]}" }, # U_AREASINE_H sub { "\\mathrm{arsinh}{$_[0]}" }, # U_AREACOSINE_H sub { "\\mathrm{arcosh}{$_[0]}" }, # B_ARCTANGENT_TWO sub { "\\mathrm{atan2}{$_[0], $_[1]}" }, ]; my $tex = $self->descend( operand_finder => sub { $_[0]->descending_operands('all') }, before => sub { $_[0]->{__precedences} = [ map { my $ttype = $_->term_type(); if ( $ttype == T_OPERATOR ) { $precedence->[ $_->type() ]; } elsif ( $ttype == T_VARIABLE ) { 100; } elsif ( $ttype == T_CONSTANT ) { 100; } else { die; } } @{ $_[0]->{operands} } ]; my $fraction_depth = $_[0]->{__fract_depth} || 0; $fraction_depth++ if $_[0]->term_type() == T_OPERATOR and $_[0]->type() == B_DIVISION; for (@{ $_[0]->{operands} }) { $_->{__fract_depth} = $fraction_depth; } return (); }, after => sub { my $self = \$_[0]; my $ttype = $$self->term_type(); if ( $ttype == T_CONSTANT ) { $$self = { text => $$self->value() }; } elsif ( $ttype == T_VARIABLE ) { my $name = $$self->name(); my $edited_name = $name; if ( exists $config{variable_mappings}{$name} ) { $edited_name = $config{variable_mappings}{$name}; } else { $edited_name = $greekify->($name); $edited_name = $subscriptify->($edited_name); } unless ( $config{exclude_signature} ) { my @sig = map { if ( exists $config{variable_mappings}{$_} ) { $config{variable_mappings}{$_}; } else { $_ = $subscriptify->($_); s/_/\_/g; $greekify->($_); } } grep { $_ ne $name } $$self->signature(); if (@sig) { $$self = { text => "$edited_name(" . join( ', ', @sig ) . ')' }; } else { $$self = { text => $edited_name }; } } else { $$self = { text => $edited_name }; } } elsif ( $ttype == T_OPERATOR ) { my $type = $$self->type(); my $prec = $precedence->[$type]; my $precs = $$self->{__precedences}; my @ops = map { $_->{text} } @{ $$self->{operands} }; unless ( $type == B_DIVISION and !$config{no_fractions} ) { for ( my $i = 0 ; $i < @ops ; $i++ ) { if ( $precs->[$i] < $prec or $precs->[$i] == $prec && $prec == 50 ) { $ops[$i] = '\\left(' . $ops[$i] . '\\right)' } } } my $fraction_depth = $$self->{__fract_depth}||0; my $text = $op_to_tex->[$type]->(@ops, $fraction_depth); $$self = { text => $text }; } else { die; } }, ); return $tex->{text}; } 1; __END__ =head1 AUTHOR Please send feedback, bug reports, and support requests to the Math::Symbolic support mailing list: math-symbolic-support at lists dot sourceforge dot net. Please consider letting us know how you use Math::Symbolic. Thank you. If you're interested in helping with the development or extending the module's functionality, please contact the developers' mailing list: math-symbolic-develop at lists dot sourceforge dot net. Steffen Müller, symbolic-module at steffen-mueller dot net =head1 SEE ALSO New versions of this module can be found on http://steffen-mueller.net or CPAN. The module development takes place on Sourceforge at http://sourceforge.net/projects/math-symbolic/ L L L L L =cut