_binary From 4405bc07cc142d9e291188250a2713d1d70a106f Mon Sep 17 00:00:00 2001
From: Niravkumar Patel <niravkumar.patel1989@gmail.com>
Date: Fri, 3 Apr 2015 02:45:02 -0700
Subject: [PATCH] Fixes Issue 0000165, Adding Portuguese Stemmer, a=Niravkumar

---
 locale/pt/resources/tokenizer.php                  |  471 ++++++++-
 tests/pt_tokenizer_test.php                        |  101 ++
 .../portuguese_stemmer/input_vocabulary.txt        | 1000 ++++++++++++++++++++
 .../portuguese_stemmer/stemmed_result.txt          | 1000 ++++++++++++++++++++
 4 files changed, 2565 insertions(+), 7 deletions(-)
 create mode 100644 tests/pt_tokenizer_test.php
 create mode 100644 tests/test_files/portuguese_stemmer/input_vocabulary.txt
 create mode 100644 tests/test_files/portuguese_stemmer/stemmed_result.txt

diff --git a/locale/pt/resources/tokenizer.php b/locale/pt/resources/tokenizer.php
index 67067f0..bafeb67 100755
--- a/locale/pt/resources/tokenizer.php
+++ b/locale/pt/resources/tokenizer.php
@@ -20,20 +20,477 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
- * @author Chris Pollett chris@pollett.org
+ * @author Niravkumar Patel niravkumar.patel1989@gmail.com
  * @license http://www.gnu.org/licenses/ GPL3
  * @link http://www.seekquarry.com/
  * @copyright 2009 - 2015
  * @filesource
  */
-if(!defined('BASE_DIR')) {echo "BAD REQUEST"; exit();}
+mb_internal_encoding("UTF-8");
+mb_http_output( "UTF-8" );
+ob_start("mb_output_handler");
+if(!defined('BASE_DIR')) {
+	echo "BAD REQUEST"; exit();
+}
+/* If you would like to use wordnet for thesaurus reordering of query results
+   define the following variable in your configs/local_config.php file with
+   the path to the WordNet executable.
+ */
+if(!defined("WORDNET_EXEC")) {
+    define("WORDNET_EXEC", "");
+}
 /**
- * Portuguese specific tokenization code. Typically, tokenizer.php
- * either contains a stemmer for the language in question or
- * it specifies how many characters in a char gram
+ * This class has a collection of methods for Portuguese locale specific
+ * tokenization. In particular, it has a stemmer implementing the
+ * Snowball Stemming algorithm presented in 
+ * http://snowball.tartarus.org/algorithms/portuguese/stemmer.html
  *
- * @author Chris Pollett
+ * @author Niravkumar Patel
  * @package seek_quarry\locale\pt
  */
-$GLOBALS['CHARGRAMS']['pt'] = 5;
+class PtTokenizer
+{   
+    /**
+     * Phrases we would like yioop to rewrite before performing a query
+     * @var array
+     */
+    static $semantic_rewrites = array(
+        "ins" => 'uscis',
+        "mimetype" => 'mime',
+        "military" => 'armed forces',
+        'full metal alchemist' => 'fullmetal alchemist',
+        'bruce schnier' => 'bruce schneier',
+    );
+    /**
+     * storage used in computing the stem
+     * @var string
+     */
+    static $buffer;
+    /**
+     * Index of the current end of the word at the current state of computing
+     * its stem
+     * @var int
+     */
+    static $k;
+	/**
+     * R1 is the region in the word after the first non-vowel following 
+	 * a vowel, or is the null region at the end of the word if there
+	 * is no such non-vowel
+     * @var string
+     */
+	static $R1 = "";
+	/**
+     * R2 is the region in the R1 after the first non-vowel following a vowel, 
+	 * or is the null region at the end of the word if there is no non-vowel
+     * @var string
+     */
+	static $R2 = "";
+	/**
+     * If the second letter is a consonant, RV is the region after the next  
+	 * following vowel, or if the first two letters are vowels, RV is the 
+	 * region after the next consonant, and otherwise (consonant-vowel case) 
+	 * RV is the region after the third letter.
+     * @var string
+     */
+	static $RV = "";    
+    /**
+     * Computes the stem of an Portuguese word
+     * For example, química, químicas, químico, químicos 
+     * all have químic as a stem
+     *
+     * @param string $word the string to stem
+     * @return string the stem of $words
+     */
+    static function stem($word)
+    {
+		self::$buffer = $word;
+        self::$k = strlen($word) - 1;
+        if(self::$k <= 1) { return $word; }
+		
+		$word = str_ireplace("ã", "a~", $word);
+		$word = str_ireplace("õ", "o~", $word);
+	
+		self::$R1 = self::findR1($word);
+		self::$R2 = self::findR1(self::$R1);
+		self::findRV($word);
+		
+		$temp_word = self::step1($word);
+		
+		if($temp_word == $word){
+			$temp_word = self::step2($word);
+		}
+		
+		if($temp_word != $word){
+			self::findRV($temp_word);
+			$temp_word = self::step3($temp_word);
+		}else{
+			$temp_word = self::step4($temp_word);
+			self::findRV($temp_word);
+		}
+		
+		$temp_word = self::step5($temp_word);
+		$temp_word = str_ireplace("a~", "ã", $temp_word);
+		$temp_word = str_ireplace("o~", "õ", $temp_word);
+		self::$RV = "";
+		$word = $temp_word;
+		return $word;
+	}	
+	/**
+     * Standard Suffix Removal Step
+     * It search for longest suffix from given set and remove
+     * if found
+     *
+     * @param string $word the string to suffix removal
+     * @return processed string
+     */
+	private static function step1($word)
+	{
+		$SuffixSet = array('eza','ezas','ico','ica','icos','icas','ismo',
+						'ismos','ável','ível','ista','istas','oso','osa',
+						'osos','osas','amento','amentos','imento','imentos',
+						'adora','ador','aça~o','adoras','adores','aço~es',
+						'ante','antes','ância');
+
+		$regex = "/[a-zA-Z~]*(eza|ezas|ico|ica|icos|icas|ismo|ismos|ável|ível|ista
+						|istas|oso|osa|osos|osas|amento|amentos|imento|imentos
+						|adora|ador|aça~o|adoras|adores|aço~es|ante|antes|ância)";
+		
+		foreach ($SuffixSet as $value){
+			$regex = "/[a-zA-Z~]*($value)$/";
+			if(preg_match($regex, self::$R2)){
+				$word = substr($word,0,-strlen($value));
+				break;
+			}
+		}
+		
+		$SpecialSuffix = array('logía' , 'logías');
+		foreach ($SpecialSuffix as $value){
+			$regex = "/[a-zA-Z~]*($value)$/";
+			if(preg_match($regex, self::$R2)){
+				$word = substr($word,0,-strlen($value))."log";
+				break;
+			}
+		}
+		
+		$SpecialSuffix = array('ución','uciones');
+		foreach ($SpecialSuffix as $value){
+			$regex = "/[a-zA-Z~]*($value)$/";
+			if(preg_match($regex, self::$R2)){
+				$word = substr($word,0,-strlen($value))."u";
+				break;
+			}
+		}
+		
+		$SpecialSuffix = array('ência','ências');
+		foreach ($SpecialSuffix as $value){
+			$regex = "/[a-zA-Z~]*($value)$/";
+			if(preg_match($regex, self::$R2)){
+				$word = substr($word,0,-strlen($value))."ente";
+				break;
+			}
+		}
+		
+		$SpecialSuffix = array('ativamente','ivamente','osamente','icamente',
+							'adamente','antemente','avelmente','ívelmente');
+		$isRemoved = false;
+		foreach ($SpecialSuffix as $value){
+			$regex = "/[a-zA-Z~]*($value)$/";
+			if(preg_match($regex, self::$R2)){
+				$word = substr($word,0,-strlen($value));
+				$isRemoved = true;
+				break;
+			}
+		}
+		
+		if(!$isRemoved){
+			$isRemoved = false;
+			$value = "amente";
+			$regex = "/[a-zA-Z~]*($value)$/";
+			if(preg_match($regex, self::$R1)){
+				$word = substr($word,0,-strlen($value));
+				$isRemoved = true;
+			}
+			
+			if(!$isRemoved){
+				$value = "mente";
+				$regex = "/[a-zA-Z~]*($value)$/";
+				if(preg_match($regex, self::$R2)){
+					$word = substr($word,0,-strlen($value));
+					$isRemoved = true;
+				}
+			}
+		}
+		
+		$SpecialSuffix = array('abilidades','icidades','ividades','abilidade',
+							'icidade','ividade','idades','idade');
+		foreach ($SpecialSuffix as $value){
+			$regex = "/[a-zA-Z~]*($value)$/";
+			if(preg_match($regex, self::$R2)){
+				$word = substr($word,0,-strlen($value));
+				break;
+			}
+		}
+		
+		$SpecialSuffix = array('ativa','ativo','ativas','ativos','iva','ivo',
+							'ivas','ivos');
+		foreach ($SpecialSuffix as $value){
+			$regex = "/[a-zA-Z~]*($value)$/";
+			if(preg_match($regex, self::$R2)){
+				$word = substr($word,0,-strlen($value));
+				break;
+			}
+		}
+		
+		$SpecialSuffix = array('iras','ira');
+		foreach ($SpecialSuffix as $value){
+			$regex = "/[a-zA-Z~]*($value)$/";
+			if(preg_match($regex, self::$RV) 
+				&& preg_match("/[a-zA-Z~]*(eiras|eira)$/",$word)){
+				$word = substr($word,0,-strlen($value))."ir";
+				break;
+			}
+		}
+		return $word;
+	}
+	/**
+     * Verb Suffix Removal Step
+     * If step 1 does not change anything than this function 
+     * will be called
+     *
+	 * It will also check for longest suffix from the suffix set
+     * Remove if found
+	 *
+	 * @param string $word the string to suffix removal
+     * @return processed string
+     */
+	private static function step2($word)
+	{
+		$SuffixSet = array("aríamos","eríamos","iríamos","ássemos","êssemos",
+			"íssemos","aríeis","eríeis","iríeis","ásseis","ésseis","ísseis",
+			"áramos","éramos","íramos","ávamos","aremos","eremos","iremos",
+			"ariam","eriam","iriam","assem","essem","issem","ara~o","era~o",
+			"ira~o","arias","erias","irias","ardes","erdes","irdes","asses",
+			"esses","isses","astes","estes","istes","áreis","areis","éreis",
+			"ereis","íreis","ireis","áveis","íamos","armos","ermos","irmos",
+			"aria","eria","iria","asse","esse","isse","aste","este","iste",
+			"arei","erei","irei","aram","eram","iram","avam","arem","erem",
+			"irem","ando","endo","indo","adas","idas","arás","aras","erás",
+			"eras","irás","avas","ares","eres","ires","íeis","ados","idos",
+			"ámos","amos","emos","imos","iras","ada","ida","ará","ara","erá",
+			"era","irá","ava","iam","ado","ido","ias","ais","eis","ira","ia",
+			"ei","am","em","ar","er","ir","as","es","is","eu","iu","ou");
+		
+		foreach ($SuffixSet as $value){
+			$regex = "/[a-zA-Z~]*($value)$/";
+			if(preg_match($regex, self::$RV)){
+				$word = substr($word,0,-strlen($value));
+				break;
+			}
+		}
+		return $word;		
+	}
+	/**
+     * Delete suffix i if in RV and preceded by c
+     * 
+	 * @param string $word the string to suffix removal
+     * @return processed string
+     */	
+	private static function step3($word)
+	{
+		$regex = "/[a-zA-Z~]*(ci)$/";
+		if(preg_match($regex, $word)){
+			$regex = "/(i)[a-zA-Z~]*$/";
+			
+			if (preg_match($regex, self::$RV)) {
+				$word = substr($word,0,-1);
+			}
+		}
+		return $word;
+	}
+	/**
+     * Residual suffix
+     * If the word ends with one of [os a i o á í ó]
+	 * in RV
+	 *
+	 * @param string $word the string to suffix removal
+     * @return processed string
+     */
+	private static function step4($word)
+	{
+		$regex = "/[a-zA-Z~]*(os)$/";
+		if (preg_match($regex, self::$RV)){
+			$word = substr($word,0,-2);
+		}else{		
+			$regex = "/[a-zA-Z~]*(a|i|o)$/";
+			if (preg_match($regex, self::$RV)){
+				$word = substr($word,0,-1);
+			}else{
+				$regex = "/[a-zA-Z~]*(á|í|ó)$/";
+				if (preg_match($regex, self::$RV)){
+					$word = substr($word,0,-2);
+				}
+			}
+		}
+		return $word;
+	}
+	/**
+     * Residual suffix
+     * If the word ends with one of [e é ê]
+	 * in RV
+	 *
+	 * @param string $word the string to suffix removal
+     * @return processed string
+     */
+	private static function step5($word)
+	{		
+		if(preg_match("/[a-zA-Z~]*(ç)$/", $word)){
+			$word = substr($word,0,-2)."c";
+			return $word;
+		}
+		
+		$SpecialSuffix = array("gue","cie");
+		foreach ($SpecialSuffix as $value){
+			$regex = "/[a-zA-Z~]*($value)$/";
+			if(preg_match($regex, $word)){
+				
+				$value = substr($value,1,strlen($value));
+				$regex = "/[a-zA-Z~]*($value)[a-zA-Z~]*$/";
+				if(preg_match($regex, self::$RV)){
+					$word = substr($word,0,-2);
+					return $word;
+				}
+			}
+		}
+		
+		$SpecialSuffix = array("gué","guê","cié","ciê");
+		foreach ($SpecialSuffix as $value){
+			$regex = "/[a-zA-Z~]*($value)$/";
+			if(preg_match($regex, $word)){
+				$value = substr($value,1,strlen($value));
+				$regex = "/[a-zA-Z~]*($value)$/";
+				if(preg_match($regex, self::$RV)){
+					$word = substr($word,0,-3);
+					return $word;
+				}
+			}
+		}
+		
+		if(preg_match("/[a-zA-Z~]*(e)$/", $word) 
+			&& preg_match("/[a-zA-Z~]*(e)[a-zA-Z~]*$/", self::$RV)){
+			$word = substr($word,0,-1);
+			return $word;
+		}
+		
+		if(preg_match("/[a-zA-Z~]*(é|ê)$/", $word) 
+			&& preg_match("/[a-zA-Z~]*(é|ê)[a-zA-Z~]*$/", self::$RV)){
+			$word = substr($word,0,-2);
+			return $word;
+		}
+		
+		return $word;
+	}
+	/**
+     * This method will find R1 region in the $word 
+	 * R1 is the region after the first non-vowel following a vowel, or is 
+	 * the null region at the end of the word if there is no such non-vowel
+	 *
+     * @param string $word
+     * @return string $R1 region
+     */
+	private static function findR1($word)
+	{
+		$word_length = mb_strlen($word);
+		$vowel = self::mbStringToArray("aeiouáéíóúâêô");
+		$first_vowel = false;
+		$second_vowel = false;
+		$first_consonent = false;
+		$second_consonent = false;
+		$R1 = "";
+		
+		$word_arry = self::mbStringToArray($word);
+		
+		$i=0;
+		for($i=0; $i<$word_length; $i++) {
+			
+			if(in_array($word_arry[$i],$vowel))
+			{
+				if($first_vowel)
+				{
+					if($first_consonent){
+						break;
+					}
+				}
+				$first_vowel = TRUE;
+				
+			}else{
+				if($first_vowel)
+				{
+					if($first_consonent){
+						break;
+					}
+					$first_consonent = TRUE;
+				}
+			}
+		}
+		return mb_substr($word, $i, mb_strlen($word));
+	}
+	/**
+     * This method will find RV region in the $word 
+	 * If the second letter is a consonant, 
+	 * RV is the region after the next following vowel, or 
+	 * if the first two letters are vowels, 
+	 * RV is the region after the next consonant, 
+	 * and otherwise (consonant-vowel case) 
+	 * RV is the region after the third letter.
+	 *
+     * @param string $word
+     * @return string $RV region
+     */
+	private static function findRV($word)
+	{
+		$vowel = self::mbStringToArray("aeiouáéíóúâêô");
+		$word_arry = self::mbStringToArray($word);
+		
+		if(!in_array($word_arry[1],$vowel)){
+			$word_length = mb_strlen($word);
+			$i=2;
+			for($i=2;$i<$word_length;$i++){
+				if(in_array($word_arry[$i],$vowel)){
+					$i++;
+					break;
+				}
+			}
+			self::$RV = mb_substr($word, $i, mb_strlen($word));
+		}else if(in_array($word_arry[0],$vowel) 
+				&& in_array($word_arry[1],$vowel)){
+			$word_length = mb_strlen($word);
+			for($i=2;$i<$word_length;$i++){
+				if(!in_array($word_arry[$i],$vowel)){
+					self::$RV = mb_substr($word, $i+1, mb_strlen($word));
+					break;
+				}
+			}
+		}else if(!in_array($word_arry[0],$vowel) 
+				&& in_array($word_arry[1],$vowel)){
+			self::$RV = mb_substr($word, 3, mb_strlen($word));			
+		}
+	}
+	/**
+	 * This method will break-up a multibyte string into its individual 
+	 * characters and generate an array of characters
+	 *
+	 * @return array of multibyte characters
+	 */
+	private static function mbStringToArray($string)
+	{ 
+		$strlen = mb_strlen($string);
+		$array = array();
+		while ($strlen) { 
+			$array[] = mb_substr($string,0,1,"UTF-8"); 
+			$string = mb_substr($string,1,$strlen,"UTF-8"); 
+			$strlen = mb_strlen($string); 
+		} 
+		return $array; 
+	}
+}
 ?>
diff --git a/tests/pt_tokenizer_test.php b/tests/pt_tokenizer_test.php
new file mode 100644
index 0000000..8373a06
--- /dev/null
+++ b/tests/pt_tokenizer_test.php
@@ -0,0 +1,101 @@
+<?php
+/**
+ * SeekQuarry/Yioop --
+ * Open Source Pure PHP Search Engine, Crawler, and Indexer
+ *
+ * Copyright (C) 2009 - 2015  Chris Pollett chris@pollett.org
+ *
+ * LICENSE:
+ *
+ * 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, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * END LICENSE
+ *
+ * @author Niravkumar Patel niravkumar.patel1989@gmail.com
+ * @license http://www.gnu.org/licenses/ GPL3
+ * @link http://www.seekquarry.com/
+ * @copyright 2009 - 2015
+ * @filesource
+ */
+if(!defined('BASE_DIR')) {echo "BAD REQUEST"; exit();}
+/**
+ * Load the Portuguese Tokenizer via phrase_parser (5.4 hack)
+ */
+require_once BASE_DIR."/lib/phrase_parser.php";
+/**
+ * Load the run function
+ */
+require_once BASE_DIR.'lib/unit_test.php';
+/**
+ * Code used to test the Portuguese stemming algorithm. The inputs for the
+ * algorithm are words in
+ * http://snowball.tartarus.org/algorithms/porter/voc.txt and the resulting
+ * stems are compared with the stem words in
+ * http://snowball.tartarus.org/algorithms/porter/output.txt
+ * Code uses orginal Porter stemmer, not Porter 2
+ *
+ * @author Niravkumar Patel
+ * @package seek_quarry\test
+ */
+class PtTokenizerTest extends UnitTest
+{
+    /**
+     * Each test we set up a new Portuguese Tokenizer object
+     */
+    function setUp()
+    {
+        $this->test_objects['FILE1'] = PhraseParser::getTokenizer("pt");
+    }
+    /**
+     * Nothing done for unit test tear done
+     */
+    function tearDown()
+    {
+    }
+    /**
+     * Tests whether the stem funtion for the Portuguese stemming algorithm
+     * stems words according to the rules of stemming. The function tests stem
+     * by calling stem with the words in $test_words and compares the results
+     * with the stem words in $stem_words
+     *
+     * $test_words is an array containing a set of words in Portuguese provided
+     * in the snowball web page
+     * $stem_words is an array containing the stems for words in $test_words
+     */
+    function stemmerTestCase()
+    {
+        $stem_dir = BASE_DIR.'/tests/test_files/portuguese_stemmer';
+        //Test word set from snowball
+        $test_words = file("$stem_dir/input_vocabulary.txt");
+        //Stem word set from snowball for comparing results
+        $stem_words = file("$stem_dir/stemmed_result.txt");
+        /**
+         * check if function stem correctly stems the words in $test_words by
+         * comparing results with stem words in $stem_words
+         */
+
+        for($i = 0; $i < count($test_words); $i++) {
+            $word = trim($test_words[$i]);
+            $stem = trim($stem_words[$i]);
+            $word_stem = $this->test_objects['FILE1']->stem($word);
+            if($word_stem != $stem ) {
+                echo $word." output:".$word_stem." stem:".$stem;
+                exit();
+            }
+            $this->assertEqual($word_stem, $stem,
+                "function stem correctly stems $word to $stem");
+        }
+    }
+}
+?>
\ No newline at end of file
diff --git a/tests/test_files/portuguese_stemmer/input_vocabulary.txt b/tests/test_files/portuguese_stemmer/input_vocabulary.txt
new file mode 100644
index 0000000..f21acbf
--- /dev/null
+++ b/tests/test_files/portuguese_stemmer/input_vocabulary.txt
@@ -0,0 +1,1000 @@
+abates
+abono
+abrirão
+abriu
+absorvente
+abstinência
+acabar
+acabo
+accor
+aceitamos
+aceitava
+acessório
+achaque
+acharam
+acidez
+acomodava
+acomodo
+acompanhado
+acontece
+acordaram
+acreditará
+acumulado
+administrar
+adminstração
+advogado
+aérea
+aéreo
+afastado
+after
+agenciava
+agrega
+ajudará
+ajudava
+alá
+alcançasse
+alccólatra
+alheio
+altaneiro
+alturas
+aluguei
+amanhecidos
+amansa
+amassava
+ameaçada
+ancestrais
+andanças
+animadores
+antecedem
+antiga
+antiquários
+antônio
+apanha
+apareciam
+apelo
+apoiado
+apoio
+apologia
+apontada
+aposentadoria
+apresentam
+aproximei
+argumentação
+argumentos
+ariel
+armar
+armazém
+arquitetônicos
+arquivo
+arremates
+arriscando
+arrumada
+arrumo
+ascensão
+assemelham
+assepsia
+assustar
+ata
+atacar
+atadas
+atendendo
+atestada
+atraem
+atrapalhar
+atrapalharem
+atribuem
+atribuídos
+atrocidades
+atrofiar
+avançar
+bá
+baderneiros
+balada
+bambi
+bandido
+bandos
+bangues
+baniu
+banzeco
+barateará
+barbeiro
+batizada
+bebericar
+becos
+beija
+beijado
+bem
+beneficiado
+benzedeira
+bergen
+béria
+berra
+betânia
+betto
+biblioteca
+bibliotecas
+boicotar
+bolhas
+bonés
+borrada
+boucinhas
+brant
+breno
+brócolis
+bumbum
+buraco
+burla
+burocratas
+bussunda
+buttons
+cabeleira
+cabello
+cabelos
+cabos
+cadê
+cafeicultores
+calabria
+calamitosa
+calil
+camelô
+camila
+campus
+caprichado
+capriglione
+carcará
+caríssimas
+carpir
+cartazes
+casaquistão
+cassio
+catástrofes
+categoria
+celas
+celesc
+células
+cervejinha
+cevar
+chaco
+chamaram
+chapa
+charlatão
+chassi
+chefes
+chefiado
+choque
+christopher
+chupar
+ciclismo
+cics
+cie
+cima
+cineasta
+cirurgiã
+cirurgias
+clamorosos
+clara
+coalition
+cobro
+coca
+coincidiu
+colaboram
+colaboraram
+colateral
+colhendo
+colocamos
+colocar
+colonizadores
+colono
+columbia
+combinaram
+começava
+comedido
+comodismo
+compressas
+comprovar
+computava
+condenação
+condenado
+condomínio
+confeccionado
+confeccionistas
+confiáveis
+conformacional
+congelando
+conjunção
+conseguem
+conseguido
+consente
+conservador
+consideram
+constância
+constatada
+construído
+consulados
+consultarem
+contrariando
+contras
+contratado
+convenções
+conversar
+conversíveis
+converso
+conversora
+convivências
+convocou
+coqueiros
+corajosa
+córdoba
+coreógrafos
+corinthiana
+corn
+corolário
+corriqueira
+cosmos
+costumes
+craque
+criatividade
+crick
+criminosos
+crise
+cristãos
+criticar
+crixás
+crônico
+cuidadoso
+cuja
+culpar
+cumprir
+custar
+custavam
+darcio
+datilografei
+debatem
+decentemente
+decepcionante
+decerto
+decidiam
+decifraremos
+definitivos
+degradar
+degraus
+demais
+demos
+demoura
+deng
+dengo
+dengoso
+depoimento
+depositado
+desarrumação
+descompromisso
+desconfiados
+desconfiou
+desconhecimento
+descumpriu
+desequilibra
+designado
+designar
+desincompatibilizar
+desmembram
+desobediente
+desodorados
+desonestidade
+despercebido
+despercebidos
+despertaram
+desprezar
+desprezível
+desqualificada
+desquitada
+destemida
+desuso
+detectaria
+detém
+detonar
+dicção
+dietilpropiona
+diferencia
+dilatador
+dilbert
+diluídas
+diminuindo
+disciplinada
+discute
+discutiram
+disparidade
+dispensar
+disquete
+distantes
+distribuem
+divertem
+divulgado
+divulgam
+divulgou
+dobradiças
+dobrar
+doente
+dolce
+dolorida
+donas
+dressing
+duarte
+durão
+é
+eclético
+edmund
+efe
+eigg
+ela
+eleotório
+eletrizante
+elógica
+ely
+embaixada
+émerson
+emperrado
+encará
+encarcerou
+encenado
+enchimento
+enciclopédica
+enclausurada
+encomendado
+encontrei
+encontro
+encorpado
+encostar
+encravadas
+encruzilhadas
+enfoque
+enfrentou
+enfurecer
+engalfinham
+engatado
+engolir
+ensaiaram
+entenderia
+enterraria
+entoado
+entra
+entrando
+entregá
+entrepostos
+envergonhem
+erguê
+erosão
+errinho
+esbarram
+escolherem
+escolhidas
+escravo
+escritor
+esdras
+espadim
+espalhafatosas
+espartana
+especificamente
+esperam
+espetacularmente
+esposa
+esquece
+esquerda
+esqui
+esquire
+esquivar
+esteróide
+estilistas
+estimam
+estivéssemos
+estonteantes
+estoques
+estórias
+estragos
+estratégica
+estrela
+estrofe
+étnica
+evitaria
+examinados
+exclusivo
+exibição
+existiram
+existiriam
+expectativa
+experiência
+experimentam
+exploram
+exportadora
+extraídas
+extremo
+fábrica
+fábula
+faixas
+fame
+faróis
+farra
+fé
+fearnside
+feitorias
+femme
+fenecessem
+ferrari
+festejam
+ficará
+ficou
+figuradamente
+figurões
+fila
+financiando
+finos
+firmem
+físicas
+flag
+flávia
+folhetim
+fome
+fones
+forest
+forja
+formações
+formas
+formava
+frankfurt
+frentes
+freqüentam
+freqüentou
+fulminado
+funcionam
+fundi
+gabriela
+ganha
+garimpos
+gelada
+geller
+generalizada
+gênero
+geniculado
+genocídio
+genuínos
+geopolítico
+gerenciado
+gergelim
+gestor
+gigantescas
+glina
+goiabada
+gonzales
+gourmet
+governados
+governaram
+graduação
+granja
+gravadora
+gravados
+gravata
+gravatas
+gripado
+habeas
+habilidade
+harold
+haverá
+hepática
+hércules
+herdeira
+hesitou
+hipótese
+histerectomias
+histéricos
+historicamente
+holística
+homogênea
+homologações
+hormonais
+hussein
+iacomini
+identificassem
+igrejas
+imediato
+imitação
+imitava
+impediam
+impedido
+imperam
+impor
+impressionante
+impulsos
+inácio
+inchados
+inconformada
+incríveis
+independentemente
+indevido
+indicações
+indicando
+indivíduos
+induzido
+infelicidade
+inibir
+insensíveis
+inspiração
+instalados
+instigante
+instinto
+instituiu
+institutos
+insuperável
+intel
+intensas
+interativas
+interessados
+interpretam
+inversamente
+investiu
+irineu
+irmã
+ironia
+irritabilidade
+irritado
+isoladamente
+itaipu
+jacqueline
+jânia
+java
+javier
+jk
+joana
+joelhos
+jóias
+jornada
+junot
+justa
+juventude
+kalume
+karam
+karla
+kauai
+kelly
+kerly
+kiberon
+lable
+ladakh
+lamarca
+lamúria
+largamente
+lee
+leiloado
+lesbian
+levantamentos
+levantei
+levaria
+libé
+liberalismo
+liberam
+lição
+limita
+lipp
+lisonjeado
+lixá
+local
+localizou
+locaset
+logotipos
+loiras
+longamente
+lotados
+loucamente
+louças
+ltda
+luciene
+lucrando
+lurex
+lutar
+lux
+lyon
+maçarico
+machos
+maciel
+madi
+maio
+maioridade
+malucas
+manda
+manipulação
+manta
+mantras
+maravilhas
+marcello
+marchou
+marjorie
+martelou
+masculinos
+matado
+matrizes
+mauá
+mecânicas
+medéia
+megassucesso
+meio
+meios
+meladas
+melbourne
+mencionar
+mensais
+menstruada
+mental
+mentiram
+mentirem
+mercadores
+metal
+metido
+metrópole
+michael
+miguel
+milagres
+milionária
+misericórdia
+mítico
+moderini
+modismo
+montanaro
+montaram
+montava
+moradia
+morrido
+moveu
+mozine
+mudou
+muggiati
+mulata
+multá
+multiplicaram
+município
+muralha
+namorado
+nanini
+natário
+navegador
+naves
+necessidade
+neosaldina
+netscape
+neurocientista
+nifutimox
+nitrogênio
+nossas
+notaram
+notório
+novaes
+novais
+obscuro
+observaríamos
+obtê
+ocioso
+ocorrência
+ocorreu
+octavio
+ocupavam
+oficiais
+oitenta
+oito
+okky
+olbers
+old
+olhado
+ombros
+operá
+ordenou
+orgulharem
+orquestrava
+orwelliano
+osaka
+osborn
+padronização
+pager
+pairava
+paolo
+papelaria
+paralelas
+parar
+parcialmente
+parlamento
+partem
+participação
+passada
+passageiro
+pasto
+patinadores
+patotinha
+patroa
+patrocinadas
+paulo
+pedetistas
+pedido
+pediu
+peixoto
+pendendo
+pender
+penduricalhos
+percorrerá
+perdendo
+peritos
+permanecem
+permaneceram
+pertencer
+pesa
+pesado
+pessoa
+petrolina
+picotada
+pifado
+pilotos
+pílulas
+pinheiros
+piores
+pitta
+plásticos
+pólo
+porões
+porosos
+porteiro
+positivo
+postulante
+pouquíssima
+povo
+power
+prateleira
+precedida
+preciosidades
+precisavam
+precisávamos
+preferindo
+pregam
+pregão
+prematuras
+premiados
+prêmio
+preocupante
+preparativos
+presidencial
+prestigiosa
+pretendo
+pretexto
+prisma
+processador
+processadores
+processos
+procon
+procurada
+procuram
+procurar
+profissional
+profundidades
+proíbe
+proibidos
+prontidão
+proveito
+provoca
+provocaram
+ptb
+publicações
+pulam
+quebrados
+queimar
+queixou
+questionar
+quinhentos
+quintana
+radunz
+raízes
+raphael
+rappers
+raríssimas
+raríssimos
+rasgaram
+raspadinha
+raw
+rbs
+readaptou
+recaem
+recanto
+recepcionista
+recitação
+recorreu
+recusava
+redescobriu
+redonda
+refaço
+referentes
+refúgio
+regenerem
+regra
+reinante
+reivindicamos
+relacionados
+relacionando
+relacionei
+religião
+religiosos
+remoção
+remodelar
+rentável
+repartições
+reportagens
+repressor
+reproduzida
+republicanos
+repudiado
+requintes
+reservou
+respectivas
+resumindo
+resumiu
+retirar
+retomada
+retrata
+revelam
+revelar
+reviravolta
+revive
+riddle
+ríspida
+rodeada
+romântico
+ronaldo
+roqueiro
+rossellini
+roubalheira
+rudolf
+sabia
+sae
+sagrados
+sagrou
+salgada
+salvado
+saneada
+sanear
+sanidade
+saraiva
+sarará
+satélite
+saturno
+secadores
+seção
+secretárias
+século
+segundas
+semanal
+semeadeira
+senado
+senegal
+sensação
+sentado
+sentarem
+sentia
+separou
+sequestro
+serei
+serviu
+setorialmente
+severo
+sexuais
+shows
+simbolismos
+simplificou
+sinagogas
+sines
+sinistras
+sirenes
+sisudamente
+sobra
+sobrecarregada
+sociedades
+sociologava
+sofisticada
+sofreu
+sofri
+sofrida
+somando
+sommelier
+sonoros
+soto
+spectr
+stamey
+steffi
+stephen
+strip
+stupas
+subestima
+sucateados
+sudão
+suficiente
+suingue
+superloja
+superman
+supersônicos
+surgido
+survival
+suspensa
+tabelionato
+tablados
+tabwin
+tamanha
+tardinha
+tatuapé
+tecendo
+teclado
+teimando
+telbrás
+telinha
+telma
+tempo
+têmporas
+tento
+terá
+terrence
+testemunhada
+típicos
+título
+tobago
+tocá
+tocamos
+tolo
+tomográficas
+tóquio
+torno
+trajada
+trajado
+trampolim
+tranquilidade
+transcreve
+transeunte
+transferido
+transtornado
+travada
+trazê
+tremendo
+tremer
+tremores
+trotskistas
+tuma
+tupi
+tupiniquins
+turboélices
+turnos
+ultrapassagem
+uniações
+urrava
+usp
+vai
+vale
+valiosa
+valorizados
+vargas
+velados
+velocidade
+venais
+vencera
+vencido
+veneno
+versolato
+vestimenta
+viabilizou
+vilarejo
+vilmacy
+vindas
+visitante
+visse
+vitrine
+viúva
+vivíssimos
+vogal
+vogue
+volta
+wall
+want
+weaver
+xeque
+xícara
+ximenes
+zanivan
+zita
\ No newline at end of file
diff --git a/tests/test_files/portuguese_stemmer/stemmed_result.txt b/tests/test_files/portuguese_stemmer/stemmed_result.txt
new file mode 100644
index 0000000..f4c29fe
--- /dev/null
+++ b/tests/test_files/portuguese_stemmer/stemmed_result.txt
@@ -0,0 +1,1000 @@
+abat
+abon
+abrirã
+abriu
+absorvent
+abstinent
+acab
+acab
+accor
+aceit
+aceit
+acessóri
+achaqu
+achar
+acidez
+acomod
+acomod
+acompanh
+acontec
+acord
+acredit
+acumul
+administr
+adminstr
+advog
+aér
+aér
+afast
+after
+agenc
+agreg
+ajud
+ajud
+alá
+alcanc
+alccólatr
+alhei
+altaneir
+altur
+alugu
+amanhec
+amans
+amass
+ameac
+ancestr
+andanc
+anim
+anteced
+antig
+antiquári
+antôni
+apanh
+aparec
+apel
+apoi
+apoi
+apolog
+apont
+aposentador
+apresent
+aproxim
+argument
+argument
+ariel
+armar
+armazém
+arquitetôn
+arquiv
+arremat
+arrisc
+arrum
+arrum
+ascensã
+assemelh
+asseps
+assust
+ata
+atac
+atad
+atend
+atest
+atra
+atrapalh
+atrapalh
+atribu
+atribuíd
+atroc
+atrofi
+avanc
+bá
+baderneir
+bal
+bamb
+band
+band
+bangu
+ban
+banzec
+barat
+barbeir
+batiz
+beberic
+bec
+beij
+beij
+bem
+benefic
+benzedeir
+bergen
+bér
+berr
+betân
+bett
+bibliotec
+bibliotec
+boicot
+bolh
+bonés
+borr
+boucinh
+brant
+bren
+brócol
+bumbum
+burac
+burl
+burocrat
+bussund
+buttons
+cabeleir
+cabell
+cabel
+cab
+cad
+cafeicultor
+calabr
+calamit
+calil
+camelô
+camil
+campus
+caprich
+capriglion
+carc
+caríssim
+carp
+cartaz
+casaquistã
+cassi
+catástrof
+categor
+cel
+celesc
+célul
+cervejinh
+cev
+chac
+cham
+chap
+charlatã
+chass
+chef
+chefi
+choqu
+christoph
+chup
+ciclism
+cics
+cie
+cim
+cineast
+cirurgiã
+cirurg
+clamor
+clar
+coalition
+cobr
+coc
+coincid
+colabor
+colabor
+colateral
+colh
+coloc
+coloc
+coloniz
+colon
+columb
+combin
+comec
+comed
+comod
+compress
+comprov
+comput
+conden
+conden
+condomíni
+confeccion
+confeccion
+confi
+conformacional
+congel
+conjunçã
+consegu
+consegu
+consent
+conserv
+consid
+constânc
+constat
+construíd
+consul
+consult
+contrari
+contr
+contrat
+convençõ
+convers
+conversív
+convers
+conversor
+convivent
+convoc
+coqueir
+coraj
+córdob
+coreógraf
+corinthian
+corn
+corolári
+corriqueir
+cosm
+costum
+craqu
+criativ
+crick
+crimin
+cris
+cristã
+critic
+crixás
+crônic
+cuidad
+cuj
+culp
+cumpr
+cust
+cust
+darci
+datilograf
+debat
+decent
+decepcion
+decert
+decid
+decifr
+definit
+degrad
+degraus
+dem
+dem
+demour
+deng
+deng
+dengos
+depoiment
+deposit
+desarrum
+descompromiss
+desconfi
+desconfi
+desconhec
+descumpr
+desequilibr
+design
+design
+desincompatibiliz
+desmembr
+desobedient
+desodor
+desonest
+desperceb
+desperceb
+despert
+desprez
+desprez
+desqualific
+desquit
+destem
+desus
+detect
+detém
+deton
+dicçã
+dietilpropion
+diferenc
+dilat
+dilbert
+diluíd
+diminu
+disciplin
+discut
+discut
+dispar
+dispens
+disquet
+distant
+distribu
+divert
+divulg
+divulg
+divulg
+dobradic
+dobr
+doent
+dolc
+dolor
+don
+dressing
+duart
+durã
+é
+eclét
+edmund
+efe
+eigg
+ela
+eleotóri
+eletriz
+elóg
+ely
+embaix
+émerson
+emperr
+encar
+encarcer
+encen
+enchiment
+enciclopéd
+enclausur
+encomend
+encontr
+encontr
+encorp
+encost
+encrav
+encruzilh
+enfoqu
+enfrent
+enfurec
+engalfinh
+engat
+engol
+ensai
+entend
+enterr
+ento
+entra
+entrand
+entreg
+entrepost
+envergonh
+ergu
+erosã
+errinh
+esbarr
+escolh
+escolh
+escrav
+escritor
+esdras
+espadim
+espalhafat
+espartan
+especif
+esper
+espetacular
+espos
+esquec
+esquerd
+esqu
+esquir
+esquiv
+esteróid
+estil
+estim
+estivéss
+estont
+estoqu
+estór
+estrag
+estratég
+estrel
+estrof
+étnic
+evit
+examin
+exclus
+exibiçã
+exist
+exist
+expect
+experient
+experiment
+explor
+export
+extraíd
+extrem
+fábric
+fábul
+faix
+fam
+faró
+farr
+fé
+fearnsid
+feitor
+femm
+fenec
+ferrar
+festej
+fic
+fic
+figur
+figurõ
+fil
+financ
+fin
+firm
+físic
+flag
+fláv
+folhetim
+fom
+fon
+forest
+forj
+formaçõ
+form
+form
+frankfurt
+frent
+freqüent
+freqüent
+fulmin
+funcion
+fund
+gabriel
+ganh
+garimp
+gel
+gell
+generaliz
+gêner
+genicul
+genocídi
+genuín
+geopolít
+gerenc
+gergelim
+gestor
+gigantesc
+glin
+goiab
+gonzal
+gourmet
+govern
+govern
+graduaçã
+granj
+gravador
+grav
+gravat
+gravat
+grip
+hab
+habil
+harold
+hav
+hepát
+hércul
+herdeir
+hesit
+hipótes
+histerectom
+histér
+histor
+holíst
+homogên
+homolog
+hormon
+hussein
+iacomin
+identific
+igrej
+imediat
+imit
+imit
+imped
+imped
+imper
+impor
+impression
+impuls
+ináci
+inchad
+inconform
+incrív
+independent
+indev
+indic
+indic
+indivídu
+induz
+infel
+inib
+insensív
+inspir
+instal
+instig
+instint
+institu
+institut
+insuper
+intel
+intens
+inter
+interess
+interpret
+invers
+invest
+irin
+irmã
+iron
+irrit
+irrit
+isol
+itaipu
+jacquelin
+jân
+jav
+javi
+jk
+joan
+joelh
+jói
+jorn
+junot
+just
+juventud
+kalum
+kar
+karl
+kaua
+kelly
+kerly
+kiberon
+labl
+ladakh
+lamarc
+lamúr
+larg
+lee
+leilo
+lesbian
+levant
+levant
+lev
+lib
+liberal
+lib
+liçã
+limit
+lipp
+lisonj
+lix
+local
+localiz
+locaset
+logotip
+loir
+long
+lot
+louc
+louc
+ltda
+lucien
+lucr
+lurex
+lut
+lux
+lyon
+maçar
+mach
+maciel
+mad
+mai
+maioridad
+maluc
+mand
+manipul
+mant
+mantr
+maravilh
+marcell
+march
+marjori
+martel
+masculin
+mat
+matriz
+mau
+mecân
+med
+megassucess
+mei
+mei
+mel
+melbourn
+mencion
+mens
+menstru
+mental
+ment
+ment
+mercador
+metal
+met
+metrópol
+michael
+miguel
+milagr
+milionár
+misericórd
+mític
+moderin
+modism
+montanar
+mont
+mont
+morad
+morr
+mov
+mozin
+mud
+muggiat
+mulat
+mult
+multiplic
+municípi
+muralh
+namor
+nanin
+natári
+naveg
+nav
+necess
+neosaldin
+netscap
+neurocient
+nifutimox
+nitrogêni
+noss
+not
+notóri
+nova
+nov
+obscur
+observ
+obtê
+ocios
+ocorrent
+ocorr
+octavi
+ocup
+ofic
+oitent
+oit
+okky
+olbers
+old
+olhad
+ombros
+oper
+orden
+orgulh
+orquestr
+orwellian
+osak
+osborn
+padroniz
+pag
+pair
+paol
+papel
+paralel
+par
+parcial
+parlament
+part
+particip
+pass
+passageir
+past
+patin
+patotinh
+patro
+patrocin
+paul
+pedet
+ped
+ped
+peixot
+pend
+pend
+penduricalh
+percorr
+perd
+perit
+permanec
+permanec
+pertenc
+pes
+pes
+pesso
+petrolin
+picot
+pif
+pilot
+pílul
+pinheir
+pior
+pitt
+plástic
+pól
+porõ
+poros
+porteir
+posit
+postul
+pouquíssim
+pov
+pow
+prateleir
+preced
+precios
+precis
+precis
+prefer
+preg
+pregã
+prematur
+premi
+prêmi
+preocup
+prepar
+presidencial
+prestigi
+pret
+pretext
+prism
+process
+process
+process
+procon
+procur
+procur
+procur
+profissional
+profund
+proíb
+proib
+prontidã
+proveit
+provoc
+provoc
+ptb
+public
+pul
+quebr
+queim
+queix
+question
+quinhent
+quintan
+radunz
+raíz
+raphael
+rappers
+raríssim
+raríss
+rasg
+raspadinh
+raw
+rbs
+readapt
+reca
+recant
+recepcion
+recit
+recorr
+recus
+redescobr
+redond
+refac
+referent
+refúgi
+regen
+regr
+reinant
+reivindic
+relacion
+relacion
+relacion
+religiã
+religi
+remoçã
+remodel
+rentável
+repartiçõ
+reportagens
+repressor
+reproduz
+republican
+repudi
+requint
+reserv
+respect
+resum
+resum
+retir
+retom
+retrat
+revel
+revel
+reviravolt
+reviv
+riddl
+rísp
+rod
+românt
+ronald
+roqueir
+rossellin
+roubalheir
+rudolf
+sab
+sae
+sagr
+sagr
+salg
+salv
+san
+san
+sanidad
+saraiv
+sar
+satélit
+saturn
+secador
+seçã
+secretár
+sécul
+segund
+semanal
+semeadeir
+sen
+senegal
+sensaçã
+sent
+sent
+sent
+separ
+sequestr
+ser
+serv
+setorial
+sever
+sexu
+shows
+simbol
+simplific
+sinagog
+sin
+sinistr
+siren
+sisud
+sobr
+sobrecarreg
+sociedad
+sociolog
+sofistic
+sofr
+sofr
+sofr
+som
+sommeli
+sonor
+sot
+spectr
+stamey
+steff
+stephen
+strip
+stup
+subestim
+sucat
+sudã
+suficient
+suing
+superloj
+superman
+supersôn
+surg
+survival
+suspens
+tabelionat
+tabl
+tabwin
+tamanh
+tardinh
+tatuap
+tec
+tecl
+teim
+telbrás
+telinh
+telm
+temp
+têmpor
+tent
+ter
+terrenc
+testemunh
+típic
+títul
+tobag
+toc
+toc
+tol
+tomográf
+tóqui
+torn
+traj
+traj
+trampolim
+tranquil
+transcrev
+transeunt
+transfer
+transtorn
+trav
+traz
+trem
+trem
+tremor
+trotskist
+tum
+tup
+tupiniquins
+turboélic
+turn
+ultrapassag
+uniaçõ
+urrav
+usp
+vai
+val
+valios
+valoriz
+varg
+vel
+veloc
+ven
+venc
+venc
+venen
+versolat
+vestiment
+viabiliz
+vilarej
+vilmacy
+vind
+visit
+viss
+vitrin
+viúv
+vivíss
+vogal
+vog
+volt
+wall
+want
+weav
+xequ
+xíc
+ximen
+zanivan
+zit
\ No newline at end of file
-- 
1.9.4.msysgit.2

