Mercurial > repos > deepakjadmin > mayatool3_test3
view mayachemtools/lib/Graph/Path.pm @ 0:73ae111cf86f draft
Uploaded
author | deepakjadmin |
---|---|
date | Wed, 20 Jan 2016 11:55:01 -0500 |
parents | |
children |
line wrap: on
line source
package Graph::Path; # # $RCSfile: Path.pm,v $ # $Date: 2015/02/28 20:49:06 $ # $Revision: 1.25 $ # # Author: Manish Sud <msud@san.rr.com> # # Copyright (C) 2015 Manish Sud. All rights reserved. # # This file is part of MayaChemTools. # # MayaChemTools is free software; you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation; either version 3 of the License, or (at your option) any # later version. # # MayaChemTools is distributed in the hope that it will be useful, but without # any warranty; without even the implied warranty of merchantability of fitness # for a particular purpose. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330, # Boston, MA, 02111-1307, USA. # use strict; use Carp; use Exporter; use Storable (); use Scalar::Util (); use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); @ISA = qw(Exporter); @EXPORT = qw(); @EXPORT_OK = qw(); %EXPORT_TAGS = (all => [@EXPORT, @EXPORT_OK]); # Setup class variables... my($ClassName, $ObjectID); _InitializeClass(); # Overload Perl functions... use overload '""' => 'StringifyPath', '==' => '_PathEqualOperator', 'eq' => '_PathEqualOperator', 'fallback' => undef; # Class constructor... sub new { my($Class, @VertexIDs) = @_; # Initialize object... my $This = {}; bless $This, ref($Class) || $Class; $This->_InitializePath(); if (@VertexIDs) { $This->AddVertices(@VertexIDs); } return $This; } # Initialize object data... # sub _InitializePath { my($This) = @_; @{$This->{Vertices}} = (); } # Initialize class ... sub _InitializeClass { #Class name... $ClassName = __PACKAGE__; } # Add a vertex to path after the end vertex... # sub AddVertex { my($This, $VertexID) = @_; if (!defined $VertexID ) { carp "Warning: ${ClassName}->AddVertex: No vertex added: Vertex ID must be specified..."; return undef; } push @{$This->{Vertices}}, $VertexID; return $This; } # Add vertices to the path after the end vertex... # sub AddVertices { my($This, @VertexIDs) = @_; if (!@VertexIDs) { carp "Warning: ${ClassName}->AddVertices: No vertices added: Vertices list is empty..."; return undef; } push @{$This->{Vertices}}, @VertexIDs; return $This; } # Add a vertex to path after the end vertex... # sub PushVertex { my($This, $VertexID) = @_; return $This->AddVertex($VertexID); } # Add vertices to the path after the end vertex... # sub PushVertices { my($This, @VertexIDs) = @_; return $This->AddVertices(@VertexIDs); } # Remove end vertex from path... # sub PopVertex { my($This) = @_; if (!@{$This->{Vertices}}) { carp "Warning: ${ClassName}->PopVertex: No vertex removed: Path is empty..."; return undef; } pop @{$This->{Vertices}}; return $This; } # Remove start vertex from path... # sub ShiftVertex { my($This) = @_; if (!@{$This->{Vertices}}) { carp "Warning: ${ClassName}->ShiftVertex: No vertex removed: Path is empty..."; return undef; } shift @{$This->{Vertices}}; return $This; } # Add a vertex to path before the start vertex... # sub UnshiftVertex { my($This, $VertexID) = @_; if (!defined $VertexID ) { carp "Warning: ${ClassName}->UnshiftVertex: No vertex added: Vertex ID must be specified..."; return undef; } unshift @{$This->{Vertices}}, $VertexID; return $This; } # Add vertices to the path before the start vertex... # sub UnshiftVertices { my($This, @VertexIDs) = @_; if (!@VertexIDs) { carp "Warning: ${ClassName}->UnshiftVertices: No vertices added: Vertices list is empty..."; return undef; } unshift @{$This->{Vertices}}, @VertexIDs; return $This; } # Get length... # sub GetLength { my($This) = @_; return scalar @{$This->{Vertices}}; } # Get start vertex... # sub GetStartVertex { my($This) = @_; if (!$This->GetLength()) { return undef; } my($Index) = 0; return $This->_GetVertex($Index); } # Get end vertex... # sub GetEndVertex { my($This) = @_; if (!$This->GetLength()) { return undef; } my($Index); $Index = $This->GetLength() - 1; return $This->_GetVertex($Index); } # Get start and end vertices... # sub GetTerminalVertices { my($This) = @_; return ( $This->GetStartVertex(), $This->GetEndVertex() ), } # Get path vertices... # sub GetVertices { my($This) = @_; return wantarray ? @{$This->{Vertices}} : scalar @{$This->{Vertices}}; } # Get a specific vertex from path with indicies starting from 0... # sub GetVertex { my($This, $Index) = @_; if ($Index < 0) { croak "Error: ${ClassName}->GetValue: Index value must be a positive number..."; } if ($Index >= $This->GetLength()) { croak "Error: ${ClassName}->GetValue: Index vaue must be less than length of path..."; } if (!$This->GetLength()) { return undef; } return $This->_GetVertex($Index); } # Get a vertex... # sub _GetVertex { my($This, $Index) = @_; return $This->{Vertices}[$Index]; } # Get path edges as pair of vertices or number of edges... # sub GetEdges { my($This) = @_; if ($This->GetLength < 1) { return undef; } # Set up edges... my($Index, $VertexID1, $VertexID2, @Vertices, @Edges); @Edges = (); for $Index (0 .. ($#{$This->{Vertices}} - 1) ) { $VertexID1 = $This->{Vertices}[$Index]; $VertexID2 = $This->{Vertices}[$Index + 1]; push @Edges, ($VertexID1, $VertexID2); } return wantarray ? @Edges : ((scalar @Edges)/2); } # Is it a cycle? # sub IsCycle { my($This) = @_; my($StartVertex, $EndVertex); ($StartVertex, $EndVertex) = $This->GetTerminalVertices(); return ($StartVertex == $EndVertex) ? 1 : 0; } # For a path to be an independent path, it must meet the following conditions: # . All other vertices are unique. # sub IsIndependentPath { my($This) = @_; # Make sure it has at least two vertices... if ($This->GetLength() < 2) { return 0; } # Check frequency of occurence for non-terminal vertices... my($VertexID, $IndependenceStatus, @Vertices, %VerticesMap); @Vertices = $This->GetVertices(); shift @Vertices; pop @Vertices; %VerticesMap = (); $IndependenceStatus = 1; VERTEXID: for $VertexID (@Vertices) { if (exists $VerticesMap{$VertexID} ) { $IndependenceStatus = 0; last VERTEXID; } $VerticesMap{$VertexID} = $VertexID; } return $IndependenceStatus; } # For a path to be an independent cyclic path, it must meet the following conditions: # . Termimal vertices are the same # . All other vertices are unique. # sub IsIndependentCyclicPath { my($This) = @_; # Make sure it's a cycle... if (!($This->GetLength() >= 3 && $This->IsCycle())) { return 0; } return $This->IsIndependentPath(); } # Is it a path object? sub IsPath ($) { my($Object) = @_; return _IsPath($Object); } # Copy path... # sub Copy { my($This) = @_; my($NewPath); $NewPath = Storable::dclone($This); return $NewPath; } # Reverse order of vertices in path... # sub Reverse { my($This) = @_; my(@VertexIDs); @VertexIDs = (); push @VertexIDs, @{$This->{Vertices}}; @{$This->{Vertices}} = (); push @{$This->{Vertices}}, reverse @VertexIDs; return $This; } # Get vertices common between two paths... # sub GetCommonVertices { my($This, $Other) = @_; my($VertexID, @CommonVertices, %OtherVerticesMap); # Setup a vertices hash for a quick look up... %OtherVerticesMap = (); for $VertexID ($Other->GetVertices()) { $OtherVerticesMap{$VertexID} = $VertexID; } @CommonVertices = (); for $VertexID ($This->GetVertices()) { if ($OtherVerticesMap{$VertexID}) { push @CommonVertices, $VertexID } } return wantarray ? @CommonVertices : scalar @CommonVertices; } # Join the existing path with a new path specifed using a path object of a list of # verticies. # sub Join { my($This, @Values) = @_; return $This->_Join(@Values); } # Join the existing path with a new path specifed using a path object at a specified # vertex. # sub JoinAtVertex { my($This, $Other, $CenterVertexID) = @_; # Make sure CenterVertexID is end vertex in This and start vertex in Other before # joining them... if ($This->GetEndVertex() != $CenterVertexID) { $This->Reverse(); } if ($Other->GetStartVertex() != $CenterVertexID) { $Other->Reverse(); } return $This->_Join($Other); } # Join the existing path with a new path specifed using a path object of a list of # verticies. # # Notes: # . Paths must have a common terminal vertex. # . Based on the common terminal vertex found, new path vertices are added to the # current path in one of the four ways: # . New path at end of current path with same vertices order : EndVertex = NewStartVertex # . New path at end of current path with reversed vertices order: EndVertex = NewEndVertex # . New path at front of current path with same vertices order: StartVertex = NewEndVertex # . New path at front of current path with reversed vertices order: StartVertex = NewStartVertex # sub _Join { my($This, @Values) = @_; if (!@Values) { return; } # Get a list of new vertex IDs.. my($NewPath, $FirstValue, $TypeOfFirstValue, @NewVertexIDs); $NewPath = $This->Copy(); @NewVertexIDs = (); $FirstValue = $Values[0]; $TypeOfFirstValue = ref $FirstValue; if ($TypeOfFirstValue =~ /^(SCALAR|HASH|CODE|REF|GLOB)/) { croak "Error: ${ClassName}->JoinPath: Trying to add vertices to path object with a reference to unsupported value format..."; } if (_IsPath($FirstValue)) { # It's another path object... push @NewVertexIDs, @{$FirstValue->{Vertices}}; } elsif ($TypeOfFirstValue =~ /^ARRAY/) { # It's array reference... push @NewVertexIDs, @{$FirstValue}; } else { # It's a list of values... push @NewVertexIDs, @Values; } my($StartVertex, $EndVertex, $NewStartVertex, $NewEndVertex); ($StartVertex, $EndVertex) = $NewPath->GetTerminalVertices(); ($NewStartVertex, $NewEndVertex) = ($NewVertexIDs[0], $NewVertexIDs[$#NewVertexIDs]); if (!($EndVertex == $NewStartVertex || $EndVertex == $NewEndVertex || $StartVertex == $NewEndVertex || $StartVertex == $NewStartVertex)) { carp "Warning: ${ClassName}->JoinPath: Paths can't be joined: No common terminal vertex found..."; return undef; } if ($EndVertex == $NewStartVertex) { # Take out EndVertex and add new path at the end... pop @{$NewPath->{Vertices}}; push @{$NewPath->{Vertices}}, @NewVertexIDs; } elsif ($EndVertex == $NewEndVertex) { # Take out EndVertex and add new path at the end with reversed vertex order... pop @{$NewPath->{Vertices}}; push @{$NewPath->{Vertices}}, reverse @NewVertexIDs; } elsif ($StartVertex == $NewEndVertex) { # Take out NewEndVertex and add new path at the front... pop @NewVertexIDs; unshift @{$NewPath->{Vertices}}, @NewVertexIDs; } elsif ($StartVertex == $NewStartVertex) { # Take out NewStartVertex and add new path at the front... shift @NewVertexIDs; unshift @{$NewPath->{Vertices}}, reverse @NewVertexIDs; } return $NewPath, } # Compare two paths... # sub _PathEqualOperator { my($This, $Other) = @_; if (!(defined($This) && _IsPath($This) && defined($Other) && _IsPath($Other))) { croak "Error: ${ClassName}->_PathEqualOperator: Path equal comparison failed: Both object must be paths..."; } if ($This->GetLength() != $Other->GetLength()) { return 0; } my($ThisID, $OtherID, $ReverseOtherID); $ThisID = join('-', @{$This->{Vertices}}); $OtherID = join('-', @{$Other->{Vertices}}); $ReverseOtherID = join('-', reverse(@{$Other->{Vertices}})); return ($ThisID =~ /^($OtherID|$ReverseOtherID)$/) ? 1 : 0; } # Return a string containing vertices in the path... sub StringifyPath { my($This) = @_; my($PathString); $PathString = "Path: " . join('-', @{$This->{Vertices}}); return $PathString; } # Is it a path object? sub _IsPath { my($Object) = @_; return (Scalar::Util::blessed($Object) && $Object->isa($ClassName)) ? 1 : 0; } 1; __END__ =head1 NAME Path - Path class =head1 SYNOPSIS use Graph::Path; use Graph::Path qw(:all); =head1 DESCRIPTION B<Path> class provides the following methods: new, AddVertex, AddVertices, Copy, GetCommonVertices, GetEdges, GetEndVertex, GetLength, GetStartVertex, GetTerminalVertices, GetVertex, GetVertices, IsCycle, IsIndependentCyclicPath, IsIndependentPath, IsPath, Join, JoinAtVertex, PopVertex, PushVertex, PushVertices, Reverse, ShiftVertex, StringifyPath, UnshiftVertex, UnshiftVertices Path is a sequential list of vertices with an edge between two successive vertices. The path becomes a cycle when start vertex and end vertex are the same. The following operators are overloaded: "" == eq =head2 METHODS =over 4 =item B<new> $NewPath = new Path(); $NewPath = new Path(@VertexIDs); Using specified I<VertexIDs>, B<new> method creates a new B<Path> object and returns newly created B<Path> object. =item B<AddVertex> $Path->AddVertex($VertexID); Adds I<VertexID> to I<Path> and returns I<Path>. =item B<AddVertices> $Path->AddVertices(@VertexIDs); Adds vertices using I<VertexIDs> to I<Path> and returns I<Graph>. =item B<Copy> $Return = $Path->Copy(); Copies I<Path> and its associated data using B<Storable::dclone> and returns a new B<Path> object. =item B<GetCommonVertices> @CommonVertices = $Path->GetCommonVertices($OtherPath); $NumOfCommonVertices = $Path->GetCommonVertices($OtherPath); Returns an array containing common vertex IDs between two paths. In scalar context, number of common vertices is returned. =item B<GetEdges> @EdgesVertexIDs = $Path->GetEdges(); $NumOfEdges = $Path->GetEdges(); Returns an array containg successive paris of vertex IDs corresponding to all edges in I<Path>. In scalar context, the number of edges is returned. =item B<GetEndVertex> $VertexID = $Path->GetEndVertex(); Returns B<VertexID> of end vertex in I<Path>. =item B<GetLength> $Length = $Path->GetLength(); Returns B<Length> of I<Path> corresponding to number of vertices in I<Path>. =item B<GetStartVertex> $VertexID = $Path->GetStartVertex(); Returns B<VertexID> of start vertex in I<Path>. =item B<GetTerminalVertices> ($StartVertexID, $EndVertexID) = $Path->GetTerminalVertices(); Returns vertex IDs of start and end vertices in I<Path>. =item B<GetVertex> $VertexID = $Path->GetVertex($Index); Returns specific vertex ID from I<Path> corresponding to I<Index> with indicies starting from 0. =item B<GetVertices> @Vertices = $Path->GetVertices(); $NumOfVertices = $Path->GetVertices(); Returns an array containing all vertex IDs in I<Path>. In scalar context, number of vertices is returned. =item B<IsCycle> $Status = $Path->IsCycle(); Returns 1 or 0 based on whether I<Path> is a B<CyclicPath> which has the same start and end vertex IDs. =item B<IsIndependentCyclicPath> $Status = $Path->IsIndependentCyclicPath(); Returns 1 or 0 based on whether I<Path> is an independent B<CyclicPath>. For a I<Path> to be an independent cyclic path, it must be a cyclic path and have unique vertices. =item B<IsIndependentPath> $Status = $Path->IsIndependentPath(); Returns 1 or 0 based on whether I<Path> is an independent B<Path>. For a I<Path> to be an independent path, it must have unique vertices. =item B<IsPath> $Status = Graph::Path::IsPath(); Returns 1 or 0 based on whether I<Object> is a B<Path> object =item B<Join> $NewPath = $Path->Join($OtherPath); $NewPath = $Path->Join(@VertexIDs); Joins existing I<Path> with a new path specified as a I<OtherPath> object or an array of I<VertexIDs> and returns I<NewPath>. In order to successfully join two paths, terminal vertices must have a common vertex. Based on the common terminal vertex found, additional path vertices are added to the current I<Path> in one of the following four ways: . EndVertex = NewStartVertex: New path at end of current path with same vertices order . EndVertex = NewEndVertex: New path at end of current path with reversed vertices order . StartVertex = NewEndVertex: New path at front of current path with same vertices order . StartVertex = NewStartVertex: New path at front of current path with reversed vertices order =item B<JoinAtVertex> $NewPath = $Path->JoinAtVertex($OtherPath, $CenterVertexID); Joins existing I<Path> with I<OtherPath> at a specified I<CeterVertexID> and returns a I<NewPath>. =item B<PopVertex> $Path->PopVertex(); Removes end vertex from I<Path> and returns I<Path>. =item B<PushVertex> $Path->PushVertex($VertexID); Adds I<VertexID> to I<Path> after end vertex and returns I<Path>. =item B<PushVertices> $Path->PushVertices(@VertexIDs); Adds I<VertexIDs> to I<Path> after end vertex and returns I<Path>. =item B<Reverse> $Path->Reverse(); Reverses order of vertices in I<Path> and returns I<Path>. =item B<ShiftVertex> $Path->ShiftVertex(); Removes start vertex from I<Path> and returns I<Path>. =item B<StringifyPath> $String = $Path->StringifyPath(); Returns a string containing information about I<Path> object. =item B<UnshiftVertex> $Path->UnshiftVertex($VertexID); Adds I<VertexID> to I<Path> before start vertex and returns I<Path>. =item B<UnshiftVertices> $Path->UnshiftVertices(@VertexIDs); Adds I<VertexIDs> to I<Path> before start vertex and returns I<Path>. =back =head1 AUTHOR Manish Sud <msud@san.rr.com> =head1 SEE ALSO PathGraph.pm, PathsTraversal.pm =head1 COPYRIGHT Copyright (C) 2015 Manish Sud. All rights reserved. This file is part of MayaChemTools. MayaChemTools is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. =cut