<?php
/**
 * Pluralize English words.
 * 
 * @file plural.php
 * 
 * Based on the algorithm by Damian Conway. See
 * http://www.csse.monash.edu.au/~damian/papers/HTML/Plurals.html
 * for more information on the algorithm used.
 */

include_once 'FileReader.php';


class 
Pluralizer {
    var 
$categories;
    var 
$classical false;
    
    function 
Pluralizer($classical false) {
        
$this->categories $this->getCategories();
        
$this->classical $classical;
    }
    
    function 
classical($classical true) {
        
$this->classical $classical;
    }
    
    function 
is_classical() {
        return 
$this->classical;
    }
    
    function 
suffix($word$suffix) {
        
$suffix_len strlen($suffix);
        if(
preg_match('/'.$suffix.'$/',$word))
            return 
true;
        return 
false;
    }
    
    function 
stem($word$suffix) {
        if(!
$this->suffix($word$suffix))
            return 
false;
        return 
substr($word0, -strlen($suffix));
    }
    
    function 
inflection($word$singular_suffix$plural_suffix) {
        
$head $this->stem($word$singular_suffix);
        if(
$head == false)
            return 
false;
        return 
$head.$plural_suffix;
    }
    
    function 
category($word$singular_suffix null$plural_suffix null) {
        if(
$singular_suffix === null && $plural_suffix === null) {
            
$key 'default';
        } else {
            
$key $singular_suffix.','.$plural_suffix;
        }
        
        if(isset(
$this->categories[$key]['r'][$word])) {
            if(
$this->classical && $this->categories[$key]['r'][$word]['c'] !== null)
                    return 
$this->categories[$key]['r'][$word]['c'];
            
            if(
$this->categories[$key]['r'][$word]['p'] !== null)
                    return 
$this->categories[$key]['r'][$word]['p'];
        }
        return 
false;
    }
    
    function 
plural($word$dict null) {
        
//special cases
        
if($dict != null) {
            if(isset(
$dict[$word]))
                return 
$dict[$word];
        }
        
        if(
$this->suffix($word'fish') || $this->suffix($word'ois') || $this->suffix($word'sheep') ||
            
$this->suffix($word'deer') || $this->suffix($word'pox') || $this->category($word'''') ||
            
$this->suffix($word'[A-Z].*ese')) {
                return 
$word;
        }
        
        if(
$this->category($word)) {
            return 
$this->category($word);
        }
        
        if(
$this->suffix($word'man'))
            return 
$this->inflection($word'man''men');
        if(
$this->suffix($word'[lm]ouse'))
            return 
$this->inflection($word'ouse''ice');
        if(
$this->suffix($word,'tooth'))
            return 
$this->inflection($word'tooth','teeth');
        if(
$this->suffix($word,'goose'))
            return 
$this->inflection($word'goose','geese');
        if(
$this->suffix($word,'foot'))
            return 
$this->inflection($word'foot','feet');
        if(
$this->suffix($word,'zoon'))
            return 
$this->inflection($word'zoon','zoa');
        if(
$this->suffix($word,'[csx]is'))
            return 
$this->inflection($word'is','es');
            
        if(
$this->category($word'us''uses'))
            return 
$this->category($word'us''uses');
        if(
$this->category($word'ex''ices'))
            return 
$this->inflection($word'ex''ices');
        if(
$this->category($word'um''a'))
            return 
$this->inflection($word'um''a');
        if(
$this->category($word'on''a'))
            return 
$this->inflection($word'on''a');
        if(
$this->category($word'a''ae'))
            return 
$this->inflection($word'a''ae');
            
        if(
$this->classical) {
            if(
$this->suffix($word'trix'))
                return 
$this->inflection('trix','trices');
            if(
$this->suffix($word'eau'))
                return 
$this->inflection('eau','eaux');
            if(
$this->suffix($word'ieu'))
                return 
$this->inflection('ieu','ieux');
            if(
$this->suffix($word'[iay]nx'))
                return 
$this->inflection('nx','nges');
            if(
$this->category($word,'en','ina'))
                return 
$this->category($word'en''ina');
            if(
$this->category($word'a''ata'))
                return 
$this->category($word'a''ata');
            if(
$this->category($word'is''ides'))
                return 
$this->category($word'is''ides');
            if(
$this->category($word'us''i'))
                return 
$this->category($word'us''i');
            if(
$this->category($word'us''us'))
                return 
$this->category($word'us''us');
            if(
$this->category($word'''i'))
                return 
$this->category($word'''i');
            if(
$this->category($word'''im'))
                return 
$this->category($word'','im');
        }
            
        if(
$this->suffix($word,'[cs]h'))
            return 
$this->inflection($word'h''hes');
        if(
$this->suffix($word,'ss'))
            return 
$this->inflection($word'ss''sses');
        if(
$this->suffix($word,'us'))
            return 
$this->inflection($word'us''uses');
            
        
//barves??
        
if($this->suffix($word,'[aeo]lf') || $this->suffix($word,'[^d]eaf'
            || 
$this->suffix($word'[^b]arf'))
            return 
$this->inflection($word'f''ves');
        
        if(
$this->suffix($word,'[nlw]ife'))
            return 
$this->inflection($word'fe''ves');
            
        if(
$this->suffix($word'[aeiou]y'))
            return 
$this->inflection($word'y','ys');
        if(
$this->suffix($word'[A-Z].*y'))
            return 
$this->inflection($word'y''ys');
        if(
$this->suffix($word'y'))
            return 
$this->inflection($word'y''ies');
        
        if(
$this->suffix($word'[aeiou]o'))
            return 
$this->inflection($word'o''os');
        if(
$this->suffix($word'o'))
            return 
$this->inflection($word'o''oes');
            
        if(
$this->suffix($word'[aeiou]x'))
            return 
$this->inflection($word'x''xes');
        
        
//add an s
        
return $word.'s';
    }
    
    function 
getCategories($filename 'plural.txt') {
        
$f = new FileReader($filename);
        
$categories = array( 'default' => array( 'r' => array()));
        
$current_cat null;
        while((
$l $f->getLine()) !== false) {
            
$line trim($l);
            
            
//comment
            
if($line[0] == ';')
                continue;
                
            
//processing instruction
            
if($line[0] == ':') {
                
preg_match('/(\:)([a-zA-Z_]+)((\(([\w \t]+(\,[\w \t]+)*)?\))?)/'$line$matches);
                
                
$instruction $matches[2];
                if(isset(
$matches[5])) {
                    
$params explode(',',$matches[5]);
                    foreach(
$params as $i => $param) {
                        
$params[$i] = trim($params[$i]);
                    }
                }
                
                if(
$instruction == 'cat') {
                    
$cat_name $params[0].','.$params[1];
                    
$categories[$cat_name] = array('s' => $params[0], 'p' => $params[1], 'r' => array());
                    
$current_cat = &$categories[$cat_name];
                } else if(
$instruction == 'end_cat') {
                    
$current_cat = &$categories['default'];
                }
                continue;
            }
            
            if(
strlen($line) > 1) {
                
$parts preg_split("/[\s]+/"$line);
                if(
count($parts) == 1) {
                    
$rule = array('s' => $parts[0], 'p' => $parts[0], 'c' => null);
                } else if(
count($parts) == 2) {
                    
$rule = array('s' => $parts[0], 'p' => $parts[1], 'c' => null);
                } else if(
count($parts) == 3) {
                    
$rule = array('s' => $parts[0], 'p' => null'c' => $parts[2]);
                } else if(
count($parts) == 4) {
                    
$rule = array('s' => $parts[0], 'p' => $parts[1], 'c' => $parts[3]);
                }
                
                if(
$current_cat != null)
                    
$current_cat['r'][$rule['s']] = $rule;
                else
                    
$categories['default']['r'][$rule['s']] = $rule;
            }
        }
        
$f->close();
    
        return 
$categories;
    }
}
?>