aboutsummaryrefslogtreecommitdiff
path: root/challenge-152/e-choroba/perl/ch-2.pl
blob: 705428433db2ed05b580873ed93d69eb07a1c5be (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
#!/usr/bin/perl
use warnings;
use strict;

# First point is the lower left, second point is the upper right.
sub canonical_rect {
    my ($rect) = @_;
    my ($point1, $point2) = @$rect;
    my @x = sort { $a <=> $b } map $_->[0], $point1, $point2;
    my @y = sort { $a <=> $b } map $_->[1], $point1, $point2;
    return [[$x[0], $y[0]], [$x[1], $y[1]]]
}

sub area {
    my ($r) = @_;
    return ($r->[1][0] - $r->[0][0]) * ($r->[1][1] - $r->[0][1])
}

sub overlap {
    my ($r1, $r2) = @_;

    my ($point1, $point2) = ([], []);

    for my $i (0, 1) {
        ($r1, $r2) = ($r2, $r1) if $r1->[0][$i] > $r2->[0][$i];
        return 0 unless $r2->[0][$i] < $r1->[1][$i]
                     && $r2->[1][$i] > $r1->[0][$i];

        push @$point1, $r2->[0][$i];
        push @$point2, (($r1->[1][$i] < $r2->[1][$i]) ? $r1 : $r2)->[1][$i];
    }

    return area([$point1, $point2])
}

sub rectangle_area {
    my ($r1, $r2) = @_;
    $_ = canonical_rect($_) for $r1, $r2;
    return area($r1) + area($r2) - overlap($r1, $r2)
}

# Count every square in both the rectangles.
sub rectangle_area_naive {
    my ($r1, $r2) = @_;

    my %grid;
    for my $r ($r1, $r2) {
        my @xs = $r->[0][0] < $r->[1][0]
               ? $r->[0][0] .. $r->[1][0] - 1
               : $r->[1][0] .. $r->[0][0] - 1;
        for my $x (@xs) {
            my @ys = $r->[0][1] < $r->[1][1]
                   ? $r->[0][1] .. $r->[1][1] - 1
                   : $r->[1][1] .. $r->[0][1] - 1;
            for my $y (@ys) {
                undef $grid{"$x $y"};
            }
        }
    }
    return scalar keys %grid
}

use Test::More tests => 35;

is_deeply canonical_rect([[1, 2], [3, 4]]), [[1, 2], [3, 4]], 'cannonical 1';
is_deeply canonical_rect([[3, 4], [1, 2]]), [[1, 2], [3, 4]], 'cannonical 2';
is_deeply canonical_rect([[1, 4], [3, 2]]), [[1, 2], [3, 4]], 'cannonical 3';
is_deeply canonical_rect([[3, 2], [1, 4]]), [[1, 2], [3, 4]], 'cannonical 4';

is area([[0, 0], [3, 3]]), 9, 'area 1';
is area([[1, 2], [4, 5]]), 9, 'area 2';

is overlap(map canonical_rect($_), [[-1,  0], [2, 2]], [[ 0, -1], [4, 4]]),
    4, 'overlap 1';
is overlap(map canonical_rect($_), [[-3, -1], [1, 3]], [[-1, -3], [2, 2]]),
    6, 'overlap 2';

is rectangle_area([[-1,  0], [2, 2]], [[ 0, -1], [4, 4]]), 22, 'Example 1';
is rectangle_area([[-3, -1], [1, 3]], [[-1, -3], [2, 2]]), 25, 'Example 2';

for (1 .. 25) {
    my @points = map [-50 + int rand 100, -50 + int rand 100], 1 .. 4;
    is rectangle_area([@points[0, 1]], [@points[2, 3]]),
        rectangle_area_naive([@points[0, 1]], [@points[2, 3]]),
        "same $_";
}

use Benchmark qw{ cmpthese };
cmpthese(-3, {
    fast => 'rectangle_area([[-20, -15], [10, 15]], [[-5, -2], [7, 9]])',
    naive => 'rectangle_area_naive([[-20, -15], [10, 15]], [[-5, -2], [7, 9]])',
});

__END__

          Rate naive  fast
naive   2625/s    --  -98%
fast  121457/s 4527%    --