File Coverage

blib/lib/Math/Logic/Ternary/Trit.pm
Criterion Covered Total %
statement 184 184 100.0
branch 71 72 98.6
condition 15 15 100.0
subroutine 63 63 100.0
pod 31 31 100.0
total 364 365 99.7


line stmt bran cond sub pod time code
1             # Copyright (c) 2012-2017 Martin Becker, Blaubeuren. All rights reserved.
2             # This package is free software; you can redistribute it and/or modify it
3             # under the same terms as Perl itself.
4              
5             package Math::Logic::Ternary::Trit;
6              
7 16     16   153747 use 5.008;
  16         59  
8 16     16   98 use strict;
  16         38  
  16         352  
9 16     16   80 use warnings;
  16         43  
  16         460  
10 16     16   77 use Carp qw(croak);
  16         49  
  16         894  
11 16     16   90 use Scalar::Util qw(blessed);
  16         32  
  16         720  
12 16     16   3603 use Role::Basic qw(with);
  16         128184  
  16         127  
13             with qw(Math::Logic::Ternary::Object);
14              
15             our $VERSION = '0.004';
16             our @CARP_NOT = qw(Math::Logic::Ternary Math::Logic::Ternary::Word);
17              
18             # ----- auxiliary constants -----
19              
20 16     16   1607 use constant TRIT_PREFIX => '$';
  16         37  
  16         1038  
21              
22 16     16   94 use constant _UINT => 0;
  16         34  
  16         685  
23 16     16   110 use constant _INT => 1;
  16         32  
  16         649  
24 16     16   89 use constant _NAME => 2;
  16         33  
  16         627  
25 16     16   90 use constant _PNAME => 3;
  16         31  
  16         679  
26 16     16   87 use constant _IS_NIL => 4;
  16         29  
  16         666  
27 16     16   85 use constant _IS_TRUE => 5;
  16         43  
  16         653  
28 16     16   82 use constant _IS_FALSE => 6;
  16         30  
  16         648  
29 16     16   94 use constant _BOOL => 7;
  16         35  
  16         676  
30 16     16   85 use constant _MODINT => 8;
  16         31  
  16         695  
31              
32 16     16   110 use constant _MAX_MEMOIZED_OPS => 364;
  16         30  
  16         21272  
33              
34             # class backing data type for logical values: singleton arrayref
35              
36             my @trits =
37             my ($nil, $true, $false) = map { bless $_ } (
38             [0, 0, 'nil' ],
39             [1, 1, 'true' ],
40             [2, -1, 'false'],
41             );
42             foreach my $trit ($nil, $true, $false) {
43             $trit->[_PNAME] = TRIT_PREFIX . $trit->[_NAME];
44             $trit->[_IS_NIL] = $trit->[_UINT] == $nil->[_UINT];
45             $trit->[_IS_TRUE] = $trit->[_UINT] == $true->[_UINT];
46             $trit->[_IS_FALSE] = $trit->[_UINT] == $false->[_UINT];
47             $trit->[_BOOL] = $trit->[_IS_NIL]? undef: $trit->[_IS_TRUE];
48             }
49              
50             # return values for trit conversions
51             my %by_name =
52             map {
53             ($_->[_NAME] => $_, $_->[_PNAME] => $_)
54             } @trits;
55              
56             # tables for parameter to index mappings
57             my @arg3s = (
58             [[0, 1, 2], [1, 3, 4], [2, 4, 5]],
59             [[1, 3, 4], [3, 6, 7], [4, 7, 8]],
60             [[2, 4, 5], [4, 7, 8], [5, 8, 9]],
61             );
62             my @arg4s = (
63             \@arg3s,
64             [
65             [[1, 3, 4], [3, 6, 7], [4, 7, 8]],
66             [[3, 6, 7], [6, 10, 11], [7, 11, 12]],
67             [[4, 7, 8], [7, 11, 12], [8, 12, 13]],
68             ],
69             [
70             [[2, 4, 5], [4, 7, 8], [5, 8, 9]],
71             [[4, 7, 8], [7, 11, 12], [8, 12, 13]],
72             [[5, 8, 9], [8, 12, 13], [9, 13, 14]],
73             ],
74             );
75              
76             # generic op prefixes
77             my %arity = (
78             c => 0,
79             u => 1,
80             b => 2,
81             s => 3,
82             t => 3,
83             q => 4,
84             Q => 4,
85             );
86              
87             # named operators
88             my @named_ops = (
89             [sn => 'u000'], # Set to Nil
90             [st => 'u111'], # Set to True
91             [sf => 'u222'], # Set to False
92             [id => 'u012'], # IDentity
93             [not => 'u021'], # NOT
94             [up => 'u120'], # increment modulo 3, UP one
95             [nup => 'u210'], # swap nil/false, Not(UP(x))
96             [dn => 'u201'], # decrement modulo 3, DowN one
97             [ndn => 'u102'], # swap nil/true, Not(DowN(x))
98             [eqn => 'u122'], # EQual to Nil
99             [eqt => 'u212'], # EQual to True
100             [eqf => 'u221'], # EQual to False
101             [nen => 'u211'], # Not Equal to Nil
102             [net => 'u121'], # Not Equal to True
103             [nef => 'u112'], # Not Equal to False
104             [hm => 'u011'], # HaMlet (x or not x)
105             [uhm => 'u110'], # Up & HaMlet
106             [dhm => 'u101'], # Down & HaMlet
107             [orn => 'u010'], # OR Nil
108             [uorn => 'u100'], # Up & OR Nil
109             [dorn => 'u001'], # Down & OR Nil
110             [qt => 'u022'], # QuanTum (x and not x)
111             [uqt => 'u220'], # Up & QuanTum
112             [dqt => 'u202'], # Down & QuanTum
113             [ann => 'u002'], # ANd Nil
114             [uann => 'u020'], # Up & ANd Nil
115             [dann => 'u200'], # Down & ANd Nil
116             [and => 'b002012222'], # AND
117             [or => 'b010111012'], # OR
118             [xor => 'b000021012'], # eXclusive OR
119             [eqv => 'b000012021'], # EQuiValent
120             [imp => 'b010012111'], # IMPlication (x ==> y)
121             [rep => 'b001111021'], # REPlication (x <== y)
122             [nand => 'b001021111'], # Not AND
123             [nor => 'b020222021'], # Not OR
124             [cmp => 'b021101220'], # CoMPare, false < nil < true
125             [asc => 'b012202110'], # ASCending
126             [tlr => 'b002012222'], # The LesseR
127             [tgr => 'b010111012'], # The GreateR
128             [eq => 'b122212221'], # EQual to
129             [ne => 'b211121112'], # Not Equal to
130             [lt => 'b212222112'], # Less Than
131             [ge => 'b121111221'], # Greater or Equal
132             [gt => 'b221121222'], # Greater Than
133             [le => 'b112212111'], # Less or Equal
134             [cmpu => 'b022102110'], # CoMPare (Unbalanced), nil < true < false
135             [ascu => 'b011201220'], # ASCending (Unbalanced)
136             [tlru => 'b000011012'], # The LesseR (Unbalanced)
137             [tgru => 'b012112222'], # The GreateR (Unbalanced)
138             [ltu => 'b211221222'], # Less Than (Unbalanced)
139             [geu => 'b122112111'], # Greater or Equal (Unbalanced)
140             [gtu => 'b222122112'], # Greater Than (Unbalanced)
141             [leu => 'b111211221'], # Less or Equal (Unbalanced)
142             [incr => 'b012120201'], # INCRement
143             [incc => 'b000010002'], # INCrement Carry
144             [inccu => 'b000001011'], # INCrement Carry (Unbalanced)
145             [inccv => 'b001000020'], # INCrement Carry (negatiVe base)
146             [decr => 'b021102210'], # DECRement
147             [decc => 'b000002010'], # DECrement Carry
148             [deccu => 'b011001000'], # DECrement Carry (Unbalanced)
149             [deccv => 'b020000001'], # DECrement Carry (negatiVe base)
150             [pty => 'b021210102'], # PariTY
151             [dpl => 'b012201120'], # DuPLicate
152             [dplc => 'b000110202'], # DuPLication Carry
153             [dplcu => 'b000011112'], # DuPLication Carry (Unbalanced)
154             [dplcv => 'b001020222'], # DuPLication Carry (negatiVe base)
155             # [hlv => 'b011022211'], # HaLVe
156             # [hlvc => 'b011100100'], # HaLVing Carry
157             # [hlvs => 'b002010002'], # HaLVing Second carry
158             # [hlvu => 'b000000000'], # HaLVe (Unbalanced)
159             # [hlvcu => 'b000000000'], # HaLVing Carry (Unbalanced)
160             # [hlvsu => 'b000000000'], # HaLVing Second carry (Unbalanced)
161             [negcv => 'b000100110'], # NEGation Carry (negatiVe base)
162             [mulcu => 'b000000001'], # MULtiplication Carry (Unbalanced)
163             [add => 's0122010120'], # ADDition
164             [addc => 's0001021002'], # ADDition Carry
165             [addcu => 's0000111112'], # ADDition Carry (Unbalanced)
166             [addcv => 't001000020000020220020220222'], # ADDition Carry (negatiVe base)
167             [addcx => 't001000101000020001020220000'], # ADDition Carry (miXed base)
168             [subt => 't021210102102021210210102021'], # SUBTraction
169             [subc => 't000010002002000202010110000'], # SUBTraction Carry
170             [subcu => 't011111112001011111000001011'], # SUBTraction Carry (Unbal.)
171             [subcv => 't020220222000020220001000020'], # SUBTraction Carry (nV.b.)
172             # [amn => 't000000000000000000000000000'], # Arithmetic MeaN
173             # [amnc => 't000000000000000000000000000'], # Arithmetic MeaN Carry
174             # [amncu => 't000000000000000000000000000'], # Arithmetic MeaN Carry (Unbal.)
175             [ipqc => 's0211020210'], # InterPolation Quadratic Coeff
176             [cmin => 't000221000121121121000220000'], # ternary Comparison to MINimum
177             [cmed => 't121102121010212010121001121'], # ternary Comparison to MEDian
178             [cmax => 't212010212202000202212112212'], # ternary Comparison to MAXimum
179             [cvld => 't100010001001111021010012111'], # ternary Comparison VaLiDation
180             [min => 's0020221222'], # MINimum of three
181             [med => 's0001021122'], # MEDian of three
182             [max => 's0101101112'], # MAXimum of three
183             [minu => 's0000001112'], # MINimum of three (Unbalanced)
184             [medu => 's0001121122'], # MEDian of three (Unbalanced)
185             [maxu => 's0121221222'], # MAXimum of three (Unbalanced)
186             [sum => 'q012201012012012'], # SUMmation
187             [sumc => 'q000102100211022'], # SUMmation Carry
188             [sumcu => 'q000011111211222'], # SUMmation Carry (Unbalanced)
189             );
190             # names of arithmetic operators with mode-dependent variants
191             my %is_ar = map {($_ => 0, $_ . 'u' => 1, $_ . 'v' => 2)} qw(
192             asc cmp ge gt le lt max med min tgr tlr
193             addc decc dplc incc negc subc sumc
194             );
195              
196             # ----- private variables -----
197              
198             # operator memoizer
199             # initialized with some special cases
200             # maps name to [argc, sub]
201             my %OP = (
202             'c0' => [0, sub { 0 }],
203             'c1' => [0, sub { 1 }],
204             'c2' => [0, sub { 2 }],
205             'u012' => [1, sub { $_[0] }],
206             );
207              
208             # ----- other initializations -----
209              
210             _load_generated_methods();
211              
212             # ----- private subroutines -----
213              
214             # raw unary op factory, takes 3 values
215             sub _unary {
216 385     385   1136 my @val = @_;
217 385     36625   1436 return sub { $val[$_[0]] };
  36625         125725  
218             }
219              
220             # argument shifter, takes 1 operator, yields 1 operator
221             sub _shiftarg {
222 17     17   48 my $op = $_[0];
223 17     71   115 return sub { shift; $op->(@_) };
  71         93  
  71         135  
224             }
225              
226             # argument chooser, takes 3 operators, yields 1 operator with extra parameter
227             sub _mpx {
228 1610     1610   3292 my @op = @_;
229 1610     34494   6508 return sub { my $i = shift; $op[$i]->(@_) };
  34494         43375  
  34494         58972  
230             }
231              
232             # symmetric ternary op factory, takes 10 values
233             sub _symmetric_3adic {
234 160     160   649 my @val = @_;
235 160     4955   687 return sub { $val[$arg3s[$_[0]]->[$_[1]]->[$_[2]]] };
  4955         18689  
236             }
237              
238             # symmetric quaternary op factory, takes 15 values
239             sub _symmetric_4adic {
240 48     48   251 my @val = @_;
241 48     6156   237 return sub { $val[$arg4s[$_[0]]->[$_[1]]->[$_[2]]->[$_[3]]] };
  6156         21199  
242             }
243              
244             # raw operator factory, takes a name
245             sub _OP {
246 6855     6855   10595 my ($name) = @_;
247 6855 100       17501 return $OP{$name} if exists $OP{$name};
248 3133         3968 my $op;
249 3133 100       10703 if ($name =~ /^u([012])([012])([012])\z/) {
    100          
    100          
    100          
    100          
    100          
250 1297 100 100     4659 if ($1 eq $2 && $1 eq $3) {
251 929         3317 return $OP{"c$1"};
252             }
253 368         631 $op = [1, _unary($1, $2, $3)];
254             }
255             elsif ($name =~ /^b([012]{3})([012]{3})([012]{3})\z/) {
256 1462 100 100     4291 if ($1 eq $2 && $1 eq $3) {
257 17         78 return $OP{$name} = [2, _shiftarg(_unary(split //, $1))];
258             }
259 1445         4127 $op = [2, _mpx(map {_OP($_)->[1]} "u$1", "u$2", "u$3")];
  4335         7510  
260             }
261             elsif ($name =~ /^s([012]{10})\z/) {
262 160         685 $op = [3, _symmetric_3adic(split //, $1)];
263             }
264             elsif ($name =~ /^t([012]{9})([012]{9})([012]{9})\z/) {
265 164         658 $op = [3, _mpx(map {_OP($_)->[1]} "b$1", "b$2", "b$3")];
  492         865  
266             }
267             elsif ($name =~ /^q([012]{15})\z/) {
268 48         241 $op = [4, _symmetric_4adic(split //, $1)];
269             }
270             elsif ($name =~ /^Q([012]{27})([012]{27})([012]{27})\z/) {
271 1         12 $op = [4, _mpx(map {_OP($_)->[1]} "t$1", "t$2", "t$3")];
  3         9  
272             }
273             else {
274 1         116 croak qq{unknown operator name "$name"};
275             }
276 2186 100       5527 if (keys(%OP) < _MAX_MEMOIZED_OPS) {
277 1933         4227 $OP{$name} = $op;
278             }
279 2186         4532 return $op;
280             }
281              
282             sub _generic {
283 2025     2025   2645 my ($argc, $op) = @{_OP($_[0])};
  2025         3385  
284             return sub {
285 50286 100   50286   99386 if (@_ < $argc) {
286 1         6 my $missing = $argc - @_;
287 1         180 croak "too few arguments, expected $missing more";
288             }
289 50285         94123 my @args = map { $_->res_mod3 } @_[0..$argc-1];
  113181         164806  
290 50285         84996 return $trits[ $op->(@args) ];
291 2024         12772 };
292             }
293              
294             sub _load_generated_methods {
295 16     16   40 foreach my $arec (@named_ops) {
296 1456         2335 my ($method, $gen_method) = @{$arec};
  1456         2662  
297             # use fully qualified method names to avoid clashes with builtins
298 1456         2727 my $tm = __PACKAGE__ . '::' . $method;
299 16     16   125 no strict 'refs';
  16         34  
  16         19299  
300 1456         2393 *$tm = _generic($gen_method);
301             }
302             }
303              
304             # ----- class methods -----
305              
306 92     92 1 966 sub nil { $nil }
307 53     53 1 566 sub true { $true }
308 55     55 1 585 sub false { $false }
309              
310             sub from_bool {
311 11     11 1 420 my $bool = $_[1];
312 11 100       40 return $true if $bool;
313 7 100       19 return $false if defined $bool;
314 3         30 return $nil;
315             }
316              
317 5     5 1 1090 sub from_sign { $trits[$_[1] <=> 0] }
318 5     5 1 1075 sub from_remainder { $trits[$_[1] % 3] }
319              
320             sub from_int {
321 361     361 1 2032 my $int = $_[1];
322 361 100 100     1229 croak qq{integer "$int" out of range -1..1} if $int < -1 || 1 < $int;
323 357         794 return $trits[$int];
324             }
325              
326             sub from_int_u {
327 383     383 1 3753 my $int = $_[1];
328 383 100 100     1128 croak qq{integer "$int" out of range 0..2} if $int < 0 || 2 < $int;
329 381         692 return $trits[$int];
330             }
331              
332             sub from_string {
333 20     20 1 1177 my $name = lc $_[1];
334 20 100       353 croak qq{unknown trit name "$_[1]"} if !exists $by_name{$name};
335 17         49 return $by_name{$name};
336             }
337              
338             sub from_modint {
339 3     3 1 3816 my $mi = $_[1];
340 3         7 my ($mod, $res) = eval { $mi->modulus, $mi->residue };
  3         23  
341 3 100 100     529 croak qq{modular integer with modulus 3 expected} if !$mod || 3 != $mod;
342 1         6 return $trits[$res];
343             }
344              
345             sub from_various {
346 13     13 1 4360 my ($class, $item) = @_;
347 13         40 my $type = blessed $item;
348 13 100       93 if ($type) {
349 6 100       14 if (eval { $item->DOES('Math::Logic::Ternary::Object') }) {
  6         68  
350 1         15 return $class->from_int($item->as_int);
351             }
352 5 100       12 if (eval { $item->isa('Math::BigInt') }) {
  5         26  
353 3         10 my $is_two = 2 == $item; # for Devel::Cover
354 3 100       338 return $is_two? $false: $class->from_int($item);
355             }
356 2 100       6 if (eval { $item->isa('Math::ModInt') }) {
  2         17  
357 1         5 return $class->from_modint($item);
358             }
359 1         88 croak qq{cannot convert "$type" object to a trit};
360             }
361 7         12 $type = ref $item;
362 7 100       15 if ($type) {
363 1         98 croak qq{cannot convert $type reference to a trit};
364             }
365 6 100       13 if (!defined $item) {
366 1         4 return $nil;
367             }
368 5 100       27 if ($item =~ /^[\+\-]?\d+\z/) {
369 3 100       11 return 2 == $item? $false: $class->from_int($item);
370             }
371 2         7 return $class->from_string($item);
372             }
373              
374 2     2 1 868 sub make_generic { _generic($_[1]) }
375              
376             sub trit_operators {
377             return (
378             [nil => 0, 0, 1],
379             [true => 0, 0, 1],
380             [false => 0, 0, 1],
381             (
382             map {
383 20     20 1 126 my ($name, $gname) = @{$_};
  1820         2350  
  1820         2894  
384             [
385             $name,
386             $arity{substr $gname, 0, 1},
387             0,
388             1,
389 1820 100       5054 exists($is_ar{$name})? $is_ar{$name}: ()
390             ]
391             }
392             @named_ops
393             ),
394             [mpx => 4, 0, 1],
395             );
396             }
397              
398             # ----- object methods -----
399              
400             sub Mpx {
401 341 100   341 1 755 if (@_ < 4) {
402 2         7 my $missing = 4 - @_;
403 2         215 croak "too few arguments, expected $missing more";
404             }
405 339         564 my ($this, $case_n, $case_t, $case_f) = @_;
406 339         584 return ($case_n, $case_t, $case_f)[$this->res_mod3];
407             }
408              
409 109     109 1 1448 sub mpx { $trits[shift->Mpx(@_)->res_mod3] }
410              
411             sub generic {
412 567     567 1 2183 my ($this, $method, @params) = @_;
413 567         1126 return _generic($method)->($this, @params);
414             }
415              
416 44780     44780 1 153037 sub is_nil { $_[0]->[_IS_NIL] }
417 966     966 1 3649 sub is_true { $_[0]->[_IS_TRUE] }
418 471     471 1 1391 sub is_false { $_[0]->[_IS_FALSE] }
419 8748     8748 1 28130 sub as_bool { $_[0]->[_BOOL] }
420              
421             sub as_modint {
422 2     2 1 5 my ($this) = @_;
423 2         3 my $mi = $this->[_MODINT];
424 2 100       7 if (!defined $mi) {
425 1 50       2 eval { require Math::ModInt }
  1         9  
426             or croak 'perl extension Math::ModInt is not available';
427 1         6 $mi = $this->[_MODINT] = Math::ModInt->new($this->[_UINT], 3);
428             }
429 2         54 return $mi;
430             }
431              
432             # role: ternary object
433              
434 14 100   14 1 490 sub is_equal { $_[1]->Rtrits <= 1 && $_[0]->as_int == $_[1]->Trit(0)->as_int }
435 30 100   30 1 164 sub Rtrits { $_[0]->[_IS_NIL]? (wantarray? (): 0): (wantarray? $_[0]: 1) }
    100          
    100          
436              
437 11     11 1 876 sub Sign { $_[0] }
438 23 100   23 1 100 sub Trit { ($_[0])[$_[1]] || $nil }
439 204 100   204 1 514 sub Trits { wantarray? $_[0]: 1 }
440 25208     25208 1 96519 sub as_int { $_[0]->[_INT] }
441 19043     19043 1 113599 sub as_int_u { $_[0]->[_UINT] }
442 30     30 1 80 sub as_int_v { $_[0]->[_UINT] }
443 113628     113628 1 191237 sub res_mod3 { $_[0]->[_UINT] }
444 3     3 1 13 sub as_string { $_[0]->[_PNAME] }
445              
446             1;
447              
448             __END__