aboutsummaryrefslogtreecommitdiff
path: root/challenge-036/javier-luque/perl6/ch-1.p6
blob: e7bda327ba16ee9c750f6203eb8c686dfd31e2b5 (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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# Test: perl6 ch-1.p6
use v6.d;

sub MAIN() {
    # Check a bunch of different world VIN's
    # These should be valid
    for ( 'SCFFDAAM4EGA15321', 'JTHBA30G355101885',
          '1D7HA18P57J602071', 'WA1LFAFP7EA118600',
          '1NXBU40E39Z155675', '3VWSK69MX5M058145',
          'JS3TY92V534101150', 'WDDHF5KBXEA837164') -> $vin {
        check-vin($vin.uc);
    }

    # These should be invalid
    for ( 'SCFFDAAM4EGA1$321', 'JTHBA30G3Z5101885',
          '1D7HA18P57J602072', '1NXBU40E79Z15567x') -> $vin {
        check-vin($vin.uc);
    }
}

# Check vin
sub check-vin(Str $vin) {
    if (_check-vin($vin)) {
        say "$vin is valid.";
    } else {
        say "$vin is not valid.";
    };
}

# Check vin (the guts)
sub _check-vin(Str $vin) {
    my $vin_re = /<[A..HJ..NPR..Z0..9]>/;

    # Check for valid World Vin
    return Nil unless ($vin ~~ /
        ^^              # Start of string
        ($vin_re ** 3)  # World identification number
        ($vin_re ** 6)  # Vehicle descriptor section
        ($vin_re ** 8)  # Vehicle identifier section
        $$              # End of string
    /);

    # Capture parts of the vin
    my $win = $0; # World identification number
    my $vds = $1; # Vehicle descriptor section
    my $vis = $2; # Vehicle identifier section

    # 1st digit of the VIS can't be a U, Z or 0
    return Nil if ($vis ~~ /^^<[UZ0]>/);

    # Need to validate check digit
    # compulsory for vehicles
    # in North America and China,
    if ($win ~~ /^^<[1..5L]>/) {
        return Nil unless check-digit($vin);
    }

    # In america and china the last 5
    # digits of the vis is numeric
    if ($win ~~  /^^<[1..5L]>/) {
        return Nil unless ($vis ~~ /
            ^^             # Start of string
            $vin_re ** 3   # First 3
            \d  ** 5       # Last 5 digits
            $$             # End of string
        /);
    }

    return 1;
}

# Calculate the check digit
sub check-digit(Str $vin) {
    my $products = 0;

    # Transliterate
    my %translate = (
        A => 1, B => 2, C => 3,
        D => 4, E => 5, F => 6,
        G => 7, H => 8, J => 1,
        K => 2, L => 3, M => 4,
        N => 5, P => 7, R => 9,
        S => 2, T => 3, U => 4,
        V => 5, W => 6, X => 7,
        Y => 8, Z => 9 );

    # Weights
    my @weights = (
      8,7,6,5,4,3,2,10,0,
      9,8,7,6,5,4,3,2
    );

    # Calculate the check digit
    my $x = 0;
    my @chars = $vin.comb;

    for (0 .. @chars.end) -> $i {
        my $val = %translate{@chars[$i]} ??
            %translate{@chars[$i]} !! @chars[$i];
            $products += $val * @weights[$i];
    }

    # Calculate the check digit
    my $mod = ($products % 11).Str;
    $mod = 'X' if $mod == 10;

    # Check the digit
    my $check_digit = substr $vin, 8, 1;
    return $mod eq $check_digit;
}