File Coverage

blib/lib/Math/Revhash.pm
Criterion Covered Total %
statement 48 50 96.0
branch 22 24 91.6
condition 5 6 83.3
subroutine 12 12 100.0
pod 5 6 83.3
total 92 98 93.8


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::Revhash;
6              
7 4     4   276360 use 5.008;
  4         39  
8 4     4   22 use strict;
  4         8  
  4         106  
9 4     4   22 use warnings FATAL => 'all';
  4         8  
  4         145  
10 4     4   21 use Carp;
  4         7  
  4         308  
11 4     4   27 use Exporter 'import';
  4         7  
  4         122  
12 4     4   4908 use Math::BigInt;
  4         115149  
  4         20  
13              
14             our $VERSION = '0.01';
15             $VERSION =~ tr/_//d;
16              
17             our @EXPORT_OK = qw( revhash revunhash );
18              
19             # Fast hashing is based on reverse modulo operations.
20             # Given HASH = NUMBER * A % C, then NUMBER = HASH * B % C in case of B is
21             # a modular inverse of A.
22             # To avoid hash collisions A should be a primary number.
23              
24             # Pre-defined $A and $B for certain lengths
25             my $AB = {
26             1 => [ 103, 7 ],
27             2 => [ 929, 69 ],
28             3 => [ 1619, 979 ],
29             4 => [ 17027, 9963 ],
30             5 => [ 88651, 58851 ],
31             6 => [ 894407, 991543 ],
32             7 => [ 16006519, 8315079 ],
33             8 => [ 130067887, 91167823 ],
34             9 => [ 2700655597, 882024933 ],
35             };
36              
37             # See UNSAFE MODE
38             our $UNSAFE = 0;
39              
40             # Parse and prepare arguments
41             # This function is used by revhash, revunhash and new subroutines
42             sub argsparse {
43 224437     224437 0 431441 my ($data, $len, $A, $B) = @_;
44              
45 224437 100       483542 croak "data not defined" unless defined $data;
46 224436 100 100     750684 croak "Invalid length specified" unless defined $len and $len > 0;
47              
48 224433         359293 my $C = 10 ** $len;
49              
50 224433 100 66     717108 croak "data ($data) is out of range" unless $data > 0 and $data < $C;
51              
52 224431 100       384690 if (defined $A) {
53 2217 100       3849 croak "Hash A value is invalid" unless $A > 0;
54             } else {
55 222214         409554 $A = $AB->{$len}->[0];
56             }
57 224430 100       384004 croak "Hash A value is undefined" unless defined $A;
58              
59 224429 100       349325 if (defined $B) {
60 2215 100       3673 croak "Hash B value is invalid" unless $B > 0;
61             } else {
62 222214         331804 $B = $AB->{$len}->[1];
63             }
64 224428 100       374902 $B = Math::BigInt->bmodinv($A, $C) unless defined $B;
65 224428 100       549629 croak "Invalid B value for such length and A" if Math::BigInt->is_nan($B);
66              
67 224427         17201787 return ($data, $len, $A, $B, $C);
68             }
69              
70             # Calculate hash of number
71             # args: $number, $length, $A, $B
72             sub revhash {
73 112222 50   112222 1 62119837 if ($UNSAFE) {
74 0         0 $_[4] = 10 ** $_[1];
75             } else {
76 112222         257797 @_ = argsparse @_;
77             }
78              
79 112212         622476 sprintf "%0$_[1]d", $_[0] * $_[2] % $_[4];
80             }
81              
82             # Calculate original number of hash
83             # args: $hash, $length, $A, $B
84             sub revunhash {
85 112212 50   112212 1 381422 if ($UNSAFE) {
86 0         0 $_[4] = 10 ** $_[1];
87             } else {
88 112212         185933 @_ = argsparse @_;
89             }
90              
91 112212         268873 $_[0] * $_[3] % $_[4];
92             }
93              
94             # OO alias
95 1107     1107 1 616390 sub hash { @_ = ($_[1], @{$_[0]}); goto &revhash }
  1107         3144  
  1107         3607  
96              
97             # OO alias
98 1107     1107 1 3560 sub unhash { @_ = ($_[1], @{$_[0]}); goto &revunhash }
  1107         2547  
  1107         2833  
99              
100             # OO ctor
101             # args: $class, $length, $A, $B
102             sub new {
103 3     3 1 2050 my $obj;
104              
105 3         14 (undef, @{$obj}) = argsparse(1, @_[1..3]);
  3         9  
106              
107 3         10 bless $obj, $_[0];
108             }
109              
110             1; # End of Math::Revhash
111              
112             __END__