File Coverage

blib/lib/Math/Logic/Ternary/Calculator/Operator.pm
Criterion Covered Total %
statement 101 250 40.4
branch 10 82 12.2
condition 4 11 36.3
subroutine 26 52 50.0
pod 0 11 0.0
total 141 406 34.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::Calculator::Operator;
6              
7 4     4   70 use 5.008;
  4         12  
8 4     4   17 use strict;
  4         12  
  4         66  
9 4     4   16 use warnings;
  4         7  
  4         96  
10 4     4   20 use Carp qw(croak);
  4         14  
  4         165  
11 4     4   1291 use Math::Logic::Ternary::Trit;
  4         12  
  4         180  
12 4     4   1575 use Math::Logic::Ternary::Word;
  4         15  
  4         180  
13 4     4   1215 use Math::Logic::Ternary::Calculator::Mode;
  4         9  
  4         145  
14              
15             our $VERSION = '0.004';
16              
17 4     4   21 use constant TRIT => Math::Logic::Ternary::Trit::;
  4         9  
  4         176  
18 4     4   18 use constant WORD => Math::Logic::Ternary::Word::;
  4         8  
  4         142  
19 4     4   18 use constant MODE => Math::Logic::Ternary::Calculator::Mode::;
  4         7  
  4         134  
20              
21             # indexes into object attributes
22 4     4   19 use constant _GEN_NAME => 0;
  4         5  
  4         127  
23 4     4   18 use constant _VAR_NAME => 1;
  4         13  
  4         146  
24 4     4   19 use constant _MIN_ARGS => 2;
  4         8  
  4         168  
25 4     4   20 use constant _VAR_ARGS => 3;
  4         7  
  4         139  
26 4     4   22 use constant _RET_VALS => 4;
  4         8  
  4         167  
27 4     4   20 use constant _OP_KIND => 5;
  4         7  
  4         151  
28 4     4   18 use constant _IS_ARITH => 6;
  4         7  
  4         142  
29              
30 4     4   45 use constant K_LOGICAL => 0;
  4         7  
  4         137  
31 4     4   20 use constant K_TRITWISE => 1;
  4         7  
  4         147  
32 4     4   20 use constant K_NUMERICAL => 2;
  4         7  
  4         134  
33 4     4   18 use constant K_VIEWING => 3;
  4         15  
  4         142  
34 4     4   18 use constant KINDS => 4;
  4         6  
  4         145  
35              
36 4     4   20 use constant _OK_OPERATORS => 0;
  4         7  
  4         142  
37 4     4   23 use constant _OK_TITLE => 1;
  4         8  
  4         169  
38              
39 4     4   21 use constant MAX_COLUMNS => 70;
  4         5  
  4         9176  
40              
41             my %operators = ();
42             my @by_kind = ();
43             my @op_kinds = (
44             'Logical Operators',
45             'Tritwise Logical Operators',
46             'Numerical Operators',
47             'Viewing Functions',
48             );
49             my %glossary = (
50             sn => 'set to nil',
51             st => 'set to true',
52             sf => 'set to false',
53             id => 'identity',
54             not => 'not',
55             up => 'up one: increment modulo 3',
56             nup => 'not up: swap nil/false',
57             dn => 'down one: decrement modulo 3',
58             ndn => 'not down: swap nil/true',
59             eqn => 'equal to nil',
60             eqt => 'equal to true',
61             eqf => 'equal to false',
62             nen => 'not equal to nil',
63             net => 'not equal to true',
64             nef => 'not equal to false',
65             hm => 'hamlet: x or not x',
66             uhm => 'up & hamlet',
67             dhm => 'down & hamlet',
68             orn => 'or nil',
69             uorn => 'up & orn',
70             dorn => 'down & orn',
71             qt => 'quantum: x and not x',
72             uqt => 'up & quantum',
73             dqt => 'down & quantum',
74             ann => 'and nil',
75             uann => 'up & ann',
76             dann => 'down & ann',
77             and => 'and',
78             or => 'or',
79             xor => 'exclusive or',
80             eqv => 'equivalent',
81             imp => 'implication (x ==> y)',
82             rep => 'replication (x <== y)',
83             nand => 'not and',
84             nor => 'not or',
85             cmp => 'compare (false < nil < true)',
86             asc => 'ascending (false < nil < true)',
87             tlr => 'the lesser (false < nil < true)',
88             tgr => 'the greater (false < nil < true)',
89             eq => 'equal to',
90             ne => 'not equal to',
91             lt => 'less than (false < nil < true)',
92             ge => 'greater or equal (false < nil < true)',
93             gt => 'greater than (false < nil < true)',
94             le => 'less or equal (false < nil < true)',
95             cmpu => 'compare (unbalanced, nil < true < false)',
96             ascu => 'ascending (unbalanced, nil < true < false)',
97             tlru => 'the lesser (unbalanced, nil < true < false)',
98             tgru => 'the greater (unbalanced, nil < true < false)',
99             ltu => 'less than (unbalanced, nil < true < false)',
100             geu => 'greater or equal (unbalanced, nil < true < false)',
101             gtu => 'greater than (unbalanced, nil < true < false)',
102             leu => 'less or equal (unbalanced, nil < true < false)',
103             incr => 'increment',
104             incc => 'increment carry',
105             inccu => 'increment carry (unbalanced)',
106             inccv => 'increment carry (negative base)',
107             decr => 'decrement',
108             decc => 'decrement carry',
109             deccu => 'decrement carry (unbalanced)',
110             deccv => 'decrement carry (negative base)',
111             pty => 'parity',
112             dpl => 'duplicate',
113             dplc => 'duplication carry',
114             dplcu => 'duplication carry (unbalanced)',
115             dplcv => 'duplication carry (negative base)',
116             hlv => 'halve',
117             hlvc => 'halving carry',
118             hlvs => 'halving second carry',
119             hlvcu => 'halving carry (unbalanced)',
120             hlvsu => 'halving second carry (unbalanced)',
121             negcv => 'negation carry (negative base)',
122             mulcu => 'multiplication carry (unbalanced)',
123             add => 'addition',
124             addc => 'addition carry',
125             addcu => 'addition carry (unbalanced)',
126             addcv => 'addition carry (negative base)',
127             addcx => 'addition carry (mixed base)',
128             subt => 'subtraction',
129             subc => 'subtraction carry',
130             subcu => 'subtraction carry (unbalanced)',
131             subcv => 'subtraction carry (negative base)',
132             amn => 'arithmetic mean',
133             amnc => 'arithmetic mean carry',
134             amncu => 'arithmetic mean carry (unbalanced)',
135             cmin => 'ternary comparison to minimum',
136             cmed => 'ternary comparison to median',
137             cmax => 'ternary comparison to maximum',
138             cvld => 'ternary comparison validation',
139             iplc => 'interpolation linear coefficient',
140             ipqc => 'interpolation quadratic coefficient',
141             lco => 'linear combination',
142             min => 'minimum of three',
143             med => 'median of three',
144             max => 'maximum of three',
145             minu => 'minimum of three (unbalanced)',
146             medu => 'median of three (unbalanced)',
147             maxu => 'maximum of three (unbalanced)',
148             sum => 'summation',
149             sumc => 'summation carry',
150             sumcu => 'summation carry (unbalanced)',
151             mpx => 'multiplex',
152             Neg => 'negate',
153             Lshift=> 'left shift',
154             Rshift=> 'right shift',
155             Sign => 'sign',
156             Incr => 'increment',
157             Decr => 'decrement',
158             Dpl => 'duplicate',
159             Hlv => 'halve',
160             Cmp => 'compare',
161             Asc => 'ascending',
162             Gt => 'greater than',
163             Lt => 'lesser than',
164             Ge => 'greater or equal',
165             Le => 'lesser or equal',
166             Sort2 => 'sort two words',
167             Tlr => 'the lesser',
168             Tgr => 'the greater',
169             Add => 'add',
170             Subt => 'subtract',
171             Amn => 'arithmetic mean',
172             Sort3 => 'sort three words',
173             Min => 'minimum',
174             Med => 'median',
175             Max => 'maximum',
176             Mul => 'multiply',
177             Div => 'divide',
178             Ldiv => 'long division',
179             Sum => 'summation',
180             Mpx => 'multiplex',
181             );
182             my @trit_l = (' nil ', ' true ', ' false ');
183             my @trit_s = map { /(\w)/ } @trit_l;
184             my @table_funcs = (
185             \&_const,
186             \&_unary,
187             \&_binary,
188             \&_ternary,
189             \&_quaternary,
190             );
191              
192             _initialize_operators();
193              
194             sub _initialize_operators {
195 4     4   24 my @modes = MODE->modes;
196 4         20 foreach my $orec (TRIT->trit_operators) {
197 380         472 my ($name, $min_args, $var_args, $ret_vals) = @{$orec};
  380         640  
198 380         523 my $NAME = uc $name;
199 380         979 $by_kind[K_LOGICAL ]->{$NAME} = $operators{$NAME} =
200             bless [$NAME, $NAME, $min_args, $var_args, $ret_vals, K_LOGICAL];
201 380         915 $by_kind[K_TRITWISE]->{$name} = $operators{$name} =
202             bless [$name, $name, $min_args, $var_args, $ret_vals, K_TRITWISE];
203 380 100       678 if (exists $glossary{$name}) {
204 368         731 $glossary{$NAME} = $glossary{$name};
205             }
206             }
207 4         65 foreach my $orec (WORD->word_operators) {
208 292         432 my ($name, $min_args, $var_args, $ret_vals, $is_arith) = @{$orec};
  292         546  
209 292 100       635 my $base = $is_arith? $modes[$is_arith]->unapply($name): $name;
210 292         682 my $desc =
211             bless [$base, $name, $min_args, $var_args, $ret_vals, K_NUMERICAL];
212 292 100       510 if (defined $is_arith) {
213 280         506 $desc->[_IS_ARITH] = $is_arith;
214             (
215 280   100     746 $by_kind[K_NUMERICAL]->{$base} = $operators{$base} ||= []
216             )->[$is_arith] = $desc;
217 280 100       484 if ($base ne $name) {
218 184         461 ($operators{$name} = [])->[$is_arith] = $desc;
219             }
220 280 100 66     846 if (exists $glossary{$base} and !exists $glossary{$name}) {
221 184         406 my $mode = $modes[$is_arith]->name;
222 184         621 $glossary{$name} = $glossary{$base} . " ($mode)";
223             }
224             }
225             else {
226 12         34 $by_kind[K_NUMERICAL]->{$base} = $operators{$base} = $desc;
227             }
228             }
229 4         64 foreach my $frec (WORD->word_formatters) {
230             # not caring for variants: provide viewing functions in any mode
231 32         46 my ($name) = @{$frec};
  32         44  
232 32         95 $by_kind[K_VIEWING]->{$name} = $operators{$name} =
233             bless [$name, $name, 1, 0, 0, K_VIEWING];
234             }
235             }
236              
237             sub _quantity {
238 0     0     my ($min, $var, $item, $items) = @_;
239 0 0         if (!defined $items) {
240 0           $items = $item . 's';
241             }
242 0 0         if (!$min) {
243             return
244 0 0         !$var ? "no $items" :
    0          
    0          
245             $var < 0 ? "any number of $items":
246             1 == $var? "one optional $item" : "$var optional $items";
247             }
248 0 0         if (!$var) {
249 0 0         return 1 == $min? "one $item": "$min $items";
250             }
251 0 0         if ($var < 0) {
252 0 0         return 1 == $min? "at least one $item": "at least $min $items";
253             }
254 0           my $max = $min + $var;
255 0 0         return 1 == $var? "$min or $max $items": "$min to $max $items";
256             }
257              
258             sub _fmt_word {
259 0     0     my ($word) = @_;
260 0           return join q[ ], map { /(\w)/g } $word->as_string;
  0            
261             }
262              
263 0     0     sub _fmt_trits { "@trit_s[map { $_->res_mod3 } @_]" }
  0            
264              
265             sub _iterate {
266 0     0     my ($args, $balanced, $meth) = @_;
267 0           my $carry = TRIT->nil;
268 0 0         if (!$args) {
269             return sub {
270 0 0   0     if ($carry->is_nil) {
271 0           $carry = TRIT->true;
272 0           return $meth->();
273             }
274 0           return ();
275 0           };
276             }
277 0 0         my $inc = $balanced? 'Incr': 'Incru';
278 0           my $in = WORD->from_trits($args);
279 0 0         $in = $in->sf if $balanced;
280             return
281             sub {
282 0 0   0     if ($carry->is_nil) {
283 0           my @trits = reverse $in->Trits;
284 0           ($in, $carry) = $in->$inc;
285 0           return $meth->(@trits);
286             }
287 0           return ();
288 0           };
289             }
290              
291             sub _iowrap {
292 0     0     my ($meth) = @_;
293             return
294             sub {
295 0     0     my @input = @_;
296 0           return (\@input, $meth->(@input));
297 0           };
298             }
299              
300 0     0     sub _name { $trit_l[ $_[0]->res_mod3 ] }
301 0     0     sub _abbr { " @trit_s[map {$_->res_mod3} @_] " }
  0            
302              
303             sub _const {
304 0     0     my ($name, $balanced, $iterator) = @_;
305             return
306 0           join q[], map { " $_\n" }
  0            
307             $name,
308             '+-------+',
309             '|' . _name($iterator->()) . '|',
310             '+-------+';
311             }
312              
313             sub _unary {
314 0     0     my ($name, $balanced, $iterator) = @_;
315 0 0         my @idx = $balanced? (2, 0, 1): (0 .. 2);
316 0           my $desc = lc($name) . ' A';
317 0           my $pw = q[];
318 0           my $pl = q[];
319 0           my $ld = length $desc;
320 0 0         if ($ld <= 5) {
321 0           $desc .= q[ ] x (5 - $ld);
322             }
323             else {
324 0           $pw = q[ ] x ($ld - 5);
325 0           $pl = q[-] x ($ld - 5);
326             }
327             return
328 0           join q[], map { " $_\n" }
329             "+-------+-------$pl+",
330             "| A | $desc |",
331             "+-------+-------$pl+",
332 0           (map { "|$trit_l[$_]|" . _name($iterator->()) . "$pw|" } @idx),
  0            
333             "+-------+-------$pl+";
334             }
335              
336             sub _binary {
337 0     0     my ($name, $balanced, $iterator) = @_;
338 0 0         my @idx = $balanced? (2, 0, 1): (0 .. 2);
339 0           $name = lc $name;
340             return
341 0           join q[], map { " $_\n" }
342             "A $name B",
343             '+---+---------------------------+',
344             "| A | B @trit_l[@idx]|",
345             '| +---+-----------------------+',
346             (map {
347 0           "|$trit_l[$_]|" .
348 0           join(q[ ], map {_name($iterator->())} @idx) . '|'
  0            
349             } @idx),
350             '+-------+-----------------------+';
351             }
352              
353             sub _ternary {
354 0     0     my ($name, $balanced, $iterator) = @_;
355 0 0         my @idx = $balanced? (2, 0, 1): (0 .. 2);
356 0           $name = lc $name;
357             return
358 0           join q[], map { " $_\n" }
359             "$name A, B, C",
360             '+-------+---+---------------------------+',
361             "| A | B | C @trit_l[@idx]|",
362             '| | +---+-----------------------+',
363             (map {
364 0           my $a = $_;
  0            
365             $a == $idx[0]? (): '| | | |',
366             (map {
367 0 0         "|$trit_l[$a]|$trit_l[$_]|" .
368 0           join(q[ ], map {_name($iterator->())} @idx) . '|'
  0            
369             } @idx)
370             } @idx),
371             '+-------+-------+-----------------------+';
372             }
373              
374             sub _quaternary {
375 0     0     my ($name, $balanced, $iterator) = @_;
376 0 0         my @idx = $balanced? (2, 0, 1): (0 .. 2);
377 0           $name = lc $name;
378             return
379 0           join q[], map { " $_\n" }
380             "$name A, B, C, D",
381             '+-------+---+---------------------------+',
382             '| A | B | C ' .
383 0           join(q[ ], map {" @trit_s[($_) x 3] "} @idx) . '|',
384             '| | +---------------------------+',
385             '| | | D ' .
386 0           join(q[ ], map {" @trit_s[@idx] "} @idx) . '|',
387             '| | +---+-----------------------+',
388             (map {
389 0           my $a = $_;
  0            
390             $a == $idx[0]? (): '| | | |',
391             (map {
392 0 0         "|$trit_l[$a]|$trit_l[$_]|" .
393             join(q[ ],
394 0           map {_abbr(map {$iterator->()} @idx)} @idx
  0            
  0            
395             ) . '|'
396             } @idx)
397             } @idx),
398             '+-------+-------+-----------------------+';
399             }
400              
401             sub find {
402 0     0 0   my ($class, $raw_name, $mode) = @_;
403 0 0         return 'operator not defined' if !exists $operators{$raw_name};
404 0           my $this = $operators{$raw_name};
405 0 0         if ('ARRAY' eq ref $this) {
406 0           $this = $this->[$mode->ordinal];
407 0 0         if (!defined $this) {
408 0           my $mname = $mode->name;
409 0           return qq{operator not available in mode "$mname"};
410             }
411             }
412 0           return $this;
413             }
414              
415 0     0 0   sub operator_kinds { @op_kinds }
416              
417             sub operator_list {
418 0     0 0   my ($class, $mode, $kind) = @_;
419 0 0         my $ops = defined($kind)? $by_kind[$kind]: \%operators;
420 0           my $omode = $mode->ordinal;
421             return
422             map {
423 0           my $orec = $ops->{$_};
424 0 0         if ('ARRAY' eq ref $orec) {
425 0           $orec = $orec->[$omode];
426             }
427 0 0         defined($orec)? $orec->name: ()
428 0           } sort keys %{$ops};
  0            
429             }
430              
431             sub signature {
432 0     0 0   my ($this) = @_;
433 0           return @{$this}[_MIN_ARGS, _VAR_ARGS, _RET_VALS];
  0            
434             }
435              
436 0     0 0   sub generic_name { $_[0]->[_GEN_NAME] }
437 0     0 0   sub name { $_[0]->[_VAR_NAME] }
438 0     0 0   sub op_kind { $_[0]->[_OP_KIND ] }
439 0     0 0   sub is_arithmetic { $_[0]->[_IS_ARITH] }
440              
441             sub execute {
442 0     0 0   my ($this, $first_arg, @more_args) = @_;
443 0           my $name = $this->name;
444 0           return $first_arg->$name(@more_args);
445             }
446              
447             sub description {
448 0     0 0   my ($this, $mode) = @_;
449 0           my $name = $this->name;
450 0           my ($min_args, $var_args, $ret_vals) = $this->signature;
451 0           my $args = _quantity($min_args, $var_args, 'argument');
452 0           my $vals = _quantity($ret_vals, undef, 'result value');
453 0           my $kind = lc $op_kinds[$this->op_kind];
454 0           $kind =~ s/s\z//;
455 0           my $ari = $this->is_arithmetic;
456 0 0         if (defined $ari) {
457 0           $kind .= q[, ] . (MODE->modes)[$ari]->name . q[ arithmetic];
458             }
459 0           $kind =~ s/s\z//;
460 0 0         my $glos = exists($glossary{$name})? qq[ "$glossary{$name}"]: q[];
461 0           my $desc = <<"EOT";
462             $name$glos
463             $args, $vals
464             ($kind)
465             EOT
466 0 0 0       if (K_LOGICAL == $this->op_kind || K_TRITWISE == $this->op_kind) {
467 0           $desc .= $this->truth_table($mode->is_equal(MODE->balanced));
468             }
469 0           return $desc;
470             }
471              
472             sub truth_table {
473 0     0 0   my ($this, $balanced) = @_;
474 0           my $name = $this->name;
475 0           my ($min_args, $var_args, $ret_vals) = $this->signature;
476 0 0         my $max_args = 0 < $var_args? $min_args + $var_args: $min_args;
477 0           my $result = q[];
478 0 0         my $inc = $balanced? 'Incr': 'Incru';
479 0           my $meth = TRIT->can(lc $name);
480 0 0         return "(no truth table for $name)\n" if !$meth;
481 0           foreach my $args ($min_args .. $max_args) {
482 0 0         if ($result ne q[]) {
483 0           $result .= "\n";
484             }
485 0 0 0       if (1 == $ret_vals && $args < @table_funcs) {
486 0           my $it = _iterate($args, $balanced, $meth);
487 0           $result .= $table_funcs[$args]->($name, $balanced, $it);
488 0           next;
489             }
490 0           my $it = _iterate($args, $balanced, _iowrap($meth));
491 0           while (my ($in, @out) = $it->()) {
492             $result .=
493 0           q[ ] . $name . q[ ] . _fmt_trits(@{$in}) .
  0            
494             q[ => ] . _fmt_trits(@out) . "\n";
495             }
496             }
497 0           return $result;
498             }
499              
500             1;
501             __END__