#!/usr/bin/env perl

# This is mk-duplicate-key-checker, a program to analyze MySQL tables for
# duplicated or redundant indexes and foreign key constraints.
# 
# This program is copyright 2007-2010 Baron Schwartz.
# Feedback and improvements are welcome.
#
# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, version 2; OR the Perl Artistic License.  On UNIX and similar
# systems, you can issue `man perlgpl' or `man perlartistic' to read these
# licenses.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
# Place, Suite 330, Boston, MA  02111-1307  USA.

use strict;
use warnings FATAL => 'all';

our $VERSION = '1.2.13';
our $DISTRIB = '6652';
our $SVN_REV = sprintf("%d", (q$Revision: 6550 $ =~ m/(\d+)/g, 0));

# ###########################################################################
# VersionParser package 5266
# This package is a copy without comments from the original.  The original
# with comments and its test file can be found in the SVN repository at,
#   trunk/common/VersionParser.pm
#   trunk/common/t/VersionParser.t
# See http://code.google.com/p/maatkit/wiki/Developers for more information.
# ###########################################################################
package VersionParser;

use strict;
use warnings FATAL => 'all';

use English qw(-no_match_vars);

use constant MKDEBUG => $ENV{MKDEBUG} || 0;

sub new {
   my ( $class ) = @_;
   bless {}, $class;
}

sub parse {
   my ( $self, $str ) = @_;
   my $result = sprintf('%03d%03d%03d', $str =~ m/(\d+)/g);
   MKDEBUG && _d($str, 'parses to', $result);
   return $result;
}

sub version_ge {
   my ( $self, $dbh, $target ) = @_;
   if ( !$self->{$dbh} ) {
      $self->{$dbh} = $self->parse(
         $dbh->selectrow_array('SELECT VERSION()'));
   }
   my $result = $self->{$dbh} ge $self->parse($target) ? 1 : 0;
   MKDEBUG && _d($self->{$dbh}, 'ge', $target, ':', $result);
   return $result;
}

sub _d {
   my ($package, undef, $line) = caller 0;
   @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
        map { defined $_ ? $_ : 'undef' }
        @_;
   print STDERR "# $package:$line $PID ", join(' ', @_), "\n";
}

1;

# ###########################################################################
# End VersionParser package
# ###########################################################################

# ###########################################################################
# Quoter package 6549
# This package is a copy without comments from the original.  The original
# with comments and its test file can be found in the SVN repository at,
#   trunk/common/Quoter.pm
#   trunk/common/t/Quoter.t
# See http://code.google.com/p/maatkit/wiki/Developers for more information.
# ###########################################################################
package Quoter;

use strict;
use warnings FATAL => 'all';
use English qw(-no_match_vars);

use constant MKDEBUG => $ENV{MKDEBUG} || 0;

sub new {
   my ( $class ) = @_;
   return bless {}, $class;
}

sub quote {
   my ( $self, @vals ) = @_;
   foreach my $val ( @vals ) {
      $val =~ s/`/``/g;
   }
   return join('.', map { '`' . $_ . '`' } @vals);
}

sub quote_val {
   my ( $self, $val ) = @_;

   return 'NULL' unless defined $val;          # undef = NULL
   return "''" if $val eq '';                  # blank string = ''
   return $val if $val =~ m/0x[0-9a-fA-F]+$/;  # hex data

   $val =~ s/(['\\])/\\$1/g;
   return "'$val'";
}

sub split_unquote {
   my ( $self, $db_tbl, $default_db ) = @_;
   $db_tbl =~ s/`//g;
   my ( $db, $tbl ) = split(/[.]/, $db_tbl);
   if ( !$tbl ) {
      $tbl = $db;
      $db  = $default_db;
   }
   return ($db, $tbl);
}

sub literal_like {
   my ( $self, $like ) = @_;
   return unless $like;
   $like =~ s/([%_])/\\$1/g;
   return "'$like'";
}

sub join_quote {
   my ( $self, $default_db, $db_tbl ) = @_;
   return unless $db_tbl;
   my ($db, $tbl) = split(/[.]/, $db_tbl);
   if ( !$tbl ) {
      $tbl = $db;
      $db  = $default_db;
   }
   $db  = "`$db`"  if $db  && $db  !~ m/^`/;
   $tbl = "`$tbl`" if $tbl && $tbl !~ m/^`/;
   return $db ? "$db.$tbl" : $tbl;
}

1;

# ###########################################################################
# End Quoter package
# ###########################################################################

# ###########################################################################
# TableParser package 5980
# This package is a copy without comments from the original.  The original
# with comments and its test file can be found in the SVN repository at,
#   trunk/common/TableParser.pm
#   trunk/common/t/TableParser.t
# See http://code.google.com/p/maatkit/wiki/Developers for more information.
# ###########################################################################
package TableParser;

use strict;
use warnings FATAL => 'all';
use English qw(-no_match_vars);
use Data::Dumper;
$Data::Dumper::Indent    = 1;
$Data::Dumper::Sortkeys  = 1;
$Data::Dumper::Quotekeys = 0;

use constant MKDEBUG => $ENV{MKDEBUG} || 0;


sub new {
   my ( $class, %args ) = @_;
   my @required_args = qw(Quoter);
   foreach my $arg ( @required_args ) {
      die "I need a $arg argument" unless $args{$arg};
   }
   my $self = { %args };
   return bless $self, $class;
}


sub parse {
   my ( $self, $ddl, $opts ) = @_;
   return unless $ddl;
   if ( ref $ddl eq 'ARRAY' ) {
      if ( lc $ddl->[0] eq 'table' ) {
         $ddl = $ddl->[1];
      }
      else {
         return {
            engine => 'VIEW',
         };
      }
   }

   if ( $ddl !~ m/CREATE (?:TEMPORARY )?TABLE `/ ) {
      die "Cannot parse table definition; is ANSI quoting "
         . "enabled or SQL_QUOTE_SHOW_CREATE disabled?";
   }

   my ($name)     = $ddl =~ m/CREATE (?:TEMPORARY )?TABLE\s+(`.+?`)/;
   (undef, $name) = $self->{Quoter}->split_unquote($name) if $name;

   $ddl =~ s/(`[^`]+`)/\L$1/g;

   my $engine = $self->get_engine($ddl);

   my @defs   = $ddl =~ m/^(\s+`.*?),?$/gm;
   my @cols   = map { $_ =~ m/`([^`]+)`/ } @defs;
   MKDEBUG && _d('Table cols:', join(', ', map { "`$_`" } @cols));

   my %def_for;
   @def_for{@cols} = @defs;

   my (@nums, @null);
   my (%type_for, %is_nullable, %is_numeric, %is_autoinc);
   foreach my $col ( @cols ) {
      my $def = $def_for{$col};
      my ( $type ) = $def =~ m/`[^`]+`\s([a-z]+)/;
      die "Can't determine column type for $def" unless $type;
      $type_for{$col} = $type;
      if ( $type =~ m/(?:(?:tiny|big|medium|small)?int|float|double|decimal|year)/ ) {
         push @nums, $col;
         $is_numeric{$col} = 1;
      }
      if ( $def !~ m/NOT NULL/ ) {
         push @null, $col;
         $is_nullable{$col} = 1;
      }
      $is_autoinc{$col} = $def =~ m/AUTO_INCREMENT/i ? 1 : 0;
   }

   my ($keys, $clustered_key) = $self->get_keys($ddl, $opts, \%is_nullable);

   return {
      name           => $name,
      cols           => \@cols,
      col_posn       => { map { $cols[$_] => $_ } 0..$#cols },
      is_col         => { map { $_ => 1 } @cols },
      null_cols      => \@null,
      is_nullable    => \%is_nullable,
      is_autoinc     => \%is_autoinc,
      clustered_key  => $clustered_key,
      keys           => $keys,
      defs           => \%def_for,
      numeric_cols   => \@nums,
      is_numeric     => \%is_numeric,
      engine         => $engine,
      type_for       => \%type_for,
   };
}

sub sort_indexes {
   my ( $self, $tbl ) = @_;

   my @indexes
      = sort {
         (($a ne 'PRIMARY') <=> ($b ne 'PRIMARY'))
         || ( !$tbl->{keys}->{$a}->{is_unique} <=> !$tbl->{keys}->{$b}->{is_unique} )
         || ( $tbl->{keys}->{$a}->{is_nullable} <=> $tbl->{keys}->{$b}->{is_nullable} )
         || ( scalar(@{$tbl->{keys}->{$a}->{cols}}) <=> scalar(@{$tbl->{keys}->{$b}->{cols}}) )
      }
      grep {
         $tbl->{keys}->{$_}->{type} eq 'BTREE'
      }
      sort keys %{$tbl->{keys}};

   MKDEBUG && _d('Indexes sorted best-first:', join(', ', @indexes));
   return @indexes;
}

sub find_best_index {
   my ( $self, $tbl, $index ) = @_;
   my $best;
   if ( $index ) {
      ($best) = grep { uc $_ eq uc $index } keys %{$tbl->{keys}};
   }
   if ( !$best ) {
      if ( $index ) {
         die "Index '$index' does not exist in table";
      }
      else {
         ($best) = $self->sort_indexes($tbl);
      }
   }
   MKDEBUG && _d('Best index found is', $best);
   return $best;
}

sub find_possible_keys {
   my ( $self, $dbh, $database, $table, $quoter, $where ) = @_;
   return () unless $where;
   my $sql = 'EXPLAIN SELECT * FROM ' . $quoter->quote($database, $table)
      . ' WHERE ' . $where;
   MKDEBUG && _d($sql);
   my $expl = $dbh->selectrow_hashref($sql);
   $expl = { map { lc($_) => $expl->{$_} } keys %$expl };
   if ( $expl->{possible_keys} ) {
      MKDEBUG && _d('possible_keys =', $expl->{possible_keys});
      my @candidates = split(',', $expl->{possible_keys});
      my %possible   = map { $_ => 1 } @candidates;
      if ( $expl->{key} ) {
         MKDEBUG && _d('MySQL chose', $expl->{key});
         unshift @candidates, grep { $possible{$_} } split(',', $expl->{key});
         MKDEBUG && _d('Before deduping:', join(', ', @candidates));
         my %seen;
         @candidates = grep { !$seen{$_}++ } @candidates;
      }
      MKDEBUG && _d('Final list:', join(', ', @candidates));
      return @candidates;
   }
   else {
      MKDEBUG && _d('No keys in possible_keys');
      return ();
   }
}

sub check_table {
   my ( $self, %args ) = @_;
   my @required_args = qw(dbh db tbl);
   foreach my $arg ( @required_args ) {
      die "I need a $arg argument" unless $args{$arg};
   }
   my ($dbh, $db, $tbl) = @args{@required_args};
   my $q      = $self->{Quoter};
   my $db_tbl = $q->quote($db, $tbl);
   MKDEBUG && _d('Checking', $db_tbl);

   my $sql = "SHOW TABLES FROM " . $q->quote($db)
           . ' LIKE ' . $q->literal_like($tbl);
   MKDEBUG && _d($sql);
   my $row;
   eval {
      $row = $dbh->selectrow_arrayref($sql);
   };
   if ( $EVAL_ERROR ) {
      MKDEBUG && _d($EVAL_ERROR);
      return 0;
   }
   if ( !$row->[0] || $row->[0] ne $tbl ) {
      MKDEBUG && _d('Table does not exist');
      return 0;
   }

   MKDEBUG && _d('Table exists; no privs to check');
   return 1 unless $args{all_privs};

   $sql = "SHOW FULL COLUMNS FROM $db_tbl";
   MKDEBUG && _d($sql);
   eval {
      $row = $dbh->selectrow_hashref($sql);
   };
   if ( $EVAL_ERROR ) {
      MKDEBUG && _d($EVAL_ERROR);
      return 0;
   }
   if ( !scalar keys %$row ) {
      MKDEBUG && _d('Table has no columns:', Dumper($row));
      return 0;
   }
   my $privs = $row->{privileges} || $row->{Privileges};

   $sql = "DELETE FROM $db_tbl LIMIT 0";
   MKDEBUG && _d($sql);
   eval {
      $dbh->do($sql);
   };
   my $can_delete = $EVAL_ERROR ? 0 : 1;

   MKDEBUG && _d('User privs on', $db_tbl, ':', $privs,
      ($can_delete ? 'delete' : ''));

   if ( !($privs =~ m/select/ && $privs =~ m/insert/ && $privs =~ m/update/
          && $can_delete) ) {
      MKDEBUG && _d('User does not have all privs');
      return 0;
   }

   MKDEBUG && _d('User has all privs');
   return 1;
}

sub get_engine {
   my ( $self, $ddl, $opts ) = @_;
   my ( $engine ) = $ddl =~ m/\).*?(?:ENGINE|TYPE)=(\w+)/;
   MKDEBUG && _d('Storage engine:', $engine);
   return $engine || undef;
}

sub get_keys {
   my ( $self, $ddl, $opts, $is_nullable ) = @_;
   my $engine        = $self->get_engine($ddl);
   my $keys          = {};
   my $clustered_key = undef;

   KEY:
   foreach my $key ( $ddl =~ m/^  ((?:[A-Z]+ )?KEY .*)$/gm ) {

      next KEY if $key =~ m/FOREIGN/;

      my $key_ddl = $key;
      MKDEBUG && _d('Parsed key:', $key_ddl);

      if ( $engine !~ m/MEMORY|HEAP/ ) {
         $key =~ s/USING HASH/USING BTREE/;
      }

      my ( $type, $cols ) = $key =~ m/(?:USING (\w+))? \((.+)\)/;
      my ( $special ) = $key =~ m/(FULLTEXT|SPATIAL)/;
      $type = $type || $special || 'BTREE';
      if ( $opts->{mysql_version} && $opts->{mysql_version} lt '004001000'
         && $engine =~ m/HEAP|MEMORY/i )
      {
         $type = 'HASH'; # MySQL pre-4.1 supports only HASH indexes on HEAP
      }

      my ($name) = $key =~ m/(PRIMARY|`[^`]*`)/;
      my $unique = $key =~ m/PRIMARY|UNIQUE/ ? 1 : 0;
      my @cols;
      my @col_prefixes;
      foreach my $col_def ( $cols =~ m/`[^`]+`(?:\(\d+\))?/g ) {
         my ($name, $prefix) = $col_def =~ m/`([^`]+)`(?:\((\d+)\))?/;
         push @cols, $name;
         push @col_prefixes, $prefix;
      }
      $name =~ s/`//g;

      MKDEBUG && _d( $name, 'key cols:', join(', ', map { "`$_`" } @cols));

      $keys->{$name} = {
         name         => $name,
         type         => $type,
         colnames     => $cols,
         cols         => \@cols,
         col_prefixes => \@col_prefixes,
         is_unique    => $unique,
         is_nullable  => scalar(grep { $is_nullable->{$_} } @cols),
         is_col       => { map { $_ => 1 } @cols },
         ddl          => $key_ddl,
      };

      if ( $engine =~ m/InnoDB/i && !$clustered_key ) {
         my $this_key = $keys->{$name};
         if ( $this_key->{name} eq 'PRIMARY' ) {
            $clustered_key = 'PRIMARY';
         }
         elsif ( $this_key->{is_unique} && !$this_key->{is_nullable} ) {
            $clustered_key = $this_key->{name};
         }
         MKDEBUG && $clustered_key && _d('This key is the clustered key');
      }
   }

   return $keys, $clustered_key;
}

sub get_fks {
   my ( $self, $ddl, $opts ) = @_;
   my $fks = {};

   foreach my $fk (
      $ddl =~ m/CONSTRAINT .* FOREIGN KEY .* REFERENCES [^\)]*\)/mg )
   {
      my ( $name ) = $fk =~ m/CONSTRAINT `(.*?)`/;
      my ( $cols ) = $fk =~ m/FOREIGN KEY \(([^\)]+)\)/;
      my ( $parent, $parent_cols ) = $fk =~ m/REFERENCES (\S+) \(([^\)]+)\)/;

      if ( $parent !~ m/\./ && $opts->{database} ) {
         $parent = "`$opts->{database}`.$parent";
      }

      $fks->{$name} = {
         name           => $name,
         colnames       => $cols,
         cols           => [ map { s/[ `]+//g; $_; } split(',', $cols) ],
         parent_tbl     => $parent,
         parent_colnames=> $parent_cols,
         parent_cols    => [ map { s/[ `]+//g; $_; } split(',', $parent_cols) ],
         ddl            => $fk,
      };
   }

   return $fks;
}

sub remove_auto_increment {
   my ( $self, $ddl ) = @_;
   $ddl =~ s/(^\).*?) AUTO_INCREMENT=\d+\b/$1/m;
   return $ddl;
}

sub remove_secondary_indexes {
   my ( $self, $ddl ) = @_;
   my $sec_indexes_ddl;
   my $tbl_struct = $self->parse($ddl);

   if ( ($tbl_struct->{engine} || '') =~ m/InnoDB/i ) {
      my $clustered_key = $tbl_struct->{clustered_key};
      $clustered_key  ||= '';

      my @sec_indexes   = map {
         my $key_def = $_->{ddl};
         $key_def =~ s/([\(\)])/\\$1/g;
         $ddl =~ s/\s+$key_def//i;

         my $key_ddl = "ADD $_->{ddl}";
         $key_ddl   .= ',' unless $key_ddl =~ m/,$/;
         $key_ddl;
      }
      grep { $_->{name} ne $clustered_key }
      values %{$tbl_struct->{keys}};
      MKDEBUG && _d('Secondary indexes:', Dumper(\@sec_indexes));

      if ( @sec_indexes ) {
         $sec_indexes_ddl = join(' ', @sec_indexes);
         $sec_indexes_ddl =~ s/,$//;
      }

      $ddl =~ s/,(\n\) )/$1/s;
   }
   else {
      MKDEBUG && _d('Not removing secondary indexes from',
         $tbl_struct->{engine}, 'table');
   }

   return $ddl, $sec_indexes_ddl, $tbl_struct;
}

sub _d {
   my ($package, undef, $line) = caller 0;
   @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
        map { defined $_ ? $_ : 'undef' }
        @_;
   print STDERR "# $package:$line $PID ", join(' ', @_), "\n";
}

1;

# ###########################################################################
# End TableParser package
# ###########################################################################

# ###########################################################################
# MySQLDump package 6345
# This package is a copy without comments from the original.  The original
# with comments and its test file can be found in the SVN repository at,
#   trunk/common/MySQLDump.pm
#   trunk/common/t/MySQLDump.t
# See http://code.google.com/p/maatkit/wiki/Developers for more information.
# ###########################################################################
package MySQLDump;

use strict;
use warnings FATAL => 'all';

use English qw(-no_match_vars);

use constant MKDEBUG => $ENV{MKDEBUG} || 0;

( our $before = <<'EOF') =~ s/^   //gm;
   /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
   /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
   /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
   /*!40101 SET NAMES utf8 */;
   /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
   /*!40103 SET TIME_ZONE='+00:00' */;
   /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
   /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
   /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
   /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
EOF

( our $after = <<'EOF') =~ s/^   //gm;
   /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
   /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
   /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
   /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
   /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
   /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
   /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
   /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
EOF

sub new {
   my ( $class, %args ) = @_;
   my $self = {
      cache => 0,  # Afaik no script uses this cache any longer because
   };
   return bless $self, $class;
}

sub dump {
   my ( $self, $dbh, $quoter, $db, $tbl, $what ) = @_;

   if ( $what eq 'table' ) {
      my $ddl = $self->get_create_table($dbh, $quoter, $db, $tbl);
      return unless $ddl;
      if ( $ddl->[0] eq 'table' ) {
         return $before
            . 'DROP TABLE IF EXISTS ' . $quoter->quote($tbl) . ";\n"
            . $ddl->[1] . ";\n";
      }
      else {
         return 'DROP TABLE IF EXISTS ' . $quoter->quote($tbl) . ";\n"
            . '/*!50001 DROP VIEW IF EXISTS '
            . $quoter->quote($tbl) . "*/;\n/*!50001 "
            . $self->get_tmp_table($dbh, $quoter, $db, $tbl) . "*/;\n";
      }
   }
   elsif ( $what eq 'triggers' ) {
      my $trgs = $self->get_triggers($dbh, $quoter, $db, $tbl);
      if ( $trgs && @$trgs ) {
         my $result = $before . "\nDELIMITER ;;\n";
         foreach my $trg ( @$trgs ) {
            if ( $trg->{sql_mode} ) {
               $result .= qq{/*!50003 SET SESSION SQL_MODE='$trg->{sql_mode}' */;;\n};
            }
            $result .= "/*!50003 CREATE */ ";
            if ( $trg->{definer} ) {
               my ( $user, $host )
                  = map { s/'/''/g; "'$_'"; }
                    split('@', $trg->{definer}, 2);
               $result .= "/*!50017 DEFINER=$user\@$host */ ";
            }
            $result .= sprintf("/*!50003 TRIGGER %s %s %s ON %s\nFOR EACH ROW %s */;;\n\n",
               $quoter->quote($trg->{trigger}),
               @{$trg}{qw(timing event)},
               $quoter->quote($trg->{table}),
               $trg->{statement});
         }
         $result .= "DELIMITER ;\n\n/*!50003 SET SESSION SQL_MODE=\@OLD_SQL_MODE */;\n\n";
         return $result;
      }
      else {
         return undef;
      }
   }
   elsif ( $what eq 'view' ) {
      my $ddl = $self->get_create_table($dbh, $quoter, $db, $tbl);
      return '/*!50001 DROP TABLE IF EXISTS ' . $quoter->quote($tbl) . "*/;\n"
         . '/*!50001 DROP VIEW IF EXISTS ' . $quoter->quote($tbl) . "*/;\n"
         . '/*!50001 ' . $ddl->[1] . "*/;\n";
   }
   else {
      die "You didn't say what to dump.";
   }
}

sub _use_db {
   my ( $self, $dbh, $quoter, $new ) = @_;
   if ( !$new ) {
      MKDEBUG && _d('No new DB to use');
      return;
   }
   my $sql = 'USE ' . $quoter->quote($new);
   MKDEBUG && _d($dbh, $sql);
   $dbh->do($sql);
   return;
}

sub get_create_table {
   my ( $self, $dbh, $quoter, $db, $tbl ) = @_;
   if ( !$self->{cache} || !$self->{tables}->{$db}->{$tbl} ) {
      my $sql = '/*!40101 SET @OLD_SQL_MODE := @@SQL_MODE, '
         . q{@@SQL_MODE := REPLACE(REPLACE(@@SQL_MODE, 'ANSI_QUOTES', ''), ',,', ','), }
         . '@OLD_QUOTE := @@SQL_QUOTE_SHOW_CREATE, '
         . '@@SQL_QUOTE_SHOW_CREATE := 1 */';
      MKDEBUG && _d($sql);
      eval { $dbh->do($sql); };
      MKDEBUG && $EVAL_ERROR && _d($EVAL_ERROR);
      $self->_use_db($dbh, $quoter, $db);
      $sql = "SHOW CREATE TABLE " . $quoter->quote($db, $tbl);
      MKDEBUG && _d($sql);
      my $href;
      eval { $href = $dbh->selectrow_hashref($sql); };
      if ( $EVAL_ERROR ) {
         warn "Failed to $sql.  The table may be damaged.\nError: $EVAL_ERROR";
         return;
      }

      $sql = '/*!40101 SET @@SQL_MODE := @OLD_SQL_MODE, '
         . '@@SQL_QUOTE_SHOW_CREATE := @OLD_QUOTE */';
      MKDEBUG && _d($sql);
      $dbh->do($sql);
      my ($key) = grep { m/create table/i } keys %$href;
      if ( $key ) {
         MKDEBUG && _d('This table is a base table');
         $self->{tables}->{$db}->{$tbl} = [ 'table', $href->{$key} ];
      }
      else {
         MKDEBUG && _d('This table is a view');
         ($key) = grep { m/create view/i } keys %$href;
         $self->{tables}->{$db}->{$tbl} = [ 'view', $href->{$key} ];
      }
   }
   return $self->{tables}->{$db}->{$tbl};
}

sub get_columns {
   my ( $self, $dbh, $quoter, $db, $tbl ) = @_;
   MKDEBUG && _d('Get columns for', $db, $tbl);
   if ( !$self->{cache} || !$self->{columns}->{$db}->{$tbl} ) {
      $self->_use_db($dbh, $quoter, $db);
      my $sql = "SHOW COLUMNS FROM " . $quoter->quote($db, $tbl);
      MKDEBUG && _d($sql);
      my $cols = $dbh->selectall_arrayref($sql, { Slice => {} });

      $self->{columns}->{$db}->{$tbl} = [
         map {
            my %row;
            @row{ map { lc $_ } keys %$_ } = values %$_;
            \%row;
         } @$cols
      ];
   }
   return $self->{columns}->{$db}->{$tbl};
}

sub get_tmp_table {
   my ( $self, $dbh, $quoter, $db, $tbl ) = @_;
   my $result = 'CREATE TABLE ' . $quoter->quote($tbl) . " (\n";
   $result .= join(",\n",
      map { '  ' . $quoter->quote($_->{field}) . ' ' . $_->{type} }
      @{$self->get_columns($dbh, $quoter, $db, $tbl)});
   $result .= "\n)";
   MKDEBUG && _d($result);
   return $result;
}

sub get_triggers {
   my ( $self, $dbh, $quoter, $db, $tbl ) = @_;
   if ( !$self->{cache} || !$self->{triggers}->{$db} ) {
      $self->{triggers}->{$db} = {};
      my $sql = '/*!40101 SET @OLD_SQL_MODE := @@SQL_MODE, '
         . q{@@SQL_MODE := REPLACE(REPLACE(@@SQL_MODE, 'ANSI_QUOTES', ''), ',,', ','), }
         . '@OLD_QUOTE := @@SQL_QUOTE_SHOW_CREATE, '
         . '@@SQL_QUOTE_SHOW_CREATE := 1 */';
      MKDEBUG && _d($sql);
      eval { $dbh->do($sql); };
      MKDEBUG && $EVAL_ERROR && _d($EVAL_ERROR);
      $sql = "SHOW TRIGGERS FROM " . $quoter->quote($db);
      MKDEBUG && _d($sql);
      my $sth = $dbh->prepare($sql);
      $sth->execute();
      if ( $sth->rows ) {
         my $trgs = $sth->fetchall_arrayref({});
         foreach my $trg (@$trgs) {
            my %trg;
            @trg{ map { lc $_ } keys %$trg } = values %$trg;
            push @{ $self->{triggers}->{$db}->{ $trg{table} } }, \%trg;
         }
      }
      $sql = '/*!40101 SET @@SQL_MODE := @OLD_SQL_MODE, '
         . '@@SQL_QUOTE_SHOW_CREATE := @OLD_QUOTE */';
      MKDEBUG && _d($sql);
      $dbh->do($sql);
   }
   if ( $tbl ) {
      return $self->{triggers}->{$db}->{$tbl};
   }
   return values %{$self->{triggers}->{$db}};
}

sub get_databases {
   my ( $self, $dbh, $quoter, $like ) = @_;
   if ( !$self->{cache} || !$self->{databases} || $like ) {
      my $sql = 'SHOW DATABASES';
      my @params;
      if ( $like ) {
         $sql .= ' LIKE ?';
         push @params, $like;
      }
      my $sth = $dbh->prepare($sql);
      MKDEBUG && _d($sql, @params);
      $sth->execute( @params );
      my @dbs = map { $_->[0] } @{$sth->fetchall_arrayref()};
      $self->{databases} = \@dbs unless $like;
      return @dbs;
   }
   return @{$self->{databases}};
}

sub get_table_status {
   my ( $self, $dbh, $quoter, $db, $like ) = @_;
   if ( !$self->{cache} || !$self->{table_status}->{$db} || $like ) {
      my $sql = "SHOW TABLE STATUS FROM " . $quoter->quote($db);
      my @params;
      if ( $like ) {
         $sql .= ' LIKE ?';
         push @params, $like;
      }
      MKDEBUG && _d($sql, @params);
      my $sth = $dbh->prepare($sql);
      $sth->execute(@params);
      my @tables = @{$sth->fetchall_arrayref({})};
      @tables = map {
         my %tbl; # Make a copy with lowercased keys
         @tbl{ map { lc $_ } keys %$_ } = values %$_;
         $tbl{engine} ||= $tbl{type} || $tbl{comment};
         delete $tbl{type};
         \%tbl;
      } @tables;
      $self->{table_status}->{$db} = \@tables unless $like;
      return @tables;
   }
   return @{$self->{table_status}->{$db}};
}

sub get_table_list {
   my ( $self, $dbh, $quoter, $db, $like ) = @_;
   if ( !$self->{cache} || !$self->{table_list}->{$db} || $like ) {
      my $sql = "SHOW /*!50002 FULL*/ TABLES FROM " . $quoter->quote($db);
      my @params;
      if ( $like ) {
         $sql .= ' LIKE ?';
         push @params, $like;
      }
      MKDEBUG && _d($sql, @params);
      my $sth = $dbh->prepare($sql);
      $sth->execute(@params);
      my @tables = @{$sth->fetchall_arrayref()};
      @tables = map {
         my %tbl = (
            name   => $_->[0],
            engine => ($_->[1] || '') eq 'VIEW' ? 'VIEW' : '',
         );
         \%tbl;
      } @tables;
      $self->{table_list}->{$db} = \@tables unless $like;
      return @tables;
   }
   return @{$self->{table_list}->{$db}};
}

sub _d {
   my ($package, undef, $line) = caller 0;
   @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
        map { defined $_ ? $_ : 'undef' }
        @_;
   print STDERR "# $package:$line $PID ", join(' ', @_), "\n";
}

1;

# ###########################################################################
# End MySQLDump package
# ###########################################################################

# ###########################################################################
# DSNParser package 6366
# This package is a copy without comments from the original.  The original
# with comments and its test file can be found in the SVN repository at,
#   trunk/common/DSNParser.pm
#   trunk/common/t/DSNParser.t
# See http://code.google.com/p/maatkit/wiki/Developers for more information.
# ###########################################################################
package DSNParser;

use strict;
use warnings FATAL => 'all';
use English qw(-no_match_vars);
use Data::Dumper;
$Data::Dumper::Indent    = 0;
$Data::Dumper::Quotekeys = 0;

eval {
   require DBI;
};
my $have_dbi = $EVAL_ERROR ? 0 : 1;

use constant MKDEBUG => $ENV{MKDEBUG} || 0;

sub new {
   my ( $class, %args ) = @_;
   foreach my $arg ( qw(opts) ) {
      die "I need a $arg argument" unless $args{$arg};
   }
   my $self = {
      opts => {}  # h, P, u, etc.  Should come from DSN OPTIONS section in POD.
   };
   foreach my $opt ( @{$args{opts}} ) {
      if ( !$opt->{key} || !$opt->{desc} ) {
         die "Invalid DSN option: ", Dumper($opt);
      }
      MKDEBUG && _d('DSN option:',
         join(', ',
            map { "$_=" . (defined $opt->{$_} ? ($opt->{$_} || '') : 'undef') }
               keys %$opt
         )
      );
      $self->{opts}->{$opt->{key}} = {
         dsn  => $opt->{dsn},
         desc => $opt->{desc},
         copy => $opt->{copy} || 0,
      };
   }
   return bless $self, $class;
}

sub prop {
   my ( $self, $prop, $value ) = @_;
   if ( @_ > 2 ) {
      MKDEBUG && _d('Setting', $prop, 'property');
      $self->{$prop} = $value;
   }
   return $self->{$prop};
}

sub parse {
   my ( $self, $dsn, $prev, $defaults ) = @_;
   if ( !$dsn ) {
      MKDEBUG && _d('No DSN to parse');
      return;
   }
   MKDEBUG && _d('Parsing', $dsn);
   $prev     ||= {};
   $defaults ||= {};
   my %given_props;
   my %final_props;
   my $opts = $self->{opts};

   foreach my $dsn_part ( split(/,/, $dsn) ) {
      if ( my ($prop_key, $prop_val) = $dsn_part =~  m/^(.)=(.*)$/ ) {
         $given_props{$prop_key} = $prop_val;
      }
      else {
         MKDEBUG && _d('Interpreting', $dsn_part, 'as h=', $dsn_part);
         $given_props{h} = $dsn_part;
      }
   }

   foreach my $key ( keys %$opts ) {
      MKDEBUG && _d('Finding value for', $key);
      $final_props{$key} = $given_props{$key};
      if (   !defined $final_props{$key}
           && defined $prev->{$key} && $opts->{$key}->{copy} )
      {
         $final_props{$key} = $prev->{$key};
         MKDEBUG && _d('Copying value for', $key, 'from previous DSN');
      }
      if ( !defined $final_props{$key} ) {
         $final_props{$key} = $defaults->{$key};
         MKDEBUG && _d('Copying value for', $key, 'from defaults');
      }
   }

   foreach my $key ( keys %given_props ) {
      die "Unknown DSN option '$key' in '$dsn'.  For more details, "
            . "please use the --help option, or try 'perldoc $PROGRAM_NAME' "
            . "for complete documentation."
         unless exists $opts->{$key};
   }
   if ( (my $required = $self->prop('required')) ) {
      foreach my $key ( keys %$required ) {
         die "Missing required DSN option '$key' in '$dsn'.  For more details, "
               . "please use the --help option, or try 'perldoc $PROGRAM_NAME' "
               . "for complete documentation."
            unless $final_props{$key};
      }
   }

   return \%final_props;
}

sub parse_options {
   my ( $self, $o ) = @_;
   die 'I need an OptionParser object' unless ref $o eq 'OptionParser';
   my $dsn_string
      = join(',',
          map  { "$_=".$o->get($_); }
          grep { $o->has($_) && $o->get($_) }
          keys %{$self->{opts}}
        );
   MKDEBUG && _d('DSN string made from options:', $dsn_string);
   return $self->parse($dsn_string);
}

sub as_string {
   my ( $self, $dsn, $props ) = @_;
   return $dsn unless ref $dsn;
   my %allowed = $props ? map { $_=>1 } @$props : ();
   return join(',',
      map  { "$_=" . ($_ eq 'p' ? '...' : $dsn->{$_})  }
      grep { defined $dsn->{$_} && $self->{opts}->{$_} }
      grep { !$props || $allowed{$_}                   }
      sort keys %$dsn );
}

sub usage {
   my ( $self ) = @_;
   my $usage
      = "DSN syntax is key=value[,key=value...]  Allowable DSN keys:\n\n"
      . "  KEY  COPY  MEANING\n"
      . "  ===  ====  =============================================\n";
   my %opts = %{$self->{opts}};
   foreach my $key ( sort keys %opts ) {
      $usage .= "  $key    "
             .  ($opts{$key}->{copy} ? 'yes   ' : 'no    ')
             .  ($opts{$key}->{desc} || '[No description]')
             . "\n";
   }
   $usage .= "\n  If the DSN is a bareword, the word is treated as the 'h' key.\n";
   return $usage;
}

sub get_cxn_params {
   my ( $self, $info ) = @_;
   my $dsn;
   my %opts = %{$self->{opts}};
   my $driver = $self->prop('dbidriver') || '';
   if ( $driver eq 'Pg' ) {
      $dsn = 'DBI:Pg:dbname=' . ( $info->{D} || '' ) . ';'
         . join(';', map  { "$opts{$_}->{dsn}=$info->{$_}" }
                     grep { defined $info->{$_} }
                     qw(h P));
   }
   else {
      $dsn = 'DBI:mysql:' . ( $info->{D} || '' ) . ';'
         . join(';', map  { "$opts{$_}->{dsn}=$info->{$_}" }
                     grep { defined $info->{$_} }
                     qw(F h P S A))
         . ';mysql_read_default_group=client';
   }
   MKDEBUG && _d($dsn);
   return ($dsn, $info->{u}, $info->{p});
}

sub fill_in_dsn {
   my ( $self, $dbh, $dsn ) = @_;
   my $vars = $dbh->selectall_hashref('SHOW VARIABLES', 'Variable_name');
   my ($user, $db) = $dbh->selectrow_array('SELECT USER(), DATABASE()');
   $user =~ s/@.*//;
   $dsn->{h} ||= $vars->{hostname}->{Value};
   $dsn->{S} ||= $vars->{'socket'}->{Value};
   $dsn->{P} ||= $vars->{port}->{Value};
   $dsn->{u} ||= $user;
   $dsn->{D} ||= $db;
}

sub get_dbh {
   my ( $self, $cxn_string, $user, $pass, $opts ) = @_;
   $opts ||= {};
   my $defaults = {
      AutoCommit         => 0,
      RaiseError         => 1,
      PrintError         => 0,
      ShowErrorStatement => 1,
      mysql_enable_utf8 => ($cxn_string =~ m/charset=utf8/ ? 1 : 0),
   };
   @{$defaults}{ keys %$opts } = values %$opts;

   if ( $opts->{mysql_use_result} ) {
      $defaults->{mysql_use_result} = 1;
   }

   if ( !$have_dbi ) {
      die "Cannot connect to MySQL because the Perl DBI module is not "
         . "installed or not found.  Run 'perl -MDBI' to see the directories "
         . "that Perl searches for DBI.  If DBI is not installed, try:\n"
         . "  Debian/Ubuntu  apt-get install libdbi-perl\n"
         . "  RHEL/CentOS    yum install perl-DBI\n"
         . "  OpenSolaris    pgk install pkg:/SUNWpmdbi\n";

   }

   my $dbh;
   my $tries = 2;
   while ( !$dbh && $tries-- ) {
      MKDEBUG && _d($cxn_string, ' ', $user, ' ', $pass, ' {',
         join(', ', map { "$_=>$defaults->{$_}" } keys %$defaults ), '}');

      eval {
         $dbh = DBI->connect($cxn_string, $user, $pass, $defaults);

         if ( $cxn_string =~ m/mysql/i ) {
            my $sql;

            $sql = 'SELECT @@SQL_MODE';
            MKDEBUG && _d($dbh, $sql);
            my ($sql_mode) = $dbh->selectrow_array($sql);

            $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1'
                 . '/*!40101, @@SQL_MODE=\'NO_AUTO_VALUE_ON_ZERO'
                 . ($sql_mode ? ",$sql_mode" : '')
                 . '\'*/';
            MKDEBUG && _d($dbh, $sql);
            $dbh->do($sql);

            if ( my ($charset) = $cxn_string =~ m/charset=(\w+)/ ) {
               $sql = "/*!40101 SET NAMES $charset*/";
               MKDEBUG && _d($dbh, ':', $sql);
               $dbh->do($sql);
               MKDEBUG && _d('Enabling charset for STDOUT');
               if ( $charset eq 'utf8' ) {
                  binmode(STDOUT, ':utf8')
                     or die "Can't binmode(STDOUT, ':utf8'): $OS_ERROR";
               }
               else {
                  binmode(STDOUT) or die "Can't binmode(STDOUT): $OS_ERROR";
               }
            }

            if ( $self->prop('set-vars') ) {
               $sql = "SET " . $self->prop('set-vars');
               MKDEBUG && _d($dbh, ':', $sql);
               $dbh->do($sql);
            }
         }
      };
      if ( !$dbh && $EVAL_ERROR ) {
         MKDEBUG && _d($EVAL_ERROR);
         if ( $EVAL_ERROR =~ m/not a compiled character set|character set utf8/ ) {
            MKDEBUG && _d('Going to try again without utf8 support');
            delete $defaults->{mysql_enable_utf8};
         }
         elsif ( $EVAL_ERROR =~ m/locate DBD\/mysql/i ) {
            die "Cannot connect to MySQL because the Perl DBD::mysql module is "
               . "not installed or not found.  Run 'perl -MDBD::mysql' to see "
               . "the directories that Perl searches for DBD::mysql.  If "
               . "DBD::mysql is not installed, try:\n"
               . "  Debian/Ubuntu  apt-get install libdbd-mysql-perl\n"
               . "  RHEL/CentOS    yum install perl-DBD-MySQL\n"
               . "  OpenSolaris    pgk install pkg:/SUNWapu13dbd-mysql\n";
         }
         if ( !$tries ) {
            die $EVAL_ERROR;
         }
      }
   }

   MKDEBUG && _d('DBH info: ',
      $dbh,
      Dumper($dbh->selectrow_hashref(
         'SELECT DATABASE(), CONNECTION_ID(), VERSION()/*!50038 , @@hostname*/')),
      'Connection info:',      $dbh->{mysql_hostinfo},
      'Character set info:',   Dumper($dbh->selectall_arrayref(
                     'SHOW VARIABLES LIKE "character_set%"', { Slice => {}})),
      '$DBD::mysql::VERSION:', $DBD::mysql::VERSION,
      '$DBI::VERSION:',        $DBI::VERSION,
   );

   return $dbh;
}

sub get_hostname {
   my ( $self, $dbh ) = @_;
   if ( my ($host) = ($dbh->{mysql_hostinfo} || '') =~ m/^(\w+) via/ ) {
      return $host;
   }
   my ( $hostname, $one ) = $dbh->selectrow_array(
      'SELECT /*!50038 @@hostname, */ 1');
   return $hostname;
}

sub disconnect {
   my ( $self, $dbh ) = @_;
   MKDEBUG && $self->print_active_handles($dbh);
   $dbh->disconnect;
}

sub print_active_handles {
   my ( $self, $thing, $level ) = @_;
   $level ||= 0;
   printf("# Active %sh: %s %s %s\n", ($thing->{Type} || 'undef'), "\t" x $level,
      $thing, (($thing->{Type} || '') eq 'st' ? $thing->{Statement} || '' : ''))
      or die "Cannot print: $OS_ERROR";
   foreach my $handle ( grep {defined} @{ $thing->{ChildHandles} } ) {
      $self->print_active_handles( $handle, $level + 1 );
   }
}

sub copy {
   my ( $self, $dsn_1, $dsn_2, %args ) = @_;
   die 'I need a dsn_1 argument' unless $dsn_1;
   die 'I need a dsn_2 argument' unless $dsn_2;
   my %new_dsn = map {
      my $key = $_;
      my $val;
      if ( $args{overwrite} ) {
         $val = defined $dsn_1->{$key} ? $dsn_1->{$key} : $dsn_2->{$key};
      }
      else {
         $val = defined $dsn_2->{$key} ? $dsn_2->{$key} : $dsn_1->{$key};
      }
      $key => $val;
   } keys %{$self->{opts}};
   return \%new_dsn;
}

sub _d {
   my ($package, undef, $line) = caller 0;
   @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
        map { defined $_ ? $_ : 'undef' }
        @_;
   print STDERR "# $package:$line $PID ", join(' ', @_), "\n";
}

1;

# ###########################################################################
# End DSNParser package
# ###########################################################################

# ###########################################################################
# OptionParser package 6322
# This package is a copy without comments from the original.  The original
# with comments and its test file can be found in the SVN repository at,
#   trunk/common/OptionParser.pm
#   trunk/common/t/OptionParser.t
# See http://code.google.com/p/maatkit/wiki/Developers for more information.
# ###########################################################################
package OptionParser;

use strict;
use warnings FATAL => 'all';

use Getopt::Long;
use List::Util qw(max);
use English qw(-no_match_vars);

use constant MKDEBUG => $ENV{MKDEBUG} || 0;

my $POD_link_re = '[LC]<"?([^">]+)"?>';

sub new {
   my ( $class, %args ) = @_;
   foreach my $arg ( qw(description) ) {
      die "I need a $arg argument" unless $args{$arg};
   }

   my ($program_name) = $PROGRAM_NAME =~ m/([.A-Za-z-]+)$/;
   $program_name ||= $PROGRAM_NAME;
   my $home = $ENV{HOME} || $ENV{HOMEPATH} || $ENV{USERPROFILE} || '.';

   my %attributes = (
      'type'       => 1,
      'short form' => 1,
      'group'      => 1,
      'default'    => 1,
      'cumulative' => 1,
      'negatable'  => 1,
   );

   my $self = {
      strict            => 1,
      prompt            => '<options>',
      head1             => 'OPTIONS',
      skip_rules        => 0,
      item              => '--(.*)',
      attributes        => \%attributes,
      parse_attributes  => \&_parse_attribs,

      %args,

      program_name      => $program_name,
      opts              => {},
      got_opts          => 0,
      short_opts        => {},
      defaults          => {},
      groups            => {},
      allowed_groups    => {},
      errors            => [],
      rules             => [],  # desc of rules for --help
      mutex             => [],  # rule: opts are mutually exclusive
      atleast1          => [],  # rule: at least one opt is required
      disables          => {},  # rule: opt disables other opts 
      defaults_to       => {},  # rule: opt defaults to value of other opt
      DSNParser         => undef,
      default_files     => [
         "/etc/maatkit/maatkit.conf",
         "/etc/maatkit/$program_name.conf",
         "$home/.maatkit.conf",
         "$home/.$program_name.conf",
      ],
      types             => {
         string => 's', # standard Getopt type
         int    => 'i', # standard Getopt type
         float  => 'f', # standard Getopt type
         Hash   => 'H', # hash, formed from a comma-separated list
         hash   => 'h', # hash as above, but only if a value is given
         Array  => 'A', # array, similar to Hash
         array  => 'a', # array, similar to hash
         DSN    => 'd', # DSN
         size   => 'z', # size with kMG suffix (powers of 2^10)
         time   => 'm', # time, with an optional suffix of s/h/m/d
      },
   };

   return bless $self, $class;
}

sub get_specs {
   my ( $self, $file ) = @_;
   $file ||= __FILE__;
   my @specs = $self->_pod_to_specs($file);
   $self->_parse_specs(@specs);

   open my $fh, "<", $file or die "Cannot open $file: $OS_ERROR";
   my $contents = do { local $/ = undef; <$fh> };
   close $file;
   if ( $contents =~ m/^=head1 DSN OPTIONS/m ) {
      MKDEBUG && _d('Parsing DSN OPTIONS');
      my $dsn_attribs = {
         dsn  => 1,
         copy => 1,
      };
      my $parse_dsn_attribs = sub {
         my ( $self, $option, $attribs ) = @_;
         map {
            my $val = $attribs->{$_};
            if ( $val ) {
               $val    = $val eq 'yes' ? 1
                       : $val eq 'no'  ? 0
                       :                 $val;
               $attribs->{$_} = $val;
            }
         } keys %$attribs;
         return {
            key => $option,
            %$attribs,
         };
      };
      my $dsn_o = new OptionParser(
         description       => 'DSN OPTIONS',
         head1             => 'DSN OPTIONS',
         dsn               => 0,         # XXX don't infinitely recurse!
         item              => '\* (.)',  # key opts are a single character
         skip_rules        => 1,         # no rules before opts
         attributes        => $dsn_attribs,
         parse_attributes  => $parse_dsn_attribs,
      );
      my @dsn_opts = map {
         my $opts = {
            key  => $_->{spec}->{key},
            dsn  => $_->{spec}->{dsn},
            copy => $_->{spec}->{copy},
            desc => $_->{desc},
         };
         $opts;
      } $dsn_o->_pod_to_specs($file);
      $self->{DSNParser} = DSNParser->new(opts => \@dsn_opts);
   }

   return;
}

sub DSNParser {
   my ( $self ) = @_;
   return $self->{DSNParser};
};

sub get_defaults_files {
   my ( $self ) = @_;
   return @{$self->{default_files}};
}

sub _pod_to_specs {
   my ( $self, $file ) = @_;
   $file ||= __FILE__;
   open my $fh, '<', $file or die "Cannot open $file: $OS_ERROR";

   my @specs = ();
   my @rules = ();
   my $para;

   local $INPUT_RECORD_SEPARATOR = '';
   while ( $para = <$fh> ) {
      next unless $para =~ m/^=head1 $self->{head1}/;
      last;
   }

   while ( $para = <$fh> ) {
      last if $para =~ m/^=over/;
      next if $self->{skip_rules};
      chomp $para;
      $para =~ s/\s+/ /g;
      $para =~ s/$POD_link_re/$1/go;
      MKDEBUG && _d('Option rule:', $para);
      push @rules, $para;
   }

   die "POD has no $self->{head1} section" unless $para;

   do {
      if ( my ($option) = $para =~ m/^=item $self->{item}/ ) {
         chomp $para;
         MKDEBUG && _d($para);
         my %attribs;

         $para = <$fh>; # read next paragraph, possibly attributes

         if ( $para =~ m/: / ) { # attributes
            $para =~ s/\s+\Z//g;
            %attribs = map {
                  my ( $attrib, $val) = split(/: /, $_);
                  die "Unrecognized attribute for --$option: $attrib"
                     unless $self->{attributes}->{$attrib};
                  ($attrib, $val);
               } split(/; /, $para);
            if ( $attribs{'short form'} ) {
               $attribs{'short form'} =~ s/-//;
            }
            $para = <$fh>; # read next paragraph, probably short help desc
         }
         else {
            MKDEBUG && _d('Option has no attributes');
         }

         $para =~ s/\s+\Z//g;
         $para =~ s/\s+/ /g;
         $para =~ s/$POD_link_re/$1/go;

         $para =~ s/\.(?:\n.*| [A-Z].*|\Z)//s;
         MKDEBUG && _d('Short help:', $para);

         die "No description after option spec $option" if $para =~ m/^=item/;

         if ( my ($base_option) =  $option =~ m/^\[no\](.*)/ ) {
            $option = $base_option;
            $attribs{'negatable'} = 1;
         }

         push @specs, {
            spec  => $self->{parse_attributes}->($self, $option, \%attribs), 
            desc  => $para
               . ($attribs{default} ? " (default $attribs{default})" : ''),
            group => ($attribs{'group'} ? $attribs{'group'} : 'default'),
         };
      }
      while ( $para = <$fh> ) {
         last unless $para;
         if ( $para =~ m/^=head1/ ) {
            $para = undef; # Can't 'last' out of a do {} block.
            last;
         }
         last if $para =~ m/^=item /;
      }
   } while ( $para );

   die "No valid specs in $self->{head1}" unless @specs;

   close $fh;
   return @specs, @rules;
}

sub _parse_specs {
   my ( $self, @specs ) = @_;
   my %disables; # special rule that requires deferred checking

   foreach my $opt ( @specs ) {
      if ( ref $opt ) { # It's an option spec, not a rule.
         MKDEBUG && _d('Parsing opt spec:',
            map { ($_, '=>', $opt->{$_}) } keys %$opt);

         my ( $long, $short ) = $opt->{spec} =~ m/^([\w-]+)(?:\|([^!+=]*))?/;
         if ( !$long ) {
            die "Cannot parse long option from spec $opt->{spec}";
         }
         $opt->{long} = $long;

         die "Duplicate long option --$long" if exists $self->{opts}->{$long};
         $self->{opts}->{$long} = $opt;

         if ( length $long == 1 ) {
            MKDEBUG && _d('Long opt', $long, 'looks like short opt');
            $self->{short_opts}->{$long} = $long;
         }

         if ( $short ) {
            die "Duplicate short option -$short"
               if exists $self->{short_opts}->{$short};
            $self->{short_opts}->{$short} = $long;
            $opt->{short} = $short;
         }
         else {
            $opt->{short} = undef;
         }

         $opt->{is_negatable}  = $opt->{spec} =~ m/!/        ? 1 : 0;
         $opt->{is_cumulative} = $opt->{spec} =~ m/\+/       ? 1 : 0;
         $opt->{is_required}   = $opt->{desc} =~ m/required/ ? 1 : 0;

         $opt->{group} ||= 'default';
         $self->{groups}->{ $opt->{group} }->{$long} = 1;

         $opt->{value} = undef;
         $opt->{got}   = 0;

         my ( $type ) = $opt->{spec} =~ m/=(.)/;
         $opt->{type} = $type;
         MKDEBUG && _d($long, 'type:', $type);


         $opt->{spec} =~ s/=./=s/ if ( $type && $type =~ m/[HhAadzm]/ );

         if ( (my ($def) = $opt->{desc} =~ m/default\b(?: ([^)]+))?/) ) {
            $self->{defaults}->{$long} = defined $def ? $def : 1;
            MKDEBUG && _d($long, 'default:', $def);
         }

         if ( $long eq 'config' ) {
            $self->{defaults}->{$long} = join(',', $self->get_defaults_files());
         }

         if ( (my ($dis) = $opt->{desc} =~ m/(disables .*)/) ) {
            $disables{$long} = $dis;
            MKDEBUG && _d('Deferring check of disables rule for', $opt, $dis);
         }

         $self->{opts}->{$long} = $opt;
      }
      else { # It's an option rule, not a spec.
         MKDEBUG && _d('Parsing rule:', $opt); 
         push @{$self->{rules}}, $opt;
         my @participants = $self->_get_participants($opt);
         my $rule_ok = 0;

         if ( $opt =~ m/mutually exclusive|one and only one/ ) {
            $rule_ok = 1;
            push @{$self->{mutex}}, \@participants;
            MKDEBUG && _d(@participants, 'are mutually exclusive');
         }
         if ( $opt =~ m/at least one|one and only one/ ) {
            $rule_ok = 1;
            push @{$self->{atleast1}}, \@participants;
            MKDEBUG && _d(@participants, 'require at least one');
         }
         if ( $opt =~ m/default to/ ) {
            $rule_ok = 1;
            $self->{defaults_to}->{$participants[0]} = $participants[1];
            MKDEBUG && _d($participants[0], 'defaults to', $participants[1]);
         }
         if ( $opt =~ m/restricted to option groups/ ) {
            $rule_ok = 1;
            my ($groups) = $opt =~ m/groups ([\w\s\,]+)/;
            my @groups = split(',', $groups);
            %{$self->{allowed_groups}->{$participants[0]}} = map {
               s/\s+//;
               $_ => 1;
            } @groups;
         }

         die "Unrecognized option rule: $opt" unless $rule_ok;
      }
   }

   foreach my $long ( keys %disables ) {
      my @participants = $self->_get_participants($disables{$long});
      $self->{disables}->{$long} = \@participants;
      MKDEBUG && _d('Option', $long, 'disables', @participants);
   }

   return; 
}

sub _get_participants {
   my ( $self, $str ) = @_;
   my @participants;
   foreach my $long ( $str =~ m/--(?:\[no\])?([\w-]+)/g ) {
      die "Option --$long does not exist while processing rule $str"
         unless exists $self->{opts}->{$long};
      push @participants, $long;
   }
   MKDEBUG && _d('Participants for', $str, ':', @participants);
   return @participants;
}

sub opts {
   my ( $self ) = @_;
   my %opts = %{$self->{opts}};
   return %opts;
}

sub short_opts {
   my ( $self ) = @_;
   my %short_opts = %{$self->{short_opts}};
   return %short_opts;
}

sub set_defaults {
   my ( $self, %defaults ) = @_;
   $self->{defaults} = {};
   foreach my $long ( keys %defaults ) {
      die "Cannot set default for nonexistent option $long"
         unless exists $self->{opts}->{$long};
      $self->{defaults}->{$long} = $defaults{$long};
      MKDEBUG && _d('Default val for', $long, ':', $defaults{$long});
   }
   return;
}

sub get_defaults {
   my ( $self ) = @_;
   return $self->{defaults};
}

sub get_groups {
   my ( $self ) = @_;
   return $self->{groups};
}

sub _set_option {
   my ( $self, $opt, $val ) = @_;
   my $long = exists $self->{opts}->{$opt}       ? $opt
            : exists $self->{short_opts}->{$opt} ? $self->{short_opts}->{$opt}
            : die "Getopt::Long gave a nonexistent option: $opt";

   $opt = $self->{opts}->{$long};
   if ( $opt->{is_cumulative} ) {
      $opt->{value}++;
   }
   else {
      $opt->{value} = $val;
   }
   $opt->{got} = 1;
   MKDEBUG && _d('Got option', $long, '=', $val);
}

sub get_opts {
   my ( $self ) = @_; 

   foreach my $long ( keys %{$self->{opts}} ) {
      $self->{opts}->{$long}->{got} = 0;
      $self->{opts}->{$long}->{value}
         = exists $self->{defaults}->{$long}       ? $self->{defaults}->{$long}
         : $self->{opts}->{$long}->{is_cumulative} ? 0
         : undef;
   }
   $self->{got_opts} = 0;

   $self->{errors} = [];

   if ( @ARGV && $ARGV[0] eq "--config" ) {
      shift @ARGV;
      $self->_set_option('config', shift @ARGV);
   }
   if ( $self->has('config') ) {
      my @extra_args;
      foreach my $filename ( split(',', $self->get('config')) ) {
         eval {
            push @extra_args, $self->_read_config_file($filename);
         };
         if ( $EVAL_ERROR ) {
            if ( $self->got('config') ) {
               die $EVAL_ERROR;
            }
            elsif ( MKDEBUG ) {
               _d($EVAL_ERROR);
            }
         }
      }
      unshift @ARGV, @extra_args;
   }

   Getopt::Long::Configure('no_ignore_case', 'bundling');
   GetOptions(
      map    { $_->{spec} => sub { $self->_set_option(@_); } }
      grep   { $_->{long} ne 'config' } # --config is handled specially above.
      values %{$self->{opts}}
   ) or $self->save_error('Error parsing options');

   if ( exists $self->{opts}->{version} && $self->{opts}->{version}->{got} ) {
      printf("%s  Ver %s Distrib %s Changeset %s\n",
         $self->{program_name}, $main::VERSION, $main::DISTRIB, $main::SVN_REV)
            or die "Cannot print: $OS_ERROR";
      exit 0;
   }

   if ( @ARGV && $self->{strict} ) {
      $self->save_error("Unrecognized command-line options @ARGV");
   }

   foreach my $mutex ( @{$self->{mutex}} ) {
      my @set = grep { $self->{opts}->{$_}->{got} } @$mutex;
      if ( @set > 1 ) {
         my $err = join(', ', map { "--$self->{opts}->{$_}->{long}" }
                      @{$mutex}[ 0 .. scalar(@$mutex) - 2] )
                 . ' and --'.$self->{opts}->{$mutex->[-1]}->{long}
                 . ' are mutually exclusive.';
         $self->save_error($err);
      }
   }

   foreach my $required ( @{$self->{atleast1}} ) {
      my @set = grep { $self->{opts}->{$_}->{got} } @$required;
      if ( @set == 0 ) {
         my $err = join(', ', map { "--$self->{opts}->{$_}->{long}" }
                      @{$required}[ 0 .. scalar(@$required) - 2] )
                 .' or --'.$self->{opts}->{$required->[-1]}->{long};
         $self->save_error("Specify at least one of $err");
      }
   }

   $self->_check_opts( keys %{$self->{opts}} );
   $self->{got_opts} = 1;
   return;
}

sub _check_opts {
   my ( $self, @long ) = @_;
   my $long_last = scalar @long;
   while ( @long ) {
      foreach my $i ( 0..$#long ) {
         my $long = $long[$i];
         next unless $long;
         my $opt  = $self->{opts}->{$long};
         if ( $opt->{got} ) {
            if ( exists $self->{disables}->{$long} ) {
               my @disable_opts = @{$self->{disables}->{$long}};
               map { $self->{opts}->{$_}->{value} = undef; } @disable_opts;
               MKDEBUG && _d('Unset options', @disable_opts,
                  'because', $long,'disables them');
            }

            if ( exists $self->{allowed_groups}->{$long} ) {

               my @restricted_groups = grep {
                  !exists $self->{allowed_groups}->{$long}->{$_}
               } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$self->{groups}};

               my @restricted_       } keys %{$se