File Coverage

blib/lib/Math/LinearApprox.pm
Criterion Covered Total %
statement 50 50 100.0
branch 20 20 100.0
condition n/a
subroutine 12 12 100.0
pod 6 6 100.0
total 88 88 100.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2             #made by: KorG
3             # vim: sw=4 ts=4 et cc=79 :
4              
5             package Math::LinearApprox;
6              
7 4     4   272216 use 5.008;
  4         42  
8 4     4   21 use strict;
  4         11  
  4         98  
9 4     4   18 use warnings FATAL => 'all';
  4         8  
  4         139  
10 4     4   22 use Carp;
  4         6  
  4         352  
11 4     4   28 use Exporter 'import';
  4         8  
  4         3119  
12              
13             our $VERSION = '0.02';
14             $VERSION =~ tr/_//d;
15              
16             our @EXPORT_OK = qw( linear_approx linear_approx_str );
17              
18             ##
19             # @brief Model constructor
20             # @param __PACKAGE__
21             # @param (optional) ARRAYref with points to add ( x1, y1, x2, y2, ... )
22             # @return blessed reference to empty model
23             sub new {
24 11     11 1 2866 my $self = bless {
25             x_sum => 0,
26             y_sum => 0,
27             N => 0,
28             delta => 0,
29             }, __PACKAGE__;
30              
31             # Handle array, if any
32 11 100       42 if (ref $_[1] eq "ARRAY") {
33 8         12 my $half = @{$_[1]} / 2;
  8         24  
34 8 100       207 croak "Array has odd number of elements!" if int $half != $half;
35 7         18 for (my $i = 0; $i < @{$_[1]}; $i += 2) {
  25         61  
36 18         39 $self->add_point($_[1]->[$i], $_[1]->[$i + 1]);
37             }
38             } else {
39 3 100       103 croak "Unknown argument specified!" if defined $_[1];
40             }
41              
42 9         31 return $self;
43             }
44              
45             ##
46             # @brief Translate two points into line equation (coefficients)
47             # @param $_[0] X_1 coordinate
48             # @param $_[1] Y_1 coordinate
49             # @param $_[2] X_2 coordinate
50             # @param $_[3] Y_2 coordinate
51             # @return ($A, $B) for equation [y = Ax + B]
52             sub _eq_by_points {
53 11 100   11   339 die "X_1 == X_2" if $_[0] == $_[2];
54              
55 10         42 my $A = ($_[3] - $_[1]) / ($_[2] - $_[0]);
56 10         27 my $B = $_[3] - ($_[2] * ($_[3] - $_[1])) / ($_[2] - $_[0]);
57              
58 10         24 return ($A, $B);
59             }
60              
61             ##
62             # @brief Get numeric equation of model
63             # @param $_[0] self reference
64             # @return undef or ($A, $B) for equation [y = Ax + B]
65             sub equation {
66             # Check conditions
67             # - check points number
68 13 100   13 1 53 return unless $_[0]->{N} > 1;
69             # - handle vertical lines
70 11 100       56 return if $_[0]->{x_last} == $_[0]->{x_0};
71              
72             # Calculate means
73 10         27 my $M_delta = $_[0]->{delta} / ( $_[0]->{x_last} - $_[0]->{x_0} );
74 10         22 my $M_x = $_[0]->{x_sum} / $_[0]->{N};
75 10         15 my $M_y = $_[0]->{y_sum} / $_[0]->{N};
76              
77             # Translate them into a line
78 10         53 my ($A, $B) = _eq_by_points($M_x, $M_y, $M_x + 1, $M_y + $M_delta);
79              
80             # Return coefficients
81 10         43 return ($A, $B);
82             }
83              
84             ##
85             # @brief Get stringified equation of model
86             # @param $_[0] self reference
87             # @return die or String in forms: "y = A * x + B", "x = X"
88             sub equation_str {
89 8     8 1 28 my ($A, $B) = $_[0]->equation();
90              
91 8 100       24 unless (defined $A) {
92 3 100       12 die "Too few points in model!" if $_[0]->{N} == 0;
93              
94             # Calculate avg
95 2         7 my $avg = $_[0]->{x_sum} / $_[0]->{N};
96 2         31 return "x = $avg";
97             }
98              
99 5         42 return "y = $A * x + $B";
100             }
101              
102             ##
103             # @brief Add new point to model
104             # @param $_[0] self reference
105             # @param $_[1] X coordinate
106             # @param $_[2] Y coordinate
107             # @return Nothing
108             sub add_point {
109             # Save first point
110 21 100   21 1 72 $_[0]->{x_0} = $_[1] unless defined $_[0]->{x_0};
111              
112             # Sum up Y deltas
113 21 100       59 $_[0]->{delta} += $_[2] - $_[0]->{y_last} if $_[0]->{N} != 0;
114              
115             # Append the point to sums
116 21         30 $_[0]->{x_sum} += $_[1];
117 21         49 $_[0]->{y_sum} += $_[2];
118              
119             # Save right-most coordinates
120 21         38 $_[0]->{x_last} = $_[1];
121 21         29 $_[0]->{y_last} = $_[2];
122              
123             # Increase x, y counters
124 21         43 $_[0]->{N}++;
125             }
126              
127             ##
128             # @brief Decorators for procedural style
129 1     1 1 935 sub linear_approx { return __PACKAGE__->new($_[0])->equation(); }
130 1     1 1 5 sub linear_approx_str { return __PACKAGE__->new($_[0])->equation_str(); }
131              
132             1;
133              
134             __END__