next_palindrome_from_non-palindrome.pl 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. #!/usr/bin/perl
  2. # Generate the next palindrome in a given base, where the input number may not be a palindrome.
  3. # Algorithm by David A. Corneth (Jun 06 2014), with extensions by Daniel Suteu (Jun 06 2020).
  4. # See also:
  5. # https://oeis.org/A002113
  6. # https://en.wikipedia.org/wiki/Palindromic_number
  7. use 5.020;
  8. use strict;
  9. use warnings;
  10. use ntheory qw(:all);
  11. use experimental qw(signatures);
  12. use Test::More tests => 41;
  13. sub next_palindrome ($n, $base = 10) {
  14. my @d = todigits($n, $base);
  15. my $l = $#d;
  16. my $i = ((scalar(@d) + 1) >> 1) - 1;
  17. my $is_palindrome = 1;
  18. foreach my $j (0 .. $i) {
  19. if ($d[$j] != $d[$l - $j]) {
  20. $is_palindrome = 0;
  21. last;
  22. }
  23. }
  24. if (!$is_palindrome) {
  25. my @copy = @d;
  26. foreach my $i (0 .. $i) {
  27. $d[$l - $i] = $d[$i];
  28. }
  29. my $is_greater = 1;
  30. foreach my $j (0 .. $i) {
  31. my $cmp = $d[$i + $j + 1] <=> $copy[$i + $j + 1];
  32. if ($cmp > 0) {
  33. last;
  34. }
  35. if ($cmp < 0) {
  36. $is_greater = 0;
  37. last;
  38. }
  39. }
  40. if ($is_greater) {
  41. return fromdigits(\@d, $base);
  42. }
  43. }
  44. while ($i >= 0 and $d[$i] == $base - 1) {
  45. $d[$i] = 0;
  46. $d[$l - $i] = 0;
  47. $i--;
  48. }
  49. if ($i >= 0) {
  50. $d[$i]++;
  51. $d[$l - $i] = $d[$i];
  52. }
  53. else {
  54. @d = (0) x (scalar(@d) + 1);
  55. $d[0] = 1;
  56. $d[-1] = 1;
  57. }
  58. fromdigits(\@d, $base);
  59. }
  60. #
  61. ## Run some tests
  62. #
  63. my @palindromes = do {
  64. my $x = 0;
  65. my @list;
  66. for (1 .. 61) {
  67. push @list, $x;
  68. $x = next_palindrome($x);
  69. }
  70. @list;
  71. };
  72. is_deeply(
  73. \@palindromes,
  74. [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 101, 111, 121,
  75. 131, 141, 151, 161, 171, 181, 191, 202, 212, 222, 232, 242, 252, 262, 272, 282, 292, 303, 313, 323, 333, 343,
  76. 353, 363, 373, 383, 393, 404, 414, 424, 434, 444, 454, 464, 474, 484, 494, 505, 515
  77. ]
  78. );
  79. is(next_palindrome(10), 11);
  80. is(next_palindrome(11), 22);
  81. is(next_palindrome(12), 22);
  82. is(next_palindrome(110), 111);
  83. is(next_palindrome(111), 121);
  84. is(next_palindrome(112), 121);
  85. is(next_palindrome(120), 121);
  86. is(next_palindrome(121), 131);
  87. is(next_palindrome(1234), 1331);
  88. is(next_palindrome(12345), 12421);
  89. is(next_palindrome(8887), 8888);
  90. is(next_palindrome(8888), 8998);
  91. is(next_palindrome(8889), 8998);
  92. is(next_palindrome(88887), 88888);
  93. is(next_palindrome(88888), 88988);
  94. is(next_palindrome(88889), 88988);
  95. is(next_palindrome(9998), 9999);
  96. is(next_palindrome(99998), 99999);
  97. is(next_palindrome(9999), 10001);
  98. is(next_palindrome(99999), 100001);
  99. is(next_palindrome(12311), 12321);
  100. is(next_palindrome(1321), 1331);
  101. is(next_palindrome(1331), 1441);
  102. is(next_palindrome(13530), 13531);
  103. is(next_palindrome(13520), 13531);
  104. is(next_palindrome(13521), 13531);
  105. is(next_palindrome(13530), 13531);
  106. is(next_palindrome(13531), 13631);
  107. is(next_palindrome(13540), 13631);
  108. is(next_palindrome(13532), 13631);
  109. is(next_palindrome(1234, 2), 1241);
  110. is(next_palindrome(1234, 3), 1249);
  111. is(next_palindrome(1234, 4), 1265);
  112. is(next_palindrome(1234, 5), 1246);
  113. is(next_palindrome(1234, 6), 1253);
  114. is(next_palindrome(12345, 2), 12483);
  115. is(next_palindrome(12345, 3), 12382);
  116. is(next_palindrome(12345, 4), 12355);
  117. is(next_palindrome(12345, 5), 12348);
  118. is(next_palindrome(12345, 6), 12439);