#!/usr/bin/perl
# anagram.pl - perl distinct and complete anagram generator
# by dave maez <sellout -at- dharmadevil.com> 
# 2007/12/06
#
# This code is rather unimportant, and done only as a programming exercise
# since anagram generators seem to only come in two flavors:
# 1. Dictionary Search
# 2. Permutative Search
#
# The first is the most useful to humans in that it attempts to find only
# anagrams that are words, however it is incomplete.  The second is complete
# in finding all permutations of a string, but if that string has duplicate
# characters, then it will generate duplicate anagrams as well.  This is due
# to the fact that a sequence of characters has n! permutations, but only
# n!/(e[1]! * e[2]! * ... e[x]!) anagrams (where n = string length and 
# e = count of each unique character in the string).
#
# The following code generates ALL anagrams of a given string, with no
# duplicates (and not through sorting and filtering).
#
# This code uses logic derived from J. Loughry, J.I. van Hemert and
# L. Schoofs' "Banker's Sequence" in "Efficiently Enumerating the Subsets of
# a Set" (http://applied-math.org/subset.pdf)
#
# (Note: This program is slow and needs to be optimized, since it gets 
#  exponentially slower with each extra character.  On my 3.2GHz Pentium 4, it
#  takes under 4 tenths of a second to generate all 5,040 anagrams of 
#  "Gotta Go", however it takes over 5 minutes to find all 3,326,400 anagrams
#  of "Banana Mango".)

use strict;

sub pInit {
  my ($plist, $elem) = @_;

  for (my $x = 0; $x < $elem; $x++) { $plist->[$x] = $x; }
}

sub pIncr {
  my ($plist, $size) = @_;

  for (my $x = @$plist - 1; $x >= 0; $x--) {
    if (++$plist->[$x] < $size) {
      for (my $y = $x + 1; $y < @$plist; $y++) {
        $plist->[$y] = $plist->[$x] + ($y - $x);
      }
      last;
    } else {
      if ($x == 0) { return(0); }
      $size--;
    }
  }
  return(1);
}

sub anagram {
  my $str = shift;
  my $size = length($str);
  my %chars;
  my @positions;
  my $done = 0;

  for (my $x = 0; $x < $size; $x++) {
    $chars{substr($str,$x,1)}++;
  }
  my @letters = sort keys(%chars);
  my @count = @chars{@letters};
  my $i = 0;
  foreach my $k (@count) {
    $positions[$i] = [ 0 ];
    pInit($positions[$i],$k);
    $i++;
  }
  while ( ! $done ) {
    $size = length($str);
    my (@newstr,@trans) = ();
    for (my $x = 0; $x < $size; $x++) { $trans[$x] = $x; }
    for (my $x = 0; $x < @positions; $x++) {
      my $index = 0;
      my @kill = ();
      for (my $y = 0; $y < @trans; $y++) {
        if (($index < $count[$x]) && ($positions[$x][$index] == $y)) {
          $newstr[$trans[$y]] = $letters[$x];
          push @kill, $y;
          $index++;
        }
      }
      foreach my $k (sort {$b <=> $a} @kill) { splice(@trans,$k,1); }
    }
    foreach my $x (@newstr) { print $x; }
    print "\n";
    for (my $x = 0; $x < @positions; $x++) {
      if ( pIncr($positions[$x],$size)) {
        last;
      } else {
        if ($x == $#positions) {
          $done = 1;
          last;
        }
        pInit($positions[$x],$count[$x]);
        $size -= $count[$x];
      }
    }
  }
}

if (@ARGV < 1) {
  print("Usage: anagram.pl <text>\n");
} else {
  anagram($ARGV[0]);
}
