@ISA
=
qw(Math::PartialOrder::Base)
;
@EXPORT
=
qw()
;
@EXPORT_OK
=
qw()
;
our
$VERSION
= 0.01;
sub
new ($;$) {
my
$class
=
shift
;
my
$args
=
shift
;
my
$self
=
bless
{
types
=> {},
root
=>
''
,
parents
=> {},
children
=> {},
attrs
=> {}
},
$class
;
$self
->_root(
$args
->{root}||
'BOTTOM'
);
$self
->_hattributes({});
return
$self
;
}
sub
types ($) {
return
values
(%{
$_
[0]->{types}}); }
sub
has_type ($$) {
return
(
defined
(
$_
[1]) &&
exists
(
$_
[0]->{types}{
$_
[1]})); }
sub
has_types ($@) {
my
$types
=
shift
->{types};
grep
{
return
''
unless
(
defined
(
$_
) &&
exists
(
$types
->{
$_
}));
}
@_
;
return
1;
}
sub
parents ($$) {
return
(
defined
(
$_
[1]) &&
exists
(
$_
[0]->{parents}{
$_
[1]})
?
values
(%{
$_
[0]->{parents}{
$_
[1]}})
:
qw()
);
}
sub
children ($$) {
return
(
defined
(
$_
[1]) &&
exists
(
$_
[0]->{children}{
$_
[1]})
?
values
(%{
$_
[0]->{children}{
$_
[1]}})
:
qw()
);
}
sub
has_parent ($$) {
return
(
defined
(
$_
[1]) &&
exists
(
$_
[0]->{parents}{
$_
[1]})
?
exists
(
$_
[0]->{parents}{
$_
[1]}{
$_
[2]})
:
''
);
}
sub
has_child ($$) {
return
(
defined
(
$_
[1]) &&
exists
(
$_
[0]->{parents}{
$_
[1]})
?
exists
(
$_
[0]->{parents}{
$_
[1]}{
$_
[2]})
:
''
);
}
sub
ancestors ($$) {
return
values
(%{
$_
[0]->_ancestors(
$_
[1])}); }
sub
descendants ($$) {
return
values
(%{
$_
[0]->_ancestors(
$_
[1])}); }
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
;
}
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
;
}
sub
add ($$@) {
my
$self
=
shift
;
my
$type
=
shift
;
unless
(
defined
(
$type
)) {
carp(
"cannot add undefined type"
);
return
$self
;
}
return
$self
->move(
$type
,
@_
)
if
(
$self
->has_type(
$type
));
$self
->{types}{
$type
} =
$type
;
@_
=
$self
->ensure_types(
@_
);
if
(
@_
) {
$self
->{parents}{
$type
} = {};
@{
$self
->{parents}{
$type
}}{
@_
} =
@_
;
}
my
$kids
=
$self
->{children};
foreach
(
@_
) {
$kids
->{
$_
}{
$type
} =
$type
; }
return
$self
;
}
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
;
}
sub
move ($$@) {
my
$self
=
shift
;
my
$type
=
shift
;
return
$self
->add(
$type
,
@_
)
unless
(
$self
->has_type(
$type
));
if
(
$type
eq
$self
->{root}) {
if
(
@_
) { croak(
"Cannot move hierarchy root '$type'"
); }
else
{
return
$self
; }
}
@_
=
$self
->ensure_types(
@_
);
my
$kids
=
$self
->{children};
my
$prts
=
$self
->{parents};
if
(
exists
(
$prts
->{
$type
})) {
foreach
(
values
(%{
$prts
->{
$type
}})) {
delete
(
$kids
->{
$_
}{
$type
});
}
}
foreach
(
@_
) {
$kids
->{
$_
}{
$type
} =
$type
; }
%{
$prts
->{
$type
}} =
map
{ (
$_
=>
$_
) }
@_
;
return
$self
}
sub
remove ($@) {
my
$self
=
shift
;
@_
=
grep
{
$self
->has_type(
$_
) &&
(
$_
ne
$self
->root ||
(carp(
"attempt to remove hierarchy root!"
) && 0))
}
@_
;
return
$self
unless
(
@_
);
delete
(@{
$self
->{types}}{
@_
});
my
(
$kids
,
$parents
,
$deleted
);
foreach
$deleted
(
@_
) {
$kids
=
$self
->{children}{
$deleted
};
$parents
=
$self
->{parents}{
$deleted
};
foreach
(
values
(
%$kids
)) {
$self
->{parents}{
$_
} = {}
unless
(
exists
(
$self
->{parents}{
$_
}));
@{
$self
->{parents}{
$_
}}{
values
(
%$parents
)} =
values
(
%$parents
);
delete
(@{
$self
->{parents}{
$_
}}{
@_
});
}
foreach
(
values
(
%$parents
)) {
$self
->{children}{
$_
} = {}
unless
(
exists
(
$self
->{children}{
$_
}));
@{
$self
->{children}{
$_
}}{
values
(
%$kids
)} =
values
(
%$kids
);
delete
(@{
$self
->{children}{
$_
}}{
@_
});
}
}
delete
(@{
$self
->{parents}}{
@_
});
delete
(@{
$self
->{children}}{
@_
});
return
$self
;
}
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}{
$_
}};
}
%{
$h1
->_attributes} = %{
$h2
->_attributes};
%{
$h1
->_hattributes} = %{
$h2
->_hattributes};
delete
(
$h1
->_attributes->{
$h2
});
return
$h1
;
}
sub
merge ($@) {
my
$h1
=
shift
;
my
(
$a2
);
while
(
$h2
=
shift
) {
unless
(
$h2
->isa(
$h1
)) {
$h1
->SUPER::merge(
$h2
);
next
;
}
@{
$h1
->{types}}{
$h2
->types} =
$h2
->types;
$h1
->move(
$h2
->{root})
unless
(
$h1
->{root} eq
$h2
->{root});
%{
$h1
->_hattributes} = (
%$h1
->_hattributes, %{
$h2
->_hattributes});
foreach
(
values
(%{
$h2
->{types}})) {
@{
$h1
->{parents}{
$_
}}{
$h2
->parents(
$_
)} =
$h2
->parents(
$_
);
@{
$h1
->{children}{
$_
}}{
$h2
->children(
$_
)} =
$h2
->children(
$_
);
if
(
defined
(
$a2
=
$h2
->_attributes(
$_
)) &&
%$a2
) {
@{
$h1
->{attrs}{
$_
}}{
keys
(
%$a2
)} =
values
(
%$a2
);
}
}
}
return
$h1
;
}
sub
clear ($) {
my
$self
=
shift
;
%{
$self
->{types}} = ();
%{
$self
->{parents}} = ();
%{
$self
->{children}} = ();
%{
$self
->{attributes}} = ();
my
$hattrs
=
$self
->_hattributes;
%{
$self
->{attrs}} = ();
%$hattrs
= ();
$self
->_hattributes(
$hattrs
);
$self
->_root(
$self
->root);
return
$self
;
}
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
)) {
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
;
}
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
)) {
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
;
}
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
;
sub
_parents ($;$) {
return
(
exists
(
$_
[1])
? (
exists
(
$_
[0]->{parents}{
$_
[1]})
?
$_
[0]->{parents}{
$_
[1]}
:
undef
)
:
$_
[0]->{parents});
}
sub
_children ($;$) {
return
(
exists
(
$_
[1])
? (
exists
(
$_
[0]->{children}{
$_
[1]})
?
$_
[0]->{children}{
$_
[1]}
:
undef
)
:
$_
[0]->{children});
}
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;