aboutsummaryrefslogtreecommitdiff
path: root/challenge-071/bob-lied/perl/ch-1.pl
blob: 50b2a0cbe130f551772f68f6f331b0c6bbe96ff1 (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
111
112
113
114
115
116
117
118
119
120
121
122
123
#!/usr/bin/env perl
# vim:set ts=4 sw=4 sts=4 et ai wm=0 nu:
#=============================================================================
# ch-1.pl
#=============================================================================
# Copyright (c) 2020, Bob Lied
#=============================================================================
# Perl Weekly Challenge 71 TASK #1 > Peak Element
#=============================================================================
# 
# You are given positive integer $N (>1).
#
# Write a script to create an array of size $N with random unique elements
# between 1 and 50.
#
# In the end it should print peak elements in the array, if found.
# An array element is called peak if it is bigger than its neighbour.
#
# Example 1
#  Array: [ 18, 45, 38, 25, 10, 7, 21, 6, 28, 48 ]
#  Peak: [ 48, 45, 21 ]
#
# Example 2
#  Array: [ 47, 11, 32, 8, 1, 9, 39, 14, 36, 23 ]
#  Peak: [ 47, 32, 39, 36 ]

use strict;
use warnings;
use feature qw(say);

use Getopt::Long;


my ($MinVal, $MaxVal) = (1, 50);

sub Usage { "Usage: $0 N\n\tN>1" }

sub isPeak
{
    my ($prev, $me, $next) = @_;
    return ($prev < $me) && ($me > $next);
}

sub peakElement
{
    my @arr = ($MinVal-1, @_, $MinVal-1);
    my @peak = ();
    my $last = $#arr - 1;

    foreach ( 1..$last )
    {
        if ( isPeak($arr[$_-1], $arr[$_], $arr[$_+1] ) )
        {
            push @peak, $arr[$_]
        }
    }

    return \@peak;
}

sub makeArray
{
    my ($min, $max, $size) = @_;
    my @chooseFrom = ( $min .. $max );
    my @result;

    for my $n ( 1..$size )
    {
        push @result, splice(@chooseFrom, int(rand(@chooseFrom)), 1)

    }
    return @result;
}

sub checkUniq
{
    my (@arr) = @_;
    my %uniq;
    $uniq{$_}++ foreach @arr;
    my $notUnique = grep { $_ != 1 } values %uniq;
    return $notUnique == 0;
}

sub runTests
{
    use Test::More;
    is( scalar(makeArray(1, 50, 10)), 10, "Make random array size  10");
    ok( checkUniq(makeArray(1, 50, 10)), "Random array unique elements");

    is_deeply(peakElement(2, 1 ),         [2],    "two elements, left");
    is_deeply(peakElement(1, 2 ),         [2],    "two elements, right");
    is_deeply(peakElement(6, 4, 2 ),      [6],    "three elements, first");
    is_deeply(peakElement(6, 8, 2 ),      [8],    "three elements, middle");
    is_deeply(peakElement(6, 7, 9 ),      [9],    "three elements, last");
    is_deeply(peakElement(8, 5, 9 ),      [8,9],  "three elements, both ends");
    is_deeply(peakElement(1, 3, 5, 4),    [5],    "even # elements, middle peak ");
    is_deeply(peakElement(9, 3, 5, 8),    [9, 8], "even # elements, both ends ");
    is_deeply(peakElement(9, 3, 4, 5, 8), [9, 8], "odd # elements, both ends ");
    is_deeply(peakElement(3, 9, 4, 8, 5), [9, 8], "odd # elements, middle peaks");

    is_deeply(peakElement(18, 45, 38, 25, 10, 7, 21, 6, 28, 48), [45, 21, 48], "example 1");
    is_deeply(peakElement(47, 11, 32, 8, 1, 9, 39, 14, 36, 23), [47, 32, 39, 36], "example 2");


    my ($result) = (done_testing())[0];
    return $result;
}

########## MAIN ##########

my $doTest;
GetOptions('test!' => \$doTest);

exit(!runTests()) if $doTest;

my $N = $ARGV[0] // 0;
die Usage() unless $N > 1;

my @uniqArray = makeArray($MinVal, $MaxVal, $N);
my $peaks = peakElement @uniqArray;

say "Array: ", join ",", @uniqArray;
say "Peaks: ", join ",", @$peaks;