File Coverage

blib/lib/Algorithm/VectorClocks.pm
Criterion Covered Total %
statement 91 106 85.8
branch 11 20 55.0
condition 11 18 61.1
subroutine 22 24 91.6
pod 8 8 100.0
total 143 176 81.2


line stmt bran cond sub pod time code
1             package Algorithm::VectorClocks;
2              
3 2     2   54303 use warnings;
  2         4  
  2         65  
4 2     2   14 use strict;
  2         4  
  2         65  
5 2     2   11 use Carp;
  2         8  
  2         186  
6              
7 2     2   1932 use version; our $VERSION = qv('0.1.2');
  2         5064  
  2         11  
8              
9 2     2   2249 use JSON::Any;
  2         53323  
  2         13  
10 2     2   20849 use List::MoreUtils qw(uniq);
  2         2446  
  2         182  
11 2     2   13 use List::Util qw(max);
  2         5  
  2         210  
12 2     2   1759 use Perl6::Export::Attrs;
  2         11007  
  2         15  
13 2     2   10717 use Sys::Hostname;
  2         2982  
  2         221  
14              
15             use overload (
16 2         30 '""' => \&serialize,
17             '++' => \&increment,
18             '+=' => \&merge,
19             '==' => \&equal,
20             'eq' => \&equal,
21             '!=' => \¬_equal,
22             'ne' => \¬_equal,
23             fallback => undef,
24 2     2   23 );
  2         4  
25              
26 2     2   256 use base qw(Class::Accessor::Fast Class::Data::Inheritable);
  2         3  
  2         1785  
27              
28             __PACKAGE__->mk_accessors(qw(clocks));
29             __PACKAGE__->mk_classdata($_) for qw(id json);
30              
31             __PACKAGE__->id(hostname);
32             __PACKAGE__->json(JSON::Any->new);
33              
34             sub new {
35 39     39 1 1357 my $class = shift;
36 39         53 my($arg) = @_;
37 39 100 100     207 my $self = UNIVERSAL::isa($arg, $class) ? $arg
38             : { clocks => $class->json->jsonToObj($arg || '{}') };
39 39         752 bless $self, $class;
40             }
41              
42             sub serialize {
43 4     4 1 2419 my $self = shift;
44 4         13 $self->json->objToJson($self->clocks);
45             }
46              
47             sub increment {
48 4     4 1 1850 my $self = shift;
49 4         12 $self->clocks->{ $self->id }++; # increment its own clock
50 4         56 $self;
51             }
52              
53             sub merge {
54 3     3 1 651 my $self = shift;
55 3         4 my($other) = @_;
56 3         11 $other = __PACKAGE__->new($other);
57 3         9 my @ids = _list_ids($self, $other);
58 3         68 for my $id (@ids) {
59 5   100     50 $self->clocks->{$id}
      50        
60             = max( ($self->clocks->{$id} || 0), ($other->clocks->{$id} || 0) );
61             }
62 3         83 $self;
63             }
64              
65             sub equal {
66 6     6 1 42 my @vcs = @_;
67 6         23 $_ = __PACKAGE__->new($_) for @vcs;
68 6         15 my @ids = _list_ids(@vcs);
69 6         59 for my $id (@ids) {
70 12 100 50     91 return 0
      50        
71             unless ($vcs[0]->clocks->{$id} || 0) == ($vcs[1]->clocks->{$id} || 0);
72             }
73 4         67 return 1;
74             }
75              
76 3     3 1 10 sub not_equal { !equal(@_) }
77              
78             sub order_vector_clocks :Export(:DEFAULT) {
79 4     4 1 5368 my($vcs) = @_;
80 4         7 my @vcs;
81 4         28 while (my($id, $vc) = each %$vcs) {
82 8         20 $vc = __PACKAGE__->new($vc);
83 8         20 $vc->{_id} = $id;
84 8         33 push @vcs, $vc;
85             }
86 4         16 @vcs = sort { _compare($b, $a) } @vcs;
  5         12  
87 4         14 _pack_independent_vector_clocks(@vcs);
88 2     2   13592 }
  2         2  
  2         25  
89              
90             sub _pack_independent_vector_clocks {
91 4     4   9 my @vcs = @_;
92 4         6 my @ret;
93 4         6 my $i = 0;
94 4         13 while ($i < @vcs) {
95 4         10 my @equals = (
96             $vcs[$i],
97 6         21 (grep { _compare($vcs[$i], $_) == 0 } @vcs[($i+1)..$#vcs]),
98             );
99 6 100       31 push @ret, @equals == 1 ? $equals[0]->{_id} : [ map $_->{_id}, @equals ];
100 6         19 $i += @equals;
101             }
102 4         29 @ret;
103             }
104              
105             sub are_independent {
106 0     0 1 0 my @vcs = @_;
107 0         0 for (my $j = 0; $j < @vcs; $j++) {
108 0         0 for (my $k = $j+1; $k < @vcs; $k++) {
109 0 0       0 return 1 if $vcs[$j]->_is_independent($vcs[$k]);
110             }
111             }
112             }
113              
114             sub _is_independent {
115 0     0   0 my $self = shift;
116 0         0 my($other) = @_;
117 0         0 $other = __PACKAGE__->new($other);
118 0         0 my @ids = _list_ids($self, $other);
119 0         0 my $res = 0;
120 0         0 for my $id (@ids) {
121 0   0     0 my $r = ($self->clocks->{$id} || 0) - ($other->clocks->{$id} || 0);
      0        
122 0 0       0 if ($res == 0 ) { $res = $r }
  0 0       0  
    0          
123             elsif ($r == 0 ) { }
124 0         0 elsif ($res != $r) { return 1 }
125             }
126 0         0 0;
127             }
128              
129             sub _compare {
130 9     9   17 my $self = shift;
131 9         14 my($other) = @_;
132 9         21 $other = __PACKAGE__->new($other);
133 9         17 my @ids = _list_ids($self, $other);
134 9         97 my $res = 0;
135 9         16 for my $id (@ids) {
136 24   100     63 my $r = ($self->clocks->{$id} || 0) - ($other->clocks->{$id} || 0);
      100        
137 24 100       290 if ($res == 0 ) { $res = $r }
  14 100       28  
    50          
138             elsif ($r == 0 ) { }
139 4         17 elsif ($res != $r) { return 0 } # independent
140             }
141 5         20 $res;
142             }
143              
144 18     18   31 sub _list_ids { uniq map { keys %{ $_->clocks } } @_ }
  40         142  
  40         101  
145              
146             1; # Magic true value required at end of module
147             __END__