aboutsummaryrefslogtreecommitdiff
path: root/challenge-116/james-smith/perl/ch-1.pl
blob: b6c856ed3586c68242a86ee36b1b576703abc839 (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
#!/usr/local/bin/perl

use strict;

use warnings;
use feature qw(say);
use Test::More;
use Benchmark qw(cmpthese);

my $nspace = join '', 1..1000;
my $ncomma = join ',',1..1000;
my $lint_x = my $lint   = ('9' x 999).'8';
my $lint_y = ++$lint_x;
$lint_y++;

my @tests = (
 [ 1234,  '1,2,3,4' ],
 [ 91011, '9,10,11' ],
 [ 10203, '10203'   ],
 ## Numerical comparisons don't work....
 [ $nspace, $ncomma ],                                     ## 2895 digit no -> 1..1000
 ## Have to store range as can't use range operator here...
 [ $lint.$lint_x.$lint_y, $lint.','.$lint_x.','.$lint_y ], ## 3001 digit no -> 2 x 1000 digit + 1001 digit no.
 [ $lint.$lint_x, $lint.','.$lint_x ],                     ## 2000 digit no -> 2 x 1000 digit
 [ $lint_x.$lint_y, $lint_x.','.$lint_y ],                 ## 2001 digit no ->     1000 digit + 1001 digit no.
 [ $nspace.'99', $nspace.'99' ],                           ## 2895 digit no -> 2895 digit no
);

is( join(',',@{splitnum($_->[0])}),$_->[1] ) foreach @tests;
is( join(',',@{splitnum_optimized($_->[0])}),$_->[1] ) foreach @tests;
done_testing();
cmpthese( 120, {
  a => sub { splitnum($_->[0]) foreach @tests; },
  c => sub { splitnum_optimized($_->[0]) foreach @tests; },
});
sub splitnum {
  my( $in, $start ) = ( shift, '' );
  for( split //, substr $in, 0, (my $len = length $in) >> 1) {
    ## $start contains the first number of sequence
    ## each time through the loop we will add the
    ## next digit eg 1, 12, 123
    ##
    ## $end contains a copy of this, which we will
    ## then incremement as we generate the sum

    my @range = ( my $str = my $end = $start .= $_ );

    ## We concatenate the of "end" onto $string until
    ## it is equal to or larger than the input number

    ($str .= ++$end) && push @range, $end while $len > length $str;

    ## Finally we return the list if the input and
    ## string are the same. Note we will always get
    ## a true value as in the last loop
    ##   $in == $start == $end == $string

    return \@range if $str eq $in;
  }
  return [$in];
}


## We work in string space - as this works best for large values of input
## like the 123....9991000 example above..
##
## There are some optimizations you could try {e.g. looping up to half the
## length of $n - but only really valid if you are getting very large
## values of $n...}

## We can add some optimization here... check that we match the string
## otherwise return;

sub splitnum_optimized {
  my( $in, $start ) = ( shift, '' );
  for( split //, substr $in, 0, (my $len = length $in) >> 1) {
    my @range = ( my $str = my $end = $start .= $_ );
    ($str .= ++$end) && push @range,$end while ($len > length $str) &&
                                               $end eq substr $in,length($str)-length($end),length($end);
    return \@range if $str eq $in;
  }
  return [$in];
}