#!/usr/bin/perl

# przeciecie_manhattan ( @proste )
#   Procedura wyszukuje punkty przeciecia poziomych i pionowych linii prostych.
#   Ta procedura wymaga funkcji proste_dodawanie_do_drzewa(),
#   proste_usuwanie_z_drzewa() i proste_przeszukiw_drzewa(), ktore zostaly
#   zdefiniowane w rozdziale 3.
#
sub przeciecie_manhattan {
    my @op; # Wspolrzedne sa tutaj przeksztalcane jak operacje.

    while (@_) {
        my @prosta = splice @_, 0, 4;

        if ($prosta[1] == $prosta[3]) {     # Pozioma prosta.
            push @op, [ @prosta, \&drzewo_sprawdzenia_przedzialu ];
        } else {                        # Pionowa prosta.
            # Odwrocenie, jesli do gory nogami.
            @prosta = @prosta[0, 3, 2, 1] if $prosta[1] > $prosta[3];

            push @op, [ @prosta[0, 1, 2, 1], \&proste_dodawanie_do_drzewa ];
            push @op, [ @prosta[0, 3, 2, 3], \&proste_usuwanie_z_drzewa ];
        }
    }

    my $drzewo_x; # Drzewo sprawdzania przedzialu.
    # Procedura porownujaca wspolrzedne x.
    my $porownaj_x = sub { $_[0]->[0] <=> $_[1]->[0] };
    my @przeciecie; # Przeciecia prostych.

    foreach my $op (sort { $a->[1] <=> $b->[1] ||
                           $a->[4] == \&drzewo_sprawdzenia_przedzialu ||
                           $a->[0] <=> $b->[0] }
                           @op) {
        if ($op->[4] == \&drzewo_sprawdzenia_przedzialu) {
            push @przeciecie, $op->[4]->( \$drzewo_x, $op, $porownaj_x );
        } else { # Dodanie lub usuniecie.
            $op->[4]->( \$drzewo_x, $op, $porownaj_x );
        }
    }

    return @przeciecie;
}

# drzewo_sprawdzenia_przedzialu( $powiazanie_drzewa, $pozioma, $porownanie )
#    Ta podprocedura zwraca liste wezlow drzewa, ktore znajduja sie w przedziale
#    od $pozioma->[0] do $pozioma->[1]. Procedura jest zalezna od drzew 
#    binarnych, ktore zostaly przedstawione w rozdziale 3.
#
sub drzewo_sprawdzenia_przedzialu {
    my ( $drzewo, $pozioma, $porownanie ) = @_;

    my @przedzial      = ( );   # Wartosc zwrotna.
    my $wezel          = $$drzewo;
    my $pionowa_x      = $wezel->{val};
    my $pozioma_dolny  = [ $pozioma->[ 0 ] ];
    my $pozioma_gorny  = [ $pozioma->[ 1 ] ];

    return unless defined $$drzewo;

    push @przedzial, drzewo_sprawdzenia_przedzialu( \$wezel->{left}, $pozioma, $porownanie )
        if defined $wezel->{left};

    push @przedzial, $pionowa_x->[ 0 ], $pozioma->[ 1 ]
        if $porownanie->( $pozioma_dolny, $pozioma ) <= 0 &&
           $porownanie->( $pozioma_gorny, $pozioma ) >= 0;

    push @przedzial, drzewo_sprawdzenia_przedzialu( \$wezel->{right}, $pozioma, $porownanie )
        if defined $wezel->{right};

    return @przedzial;
}

@proste = ( 1, 6,  1, 3,  1, 2,  3, 2,  1, 1,  4, 1,
            2, 4,  7, 4,  3, 0,  3, 6,  4, 3,  4, 7,
            5, 7,  5, 4,  5, 2,  7, 2 );

print join(" ", przeciecie_manhattan (@proste)), "\n";

# ($powiazanie, $wezel) = proste_przeszukiw_drzewa( \$drzewo, $cel, $porownaj )
#
# Przeszukuje drzewo \$drzewo w poszukiwaniu $cel. Opcjonalny 
# argument $porownaj poleca uzyc alternatywnej procedury porownujcej
# (przyzywanej poprzez $porownaj->( $element1, $element2 ) zamiast
# domyslnego porownywania liczbowego. Powinna ona zwrocic 
# wartosc spojn z operatorami <=> lub cmp. 
#
# Zwraca dwie rzeczy:
#
#    1. odwolanie do powiazania wskazujacego do wezla
#       (jesli zostal znaleziony) lub do miejsca, gdzie
#       wezel powinien sie pojawic (jesli go nie ma)
#
#    2. Sam wezel (lub wartosc undef, jesli nie istnieje)

sub proste_przeszukiw_drzewa {
    my ($drzewo_powiazanie, $cel, $porownaj) = @_;
    my $wezel;

    # $powiazanie_drzewa to nastepny wskaznik za ktorym trzeba pojsc.
    # Bedzie mial wartosc undef, jesli osiagniemy dol drzewa.
    while ( $wezel = $$powiazanie_drzewa ) {
        local $^W = 0;  # bez ostrzezen, oczekujemy wartosci undef 

        my $relacja = ( defined $porownaj
                    ? $porownaj->( $cel, $wezel->{wartosc} )
                    : $cel <=> $wezel->{wartosc} );

        # Jesli go znalezlismy, zwracamy odpowiedz.
        return ($powiazanie_drzewa, $wezel) if $relacja == 0;

        # Nic - idziemy nizej - decydujemy w ktor strone.
        $powiazanie_drzewa = $relacja > 0 ? \$wezel->{lewo} : \$wezel->{prawo};
    }

    # Dotarlismy na sam dol, wiec elementu nie ma w drzewie, jednak
    # informujemy kod przywolujacy, gdzie dodac nowy element (jesli chce).
    return ($powiazanie_drzewa, undef);
}

# $wezel = proste_dodawanie_do_drzewa( \$drzewo, $cel, $porownaj );
#
# Jesli w drzewie \$drzewo nie ma jeszcze wezla o wartosci
# $cel, tworzy go. Zwraca nowy lub juz istniejacy
# wezel. Trzeci argument definiuje opcjonalna procedure
# porownywania i jest po prostu przesylany procedurze 
# proste_przeszukiw_drzewa.

sub proste_dodawanie_do_drzewa {
    my ($powiazanie_drzewa, $cel, $porownaj) = @_;
    my $znaleziony;

    ($powiazanie_drzewa, $znaleziony) = proste_przeszukiw_drzewa( $powiazanie_drzewa, $cel, $porownaj );

    unless ($znaleziony) {
        $znaleziony = {
            lewo  => undef,
            prawo => undef,
            wartosc   => $cel
        };
        $$powiazanie_drzewa = $znaleziony;
    }

    return $znaleziony;
}

# $wartosc = proste_usuwanie_z_drzewa( \$drzewo, $cel[, $porownaj ] );
#
# Znajduje element drzewa \$drzewo o wartoci $wartosc 
# i usuwa go z drzewa. Zwraca wartosc, lub 
# undef, jesli takiego elementu nie bylo 
# w drzewie.

sub proste_usuwanie_z_drzewa {
    my ($powiazanie_drzewa, $cel, $porownaj) = @_;
    my $znaleziony;

    ($powiazanie_drzewa, $znaleziony) = proste_przeszukiw_drzewa ( $powiazanie_drzewa, $cel, $porownaj );

    return undef unless $znaleziony;

    # $powiazanie_drzewa ma wskazywac do potomkow $znaleziony:
    #  jesli $znaleziony nie ma potomkow, przypisz mu null
    #  jesli jest tylko jeden, moe po prostu wejsc w miejsce 
    #  elementu $znaleziony
    #  Jesli jednak jest ich wiecej, trzeba cos z nimi zrobic,
    #  aby mozna bylo je wpasowac pod jedno odwolanie.
    #
    if ( ! defined $znaleziony->{lewo} ) {
        $$powiazanie_drzewa = $znaleziony->{prawo};
    } elsif ( ! defined $znaleziony->{prawo} ) {
        $$powiazanie_drzewa = $znaleziony->{lewo};
    } else {
        POLACZ_JAKOS( $powiazanie_drzewa, $znaleziony );
    }

    return $znaleziony->{wartosc};
}

# POLACZ_JAKOS
#
# Podpinamy pod $powiazanie_drzewa elementy $znaleziony->{lewo} i $znaleziony->{prawo}.

# Podpinamy $znaleziony->{lewo} po lewej stronie $znaleziony->{prawo}
# a nastpnie $znaleziony->{prawo} podpinamy do $$powiazanie_drzewa.

sub POLACZ_JAKOS {
    my ($powiazanie_drzewa, $znaleziony) = @_;
    my $lewe_z_prawej = $znaleziony->{prawo};
    my $nastepny_lewo;

    $lewe_z_prawej = $nastepny_lewo
        while $nastepny_lewo = $lewe_z_prawej->{lewo};

    $lewe_z_prawej->{lewo} = $znaleziony->{lewo};

    $$powiazanie_drzewa = $znaleziony->{prawo};
}

# trawersowanie( $drzewo, $funkc )
#
# Przeglada drzewo $drzewo przyzywajac po kolei dla kazdego
#    elementu funkcje $funkc().

sub trawersowanie {
    my $drzewo = shift or return;   # pomin wskazniki undef 
    my $funkc = shift;

    trawersowanie( $drzewo->{lewo}, $funkc );
    &$funkc( $drzewo );
    trawersowanie( $drzewo->{prawo}, $funkc );
}


