The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# $Id: Graph.pm,v 1.5 2007/08/14 15:45:51 ajk Exp $

use strict;
use warnings;

package Data::Passphrase::Graph; {
    use Object::InsideOut qw(Exporter);

    # export utility routines and configuration directive names
    BEGIN {
        our @EXPORT_OK = qw(build_graph graph_check);
    }

    # object attributes
    my @debug :Field(Std => 'debug', Type => 'numeric' );
    my @graph :Field(Std => 'graph', Type => 'hash_ref');

    my %init_args :InitArgs = (
        debug  => {Def => 0,  Field => \@debug, Type => 'numeric'},
        graph  => {Def => {}, Field => \@graph, Type => 'HASH'   },
    );

    # is a word on the graph?
    sub has {
        my ($self, $word) = @_;
        while ($word =~ s/(.)(.)/$2/) {
            return 0 if !exists $self->get_graph()->{$1}{$2};
        }
        return 1;
    }

    # procedural interface
    sub build_graph {
        my ($type) = @_;

        my $class = __PACKAGE__;
        if (defined $type) {
            $class .= ucfirst $type;
        }

        return $class->new();
    }

    # is the word contained by the graph?
    sub graph_check {
        my ($graph, $word) = @_;
        return $graph->has($word);
    }
}

1;
__END__

=head1 NAME

Data::Passphrase::Graph - directed graphs for passphrase strength checking

=head1 SYNOPSIS

Object-oriented interface:

 use Data::Passphrase::Qwerty;

 my $graph = Data::Passphrase::Qwerty->new();
 print $graph->has('qwerty');          # prints 1
 print $graph->has('ytrewq');          # prints 1
 print $graph->has('qazxdr');          # prints 1
 print $graph->has('qwerfvgtrdxz');    # prints 1

 use Data::Passphrase::Roman;

 $graph = Data::Passphrase::Roman->new();
 print $graph->has('abcdef');          # prints 1
 print $graph->has('fedcba');          # prints 1
 print $graph->has('xyzabc');          # prints 1
 print $graph->has('cbazyx');          # prints 1

Procedural interface:

 use Data::Passphrase qw(build_graph graph_check);

 my $graph = build_graph 'qwerty';
 print graph_check $graph, 'qwerty';          # prints 1
 print graph_check $graph, 'ytrewq';          # prints 1
 print graph_check $graph, 'qazxdr';          # prints 1
 print graph_check $graph, 'qwerfvgtrdxz';    # prints 1

 $graph = build_graph 'roman';
 print graph_check $graph, 'abcdef';          # prints 1
 print graph_check $graph, 'fedcba';          # prints 1
 print graph_check $graph, 'xyzabc';          # prints 1
 print graph_check $graph, 'cbazyx';          # prints 1

=head1 DESCRIPTION

This module provides a simple interface for using directed graphs with
L<Data::Passphrase|Data::Passphrase> to find trivial patterns in
passphrases.

=head2 Graph Format

Graphs are represented by hashes.  Each node on the graph is a key
whose value is a hash of adjacent nodes.  So a bidirectional graph of
the alphabet would contain the element

 b => {a => 1, c => 1}

because each letter (C<b> in this case) should be linked to both the
previous letter (C<a>) and the next letter (C<c>).  See L</SYNOPSIS>
and L</EXAMPLES> for more examples.

=head1 OBJECT-ORIENTED INTERFACE

This module provides a constructor C<new>, which takes a reference to
a hash of initial attribute settings, and accessor methods of the form
get_I<attribute>() and set_I<attribute>().  See L</Attributes>.

Normally, the OO interface is accessed via subclasses.  For example,
you'd call Data::Passphrase::Graph::Roman->new() to construct a graph
of the alphabet.  The inherited methods and attributes are documented
here.

=head2 Methods

In addition to the constructor and accessor methods, the following
special methods are available.

=head3 has()

 $value = $self->has($word)

Returns TRUE if C<$word> is contained by the graph, FALSE if it isn't.

=head2 Attributes

The following attributes may be accessed via methods of the form
get_I<attribute>() and set_I<attribute>().

=head3 debug

If TRUE, enable debugging to the Apache error log.

=head3 graph

The graph itself (see L<"Graph Format">).

=head1 PROCEDURAL INTERFACE

Unlike the object-oriented interface, the procedural interface can
create any type of graph, specified as the argument to
L<build_graph()|/build_graph()>.  Then,
L<graph_check()|/graph_check()> is used to determine if a word is
contained by the graph.

=head3 build_graph()

 $graph = build_graph $type

Build a graph of type C<$type>.  This subroutine will essentially
construct a new object of the type

 "Data::Passphrase::Graph::" . ucfirst $type

and return the graph itself for use with
L<graph_check()|/graph_check()>.

=head3 graph_check()

 $value = graph_check $graph, $word

Returns TRUE if C<$word> is contained by C<$graph>, FALSE if it isn't.

=head1 EXAMPLES

The graph

   a -> b -> c
   ^         |
   `---------'

would be represented as

 %graph = (
     a => { b => 1 },
     b => { c => 1 },
     c => { a => 1 },
 );

Here's how to use it:

 $graph = Data::Passphrase::Graph->new({graph => \%graph});
 print $graph->has('abc');    # prints 1
 print $graph->has('cba');    # prints 0
 print $graph->has('cab');    # prints 1

=head1 AUTHOR

Andrew J. Korty <ajk@iu.edu>

=head1 SEE ALSO

Data::Passphrase(3)