File Coverage

blib/lib/Math/Symbolic/Custom/CCompiler.pm
Criterion Covered Total %
statement 103 110 93.6
branch 9 16 56.2
condition 4 6 66.6
subroutine 18 18 100.0
pod 2 2 100.0
total 136 152 89.4


line stmt bran cond sub pod time code
1             package Math::Symbolic::Custom::CCompiler;
2              
3 1     1   182219 use 5.006;
  1         6  
  1         49  
4 1     1   6 use strict;
  1         2  
  1         37  
5 1     1   5 use warnings;
  1         6  
  1         37  
6 1     1   1132 use Inline;
  1         27253  
  1         8  
7 1     1   66 use Carp qw/croak carp cluck confess/;
  1         2  
  1         97  
8              
9 1     1   6 use Math::Symbolic::Custom::Base;
  1         2  
  1         40  
10 1     1   21 BEGIN {*import = \&Math::Symbolic::Custom::Base::aggregate_import}
11              
12 1     1   7 use Math::Symbolic::ExportConstants qw/:all/;
  1         1  
  1         502  
13             our $VERSION = '1.04';
14              
15             our $Aggregate_Export = [qw/to_c to_compiled_c/];
16              
17             our @Operators_To_C = (
18             # B_SUM
19             '$_[0] + $_[1]',
20             # B_DIFFERENCE
21             '$_[0] - $_[1]',
22             # B_PRODUCT
23             '$_[0] * $_[1]',
24             # B_DIVISION
25             '$_[0] / $_[1]',
26             # U_MINUS
27             '-$_[0]',
28             # U_P_DERIVATIVE
29             'ERROR',
30             # U_T_DERIVATIVE
31             'ERROR',
32             # B_EXP
33             'pow($_[0], $_[1])',
34             # B_LOG
35             'log($_[1]) / log($_[0])',
36             # U_SINE
37             'sin($_[0])',
38             # U_COSINE
39             'cos($_[0])',
40             # U_TANGENT
41             'tan($_[0])',
42             # U_COTANGENT
43             'cos($_[0]) / sin($_[0])',
44             # U_ARCSINE
45             'asin($_[0])',
46             # U_ARCCOSINE
47             'acos($_[0])',
48             # U_ARCTANGENT
49             'atan($_[0])',
50             # U_ARCCOTANGENT
51             'atan2( 1 / $_[0], 1 )',
52             # U_SINE_H
53             'sinh($_[0])',
54             # U_COSINE_H
55             'cosh($_[0])',
56             # U_AREASINE_H
57             'log( $_[0] + sqrt( $_[0] * $_[0] + 1 ) )',
58             # U_AREACOSINE_H
59             'log( $_[0] + sqrt( $_[0] * $_[0] - 1 ) )',
60             # B_ARCTANGENT_TWO
61             'atan2($_[0], $_[1])',
62             );
63              
64             sub to_compiled_c {
65 1     1 1 345 my $tree = shift;
66 1   50     9 my $order = shift || [];
67 1         8 my $code = $tree->to_c($order);
68 1         5 Math::Symbolic::Custom::CCompiler::_Compiled::compile($code);
69 1 50       8473 $code =~ /^\s*double\s*(\w+)\(/
70             or croak "Compilation to C failed for unknown reasons.";
71 1         4 my $f_name = $1;
72 1     1   9 no strict 'refs';
  1         2  
  1         136  
73 1         2 my $ref = *{"Math::Symbolic::Custom::CCompiler::_Compiled::$f_name"}{CODE};
  1         7  
74 1         12 delete(${Math::Symbolic::Custom::CCompiler::_Compiled::}{$f_name});
75 1         4 return $ref;
76             }
77              
78             sub to_c {
79 2     2 1 11473 my $tree = shift;
80 2   100     22 my $order = shift || [];
81 2         3 my $count = 0;
82 2         8 my %order = map { ( $_, $count++ ) } @$order;
  0         0  
83 1     1   6 no warnings 'recursion';
  1         1  
  1         272  
84              
85 2         12 my $vars = [ $tree->explicit_signature() ];
86              
87 2         437 my %vars;
88             my @not_placed;
89 2         5 foreach (@$vars) {
90 6         7 my $pos = $order{$_};
91 6 50       13 if ( defined $pos ) {
92 0         0 $vars{$_} = $pos;
93             }
94             else {
95 6         16 push @not_placed, $_;
96             }
97             }
98              
99 2         5 $count = 0;
100 2         8 foreach ( sort @not_placed ) {
101 6         17 $vars{$_} = @$vars - @not_placed + $count++;
102             }
103 2         11 my @sorted_vars = sort {$vars{$a} <=> $vars{$b}} keys %vars;
  5         15  
104              
105 2         8 my $subname = _find_subname(
106             'Math::Symbolic::Custom::CCompiler::_Compiled'
107             );
108 2         6 my $code = "double $subname(";
109 2         4 my $first = 1;
110 2         3 my @varmap;
111 2         3 my $startvar = 'aaaaaa';
112 2         4 foreach (@sorted_vars) {
113 6         41 push @varmap, '_V'.$startvar;
114 6 100       18 $code .= ', ' unless $first-- == 1;
115 6         12 $code .= "double _V$startvar";
116 6         13 $startvar++;
117             }
118 2         6 $code .= ") {\nreturn( ";
119            
120 1     1   6 no warnings 'recursion';
  1         2  
  1         509  
121            
122 2         8 $code .= _rec_ms_to_c($tree, \%vars, \@varmap);
123              
124 2         6 $code .= " );\n}\n";
125 2         12 return $code;
126             }
127              
128             sub _rec_ms_to_c {
129 10     10   17 my $tree = shift;
130 10         12 my $vars = shift;
131 10         14 my $varmap = shift;
132              
133 10         21 my $code = '';
134 10         33 my $ttype = $tree->term_type();
135              
136 10 50       50 if ( $ttype == T_CONSTANT ) {
    100          
137 0         0 my $value = $tree->value();
138 0 0       0 $value .= '.' if $value !~ /\./;
139 0         0 $code .= $value;
140             }
141             elsif ( $ttype == T_VARIABLE ) {
142 6         22 $code .= ' ' . $varmap->[$vars->{ $tree->name() }] . ' ';
143             }
144             else {
145 4         13 my $type = $tree->type();
146 4         25 my $otype = $Math::Symbolic::Operator::Op_Types[$type];
147 4         10 my $app = $otype->{application};
148 4 50       8 if ( ref($app) eq 'CODE' ) {
149 0         0 confess("Trying to compile differential operator to C.\n" .
150             "This is not supported by " .
151             "Math::Symbolic::Custom::CCompiler\n");
152             }
153             else {
154 4         10 $app = $Operators_To_C[$type];
155 4         36 my @app = split /\$_\[(\d+)\]/, $app;
156 4 50       14 if ( @app > 1 ) {
157 4         12 for ( my $i = 1 ; $i < @app ; $i += 2 ) {
158 8         37 $app[$i] = '('
159             . _rec_ms_to_c( $tree->{operands}[ $app[$i] ],
160             $vars, $varmap )
161             . ')';
162             }
163             }
164 4         16 $code .= join '', @app;
165             }
166             }
167 10         242 return $code;
168             }
169              
170             sub _find_subname {
171 2     2   6 my $package = shift;
172 2   50     12 my $min_length = shift || 5;
173 1     1   8 no strict 'refs';
  1         2  
  1         56  
174 2         4 my $ref = \%{$package.'::'};
  2         12  
175 1     1   5 use strict 'refs';
  1         2  
  1         149  
176 2         8 my $name = 'A'x$min_length;
177 2         16 while (exists $ref->{$name}) {
178 0         0 $name++;
179             }
180 2         7 return $name;
181             }
182              
183             1;
184             package Math::Symbolic::Custom::CCompiler::_Compiled;
185             sub compile {
186 1     1   2 my $code = shift;
187 1         22 Inline->bind(C => $code);
188             }
189              
190              
191              
192             1;
193             __END__