メタFizzBuzz

#! /usr/bin/perl

use strict;
use warnings;

use constant PI => 4 * atan2(1, 1);
use Math::FFT;

sub calc_length {
    my $n = shift;

    my $mask = 1;
    while (($n - 1) & ~$mask) {
        $mask = ($mask << 1) | 1;
    }
    return $mask + 1;
}

sub main {
    my $s;
    foreach my $i (1 .. 100) {
        $s .= [$i, "Fizz", "Buzz", "FizzBuzz"]->[($i % 3 == 0) | ($i % 5 == 0) << 1];
    }

    my @data = unpack("C*", $s);
    my $n = scalar @data;

    @data = (@data, @data)[0 .. &calc_length(scalar @data) - 1];

    my $fft = Math::FFT->new(\@data);
    my $coeff = $fft->ddct();

    print << 'EOM';
#! /usr/bin/perl

use strict;
use warnings;

use constant PI => 4 * atan2(1, 1);

my $n = <DATA>;
my @coeff = <DATA>;

foreach my $k (0 .. $n - 1) {
    my $sum = $coeff[0] * 0.5;
    foreach my $j (1 .. scalar @coeff - 1) {
        $sum += $coeff[$j] * cos(PI * $j * ($k + 0.5) / scalar @coeff);
    }
    printf "%c", int($sum * 2.0 / scalar @coeff + 0.5);
}
print "\n";

__DATA__
EOM

    print "$n\n";

    foreach my $k (@{$coeff}) {
        printf "%f\n", $k;
    }
}
&main;

これを実行すると、以下のプログラムが得られる。

$ perl gen_fizzbuzz.pl > fizzbuzz.pl
#! /usr/bin/perl

use strict;
use warnings;

use constant PI => 4 * atan2(1, 1);

my $n = <DATA>;
my @coeff = <DATA>;

foreach my $k (0 .. $n - 1) {
    my $sum = $coeff[0] * 0.5;
    foreach my $j (1 .. scalar @coeff - 1) {
        $sum += $coeff[$j] * cos(PI * $j * ($k + 0.5) / scalar @coeff);
    }
    printf "%c", int($sum * 2.0 / scalar @coeff + 0.5);
}
print "\n";

__DATA__
313
45205.000000
50.764294
-165.956675
249.478163
94.463205
-42.047046
137.718246
209.111882
-149.663591
181.943045
111.630749
-42.636863
0.608121
278.307401
-163.125127
44.902359
150.049709
32.638000
-227.295107
399.322867
14.425217
-193.998248
-118.206471
-456.664292
-112.707493
-19.884377
-208.176709
-182.426475
-70.901505
-89.439483
-273.809621
-48.518645
-246.765972
-279.709301
-76.121350
-88.874974
-241.260653
-91.854384
-108.789924
-158.121206
-154.150749
-46.620651
-174.372002
-72.963170
-115.194829
-69.948509
-107.788859
-32.668747
-127.192102
41.914475
-103.428954
-37.081897
30.849816
40.325971
-176.146932
242.421619
22.442430
-154.832062
217.194620
393.328280
-567.101771
539.531243
1002.001793
-1813.874300
-1027.533983
-3082.906226
912.398334
300.451378
-747.387373
238.371299
363.002434
-367.712101
24.186177
377.270836
-182.490868
-10.173310
207.525863
-11.219355
-177.723766
223.409696
-9.555635
-110.286551
16.809210
142.380228
-211.139600
-78.199062
50.196804
-41.262051
-207.096413
160.050546
-125.638990
-153.473354
-62.698442
136.452001
-399.015227
32.300335
33.334892
-198.454736
-361.845266
521.223031
-458.993511
-204.965072
425.622412
345.453662
-1413.373100
2865.969000
1135.587678
2818.157624
-1516.412487
-2104.082088
744.494362
-990.868195
-776.037726
-132.309238
-256.415480
-880.084717
9.369799
-342.458778
-558.073596
-247.168604
-117.307618
-568.631763
-143.645743
-177.095173
-383.042105
-217.474180
-89.387586
-377.541900
55.079218
-39.031538
-205.429479
-193.764081
30.336008
-208.813477
-143.477004
73.648108
-180.108865
-91.274086
174.045659
-57.168024
-170.855963
143.470642
-44.696740
-126.108870
167.832462
305.213523
-482.429511
899.586068
613.489285
413.279749
-439.670980
-1903.725148
406.136030
-557.801711
-383.495541
-323.152211
58.799433
-617.474686
3.594296
-269.552954
-181.640264
-352.765554
67.002959
-452.652433
84.899462
-331.737110
1.788564
-133.165161
318.297610
-949.065850
4470.193369
663.992487
-213.420189
-913.173674
-797.000201
333.618638
-829.651365
-376.731900
18.755858
-348.537827
-536.248578
204.662512
-363.934895
-365.721413
67.942556
-65.919374
-423.694377
335.092326
-211.813146
13.392186
434.849436
-61.189988
-842.991114
-2026.163581
819.959530
-309.089355
-414.872132
396.018738
132.693967
-402.609715
508.568854
201.754313
-190.032367
405.433451
432.397780
-188.419283
415.558966
511.693671
-40.282845
310.401655
647.980165
28.939196
271.555257
507.565908
149.591730
128.644657
829.581720
186.976438
128.403331
726.699911
362.124627
-32.871243
749.279735
380.929466
-70.279062
548.543578
553.561607
-252.891326
505.594339
581.411622
-359.026317
164.843223
1001.888784
-1353.437374
-684.696075
-2558.401651
1165.267282
754.186778
-76.864479
411.436787
267.299005
165.012949
138.421628
167.306990
97.321595
100.061974
26.306018
125.147828
52.963820
-49.894903
31.443204
68.725785
-154.181690
0.746770
33.250823
-230.369600
-84.145707
83.506105
-150.382545
-81.991056
23.009046
-103.688060
-180.227510
91.045688
-135.612720
-144.238215
25.825913
-34.024845
-240.685974
208.301690
-69.714480
-96.412013
135.374726
250.183693
-332.814057
792.261519
269.545399
896.700490
-1326.730637
-981.024542
219.639438
-553.088234
-95.380805
-95.829656
-112.521496
-203.431360
108.920963
-199.339629
17.099629
17.477961
-3.100148
-98.122058
130.946726
-67.426485
6.442805
28.790979
79.574770
-181.744992
140.918959
110.254128
104.884980
148.818156
22.943471
35.043113
123.248132
24.729382
20.880203
91.498687
53.212943
-17.959559
93.448135
-0.548359
-30.648591
50.717305
24.072496
-80.681267
22.889151
41.225686
-182.306010
-41.519372
-352.270444
-150.730273
146.525442
-96.832501
-46.859844
-35.597657
23.451671
-165.130961
7.682923
-48.566593
-80.896165
-79.377125
38.365263
-188.167575
-89.707767
-5.578370
-131.902205
-241.152990
131.621972
-327.306368
-327.133388
636.887283
168.291315
357.919030
-99.340038
38.621214
51.681114
-32.109799
-3.726304
31.321032
-42.822554
11.399801
13.385410
-4.562241
4.261528
22.156938
-15.885935
5.213571
7.782969
13.742444
-75.496397
12.327892
-132.580440
168.097636
260.801331
-39.644783
147.174456
99.415581
52.139002
51.107841
166.665526
-3.316107
138.914857
209.818033
-11.300179
13.995645
94.368410
21.575195
37.537771
91.427543
68.782938
-38.021621
239.958958
-10.267622
-146.038624
-71.679989
-92.655082
94.814126
-40.652713
19.014071
-13.723059
47.418263
-41.698042
22.081182
-5.383592
55.570638
-66.549714
35.823274
-22.320070
-21.561687
-92.033065
141.567838
-262.237077
57.428136
-111.112891
708.065596
1004.718292
-423.428990
231.567284
-8.430926
46.794235
-107.823341
153.898789
-116.338264
38.720517
-47.116793
58.487480
-98.911331
89.111725
-80.098336
11.292761
-53.628494
33.001533
-122.754929
66.397343
-161.951390
-29.359629
-72.261334
105.142848
-22.850572
-33.133157
-20.152731
25.748503
-110.087374
28.949834
-56.390231
-14.630360
-63.081111
68.221114
-216.625100
0.766961
-97.546215
-51.309808
-235.750057
77.386800
-308.065830
-131.927035
-615.691365
-213.118345
709.563543
-25.709976
52.721922
-30.984166
97.640004
-76.317799
-25.175607
12.195357
-23.302715
-92.880678
3.323743
-22.008296
-102.144434
-19.505878
-11.654629
-93.198876
-59.820310
14.040052
-94.144037
-61.174346
-16.869823
-67.003903
-146.660944
38.495782
-70.409557
-104.351887
-35.080039
14.891739
-158.267222
-48.084951
-25.552651
-142.206386
95.008677
125.289758
-142.567242
29.955379
60.831992
-23.262109
-99.899903
164.358978
-82.081364
-15.723722
169.324262
79.204939
-154.077510
167.343013
62.804994
-46.910734
27.075653
268.659827
-163.989450
133.908065
180.334733
65.268863
-116.416888
571.436237
-98.551376
100.080247
438.791258
583.923700
-637.138013
1990.426202
787.784537

これを実行すると、以下の結果が得られる。

$ perl fizzbuzz.pl
12Fizz4BuzzFizz78FizzBuzz11Fizz1314FizzBuzz1617Fizz19BuzzFizz2223FizzBuzz26Fizz2829FizzBuzz3132Fizz34BuzzFizz3738FizzBuzz41Fizz4344FizzBuzz4647Fizz49BuzzFizz5253FizzBuzz56Fizz5859FizzBuzz6162Fizz64BuzzFizz6768FizzBuzz71Fizz7374FizzBuzz7677Fizz79BuzzFizz8283FizzBuzz86Fizz8889FizzBuzz9192Fizz94BuzzFizz9798FizzBuzz