/usr/local/CPAN/Graph-SocialMap/Graph/SocialMap.pm


package Graph::SocialMap;
use Spiffy 0.21 qw(-Base field);
use Graph 0.54;
use Quantum::Superpositions;
our $VERSION = '0.12';

sub paired_arguments {qw(-relation -file -format)}

# Cached fields
field '_relation';
field '_issues';
field '_people';

# weight of person: number of occurences of a person in whole relation.
field '_wop';

# under lying Graph::* object
field '_type1';
field '_type2';
field '_type3';
field '_apsp';
field '_issue_network';

# graphviz parameters
field layout    => 'neato';
field rank      => 'same';
field ranksep   => 1.5;
field no_overlap => 0;
field splines   => 'false';
field arrowsize => 0.5;
field fontsize  => 12;
field ordering  => 'out';
field epsilon   => 1;
field concentrate => 'true';
field ratio => 'auto';

sub relation {
    my $newval = shift;
    if($newval) {
        $self->_relation($newval);
        for(qw(_people _issues _type1 _type2 _type3 _apsp _wop
                   _issue_network)) {
            $self->$_(undef);
        }
    }
    return $self->_relation;
}

sub issues {
    return $self->_issues if $self->_issues;
    my $issues = [keys %{$self->relation}];
    $self->_issues($issues);
    return $issues;
}

sub people {
    return $self->_people if ($self->_people);
    my $p={};
    my $r=$self->relation;
    for(keys %$r) {
	$p->{$_}++ for @{$r->{$_}};
    }
    $self->_wop($p);
    my $people = [keys %$p];
    $self->_people($people);
    return $people;
}

sub wop {
    return $self->_wop if $self->_wop;
    $self->people;
    $self->_wop;
}

sub type2 {
    return $self->_type2 if ($self->_type2);
    my $isu = $self->issues;
    my $rel = $self->relation;
    my $type2 = Graph->new;

    for my $i (@$isu) {
	for my $e ($self->pairs(@{$rel->{$i}})) {
	    unless($type2->has_edge($e->[0],$e->[1])) {
		$type2->add_edge($e->[0],$e->[1]);
		$type2->add_edge($e->[1],$e->[0]);
	    }
	}
    }
    $self->_type2($type2);
    return $type2;
}

*people_network = \&type2;

sub issue_network {
    return $self->_issue_network if $self->_issue_network;
    my $isu = $self->issues;
    my $rel = $self->relation;
    my $n = Graph::Undirected->new;
    for my $i ($self->pairs(@$isu)) {
        next if $n->has_edge($i->[0],$i->[1]);
        $n->add_edge($i->[0],$i->[1])
            if any(@{$rel->{$i->[0]}}) eq any(@{$rel->{$i->[1]}});
    }
    $self->_issue_network($n);
    return $n;
}

sub apsp {
    return $self->_apsp if($self->_apsp);
    my $a = $self->type2->APSP_Floyd_Warshall;
    $self->_apsp($a);
    return $a;
}

sub type1 {
    return $self->_type1 if ($self->_type1);
    my $type1 = Graph::Undirected->new;
    my $people = $self->people;
    my $isu = $self->issues;
    my $rel = $self->relation;

    for (@$people) {
	my $node_name = "People/$_";
	$type1->add_vertex($node_name);
        $type1->set_vertex_attribute($node_name,shape => 'plaintext');
        $type1->set_vertex_attribute($node_name,label => $_);
    }

    for my $i (@$isu) {
	my $node_name = "Issue/$i";
	$type1->add_vertex($node_name);
        $type1->set_vertex_attribute($node_name, shape => "box");
        $type1->set_vertex_attribute($node_name, label => $i);
	$type1->add_edge("People/$_",$node_name) for @{$rel->{$i}};
    }

    $self->_type1($type1);
    return $type1;
}

*affiliation_network = \&type1;

# type3, directed people-to-people graph, in the given order
sub type3 {
    return $self->_type3 if ($self->_type3);
    my $rel = $self->relation;
    my $isu = $self->issues;
    my $type3 = Graph->new;
    my $people = $self->people;

    $type3->add_vertices(@$people);
    for my $i (@$isu) {
	my @list = @{$rel->{$i}};
	for my $i (0..$#list-1) {
	    for my $j ($i+1..$#list) {
		$type3->add_edge(@list[$j,$i])
		    unless($type3->has_edge(@list[$j,$i]));
	    }
	}
    }

    $self->_type3($type3);
    return $type3;
}

sub type3_adj_matrix {
    my $m = {};
    for($self->type3->edges) {
        $m->{$_->[0]}->{$_->[1]} = 1;
    }
    return $m;
}

# Degree of seperation of two people.
sub dos {
    my ($alice,$bob) = @_;
    my $apsp = $self->apsp;
    my $w = $apsp->path_length($alice,$bob);
    $w = -1 if(!defined $w);
    return $w;
}

# retrurn all-pair dos
sub all_dos {
    my $people = $self->people;
    my $d = {};
    for my $alice (@$people) {
	for my $bob (@$people) {
	    $d->{$alice}->{$bob} = $self->dos($alice,$bob);
	}
    }
    return $d;
}

# return a list of all pairs.
sub pairs {
    my @list = @_;
    my @pairs;
    for my $i (0..$#list) {
	for my $j ($i+1..$#list) {
	    my ($a,$b) = @list[$i,$j];
	    push @pairs, [$a,$b];
	}
    }
    return @pairs;
}