File Coverage

blib/lib/Math/NumSeq/DigitSumModulo.pm
Criterion Covered Total %
statement 60 65 92.3
branch 8 14 57.1
condition 4 12 33.3
subroutine 18 18 100.0
pod 5 5 100.0
total 95 114 83.3


line stmt bran cond sub pod time code
1             # Copyright 2010, 2011, 2012, 2013, 2014 Kevin Ryde
2              
3             # This file is part of Math-NumSeq.
4             #
5             # Math-NumSeq is free software; you can redistribute it and/or modify
6             # it under the terms of the GNU General Public License as published by the
7             # Free Software Foundation; either version 3, or (at your option) any later
8             # version.
9             #
10             # Math-NumSeq is distributed in the hope that it will be useful, but
11             # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12             # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13             # for more details.
14             #
15             # You should have received a copy of the GNU General Public License along
16             # with Math-NumSeq. If not, see .
17              
18              
19             # Christopher Williamson, "An Overview of the Thue-Morse Sequence",
20             # www.math.washington.edu/~morrow/336_12/papers/christopher.pdf
21              
22              
23             package Math::NumSeq::DigitSumModulo;
24 1     1   2792 use 5.004;
  1         4  
  1         47  
25 1     1   8 use strict;
  1         2  
  1         38  
26 1     1   6 use List::Util 'sum';
  1         2  
  1         128  
27              
28 1     1   7 use vars '$VERSION', '@ISA';
  1         3  
  1         76  
29             $VERSION = 71;
30              
31 1     1   6 use Math::NumSeq;
  1         2  
  1         30  
32 1     1   6 use Math::NumSeq::Base::IterateIth;
  1         2  
  1         213  
33             @ISA = ('Math::NumSeq::Base::IterateIth',
34             'Math::NumSeq');
35             *_is_infinite = \&Math::NumSeq::_is_infinite;
36              
37 1     1   8 use Math::NumSeq::Repdigits;
  1         3  
  1         44  
38             *_digit_split_lowtohigh = \&Math::NumSeq::Repdigits::_digit_split_lowtohigh;
39              
40             # uncomment this to run the ### lines
41             #use Smart::Comments;
42              
43              
44 1     1   6 use Math::NumSeq::Base::Digits;
  1         1  
  1         97  
45 1         14 use constant parameter_info_array =>
46             [ Math::NumSeq::Base::Digits->parameter_info_list,
47             { name => 'modulus',
48             share_key => 'modulus_0',
49             type => 'integer',
50             display => Math::NumSeq::__('Modulus'),
51             default => 0,
52             minimum => 0,
53             width => 3,
54             description => Math::NumSeq::__('Modulus, or 0 to use the radix.'),
55             },
56 1     1   6 ];
  1         2  
57              
58 1     1   5 use constant i_start => 0;
  1         2  
  1         52  
59 1     1   5 use constant characteristic_smaller => 1;
  1         2  
  1         40  
60 1     1   6 use constant characteristic_integer => 1;
  1         1  
  1         53  
61 1     1   5 use constant values_min => 0;
  1         1  
  1         524  
62             sub values_max {
63 9     9 1 11 my ($self) = @_;
64 9 50       27 if (my $modulus = $self->{'modulus'}) {
65 9         31 return $modulus - 1;
66             }
67 0         0 return $self->{'radix'} - 1;
68             }
69              
70             # use constant name => Math::NumSeq::__('Digit Sum Modulo');
71             sub description {
72 2     2 1 9 my ($self) = @_;
73 2 100       6 if (ref $self) {
74 1         3 my $radix = $self->{'radix'};
75 1 50       4 my $modulus = ($self->{'modulus'} ? $self->{'modulus'} : $radix);
76 1 50 33     4 return Math::NumSeq::__('Sum digits of i in base ') . $radix
77             . Math::NumSeq::__(', then that sum taken modulo ') . $modulus
78             . ($radix == 2 && $modulus == 2
79             ? Math::NumSeq::__(", which means bitwise parity.")
80             : Math::NumSeq::__('.'));
81             } else {
82 1         4 return Math::NumSeq::__('Sum of the digits in the given radix, modulo that radix or a given modulus. Eg. for binary this is the bitwise parity.');
83             }
84             }
85              
86             # cf A001969 "evil" numbers with even 1s
87             # A000069 "odious" numbers with odd 1s
88             # A026147 position of n'th thue-morse parity 1
89             # A059448 1,0 parity of number of 0 digits when written in binary
90             # A001285 Thue-Morse as 1,2
91             # A010059 Thue-Morse as 1,0
92             # A010060 Thue-Morse as 0,1
93             # A106400 Thue-Morse as 1,-1 1, -1, -1, 1, -1, 1
94             # A186032 Thue-Morse as -1,1 offset one 1, 1, -1, 1, -1
95             # A108784 Thue-Morse as -1,1 1, 1, -1, 1, -1
96             # A076826 Thue-Morse as 0,2 with a(2n+1)=1 in between
97             # A080813 lexico is 1,1,0,1,1,0 then thue-morse 0,1 thue-morse
98             #
99             # A143579 alternately odious and evil, permutation of the integers
100             # A143580 alternately, modulo 2
101             # seems A143580 == A010059 thue-morse 1,0
102             #
103             my %oeis_anum = ('2,2' => 'A010060',
104             '3,3' => 'A053838',
105             '4,4' => 'A053839', # radix=4
106             '5,5' => 'A053840', # radix=5
107             '6,6' => 'A053841', # radix=6
108             '7,7' => 'A053842', # radix=7
109             '8,8' => 'A053843', # radix=8
110             '9,9' => 'A053844', # radix=9
111             '10,10' => 'A053837',
112             # OEIS-Catalogue: A053837
113             # OEIS-Catalogue: A053844 radix=9
114             # OEIS-Catalogue: A053843 radix=8
115             # OEIS-Catalogue: A053842 radix=7
116             # OEIS-Catalogue: A053841 radix=6
117             # OEIS-Catalogue: A053840 radix=5
118             # OEIS-Catalogue: A053839 radix=4
119             # OEIS-Catalogue: A053838 radix=3
120             # OEIS-Catalogue: A010060 radix=2 # binary, Thue-Morse
121              
122             '10,2' => 'A179081',
123             # OEIS-Catalogue: A179081 modulus=2
124              
125             '2,3' => 'A071858',
126             '2,4' => 'A179868',
127             # OEIS-Catalogue: A071858 radix=2 modulus=3
128             # OEIS-Catalogue: A179868 radix=2 modulus=4
129              
130             '3,4' => 'A051329', # ternary modulo 4
131             # OEIS-Catalogue: A051329 radix=3 modulus=4
132             );
133             sub oeis_anum {
134 1     1 1 5 my ($self) = @_;
135 1         3 my $radix = $self->{'radix'};
136 1   33     5 my $modulus = ($self->{'modulus'} || $radix);
137              
138 1 50       4 if ($modulus == 1) {
139 0         0 return 'A000004'; # all zeros
140             # OEIS-Other: A000004 modulus=1
141             }
142              
143             # radix==M+1, 2M+1 etc any radix==1modM is same as whole modulo M.
144             # Including radix=odd modulus=2 is 0,1 repeating.
145 1 50       5 if (($radix % $modulus) == 1) {
146             ### ENHANCE-ME: Modulo a-num without creating object, maybe
147 0         0 require Math::NumSeq::Modulo;
148 0         0 return Math::NumSeq::Modulo->new(modulus=>$modulus)->oeis_anum;
149              
150             # OEIS-Other: A000035 radix=3 modulus=2 # n mod 2, parity
151             # OEIS-Other: A000035 radix=5 modulus=2
152             # OEIS-Other: A000035 radix=37 modulus=2
153             # OEIS-Other: A010872 radix=4 modulus=3 # n mod 3
154             }
155              
156 1         4 return $oeis_anum{"$radix,$modulus"};
157             }
158              
159             # ENHANCE-ME:
160             # next() is +1 mod m, except when xx09 wraps to xx10 which is +2,
161             # or when x099 to x100 then +3, etc extra is how many low 9s
162             #
163             # sub next {
164             # my ($self) = @_;
165             # my $radix = $self->{'radix'};
166             # my $sum = $self->{'sum'} + 1;
167             # if (++$self->{'digits'}->[0] >= $radix) {
168             # $self->{'digits'}->[0] = 0;
169             # my $i = 1;
170             # for (;;) {
171             # $sum++;
172             # if (++$self->{'digits'}->[$i] < $radix) {
173             # last;
174             # }
175             # }
176             # }
177             # return ($self->{'i'}++, ($self->{'sum'} = ($sum % $radix)));
178             # }
179              
180             sub ith {
181 93     93 1 181 my ($self, $i) = @_;
182              
183 93 50       371 if (_is_infinite ($i)) {
184 0         0 return $i;
185             }
186 93         581 my $radix = $self->{'radix'};
187 93   33     239 return sum(0,_digit_split_lowtohigh($i,$radix))
188             % ($self->{'modulus'} || $radix);
189             }
190              
191             sub pred {
192 9     9 1 41 my ($self, $value) = @_;
193 9   33     42 return ($value == int($value)
194             && $value >= 0
195             && $value <= $self->values_max);
196             }
197              
198             1;
199             __END__