
var testing = 0;
function porterAlgorithm(str){
//only strings greater than 2 are stemmed
	if (str.length > 2) {
    	str = porterAlgorithmStep1(str);
   		str = porterAlgorithmStep2(str);
  		str = porterAlgorithmStep3(str);
     	str = porterAlgorithmStep4(str);
	    str = porterAlgorithmStep5(str);

	}
	//End If

//End of Porter's algorithm.........returning the word
var porterAlgorithm = str;
if(testing==1) {alert(porterAlgorithm);}
return porterAlgorithm;
}
//End function

function porterAlgorithmStep1(str) {
if(testing==1) {alert('In step 1');}
/*
STEP 1A

SSES -> SS                         caresses  ->  caress
IES  -> I                          ponies    ->  poni

SS   -> SS                         caress    ->  caress
S    ->                            cats      ->  cat
*/
//setup 2D array
var a = new Array(4);
	for (i=0; i < 4; i++) {
   		a[i] = new Array(2);
	}

//initializing contents of 2D array
a[0][0] = "sses";
a[0][1] = "ss";
a[1][0] = "ies";
a[1][1] = "i";
a[2][0] = "ss";
a[2][1] = "ss";
a[3][0] = "s";
a[3][1] = "";

//testing 2D array
if (testing == 2) {
for (i=0; i<4; i++) {
	for (j=0; j<2; j++) {
		//alert(a[i][j]);
	}
}
}
//checking word
	for (i=0; i<a.length; i++) {
		var tester = porterEndsWith(str, a[i][0]);
    	if (tester) {
	        str = porterTrimEnd(str, a[i][0].length);
            str = porterAppendEnd(str, a[i][1]);
			break;
		}
    	//End If
	//End for
	}
	
/*
STEP 1B

  If
      (m>0) EED -> EE                     feed      ->  feed
                                          agreed    ->  agree
   Else
       (*v*) ED  ->                        plastered ->  plaster
                                           bled      ->  bled
       (*v*) ING ->                        motoring  ->  motor
                                           sing      ->  sing

If the second or third of the rules in Step 1b is successful, the following
is done:

    AT -> ATE                       conflat(ed)  ->  conflate
    BL -> BLE                       troubl(ed)   ->  trouble
    IZ -> IZE                       siz(ed)      ->  size
    (*d and not (*L or *S or *Z))
       -> single letter
                                    hopp(ing)    ->  hop
                                    tann(ed)     ->  tan
                                    fall(ing)    ->  fall
                                    hiss(ing)    ->  hiss
                                    fizz(ed)     ->  fizz
   (m=1 and *o) -> E               fail(ing)    ->  fail
                                    fil(ing)     ->  file

The rule to map to a single letter causes the removal of one of the double
letter pair. The -E is put back on -AT, -BL and -IZ, so that the suffixes
-ATE, -BLE and -IZE can be recognised later. This E may be removed in step
4.
*/
//declaring local variables
var m;
var temp;
var second_third_success;

//initializing contents of 2D array
second_third_success = false;

//(m>0) EED -> EE..else..(*v*) ED  ->(*v*) ING  ->
if (porterEndsWith(str, "eed")) {
	//counting the number of m's
    temp = porterTrimEnd(str, "eed".length);
    m = porterCountm(temp);

    if (m > 0) {
            str = porterTrimEnd(str, "eed".length);
            str = porterAppendEnd(str, "ee");
    }
	//End If

}

else if (porterEndsWith(str, "ed")){
        //trim and check for vowel
        temp = porterTrimEnd(str, "ed".length);

        if (porterContainsVowel(temp)) {
            str = porterTrimEnd(str, "ed".length);
            second_third_success = true;
        }//End If
}//end if
        
else if (porterEndsWith(str, "ing")) {
        //trim and check for vowel
        temp = porterTrimEnd(str, "ing".length);
        
        if (porterContainsVowel(temp)) {
            str = porterTrimEnd(str, "ing".length);
            second_third_success = true;
        }//End If

}// End If
/*
If the second or third of the rules in Step 1b is SUCCESSFUL, the following
is done:

    AT -> ATE                       conflat(ed)  ->  conflate
    BL -> BLE                       troubl(ed)   ->  trouble
    IZ -> IZE                       siz(ed)      ->  size
    (*d and not (*L or *S or *Z))
       -> single letter
                                    hopp(ing)    ->  hop
                                    tann(ed)     ->  tan
                                    fall(ing)    ->  fall
                                    hiss(ing)    ->  hiss
                                    fizz(ed)     ->  fizz
    (m=1 and *o) -> E               fail(ing)    ->  fail
                                    fil(ing)     ->  file
*/
if (second_third_success == 'true') {
// If the second or third of the rules in Step 1b is SUCCESSFUL       
    if (porterEndsWith(str, "at")) {// AT -> ATE
            str = porterTrimEnd(str, "at".length);
            str = porterAppendEnd(str, "ate");
	} // end if
    else if (porterEndsWith(str, "bl")) {// BL -> BLE
            str = porterTrimEnd(str, "bl".length);
            str = porterAppendEnd(str, "ble");
    } // end if
	else if (porterEndsWith(str, "iz")) {// IZ -> IZE
            str = porterTrimEnd(str, "iz".length);
            str = porterAppendEnd(str, "ize");
	} // end if
    else if (porterEndsDoubleConsonent(str)) {// (*d and not (*L or *S or *Z))-> single letter
            if (!(porterEndsWith(str, "l")) ||
				!(porterEndsWith(str, "s")) ||
				!(porterEndsWith(str, "z"))
				) {
					str = porterTrimEnd(str, 1);
			} // End If
	} // end if
    else if (porterCountm(str) = 1) {// (m=1 and *o) -> E
            if (porterEndsCVC(str)) {
	            str = porterAppendEnd(str, "e");
            } // end if
	} // End If
} // End If
/*---------------------------------------------------------------------------------------------'
'STEP 1C
'
'    (*v*) Y -> I                    happy        ->  happi
'                                    sky          ->  sky
*/
if (porterEndsWith(str, "y")) {        
        // trim and check for vowel
        temp = porterTrimEnd(str, 1);

        if (porterContainsVowel(temp)) {
            str = porterTrimEnd(str, "y".length);
            str = porterAppendEnd(str, "i");
        } // End If
} // End If

//retuning the word
var porterAlgorithmStep1 = str;
//alert(porterAlgorithmStep1);
return porterAlgorithmStep1;			
}// end function



function porterAlgorithmStep2(str) {
if(testing==1) {alert('In step 2');}
/*
'STEP 2
'
'    (m>0) ATIONAL ->  ATE           relational     ->  relate
'    (m>0) TIONAL  ->  TION          conditional    ->  condition
'                                    rational       ->  rational
'    (m>0) ENCI    ->  ENCE          valenci        ->  valence
'    (m>0) ANCI    ->  ANCE          hesitanci      ->  hesitance
'    (m>0) IZER    ->  IZE           digitizer      ->  digitize
'Also,
'    (m>0) BLI    ->   BLE           conformabli    ->  conformable
'
'    (m>0) ALLI    ->  AL            radicalli      ->  radical
'    (m>0) ENTLI   ->  ENT           differentli    ->  different
'    (m>0) ELI     ->  E             vileli        - >  vile
'    (m>0) OUSLI   ->  OUS           analogousli    ->  analogous
'    (m>0) IZATION ->  IZE           vietnamization ->  vietnamize
'    (m>0) ATION   ->  ATE           predication    ->  predicate
'    (m>0) ATOR    ->  ATE           operator       ->  operate
'    (m>0) ALISM   ->  AL            feudalism      ->  feudal
'    (m>0) IVENESS ->  IVE           decisiveness   ->  decisive
'    (m>0) FULNESS ->  FUL           hopefulness    ->  hopeful
'    (m>0) OUSNESS ->  OUS           callousness    ->  callous
'    (m>0) ALITI   ->  AL            formaliti      ->  formal
'    (m>0) IVITI   ->  IVE           sensitiviti    ->  sensitive
'    (m>0) BILITI  ->  BLE           sensibiliti    ->  sensible
'Also,
'    (m>0) LOGI    ->  LOG           apologi        -> apolog
'
'The test for the string S1 can be made fast by doing a program switch on
'the penultimate letter of the word being tested. This gives a fairly even
'breakdown of the possible values of the string S1. It will be seen in fact
'that the S1-strings in step 2 are presented here in the alphabetical order
'of their penultimate letter. Similar techniques may be applied in the other
'steps.
*/
//setup 2D array
var step2 = new Array(20);
	for (i=0; i<21; i++) {
   		step2[i] = new Array(2);
	}

//initializing contents of 2D array
step2[0][0] = "ational";
step2[0][1] = "ate";
step2[1][0] = "tional";
step2[1][1] = "tion";
step2[2][0] = "enci";
step2[2][1] = "ence";
step2[3][0] = "anci";
step2[3][1] = "ance";
step2[4][0] = "izer";
step2[4][1] = "ize";
step2[5][0] = "bli";
step2[5][1] = "ble";
step2[6][0] = "alli";
step2[6][1] = "al";
step2[7][0] = "entli";
step2[7][1] = "ent";
step2[8][0] = "eli";
step2[8][1] = "e";
step2[9][0] = "ousli";
step2[9][1] = "ous";
step2[10][0] = "ization";
step2[10][1] = "ize";
step2[11][0] = "ation";
step2[11][1] = "ate";
step2[12][0] = "ator";
step2[12][1] = "ate";
step2[13][0] = "alism";
step2[13][1] = "al";
step2[14][0] = "iveness";
step2[14][1] = "ive";
step2[15][0] = "fulness";
step2[15][1] = "ful";
step2[16][0] = "ousness";
step2[16][1] = "ous";
step2[17][0] = "aliti";
step2[17][1] = "al";
step2[18][0] = "iviti";
step2[18][1] = "ive";
step2[19][0] = "biliti";
step2[19][1] = "ble";
step2[20][0] = "logi";
step2[20][1] = "log";


//checking word
	for (i=0; i<step2.length; i++) {
		var tester = porterEndsWith(str, step2[i][0]);
    	if (tester) {
	        str = porterTrimEnd(str, step2[i][0].length);
            str = porterAppendEnd(str, step2[i][1]);
			break;
		}
    	//End If
	//End for
	}
//retuning the word
var porterAlgorithmStep2 = str;
//alert(porterAlgorithmStep2);
return porterAlgorithmStep2;	

}// end function

function porterAlgorithmStep3(str) {
if(testing==1) {alert('In step 3');}
/*
'STEP 3
'
'    (m>0) ICATE ->  IC              triplicate     ->  triplic
'    (m>0) ATIVE ->                  formative      ->  form
'    (m>0) ALIZE ->  AL              formalize      ->  formal
'    (m>0) ICITI ->  IC              electriciti    ->  electric
'    (m>0) ICAL  ->  IC              electrical     ->  electric
'    (m>0) FUL   ->                  hopeful        ->  hope
'    (m>0) NESS  ->                  goodness       ->  good


*/
//setup 2D array
var step3 = new Array(6);
	for (i=0; i < 7; i++) {
   		step3[i] = new Array(2);
	}

//initializing contents of 2D array
step3[0][0] = "icate";
step3[0][1] = "ic";
step3[1][0] = "ative";
step3[1][1] = "";
step3[2][0] = "alize";
step3[2][1] = "al";
step3[3][0] = "iciti";
step3[3][1] = "ic";
step3[4][0] = "ical";
step3[4][1] = "ic";
step3[5][0] = "ful";
step3[5][1] = "";
step3[6][0] = "ness";
step3[6][1] = "";

//checking word
	for (i=0; i<step3.length; i++) {
		var tester = porterEndsWith(str, step3[i][0]);
    	if (tester) {
	        str = porterTrimEnd(str, step3[i][0].length);
            str = porterAppendEnd(str, step3[i][1]);
			break;
		}
    	//End If
	//End for
	}

//retuning the word
var porterAlgorithmStep3 = str;
//alert(porterAlgorithmStep3);
return porterAlgorithmStep3;	
} //End function

function porterAlgorithmStep4(str) {
if(testing==1) {alert('In step 4');}
/*

'STEP 4
'
'    (m>1) AL    ->                  revival        ->  reviv
'    (m>1) ANCE  ->                  allowance      ->  allow
'    (m>1) ENCE  ->                  inference      ->  infer
'    (m>1) ER    ->                  airliner       ->  airlin
'    (m>1) IC    ->                  gyroscopic     ->  gyroscop
'    (m>1) ABLE  ->                  adjustable     ->  adjust
'    (m>1) IBLE  ->                  defensible     ->  defens
'    (m>1) ANT   ->                  irritant       ->  irrit
'    (m>1) EMENT ->                  replacement    ->  replac
'    (m>1) MENT  ->                  adjustment     ->  adjust
'    (m>1) ENT   ->                  dependent      ->  depend
'    (m>1 and (*S or *T)) ION ->     adoption       ->  adopt
'    (m>1) OU    ->                  homologou      ->  homolog
'    (m>1) ISM   ->                  communism      ->  commun
'    (m>1) ATE   ->                  activate       ->  activ
'    (m>1) ITI   ->                  angulariti     ->  angular
'    (m>1) OUS   ->                  homologous     ->  homolog
'    (m>1) IVE   ->                  effective      ->  effect
'    (m>1) IZE   ->                  bowdlerize     ->  bowdler
'
'The suffixes are now removed. All that remains is a little tidying up.
*/
//setup 2D array
var step4 = new Array(19);

//initializing contents of 2D array
step4[0] = "al";
step4[1] = "ance";
step4[2] = "ence";
step4[3] = "er";
step4[4] = "ic";
step4[5] = "able";
step4[6] = "ible"
step4[7] = "ant";
step4[8] = "ement";
step4[9] = "ment";
step4[10] = "ent";
step4[11] = "ion";
step4[12] = "ou";
step4[13] = "ism";
step4[14] = "ate";
step4[15] = "iti";
step4[16] = "ous";
step4[17] = "ive";
step4[18] = "ize";

//checking word
for (i=0; i<step4.length; i++) {
    if (porterEndsWith(str, step4[i])) {
	    
            temp = porterTrimEnd(str, step4[i].length);
            
           if (porterCountm(temp) > 1) {
            
              if (porterEndsWith(str, "ion")) {
                if (porterEndsWith(temp, "s") ||
					porterEndsWith(temp, "t")
					) {
                 		str = porterTrimEnd(str, step4[i].length);
                 		str = porterAppendEnd(str, "");
                 } // End If
             	} // end if
			
				else {
                    str = porterTrimEnd(str, step4[i].length);
                    str = porterAppendEnd(str, "");
                } //End else

			} //End If
			else { 
                    str = porterTrimEnd(str, step4[i].length);
                    str = porterAppendEnd(str, "");
		 	} // end else
            //alert('ending step 4');
    } //End If
} // end for

//retuning the word
var porterAlgorithmStep4 = str;
//alert(porterAlgorithmStep4);
return porterAlgorithmStep4;	

} // End function

function porterAlgorithmStep5(str) {

if(testing==1) {alert('In step 5');}
/*
'STEP 5a
'
'    (m>1) E     ->                  probate        ->  probat
'                                    rate           ->  rate
'    (m=1 and not *o) E ->           cease          ->  ceas
'
'STEP 5b
'
'    (m>1 and *d and *L) -> single letter
'                                    controll       ->  control
'                                    roll           ->  roll

'declaring local variables
*/
var i;
var temp;


//Step5a
if (porterEndsWith(str, "e")) { //word ends with e
    temp = porterTrimEnd(str, 1);
    if (porterCountm(temp) > 1 ) { //m>1
        str = porterTrimEnd(str, 1);
	} // end if
    else if (porterCountm(temp) == 1) { //m=1
        if ( !(porterEndsCVC(temp))) { //not *o
            str = porterTrimEnd(str, 1);
        } //End If
    } // End else
} //End If

//Step5b
if (porterCountm(str) > 1) {
    if ((porterEndsDoubleConsonent(str)) &&
		(porterEndsWith(str, "l"))
		) {
        str = porterTrimEnd(str, 1);
    } // End If
}// End If

//retuning the word
var porterAlgorithmStep5 = str;
//alert(porterAlgorithmStep5);
return porterAlgorithmStep5;	
} // End function


function porterEndsWith(str, ends) {
//declaring local variables
var length_str;
var length_ends;
var hold_ends;
var porterEndsWith;

//finding the length of the string
length_str = str.length;
length_ends = ends.length;

//if length of str is greater than the length of length_ends, only then proceed..else return false
	if (length_ends >= length_str) {
    	porterEndsWith = false;
	} 
	//End if
	else {
    	//extract characters from right of str
    	hold_ends = str.slice((length_str-length_ends), length_str);
    	//alert(hold_ends);
    	//comparing to see whether hold_ends=ends
    	if (hold_ends == ends) {
        	porterEndsWith = true;
		}
		//End if
    	else {
        	porterEndsWith = false;
    	}
		//End else
	}  
	//End else
return porterEndsWith;
}
//End function


function porterContains(str, present) {
// checking whether strr contains present
if (InStr(str, present) = 0) {
    porterContains = false;
} // end if
else {
    porterContains = true;
} //End else

} //End function


function porterContainsVowel(str) {
//checking word to see if vowels are present
var chars = new Array();
var i;
var pattern;
var porterContainsVowel;

if (str.length >= 0) {

    //find out the CVC pattern
    pattern = returnCVCpattern(str);
	
	//converting string to char array
	for(x=0; x<str.length; x++) {
		chars[x] = str.charAt(x);   	
	} // end for
	
    //check to see if the return pattern contains a vowel
	for(y=0; y<chars.length; y++) {
		if (chars[y] == "v") {
	        porterContainsVowel = false;
    	}//end if
		
		else {
        porterContainsVowel = true;
    	}//end else
	}//end for
}//end if

else {
    porterContainsVowel = false;
} //end else

return porterContainsVowel;
}
//End function


function porterEndsDoubleConsonent(str) {
if(testing==1) {alert('In step porterEndsDoubleConsonent');}
//checking whether word ends with a double consonant (e.g. -TT, -SS).

//declaring local variables
var holds_ends;
var hold_third_last;
var chars = new Array();

//first check whether the size of the word is >= 2
if(str.length >= 2) {
    //extract 2 characters from right of str
    holds_ends = str.slice((str.length-2),str.length);
        
	//converting string to char array
	for(x=0; x<holds_ends.length; x++) {
		chars[x] = holds_ends.charAt(x);   	
	} // end for
	
    //checking if both the characters are same
    if (chars[0] == chars[1]) {    
        //check for double consonent
        if ((holds_ends = "aa") ||
			(holds_ends = "ee") ||
			(holds_ends = "ii") ||
			(holds_ends = "oo") ||
			(holds_ends = "uu")
			) {            
            porterEndsDoubleConsonent = false;
		} //end if
            
        else {
        //if the second last character is y, and there are atleast three letters in str
            if ((holds_ends = "yy") && 
				(str.length > 2)
				) {            
                //extracting the third last character
                hold_third_last = str.charAt((str.length-3));
                
                if (!(
					(hold_third_last = "a") ||
					(hold_third_last = "e") ||
					(hold_third_last = "i") ||
					(hold_third_last = "o") ||
					(hold_third_last = "u") 
					)) {                    
                    porterEndsDoubleConsonent = false;
                } //end if    
                else {                
                    porterEndsDoubleConsonent = true;
				} //end else
            } // end if        
            else {
            	porterEndsDoubleConsonent = true;
            } //end else
    
        } //End else
    } // end if
    else {    
        porterEndsDoubleConsonent = false;
    } //End else
} // end if    
else {
    porterEndsDoubleConsonent = false;
} // End If
}//End function


function porterEndsCVC(str) {
/*
'*o  - the stem ends cvc, where the second c is not W, X or Y (e.g. -WIL, -HOP).
*/
//declaring local variables
var chars = new Array();
var const_vowel;
var i;
var pattern;

//check to see if atleast 3 characters are present
if (str.length >= 3) {    
	//converting string to char array
	for(x=0; x<str.length; x++) {
		chars[x] = str.charAt(x);   	
	} // end for
    
    //find out the CVC pattern
    pattern = returnCVCpattern(str);
    
    //we need to check only the last three characters
    pattern = pattern.slice((pattern.length - 3), pattern.length);
      
    //check to see if the letters in str match the sequence cvc
    if (pattern = "cvc") {
        if (!(
			(chars[chars.length] == "w") ||
			(chars[chars.length] == "x") ||
			(chars[chars.length] == "y")
			)) {
            porterEndsCVC = true;
		} //end if
        else {
            porterEndsCVC = false;
        } //End else
    } // end if
	else {
        porterEndsCVC = false;
    } // End else
} // end if    
else {
    porterEndsCVC = false;
} // End If
return porterEndsCVC;
} //End function


function porterTrimEnd(str, length) {
//returning the trimmed string
var porterTrimEnd = str.slice(0,(str.length-length));
return porterTrimEnd;
}
//End function

function porterAppendEnd(str, ends) {
//returning the appended string
var porterAppendEnd = str + ends;
return porterAppendEnd;
}
//End function



function porterCountm(str) {
/*
'A \consonant\ in a word is a letter other than A, E, I, O or U, and other
'than Y preceded by a consonant. (The fact that the term `consonant' is
'defined to some extent in terms of itself does not make it ambiguous.) So in
'TOY the consonants are T and Y, and in SYZYGY they are S, Z and G. If a
'letter is not a consonant it is a \vowel\.
*/
//declaring local variables
var chars = new Array();
var const_vowel;
var i;
var m;
var flag;
var pattern;

//initializing
const_vowel = "";
m = 0;
flag = false;


if (!(str.length = 0)) {

    //find out the CVC pattern
    pattern = returnCVCpattern(str);
    
    //converting const_vowel to byte array
    for(x=0; x<str.length; x++) {
		chars[x] = str.charAt(x);   	
	}
    
    //counting the number of m's...
    for (i = 0; i<chars.length; i++) {
        if ((chars[i] == "v") ||
			(flag == true)
			) {
            flag = true;
            
			if (chars[i] == "c") {
                m = m + 1;
                flag = false;
			} //  End If
        } // End If
    } // end for
    
} //End If
var porterCountm = m;
//alert(porterCountm);
return porterCountm;
} //End function


function returnCVCpattern(str) {
//local variables
var chars = new Array();
var const_vowel;
var returnCVCpattern;

//converting string to char array
for(x=0; x<str.length; x++) {
	chars[x] = str.charAt(x);
	
}


//checking each character to see if it is a consonent or a vowel. also inputs the information in const_vowel

for (i=0; i<chars.length; i++) {
    if ((chars[i] == "a") ||
		(chars[i] == "e") ||
		(chars[i] == "i") ||
		(chars[i] == "o") ||
		(chars[i] == "u")
		) {
        const_vowel = const_vowel + "v";
	}
	// end if
    else {
	if (chars[i] == "y") {
    //if y is not the first character, only then check the previous character
        if (i > 0) {
        //check to see if previous character is a consonent
            if (!(chars[i-1] == "a") ||
				(chars[i - 1] == "e") ||
				(chars[i - 1] == "i") ||
				(chars[i - 1] == "o") ||
				(chars[i - 1] == "u") 
				) {
                const_vowel = const_vowel + "v";
            } //end if	
			else {
                const_vowel = const_vowel + "c";
            } // end else
		} //end if
        else {
            const_vowel = const_vowel + "c";
        } //end else
	} //end if
    else {
        const_vowel = const_vowel + "c";
    } //end else        
	} //end else        
}// end for    

returnCVCpattern = const_vowel;
return returnCVCpattern;
} //End function

