<?php

define('WORD_LIST_FILENAME', '/usr/share/dict/words');


class AnagramLookup
{
    private $lookup;

    //  Loads a file with one word per line
    private function load_word_list($filename)
    {
        $lines = file($filename);                     // One word per line
        $lines = array_map('trim', $lines);           // Strip any excess whitespace
        $lines = array_filter($lines, 'ctype_alpha'); // Words been to match [a-zA-Z]
        $lines = array_map('strtolower', $lines);     // Set all the words to lowercase
        $lines = array_unique($lines);                // Remove any duplicate words
        $lines = array_diff($lines, array(''));       // Remove any empty lines

        return $lines;
    }

    //  Sort the individual letters in a string
    //  i.e.   tale  =>  aelt
    private function sort_letters($word)
    {
        $letters = str_split($word);

        sort($letters);

        $sorted_word = implode('', $letters);

        return $sorted_word;
    }

    //  Generate our lookup table.  This takes ~1.5second for 70,000 words
    //  $lookup ends up looking like:
    //
    //  $lookup[4] = array
    //  (
    //      'aelt' => array('late', 'tale', 'leta', 'teal'),
    //      'belu' => array('blue', 'lube'),
    //      [etc...]
    //  )
    //  $lookup[5] = array
    //  (
    //      'allms' => array('small', 'malls'),
    //      [etc...]
    //  )
    //
    //  4 and 5 are the word lengths, while 'aelt', 'belu' and 'allms'  contains
    //  an array of all the words that can be spelt using these letters
    public function __construct($filename)
    {
        $word_list = $this->load_word_list($filename);

        $lookup = array();

        foreach($word_list as $word)
        {
            $length = strlen($word);

            if(!isset($lookup[$length]))
            {
                $lookup[$length] = array();
            }

            $sorted_word = $this->sort_letters($word);

            if(!isset($lookup[$length][$sorted_word]))
            {
                $lookup[$length][$sorted_word] = array();
            }

            $lookup[$length][$sorted_word][] = $word;
        }

        $this->lookup = $lookup;
    }

    //  Return all the anagrams of the passed word
    public function lookup_word($word)
    {
        $word_length = strlen($word);
        $sorted_word = $this->sort_letters($word);

        if(isset($this->lookup[$word_length][$sorted_word]))
        {
            return $this->lookup[$word_length][$sorted_word];
        }

        return array();
    }

    //  Return an array of all the sets of anagrams with a specific length
    //
    //  Example result for a word length of 14:
    //
    //  array
    //  (
    //      [0] => array('certifications','rectifications'),
    //      [1] => array('impressiveness','permissiveness'),
    //      [2] => array('tablespoonfuls','tablespoonsful'),
    //  )
    public function all_of_length($word_length)
    {
        if(!isset($this->lookup[$word_length]))
        {
            return array();
        }

        $results = array();

        foreach($this->lookup[$word_length] as $words)
        {
            if(count($words) > 1)
            {
                $results[] = $words;
            }
        }

        return $results;
    }
}


$anagram = new AnagramLookup(WORD_LIST_FILENAME);

printf("Anagrams of 'blue': %s\n", implode(', ', $anagram->lookup_word('blue')));
printf("Anagrams of 'late': %s\n", implode(', ', $anagram->lookup_word('late')));
printf("Anagrams of 'slow': %s\n", implode(', ', $anagram->lookup_word('slow')));
printf("Anagrams of 'seven':  %s\n", implode(', ', $anagram->lookup_word('seven')));
printf("Anagrams of 'anagram': %s\n", implode(', ', $anagram->lookup_word('anagram')));

printf("All anagrams of word length 14\n");

foreach($anagram->all_of_length(7) as $words)
{
    printf(" * %s\n", implode(', ', $words));
}


?>