aboutsummaryrefslogtreecommitdiff
path: root/challenge-069/sgreen/perl/ch-1.pl
blob: 4c35b26d1b24bece1ad4993673f7d171f1323a56 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#!/usr/bin/perl

use strict;
use warnings;
use 5.10.1;

sub _generate_number ($$) {
    # Given the first half of a number, generate the whole number
    my ( $number, $length ) = @_;
    my %switch = ( 0 => 0, 6 => 9, 8 => 8, 9 => 6 );
    my @digits = reverse( split //, $number );

    # If an odd number of digits required, the middle (last) digit needs
    #  to be 0 or 8
    return 0 if $length % 2 and shift(@digits) !~ /[08]/;

    # Take each remaining digitis (in reverse order) to calculate the
    #  whole number
    foreach my $digit (@digits) {
        $number .= $switch{$digit};
    }

    return $number;
}

sub _half_length ($) {
    # Calculate the minimum number of digits to generate at least half
    #  the number of digits
    return int( ( shift() + 1 ) / 2 );
}

sub main (@) {
    my ( $a, $b ) = @_;
    my @numbers = ();

    # Sanity checks of the input
    die "First value is not between 1 and 10^15"
      unless $a =~ /^[0-9]+$/
      and $a >= 1
      and $a <= 10**15;

      die "Second value is not between 1 and 10^15"
      unless $b =~ /^[0-9]+$/
      and $b >= 1
      and $b <= 10**15;

    # 10^15 isn't strobogrammatic, so do one less
    $b -= 1 if $b == 10**15;

    # Digits that are strobogrammatic
    my @d = ( 0, 6, 8, 9 );

    # ... however 0 can't be the first digit
    my @list = ( 6, 8, 9 );

    for my $length ( 1 .. length($b) ) {
        # Do we need to an another digit to the list
        if ( length( $list[0] ) < _half_length($length) ) {
            my @new_list = ();
            foreach my $old_list (@list) {
                push @new_list, map { $old_list . $_ } @d;
            }
            @list = @new_list;
        }

        # We now have the first half of all the digits that are
        #  strobogrammatic for the given length. Time to generate
        #  the whole number
        for my $item (@list) {
            my $number = _generate_number( $item, $length );

            # Don't add the number if it isn't valid (middle digit != 0, 8)
            #  or less than the minimum
            next if !$number or $number < $a;

            # If the number is grater than than the maximum, we can exit
            #  as we know @list is in order.
            last if $number > $b;

            push @numbers, $number if $number;
        }
    }

    say join( ' ', @numbers );
}

main(@ARGV);