File Coverage

blib/lib/Math/SimpleHisto/XS/Named.pm
Criterion Covered Total %
statement 83 98 84.6
branch 9 14 64.2
condition 1 3 33.3
subroutine 17 22 77.2
pod 0 7 0.0
total 110 144 76.3


line stmt bran cond sub pod time code
1             package Math::SimpleHisto::XS::Named;
2 1     1   1665 use strict;
  1         2  
  1         30  
3 1     1   4 use warnings;
  1         1  
  1         25  
4 1     1   5 use Math::SimpleHisto::XS;
  1         1  
  1         45  
5              
6             our $VERSION = '0.02';
7              
8 1     1   4 use vars qw($AUTOLOAD);
  1         2  
  1         45  
9 1     1   10 use Carp qw(croak);
  1         2  
  1         259  
10              
11             sub new {
12 1     1 0 13 my $class = shift;
13 1         4 my %param = @_;
14              
15 1         2 my $names = $param{names};
16 1 50 33     7 croak("Need list of bin names!")
17             if not $names or not ref($names) eq 'ARRAY';
18              
19 1         3 my $nbins = @$names;
20 1         7 my $hist = Math::SimpleHisto::XS->new(
21             nbins => $nbins,
22             min => 0,
23             max => $nbins,
24             );
25              
26 1         1 my %namehash;
27 1         1 my $i = 0;
28 1         7 $namehash{$_} = $i++ for @$names;
29 1         6 my $self = bless(
30             {
31             # careful about the cloning logic below and dump()
32             hist => $hist,
33             names => [@$names],
34             namehash => \%namehash,
35             } => $class
36             );
37              
38 1         4 return $self;
39             }
40              
41             # Generate bin-number-as-first-parameter delegation
42             foreach my $methname (qw(
43             bin_content set_bin_content
44             )) {
45             my $sub = sub {
46 12     12   4160 my $self = shift;
47 12         16 my $name = shift;
48 12 50       33 croak("Invalid bin name '$name'") if not exists $self->{namehash}{$name};
49 12         19 my $ibin = $self->{namehash}{$name};
50 12         69 return $self->{hist}->$methname($ibin, @_);
51             };
52 1         121 SCOPE: {
53 1     1   4 no strict 'refs';
  1         1  
54             *{"$methname"} = $sub;
55             }
56             }
57              
58             # generate cloning delegation
59             foreach my $methname (qw(clone new_alike)) {
60             my $sub = sub {
61 3     3   375 my $self = shift;
62 3         10 my $clone = bless({
63             %$self,
64 3         33 names => [@{$self->{names}}],
65 3         8 namehash => {%{$self->{namehash}}},
66             hist => $self->{hist}->$methname(@_),
67             } => ref($self));
68 3         15 return $clone;
69             };
70 1         90 SCOPE: {
71 1     1   5 no strict 'refs';
  1         1  
72             *{"$methname"} = $sub;
73             }
74             }
75              
76             # Generate methods that croak because they make no sense
77             # on named bins
78             foreach my $methname (qw(
79             find_bin min max binsize width
80             new_from_bin_range new_alike_from_bin_range
81             integral
82             rand
83             bin_center bin_lower_boundary bin_upper_boundary
84             bin_centers bin_lower_boundaries bin_upper_boundaries
85             )) {
86             my $sub = sub {
87 0     0   0 croak("The '$methname' method makes little sense for named bins");
88             };
89 1         310 SCOPE: {
90 1     1   9 no strict 'refs';
  1         20  
91             *{"$methname"} = $sub;
92             }
93             }
94              
95              
96              
97             sub fill {
98 5     5 0 1049 my $self = shift;
99 5 50       12 croak("Need at least one argument") if not @_;
100 5         7 my ($x, $w) = @_;
101 5         12 my $hist = $self->{hist};
102 5         6 my $namehash = $self->{namehash};
103 5 100       14 if (ref($x) eq 'ARRAY') {
104 2         10 $x = [map $namehash->{$_}, @$x];
105             }
106             else {
107 3         5 $x = $namehash->{$x};
108             }
109 5 100       47 return $hist->fill($x, defined($w) ? ($w) : ());
110             }
111              
112             *fill_by_bin = \&fill;
113              
114             sub get_bin_names {
115 0     0 0 0 return @{ $_[0]->{names} };
  0         0  
116             }
117              
118             # Not a fan, but unless I find an elegant way to attach more data to the
119             # XS object, I can't think of anything else. Retrofitting XS::Object::Magic
120             # to the SimpleHisto implementation is too annoying.
121             sub AUTOLOAD {
122 6     6   1173 my $self = $_[0];
123              
124 6         8 my $methname = $AUTOLOAD;
125 6 50       33 $methname =~ /^(.*)::([^:]+)$/ or die "Should not happen";
126 6         17 (my $class, $methname) = ($1, $2);
127              
128 6         11 my $hist = $self->{hist};
129 6 50       26 if ($hist->can($methname)) {
130             my $delegate = sub {
131 51     51   97 my $self = shift;
132 51         292 return $self->{hist}->$methname(@_);
133 6         21 };
134 1         316 SCOPE: {
135 1     1   4 no strict 'refs';
  1         1  
  6         8  
136 6         7 *{"$methname"} = $delegate;
  6         17  
137             }
138 6         16 goto &$methname;
139             }
140 0         0 croak(qq{Can't locate object method "$methname" via package "$class"});
141             }
142              
143             sub dump {
144 1     1 0 67 my $self = shift;
145 1         5 my $type = lc(shift);
146              
147 1         8 my $hist_dump = $self->{hist}->dump($type);
148              
149 1         13 my $rv = $Math::SimpleHisto::XS::JSON->encode({
150             %$self,
151             hist => $hist_dump,
152             class => ref($self),
153             histclass => ref($self->{hist})
154             });
155 1         353 return $rv;
156             }
157              
158             sub new_from_dump {
159 1     1 0 6 my $class = shift;
160 1         3 my $type = lc(shift);
161 1         1 my $data = shift;
162              
163 1         4 my $struct = $Math::SimpleHisto::XS::JSON->decode($data);
164 1         1581 $class = delete $struct->{class};
165 1         3 my $hclass = delete $struct->{histclass};
166 1         7 $struct->{hist} = $hclass->new_from_dump($type, delete $struct->{hist});
167 1         6 return bless($struct => $class);
168             }
169              
170             # Can't simply be delegated, eventhough the implementation is the same :(
171             sub STORABLE_freeze {
172 0     0 0   my $self = shift;
173 0           my $cloning = shift;
174 0           my $serialized = $self->dump('simple');
175 0           return $serialized;
176             }
177              
178             # Can't simply be delegated, eventhough the implementation is the same :(
179             sub STORABLE_thaw {
180 0     0 0   my $self = shift;
181 0           my $cloning = shift;
182 0           my $serialized = shift;
183 0           my $new = ref($self)->new_from_dump('simple', $serialized);
184 0           $$self = $$new;
185 0           $new = undef; # need to care about DESTROY here, normally
186             }
187              
188 0     0     sub DESTROY {}
189              
190             1;
191              
192             __END__