From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

# -*- Mode: Perl -*-
#
# Copyright (c) 2001, Bryan Jurish. All rights reserved.
#
# This package is free software. You may redistribute it
# and/or modify it under the same terms as Perl itself.
#
###############################################################
#
# File: Math::PartialOrder::Std.pm
# Author: Bryan Jurish <jurish@ling.uni-potsdam.de>
#
# Description: standard iterative PartialOrder class
#
###############################################################
# System modules
use Carp;
#require Exporter;
# 3rd party exstensions
# user extension modules
@ISA = qw(Math::PartialOrder::Base);
@EXPORT = qw();
@EXPORT_OK = qw();
our $VERSION = 0.01;
###############################################################
# Initialization
# + object structure:
# {
# types => {type1=>type1, ... }
# root => scalar
# parents => { type1 => {p1a=>p1a, c1b=>c1b,... }, ... }
# children => { type1 => {c1a=>c1a, c1b=>c1b,... }, ... }
# attributes => { type1 => { attr1.1 => val1.1, ... }, ... }
# }
###############################################################
#----------------------------------------------------------------------
# new( {root=>$r} )
# + initialization routine: returns the object
#----------------------------------------------------------------------
sub new ($;$) {
my $class = shift;
my $args = shift;
my $self = bless {
types => {},
root => '',
parents => {},
children => {},
attrs => {}
}, $class;
# root node
$self->_root($args->{root}||'BOTTOM');
# hierarchy attributes
$self->_hattributes({});
return $self;
}
###############################################################
# Hierarchy Information
# information
###############################################################
# @types = $h->types()
sub types ($) { return values(%{$_[0]->{types}}); }
#--------------------------------------------------------------
# $bool = $h->has_type($t)
sub has_type ($$) { return (defined($_[1]) && exists($_[0]->{types}{$_[1]})); }
# $bool = $h->has_types(@types)
sub has_types ($@) {
my $types = shift->{types};
grep {
return '' unless (defined($_) && exists($types->{$_}));
} @_;
return 1;
}
#--------------------------------------------------------------
# @ps = $h->parents($typ)
sub parents ($$) {
return
(defined($_[1]) && exists($_[0]->{parents}{$_[1]})
? values(%{$_[0]->{parents}{$_[1]}})
: qw());
}
#--------------------------------------------------------------
# @cs = $h->children($typ)
sub children ($$) {
return
(defined($_[1]) && exists($_[0]->{children}{$_[1]})
? values(%{$_[0]->{children}{$_[1]}})
: qw());
}
#--------------------------------------------------------------
# $bool = $h->has_parent($kid,$prt);
sub has_parent ($$) {
return
(defined($_[1]) && exists($_[0]->{parents}{$_[1]})
? exists($_[0]->{parents}{$_[1]}{$_[2]})
: '');
}
#--------------------------------------------------------------
# $bool = $h->has_child($prt,$kid);
sub has_child ($$) {
return
(defined($_[1]) && exists($_[0]->{parents}{$_[1]})
? exists($_[0]->{parents}{$_[1]}{$_[2]})
: '');
}
#--------------------------------------------------------------
# @ancs = $h->ancestors($typ)
sub ancestors ($$) { return values(%{$_[0]->_ancestors($_[1])}); }
#--------------------------------------------------------------
# @dscs = $h->descendants($typ)
sub descendants ($$) { return values(%{$_[0]->_ancestors($_[1])}); }
#--------------------------------------------------------------
# $bool = has_ancestor($t1,$t2) : inherited
#--------------------------------------------------------------
# $bool = has_descendant($t1,$t2) : inherited
###############################################################
# Additional Information
###############################################################
# $hashref = $h->_ancestors($t)
sub _ancestors ($$) {
return
($_[0]->has_type($_[1])
? $_[0]->iterate_cp_step(\&_ancestors_callback,
{
start => $_[1],
return => {}
})
: {});
}
sub _ancestors_callback ($$$) {
@{$_[2]->{return}}{$_[0]->parents($_[1])} = $_[0]->parents($_[1]);
return undef;
}
# $hashref = $h->_descendants($t)
sub _descendants ($$) {
($_[0]->has_type($_[1])
? $_[0]->iterate_pc_step(\&_descendants_callback,
{
start => $_[1],
return => {}
})
: {});
}
sub _descendants_callback ($$$) {
@{$_[2]->{return}}{$_[0]->children($_[1])} = $_[0]->children($_[1]);
return undef;
}
# end information
###############################################################
# Hierarchy Manipulation
###############################################################
#--------------------------------------------------------------
# add($type,@parents)
sub add ($$@) {
my $self = shift;
my $type = shift;
# sanity checks
unless (defined($type)) {
carp("cannot add undefined type");
return $self;
}
return $self->move($type, @_) if ($self->has_type($type));
# add this type
$self->{types}{$type} = $type;
# ensure parents are well-defined & well-placed
@_ = $self->ensure_types(@_);
# set parents-relation for new $type
if (@_) {
$self->{parents}{$type} = {};
@{$self->{parents}{$type}}{@_} = @_;
}
# set children-relation for new $type
my $kids = $self->{children};
foreach (@_) { $kids->{$_}{$type} = $type; }
return $self;
}
#--------------------------------------------------------------
# sub add_parents($h,$type,@parents) : INHERITED
#--------------------------------------------------------------
# replace($old,$new)
sub replace ($$$) {
my ($h, $old, $new) = @_;
return $h->add($new,$h->root) unless ($h->has_type($old));
unless (defined($old) && defined($new)) {
carp("cannot add undefined type");
return $h;
}
$h->{types}{$new} = $new;
foreach (qw(parents children attrs)) {
if (exists($h->{$_}{$old})) {
$h->{$_}{$new} = $h->{$_}{$old};
delete($h->{$_}{$old});
} else {
delete($h->{$_}{$new});
}
}
if ($old eq $h->{root}) { $h->_root($new); }
return $h;
}
#--------------------------------------------------------------
# $move($type,@newps)
sub move ($$@) {
my $self = shift;
my $type = shift;
# sanity check: type existence
return $self->add($type, @_) unless ($self->has_type($type));
# sanity check: root node
if ($type eq $self->{root}) {
if (@_) { croak("Cannot move hierarchy root '$type'"); }
else { return $self; }
}
# ensure parents are well-defined & well-placed
@_ = $self->ensure_types(@_);
my $kids = $self->{children};
my $prts = $self->{parents};
if (exists($prts->{$type})) {
# adjust old child-relations for moved $type
foreach (values(%{$prts->{$type}})) {
delete($kids->{$_}{$type});
}
}
# add new child-relations for moved $type
foreach (@_) { $kids->{$_}{$type} = $type; }
# adjust parent-relation for moved $type
%{$prts->{$type}} = map { ($_ => $_) } @_;
return $self
}
#--------------------------------------------------------------
# BUG (fixed): deleting an intermediate type orphans its descendants
sub remove ($@) {
my $self = shift;
@_ = # sanity check
grep {
$self->has_type($_) &&
($_ ne $self->root ||
(carp("attempt to remove hierarchy root!") && 0))
} @_;
return $self unless (@_); # not really deleting anything
delete(@{$self->{types}}{@_});
my ($kids,$parents,$deleted);
foreach $deleted (@_) {
# adopt orphans
$kids = $self->{children}{$deleted};
$parents = $self->{parents}{$deleted};
foreach (values(%$kids)) { # $_ is an orphaned child
$self->{parents}{$_} = {} unless (exists($self->{parents}{$_}));
@{$self->{parents}{$_}}{values(%$parents)} = values(%$parents);
delete(@{$self->{parents}{$_}}{@_});
}
foreach (values(%$parents)) { # $_ is an adopting grandparent
$self->{children}{$_} = {} unless (exists($self->{children}{$_}));
@{$self->{children}{$_}}{values(%$kids)} = values(%$kids);
delete(@{$self->{children}{$_}}{@_});
}
}
# delete inheritance information for deleted types
delete(@{$self->{parents}}{@_});
delete(@{$self->{children}}{@_});
return $self;
}
#--------------------------------------------------------------
# $h1->assign($h2)
sub assign ($$) {
my ($h1,$h2) = @_;
return $h1->SUPER::assign($h2) unless ($h2->isa($h1));
$h1->clear();
%{$h1->{types}} = %{$h2->{types}};
$h1->_root($h2->{root});
foreach (values(%{$h1->{types}})) {
%{$h1->{parents}{$_}} = %{$h2->{parents}{$_}};
%{$h1->{children}{$_}} = %{$h2->{children}{$_}};
}
# assign attributes
%{$h1->_attributes} = %{$h2->_attributes};
%{$h1->_hattributes} = %{$h2->_hattributes};
delete($h1->_attributes->{$h2});
return $h1;
}
#--------------------------------------------------------------
# $h1->merge($h2,...)
sub merge ($@) {
my $h1 = shift;
my ($a2);
while ($h2 = shift) {
unless ($h2->isa($h1)) {
$h1->SUPER::merge($h2);
next;
}
# add all types
@{$h1->{types}}{$h2->types} = $h2->types;
# adopt $h2->root under $h1->root if they differ
$h1->move($h2->{root}) unless ($h1->{root} eq $h2->{root});
# merge/override hierarchy-attributes
%{$h1->_hattributes} = (%$h1->_hattributes, %{$h2->_hattributes});
# merge in all parent-child relations and attributes
foreach (values(%{$h2->{types}})) {
# parents
@{$h1->{parents}{$_}}{$h2->parents($_)} = $h2->parents($_);
# children
@{$h1->{children}{$_}}{$h2->children($_)} = $h2->children($_);
# attributes
if (defined($a2 = $h2->_attributes($_)) && %$a2) {
@{$h1->{attrs}{$_}}{keys(%$a2)} = values(%$a2);
}
}
}
return $h1;
}
#--------------------------------------------------------------
# $h->clear
sub clear ($) {
my $self = shift;
%{$self->{types}} = ();
%{$self->{parents}} = ();
%{$self->{children}} = ();
%{$self->{attributes}} = ();
my $hattrs = $self->_hattributes; # save this reference!
%{$self->{attrs}} = ();
%$hattrs = ();
$self->_hattributes($hattrs);
# make sure we still have the root type!
$self->_root($self->root);
return $self;
}
# end manipulation
###############################################################
# additional sorting / comparison
###############################################################
# $h->_minimize($hashref)
sub _minimize ($$) {
my ($self,$hash) = @_;
my ($t1,$t2);
my @members = values(%$hash);
MINIMIZE_T1:
foreach $t1 (@members) {
next unless (exists($hash->{$t1}));
unless ($self->has_type($t1)) {
# sanity check
delete($hash->{$t1});
next;
}
foreach $t2 (@members) {
next unless (exists($hash->{$t2}));
if ($self->has_ancestor($t1,$t2)) {
delete($hash->{$t1});
next MINIMIZE_T1;
}
}
}
return $hash;
}
# $h->_maximize($hashref)
sub _maximize ($$) {
my ($self,$hash) = @_;
my ($t1,$t2);
my @members = values(%$hash);
MAXIMIZE_T1:
foreach $t1 (@members) {
next unless (exists($hash->{$t1}));
unless ($self->has_type($t1)) {
# sanity check
delete($hash->{$t1});
next;
}
foreach $t2 (@members) {
next unless (exists($hash->{$t2}));
if ($self->has_descendant($t1,$t2)) {
delete($hash->{$t1});
next MAXIMIZE_T1;
}
}
}
return $set;
}
# end sorting/comparison
###############################################################
# unsorted
###############################################################
###############################################################
# Type operations
###############################################################
#--------------------------------------------------------------
# lub : inherited
#--------------------------------------------------------------
# glb : inherited
###############################################################
# Accessors/manipulators
###############################################################
sub _types ($) { return $_[0]->{types}; }
sub _root ($;$) {
my $self = shift;
return $self->{root} unless (@_);
my $root = shift(@_);
$root = 'BOTTOM' unless (defined($root));
$self->{types}{$root} = $root;
$self->{parents}{$root} = {} unless (defined($self->{parents}{$root}));
$self->{children}{$root} = {} unless (defined($self->{children}{$root}));
return $self->{root} = $root;
}
*root = \&_root;
# \%prts_by_type = $h->_parents()
# \%type_prts = $h->_parents($type)
sub _parents ($;$) {
return
(exists($_[1])
? (exists($_[0]->{parents}{$_[1]})
? $_[0]->{parents}{$_[1]}
: undef)
: $_[0]->{parents});
}
# \%kids_by_type = $h->_children()
# \%type_kids = $h->children($type)
sub _children ($;$) {
return
(exists($_[1])
? (exists($_[0]->{children}{$_[1]})
? $_[0]->{children}{$_[1]}
: undef)
: $_[0]->{children});
}
# * C<_attributes()>, C<_attributes($type)>, C<_attributes($type, $hashref)>
sub _attributes ($;$$) {
return
(exists($_[1])
? (defined($_[1])
? (exists($_[2])
? (defined($_[2])
? $_[0]->{attrs}{$_[1]} = $_[2]
: delete($_[0]->{attrs}{$_[1]}))
: $_[0]->{attrs}{$_[1]})
: undef)
: $_[0]->{attrs});
}
1;
__END__