File Coverage

blib/lib/FarmBalance.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package FarmBalance;
2 1     1   346 use Mouse;
  0            
  0            
3             our $VERSION = '0.03';
4              
5             #- Input
6             has 'farms' => (
7             is=>'rw',
8             isa=>'Int',
9             required=>1
10             );
11             has 'stats' => (
12             is=>'rw',
13             isa=>'HashRef[ArrayRef[Num]]',
14             required=>1,
15             );
16             has 'input' => (
17             is=>'rw',
18             isa=>'HashRef[Num]'
19             );
20              
21             has 'debug' => (
22             is=>'rw',
23             isa=>'Bool',
24             default=>0
25             );
26             #- Output
27             has 'effective_farm' => (
28             is=>'rw',
29             isa=>'Int'
30             );
31             has 'effect_in_farm_max' => (
32             is=>'rw',
33             isa=>'Int'
34             );
35              
36             #- Other
37             has 'percent' => (
38             is=>'rw',
39             isa=>'Int',
40             required=>1,
41             default=>100
42             );
43              
44             __PACKAGE__->meta->make_immutable;
45             no Mouse;
46              
47              
48             #- if parameter 'input' is empty, fill average values in balance keys.
49             sub input_fill_avg {
50             my $self = shift;
51             foreach my $bkey ( keys %{ $self->{stats} } ) {
52             my $arrayref = $self->{stats}->{$bkey};
53             my $avg = $self->average($arrayref);
54             $self->{input}->{$bkey} = $avg;
55             }
56             }
57              
58             #- check parameters.
59             sub check_param {
60             my $self = shift;
61             if ( $self->{farms} < 1 ) {
62             die "Error: farms must be larger than 0\n";
63             } elsif ( $self->{farms} > 10000000 ) {
64             die "Error: farms must be less than 10000000. Is it real system??\n";
65             }
66             foreach my $bkey ( keys %{ $self->{stats} } ) {
67             if ( $#{$self->{stats}->{$bkey}} != ($self->{farms} - 1) ) {
68             die "Error: numbers of stats differ from farm number\n";
69             }
70             }
71             if ( defined $self->{input} ) {
72             my @input_array = keys %{$self->{input}};
73             my @bkey_array = keys %{$self->{stats}};
74             if ( $#input_array != $#bkey_array ) {
75             die "Error: numbers of input differ from stats blance key number\n";
76             }
77             }
78             return 0;
79             }
80              
81             #- Define Farm Number
82             sub define_farm {
83             my $self = shift;
84             #- init, regarding bulk operation.
85             $self->{effective_farm} = undef;
86             $self->{effect_in_farm_max} = undef;
87             #- check stats and input parameters.
88             $self->check_param;
89             #- if traffic and data unknown, fill average values.
90             if ( ! defined $self->{input} ) {
91             $self->input_fill_avg;
92             }
93             #- effect calculation for each nodes.
94             my $second_farm; #- looser
95             for ( my $farm = 0; $farm < $self->{farms}; $farm++ ) {
96             my $farm_str = $farm + 1;
97             my $effect_in_farm = 0;
98             print "NODE: $farm_str\n" if ( $self->{debug} );
99             foreach my $b_key ( keys %{$self->{input}} ) {
100             #- standard deviation : before insert.
101             my ( $sd_before ) = sprintf("%.2f",$self->sd_percent($self->{stats}->{$b_key}));
102             if ( $self->{debug} ) {
103             print "$b_key\n";
104             print " before:\t$self->{stats}->{$b_key}->[$farm]\tsd:\t$sd_before\n";
105             }
106             #- if added to this node.
107             $self->{stats}->{$b_key}->[$farm] += $self->{input}->{$b_key};
108             #- statndard deviation : after insert.
109             my ( $sd_after ) = sprintf("%.2f",$self->sd_percent($self->{stats}->{$b_key}));
110             #- if effect is large number, it's better farming.
111             my $effect = $sd_before - $sd_after;
112             if ( $self->{debug} ) {
113             print " after:\t$self->{stats}->{$b_key}->[$farm]\tsd:\t$sd_after\n";
114             print " effect:\t" . sprintf("%.2f", $effect) . "\n";
115             }
116             $effect_in_farm += $effect;;
117             }
118             print "\t->TotalEffect:\t" . sprintf("%.2f", $effect_in_farm) . "\n" if ( $self->{debug} );
119             #- chose most effective farm.
120             if ( ! defined $self->{effect_in_farm_max} ) {
121             $self->change_effective_farm($farm_str, $effect_in_farm);
122             } elsif ( $effect_in_farm > $self->{effect_in_farm_max} ) {
123             #- consider looser.
124             $second_farm = $self->{effective_farm} - 1;
125             $self->change_effective_farm($farm_str, $effect_in_farm, $second_farm);
126             } else {
127             #- rollback
128             $self->rollback_stat($farm);
129             }
130             }
131              
132             }
133              
134             sub change_effective_farm {
135             my ( $self, $farm_str, $effect_in_farm, $second_farm ) = @_;
136             $self->{effect_in_farm_max} = $effect_in_farm;
137             $self->{effective_farm} = $farm_str;
138             $self->rollback_stat($second_farm) if ( defined $second_farm);
139             }
140              
141             sub rollback_stat {
142             my ( $self, $farm ) = @_;
143             foreach my $bkey ( keys %{ $self->{input} } ) {
144             $self->{stats}->{$bkey}->[$farm] -= $self->{input}->{$bkey};
145             }
146             }
147              
148             sub report {
149             my $self = shift;
150             $self->check_param;
151             my $stats = $self->{stats};
152             print "-----------------------------\n";
153             print "farm";
154             foreach my $key ( keys %$stats ) {
155             print "\t$key";
156             }
157             print "\n";
158             for ( my $farm = 0; $farm < $self->{farms}; $farm++ ) {
159             print $farm + 1 . ':';
160             foreach my $key ( keys %$stats ) {
161             print "\t", $stats->{$key}->[$farm];
162             }
163             print "\n";
164             }
165             print "sd";
166             my $total_sd = 0;
167             foreach my $key ( keys %$stats ) {
168             print "\t" , sprintf("%.2f", $self->sd_percent($stats->{$key}));
169             $total_sd += $self->sd_percent($stats->{$key});
170             }
171             print "\n";
172             print "SD_Total:\t" . sprintf("%.2f",$total_sd) . "\n";
173             print "-----------------------------\n";
174            
175             }
176              
177             #- return standard deviation, if all summ is 100(%).
178             sub sd_percent {
179             my ( $self, $a_ref ) = @_;
180             $a_ref = $self->arrange_array($a_ref);
181             return $self->sd($a_ref);
182             }
183              
184             #- return array that has sum = 100.
185             sub arrange_array {
186             my ( $self, $arrayref) = @_;
187             my $sum = $self->array_val_sum($arrayref);
188             my $kei = $self->{'percent'} / $sum;
189             my @nums_new = map { $_ * $kei } @{$arrayref};
190             return \@nums_new;
191             }
192              
193             #- return standard deviation
194             sub sd {
195             my ( $self, $arrayref ) = @_;
196             my $avg = $self->average($arrayref);
197             my $ret = 0;
198             for (@{$arrayref}) {
199             $ret += ($_ - $avg)**2;
200             }
201             return ( $ret/($#$arrayref + 1));
202             }
203             sub average {
204             my ( $self, $arrayref) = @_;
205             my $sum = $self->array_val_sum($arrayref);
206             return ( $sum / ( $#$arrayref + 1) );
207             }
208             #- summarize array values
209             sub array_val_sum {
210             my ( $self, $arrayref) = @_;
211             my $sum = 0;
212             for (@{$arrayref}) {
213             $sum += $_;
214             }
215             return $sum;
216             }
217              
218             1;
219             __END__