aboutsummaryrefslogtreecommitdiff
path: root/challenge-241/jo-37/perl/ch-1.pl
blob: 93563fa2d36057f16b57f4a824d5c022e94c7e47 (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
#!/usr/bin/perl -s

use v5.24;
use Test2::V0 '!float';
use PDL;

our ($tests, $examples, $diff);

run_tests() if $tests || $examples;	# does not return

die <<EOS unless defined $diff && @ARGV > 2;
usage: $0 [-examples] [-tests] [-diff=D N...]

-examples
    run the examples from the challenge
 
-tests
    run some tests

-diff=D
    count arithmetic triplets having a difference of D

N...
    three or more integers

EOS


### Input and Output

say arith_triplets($diff, @ARGV);


### Implementation

# Using graph theory to solve the task.  We take the given integers as
# the vertices of a directed graph with their indices as unique
# identifiers and the integers as values.  Two vertices are connected
# with an edge if and only if the indices are in correct order and the
# difference between the values equals the given difference.
#
# The adjacency matrix of a graph shows all edges, i.e. direct
# neighboring vertices.  Furthermore, the squared adjacency matrix shows
# the number of 2-walks between any two vertices.  In the constructed
# "forward only" graph every walk is actually a path, each 2-path
# corresponds to one arithmetic triplet for the given difference and the
# sum over the elements of this squared matrix is the requested number
# of arithmetic triplets.
#
# There is neither a need for the numbers to be in increasing order nor
# for the difference to be positive.

sub arith_triplets {
    my $diff = shift;
	my $l = long @_;
    # Build the adjacency matrix.
    my $adj =
        (sequence($l) > sequence($l)->dummy(0))
        & ($l - $l->dummy(0) == $diff);
        
    # Count the number of 2-paths in the graph.
    ($adj x $adj)->sum;
}


### Examples and tests

sub run_tests {
    SKIP: {
        skip "examples" unless $examples;
        is arith_triplets(3 ,=> 0, 1, 4, 6, 7, 10), 2, 'example 1';
        is arith_triplets(2 ,=> 4, 5, 6, 7, 8, 9), 2, 'example 2';
    }

    SKIP: {
        skip "tests" unless $tests;

        is arith_triplets(1 ,=> 1, 2, 4, 5, 8, 9), 0, 'no triplet';
        is arith_triplets(2 ,=> 1, 3, 3, 5), 2, 'multiple paths';
        is arith_triplets(1 ,=> 9, 1, 8, 2, 7, 3), 1, 'not ordered';
        is arith_triplets(0 ,=> 1, 2, 1, 4, 1, 6), 1, 'zero diff';
        is arith_triplets(-1 ,=> 4, 3, 2, 1), 2, 'negative diff';
	}

    done_testing;
    exit;
}