aboutsummaryrefslogtreecommitdiff
path: root/challenge-023/athanasius/perl6/ch-1.p6
blob: bec5457e968cce00a5e60f9dd50d9f2709eeae8f (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
use v6;

################################################################################
=begin comment

Perl Weekly Challenge 023
=========================

Task #1
-------
Create a script that prints nth order forward difference series. You should be a
able to pass the list of numbers and order number as command line parameters.
Let me show you with an example.

  Suppose we have list (X) of numbers: 5, 9, 2, 8, 1, 6 and we would like to
  create 1st order forward difference series (Y). So using the formula Y(i) =
  X(i+1) - X(i), we get the following numbers: (9-5), (2-9), (8-2), (1-8),
  (6-1). In short, the final series would be: 4, -7, 6, -7, 5. If you noticed,
  it has one less number than the original series. Similary you can carry on 2nd
  order forward difference series like: (-7-4), (6+7), (-7-6), (5+7) => -11, 13,
  -13, 12.

=end comment
################################################################################

#--------------------------------------#
# Copyright © 2019 PerlMonk Athanasius #
#--------------------------------------#

my UInt:D constant $ORDER = 1;

BEGIN say '';

#===============================================================================
sub MAIN(*@series, UInt:D :$order = $ORDER)
#===============================================================================
{
    check-arguments(@series, $order);

    for 1 .. $order
    {
        my UInt $max-index = @series.elems - 1;

        my @new-series;
           @new-series.push(@series[$_] - @series[$_ - 1]) for 1 .. $max-index;

        @series = @new-series;
    }

    "%s order forward difference series:\n(%s)\n".printf:
        ordinal($order), join(', ', @series);

    CATCH
    {
        default 
        {
            $*ERR.say: .message ~ "\n" ~ $*USAGE;
        }
    }
}

#-------------------------------------------------------------------------------
sub check-arguments(@series, UInt:D $order)
#-------------------------------------------------------------------------------
{
    my UInt $elements = @series.elems;

    $elements > 1
        or die "Invalid number of elements ($elements) in the series: must " ~
               "be at least 2";

    $order > 0
        or die "Invalid order ($order): must be an integer > 0";

    $order < $elements
        or die "Invalid order ($order): a series of $elements elements " ~
               "cannot have an order > { $elements - 1 }";
}

#-------------------------------------------------------------------------------
sub ordinal(UInt:D $cardinal)
#-------------------------------------------------------------------------------
{
    my $suffix  = 'th';
    my $digit_0 =  $cardinal % 10;
    my $digit_1 = ($cardinal / 10).floor % 10;

    if ($digit_1 != 1)
    {
        $suffix = $digit_0 == 1 ?? 'st' !!
                  $digit_0 == 2 ?? 'nd' !!
                  $digit_0 == 3 ?? 'rd' !! 'th';
    }

    return $cardinal ~ $suffix;
}

################################################################################