% porter.pl % Prolog implementation of the Porter Stemmer % Philip Brooks % philip.brooks@gmail.com % Special thanks to Jon McClain, who helped with an earlier attempt % October 10, 2005 % Release 1 % The original Porter Stemmer algorithm is described in: % Porter, 1980, An algorithm for suffix stripping, Program, Vol. 14, % no. 3, pp 130-137 % Or see http://www.tartarus.org/~martin/PorterStemmer/ % stem_token/2 implementation expects input tokens to be in the form: % w([X,Y,Z,...]) % where X is the first letter, Y is the second, Z is the third, and so % on. This is the output format of Michael Covington's excellent Efficient % Tokenizer, available at http://www.ai.uga.edu/mc/pronto/ % If you have et.pl in a directory where Prolog can find it and have an ISO- % compliant Prolog, you can uncomment the predicates test_stemmer/2, % write_token/1, and write_tokens/1, below, and use them to test the program % on a vocabulary file such as the voc.txt provided on the Porter Stemmer % website. % stem_token/2 will ignore non-words (tokens with something beside 'w' for % a functor) and will treat all non-lowercase, non-alphabetical, or non- % ASCII characters as consonants, which may lead to strange behavior. % Another way to call the Stemmer is stem_character_list/2, which expects % and returns a list of one-character atoms (again treating all exceptional % characters as consonants). stem_tokens/2 and stem_character_lists/2 will % handle lists of the appropriate inputs. % Tested in SWI-Prolog 5.4.5 on Windows. % Available from http://www.swi-prolog.org/ % Accurately stems the sample vocabulary. % Note that because tokens are stored in linked lists instead of arrays, % the performance of this implementation suffers compared to those that % use data structures with more efficient random access. This processes % 1 megabyte of text in about 3.4 seconds on an Athlon64 3200+. That's % about a half-second longer than the Perl version. % % Test Predicates % %% test_stemmer(+Vocabulary, +Output) %% Reads in tokens from the specified Vocabulary file using Covington's %% Efficient Tokenizer and writes them, one per line, to the Output file. %% Requires an ISO-compliant Prolog implementation. % %:- ensure_loaded('et.pl'). %test_stemmer(In, Out) :- % tokenize_file(In, Tokens), % stem_tokens(Tokens, STokens), % open(Out, write, Stream, []), % set_output(Stream), % write_tokens(STokens), % close(Stream). % %% write_tokens(+Tokens) %% Given a list of ET-style tokens, writes them as plain text to %% the current output, one per line. % %write_tokens([H|T]) :- % write_token(H), % nl, % write_tokens(T). %write_tokens([]). % %% write_token(+Token) %% Given an ET-style token, writes it as plain text to the current %% output. % %write_token(Token) :- % arg(1, Token, String), % write_token_x(String). %write_token_x([H|T]) :- % put_char(H), % write_token_x(T). %write_token_x([]). % % User Predicates % % stem_token(+Word, -Stem) % Given a word in the format w([a,b,c...]), applies the Porter % Stemmer algorithm to it and instantiates Stem to its stem, also % in the format w([...]). % If not given a word, it instantiates Word to Stem. stem_token(w([W1,W2,W3|T]),Stem) :- !, decompose(w([W1,W2,W3|T]),DWord), step1a(DWord,DWord1a), step1b(DWord1a,DWord1b), step1c(DWord1b,DWord1c), step2(DWord1c,DWord2), step3(DWord2,DWord3), step4(DWord3,DWord4), step5a(DWord4,DWord5a), step5b(DWord5a,DStem), reconstitute(DStem,Stem). % If it's not a word or it's less than three characters long, % do nothing to it. stem_token(X,X). % stem_tokens(+List, -Stems) % Given a list of ET-syle tokens, stems each one and instantiates % Stems to a list of their stems. stem_tokens([H|T], [SH|ST]) :- stem_token(H, SH), stem_tokens(T, ST). stem_tokens([], []). % stem_character_list(+List, -Stem) % Given a word in the format [a,b,c,...], applies the Porter % Stemmer algorithm to it and instantiates Stem to its stem, % also as a list of characters. stem_character_list(List, Stem) :- stem_token(w(List), w(Stem)). % stem_character_lists(+List, -Stems) % Given a list of words in the format [a,b,c,...], applies the % Porter Stemmer algorithm to each one, instantiating Stem % to their stems, also as lists of characters. stem_character_lists([H|T], [SH|ST]) :- stem_character_list(H,SH), stem_character_lists(T,ST). stem_character_lists([], []). % % Private Predicates % % decompose(+Word,-Components) % Reverses a w([a,b,c...]) Word and decomposes it into a list of alternating % consonant and vowel components, of the form: % [c([...]),v([...]),c([...]),...] (alternatively starting with v() % instead of c()) decompose(w([H|T]), Stem) :- yconsonant(H),!, decompose_consonant(T, [], [H], Stem). decompose(w([H|T]), Stem) :- decompose_vowel(T, [], [H], Stem). decompose_consonant([H|T], Stem, Consonant, Answer) :- consonant(H),!, decompose_consonant(T, Stem, [H|Consonant], Answer). decompose_consonant([H|T], Stem, Consonant, Answer) :- yvowel(H),!, decompose_vowel(T, [c(Consonant)|Stem], [H], Answer). decompose_consonant([], Stem, Consonant, [c(Consonant)|Stem]). decompose_vowel([H|T], Stem, Vowel, Answer) :- vowel(H),!, decompose_vowel(T, Stem, [H|Vowel], Answer). decompose_vowel([H|T], Stem, Vowel, Answer) :- yconsonant(H),!, decompose_consonant(T, [v(Vowel)|Stem], [H], Answer). decompose_vowel([], Stem, Vowel, [v(Vowel)|Stem]). % reconstitute(+DWord,-Word) % Takes a decomposed word and puts it back together in the original order. reconstitute(DWord, w(Word)) :- reconstitute_x(DWord, [], Word). reconstitute_x([H|T], Word, Final) :- arg(1, H, A), reverse_append(A, Word, NWord), reconstitute_x(T, NWord, Final). reconstitute_x([], Final, Final). % reverse_append(?List1, ?List2, ?List3) % List3 is the concatenation of the reversal of List1 to the % beginning of List2. reverse_append([H|T], List2, List3) :- reverse_append(T, [H|List2], List3). reverse_append([], List, List). % step1a(+In,-Out) % Applies Step 1a % SSES -> SS step1a([c([s]),v([e]),c([s,s|T1])|T2],[c([s,s|T1])|T2]) :- !. % IES -> I step1a([c([s]),v([e,i|T1])|T2],[v([i|T1])|T2]) :- !. % SS -> SS step1a([c([s,s|T1])|T2],[c([s,s|T1])|T2]) :- !. % S -> step1a([c([s])|T],T) :- !. step1a([c([s|T1])|T2],[c(T1)|T2]) :- !. % Everything else step1a(X,X). % step1b(+In,-Out) % Applies Step 1b % (m>0) EED -> EE step1b([c([d]),v([e,e|T1]),c(X),v(Y)|T2], [v([e,e|T1]),c(X),v(Y)|T2]) :- !. step1b([c([d]),v([e,e|T1])|T2], [c([d]),v([e,e|T1])|T2]) :- !. % (*v*) ED -> step1b([c([d]),v([e]),c(X),v(Y)|T], Transformed) :- !, step1b_x([c(X),v(Y)|T],Transformed). step1b([c([d]),v([e,V|T1])|T2], Transformed) :- !, step1b_x([v([V|T1])|T2], Transformed). % (*v*) ING -> step1b([c([g,n]),v([i]),c(C1),v(V1)|T], Transformed) :- !, step1b_x([c(C1),v(V1)|T], Transformed). step1b([c([g,n]),v([i,V|T1])|T2], Transformed) :- !, step1b_x([v([V|T1])|T2], Transformed). % Everything else step1b(X,X). % Aux rules for step1b % AT -> ATE step1b_x([c([t]),v([a|T1])|T2], [v([e]),c([t]),v([a|T1])|T2]) :- !. % BL -> BLE step1b_x([c([l,b|T1])|T2],[v([e]),c([l,b|T1])|T2]) :- !. % IZ -> IZE step1b_x([c([z]),v([i|T1])|T2], [v([e]),c([z]),v([i|T1])|T2]) :- !. % (*d and not (*L or *S or *Z)) -> single letter step1b_x([c([X,X|T1])|T2], [c([X|T1])|T2]) :- X \= 'l', X \= 's', X \= 'z', !. % (m=1 and *o) -> E step1b_x([c([C1]),v([V]),c([C2|T])], [v([e]),c([C1]),v([V]),c([C2|T])]) :- C1 \= 'w', C1 \= 'x', C1 \= 'y', !. % Everything else step1b_x(X,X). % step1c(+In,-Out) % Applies Step 1c % (*v*) Y -> I step1c([v([y]),c(C1),v(V1)|T],[v([i]),c(C1),v(V1)|T]) :- !. step1c([c([y]),v(V1)|T], [v([i|V1])|T]) :- !. % Everything else step1c(X,X). % step2(+In,-Out) % Applies Step 2 % Penultimate a step2([c([l]),v([a]),c([n]),v([o,i])|T], Transformed) :- !, step2_a(T,Transformed). % Penultimate c step2([v([i]),c([c,n])|T], Transformed) :- !, step2_c(T,Transformed). % (m>0) IZER -> IZE step2([c([r]),v([e]),c([z]),v([i|T1]),c(C1),v(V1)|T2], [v([e]),c([z]),v([i|T1]),c(C1),v(V1)|T2]) :- !. % (m>0) LOGI -> LOG step2([v([i]),c([g]),v([o]),c([l]),v(V1),c(C1)|T], [c([g]),v([o]),c([l]),v(V1),c(C1)|T]) :- !. step2([v([i]),c([g]),v([o]),c([l,C|T1]),v(V1)|T2], [c([g]),v([o]),c([l,C|T1]),v(V1)|T2]) :- !. % Penultimate l step2([v([i]),c([l|T1])|T2], Transformed) :- !, step2_l([c([l|T1])|T2], Transformed). % Penultimate o step2([c(X),v([o|T1])|T2], Transformed) :- !, step2_o([c(X),v([o|T1])|T2], Transformed). % Penultimate s step2([c([X,s])|T], Transformed) :- !, step2_s([c([X,s])|T], Transformed). % Penultimate t step2([v([i]),c([t]),v([i])|T], Transformed) :- !, step2_t(T, Transformed). % Everything else step2(X,X). % Ending with IONAL % (m>0) ATIONAL -> ATE step2_a([c([t]),v([a|T1]),c(C1),v(V1)|T2], [v([e]),c([t]),v([a|T1]),c(C1),v(V1)|T2]) :- !. step2_a([c([t]),v([a|T1])|T2], [c([l]),v([a]),c([n]),v([o,i]),c([t]),v([a|T1])|T2]) :- !. % (m>0) TIONAL -> TION step2_a([c([t|T1])|T2], [c([n]),v([o,i]),c([t|T1])|T2]) :- !. % Other switched cases step2_a(X, [c([l]),v([a]),c([n]),v([o,i])|X]) :- !. % Ending with NCI % (m>0) ENCI -> ENCE step2_c([v([e|T1]),c(X),v(Y)|T2], [v([e]),c([c,n]),v([e|T1]),c(X),v(Y)|T2]) :- !. % (m>0) ANCI -> ANCE step2_c([v([a|T1]),c(X),v(Y)|T2], [v([e]),c([c,n]),v([a|T1]),c(X),v(Y)|T2]) :- !. % Other switched cases step2_c(X,[v([i]),c([c,n])|X]). % Ending with LI % (m>0) BLI -> BLE step2_l([c([l,b,C|T1]),v(V1)|T2], [v([e]),c([l,b,C|T1]),v(V1)|T2]) :- !. step2_l([c([l,b]),v(V1),c(C1),v(V2)|T], [v([e]),c([l,b]),v(V1),c(C1),v(V2)|T]) :- !. % (m>0) ALLI -> AL step2_l([c([l,l]),v([a|T1]),c(C1),v(V1)|T2], [c([l]),v([a|T1]),c(C1),v(V1)|T2]) :- !. % (m>0) ENTLI -> ENT step2_l([c([l,t,n]),v([e|T1]),c(C1),v(V1)|T2], [c([t,n]),v([e|T1]),c(C1),v(V1)|T2]) :- !. % (m>0) ELI -> E step2_l([c([l]),v([e|T1]),c(C1),v(V1)|T2], [v([e|T1]),c(C1),v(V1)|T2]) :- !. % (m>0) OUSLI -> OUS step2_l([c([l,s]),v([u,o|T1]),c(C1),v(V1)|T2], [c([s]),v([u,o|T1]),c(C1),v(V1)|T2]) :- !. % Other switched cases step2_l(X,[v([i])|X]). % Penultimate O % (m>0) IZATION -> IZE step2_o([c([n]),v([o,i]),c([t]),v([a]),c([z]),v([i|T1]),c(C1),v(V1)|T2], [v([e]),c([z]),v([i|T1]),c(C1),v(V1)|T2]) :- !. step2_o([c([n]),v([o,i]),c([t]),v([a]),c([z]),v([i|T1])|T2], [c([n]),v([o,i]),c([t]),v([a]),c([z]),v([i|T1])|T2]) :- !. % (m>0) ATION -> ATE step2_o([c([n]),v([o,i]),c([t]),v([a|T1]),c(C1),v(V1)|T2], [v([e]),c([t]),v([a|T1]),c(C1),v(V1)|T2]) :- !. % (m>0) ATOR -> ATE step2_o([c([r]),v([o]),c([t]),v([a|T1]),c(C1),v(V1)|T2], [v([e]),c([t]),v([a|T1]),c(C1),v(V1)|T2]) :- !. % Other switched cases step2_o(X,X). % Penultimate S % (m>0) ALISM -> AL step2_s([c([m,s]),v([i]),c([l]),v([a|T1]),c(C1),v(V1)|T2], [c([l]),v([a|T1]),c(C1),v(V1)|T2]) :- !. % (m>0) IVENESS -> IVE step2_s([c([s,s]),v([e]),c([n]),v([e]),c([v]),v([i|T1]),c(C1),v(V1)|T2], [v([e]),c([v]),v([i|T1]),c(C1),v(V1)|T2]) :- !. % (m>0) FULNESS -> FUL step2_s([c([s,s]),v([e]),c([n,l]),v([u]),c([f]),v(V1),c(C1),v(V2)|T], [c([l]),v([u]),c([f]),v(V1),c(C1),v(V2)|T]) :- !. step2_s([c([s,s]),v([e]),c([n,l]),v([u]),c([f|T1]),v(V1)|T2], [c([l]),v([u]),c([f|T1]),v(V1)|T2]) :- !. % (m>0) OUSNESS -> OUS step2_s([c([s,s]),v([e]),c([n,s]),v([u,o|T1]),c(C1),v(V1)|T2], [c([s]),v([u,o|T1]),c(C1),v(V1)|T2]) :- !. % Other switched cases step2_s(X,X). % Ending with ITI % (m>0) ALITI -> AL step2_t([c([l]),v([a|T1]),c(C1),v(V1)|T2], [c([l]),v([a|T1]),c(C1),v(V1)|T2]) :- !. % (m>0) IVITI -> IVE step2_t([c([v]),v([i|T1]),c(C1),v(V1)|T2], [v([e]),c([v]),v([i|T1]),c(C1),v(V1)|T2]) :- !. % (m>0) BILITI -> BLE step2_t([c([l]),v([i]),c([b,C|T1]),v(V1)|T2], [v([e]),c([l,b,C|T1]),v(V1)|T2]) :- !. step2_t([c([l]),v([i]),c([b]),v(V1),c(C1),v(V2)|T], [v([e]),c([l,b]),v(V1),c(C1),v(V2)|T]) :- !. % Other switched cases step2_t(X,[v([i]),c([t]),v([i])|X]). % step3(+In,-Out) % Applies Step 3 % Penultimate t step3([v([V]),c([t|T1])|T2], Transformed) :- !, step3_t([v([V]),c([t|T1])|T2], Transformed). % (m>0) ATIVE -> step3([v([e]),c([v]),v([i]),c([t]),v([a,V1|T1]),c(C),v(V2)|T2], [v([V1|T1]),c(C),v(V2)|T2]) :- !. step3([v([e]),c([v]),v([i]),c([t]),v([a]),c(C),v(V2)|T2], [c(C),v(V2)|T2]) :- !. % (m>0) ALIZE -> AL step3([v([e]),c([z]),v([i]),c([l]),v([a|T1]),c(C),v(V)|T2], [c([l]),v([a|T1]),c(C),v(V)|T2]) :- !. % (m>0) ICAL -> IC step3([c([l]),v([a]),c([c]),v([i|T1]),c(C),v(V)|T2], [c([c]),v([i|T1]),c(C),v(V)|T2]) :- !. % (m>0) FUL -> step3([c([l]),v([u]),c([f,C|T1]),v(V)|T2], [c([C|T1]),v(V)|T2]) :- !. step3([c([l]),v([u]),c([f]),v(V1),c(C),v(V2)|T], [v(V1),c(C),v(V2)|T]) :- !. % (m>0) NESS -> step3([c([s,s]),v([e]),c([n,C|T1]),v(V)|T2], [c([C|T1]),v(V)|T2]) :- !. step3([c([s,s]),v([e]),c([n]),v(V1),c(C),v(V2)|T], [v(V1),c(C),v(V2)|T]) :- !. % Everything else step3(X,X). % Penultimate t % (m>0) ICATE -> IC step3_t([v([e]),c([t]),v([a]),c([c]),v([i|T1]),c(C),v(V)|T2], [c([c]),v([i|T1]),c(C),v(V)|T2]) :- !. % (m>0) ICITI -> IC step3_t([v([i]),c([t]),v([i]),c([c]),v([i|T1]),c(C),v(V)|T2], [c([c]),v([i|T1]),c(C),v(V)|T2]) :- !. % Other switched cases step3_t(X,X). % step4(+In,-Out) % Applies Step 4 % (m>1) AL -> step4([c([l]),v([a]),c(C1),v(V1),c(C2),v(V2)|T], [c(C1),v(V1),c(C2),v(V2)|T]) :- !. step4([c([l]),v([a|T1]),c(C1),v(V1),c(C2),v(V2)|T2], [v(T1),c(C1),v(V1),c(C2),v(V2)|T2]) :- !. % Penultimate c step4([v([e]),c([c,n])|T], Transformed) :- !, step4_c(T,Transformed). % (m>1) ER -> step4([c([r]),v([e]),c(C1),v(V1),c(C2),v(V2)|T], [c(C1),v(V1),c(C2),v(V2)|T]) :- !. step4([c([r]),v([e|T1]),c(C1),v(V1),c(C2),v(V2)|T2], [v(T1),c(C1),v(V1),c(C2),v(V2)|T2]) :- !. % (m>1) IC -> step4([c([c]),v([i]),c(C1),v(V1),c(C2),v(V2)|T], [c(C1),v(V1),c(C2),v(V2)|T]) :- !. step4([c([c]),v([i|T1]),c(C1),v(V1),c(C2),v(V2)|T2], [v(T1),c(C1),v(V1),c(C2),v(V2)|T2]) :- !. % Penultimate l step4([v([e]),c([l,b])|T], Transformed) :- !, step4_l(T, Transformed). % Penultimate n step4([c([t,n])|T], Transformed) :- !, step4_n(T, Transformed). % (m>1 and (*S or *T)) ION -> step4([c([n]),v([o,i]),c([s|T1]),v(V1),c(C1),v(V2)|T2], [c([s|T1]),v(V1),c(C1),v(V2)|T2]) :- !. step4([c([n]),v([o,i]),c([t|T1]),v(V1),c(C1),v(V2)|T2], [c([t|T1]),v(V1),c(C1),v(V2)|T2]) :- !. % (m>1) OU -> step4([v([u,o]),c(C1),v(V1),c(C2),v(V2)|T], [c(C1),v(V1),c(C2),v(V2)|T]) :- !. step4([v([u,o|T1]),c(C1),v(V1),c(C2),v(V2)|T2], [v(T1),c(C1),v(V1),c(C2),v(V2)|T2]) :- !. % (m>1) ISM -> step4([c([m,s]),v([i]),c(C1),v(V1),c(C2),v(V2)|T], [c(C1),v(V1),c(C2),v(V2)|T]) :- !. step4([c([m,s]),v([i|T1]),c(C1),v(V1),c(C2),v(V2)|T2], [v(T1),c(C1),v(V1),c(C2),v(V2)|T2]) :- !. % Penultimate t step4([v([V]),c([t|T1])|T2], Transformed) :- !, step4_t([v([V]),c([t|T1])|T2], Transformed). % (m>1) OUS -> step4([c([s]),v([u,o]),c(C1),v(V1),c(C2),v(V2)|T], [c(C1),v(V1),c(C2),v(V2)|T]) :- !. step4([c([s]),v([u,o|T1]),c(C1),v(V1),c(C2),v(V2)|T2], [v(T1),c(C1),v(V1),c(C2),v(V2)|T2]) :- !. % (m>1) IVE -> step4([v([e]),c([v]),v([i]),c(C1),v(V1),c(C2),v(V2)|T], [c(C1),v(V1),c(C2),v(V2)|T]) :- !. step4([v([e]),c([v]),v([i|T1]),c(C1),v(V1),c(C2),v(V2)|T2], [v(T1),c(C1),v(V1),c(C2),v(V2)|T2]) :- !. % (m>1) IZE -> step4([v([e]),c([z]),v([i]),c(C1),v(V1),c(C2),v(V2)|T], [c(C1),v(V1),c(C2),v(V2)|T]) :- !. step4([v([e]),c([z]),v([i|T1]),c(C1),v(V1),c(C2),v(V2)|T2], [v(T1),c(C1),v(V1),c(C2),v(V2)|T2]) :- !. % Everything else step4(X,X). % Ending in NT % (m>1) ANT -> step4_n([v([a]),c(C1),v(V1),c(C2),v(V2)|T], [c(C1),v(V1),c(C2),v(V2)|T]) :- !. step4_n([v([a|T1]),c(C1),v(V1),c(C2),v(V2)|T2], [v(T1),c(C1),v(V1),c(C2),v(V2)|T2]) :- !. % (m>1) EMENT -> step4_n([v([e]),c([m]),v([e]),c(C1),v(V1),c(C2),v(V2)|T], [c(C1),v(V1),c(C2),v(V2)|T]) :- !. step4_n([v([e]),c([m]),v([e|T1]),c(C1),v(V1),c(C2),v(V2)|T2], [v(T1),c(C1),v(V1),c(C2),v(V2)|T2]) :- !. step4_n([v([e]),c([m]),v([e|T1])|T2], [c([t,n]),v([e]),c([m]),v([e|T1])|T2]) :- !. % (m>1) MENT -> step4_n([v([e]),c([m]),v(V1),c(C1),v(V2),c(C2),v(V3)|T], [v(V1),c(C1),v(V2),c(C2),v(V3)|T]) :- !. step4_n([v([e]),c([m,C|T1]),v(V1),c(C1),v(V2)|T2], [c([C|T1]),v(V1),c(C1),v(V2)|T2]) :- !. step4_n([v([e]),c([m|T1])|T2], [c([t,n]),v([e]),c([m|T1])|T2]) :- !. % (m>1) ENT -> step4_n([v([e]),c(C1),v(V1),c(C2),v(V2)|T], [c(C1),v(V1),c(C2),v(V2)|T]) :- !. step4_n([v([e|T1]),c(C1),v(V1),c(C2),v(V2)|T2], [v(T1),c(C1),v(V1),c(C2),v(V2)|T2]) :- !. % Other switched cases step4_n(T, [c([t,n])|T]). % Ending in BLE % (m>1) ABLE -> step4_l([v([a]),c(C1),v(V1),c(C2),v(V2)|T], [c(C1),v(V1),c(C2),v(V2)|T]) :- !. step4_l([v([a|T1]),c(C1),v(V1),c(C2),v(V2)|T2], [v(T1),c(C1),v(V1),c(C2),v(V2)|T2]) :- !. % (m>1) IBLE -> step4_l([v([i]),c(C1),v(V1),c(C2),v(V2)|T], [c(C1),v(V1),c(C2),v(V2)|T]) :- !. step4_l([v([i|T1]),c(C1),v(V1),c(C2),v(V2)|T2], [v(T1),c(C1),v(V1),c(C2),v(V2)|T2]) :- !. % Other switched cases step4_l(X, [v([e]),c([l,b])|X]). % Ending in NCE % (m>1) ANCE -> step4_c([v([a]),c(C1),v(V1),c(C2),v(V2)|T], [c(C1),v(V1),c(C2),v(V2)|T]) :- !. step4_c([v([a|T1]),c(C1),v(V1),c(C2),v(V2)|T2], [v(T1),c(C1),v(V1),c(C2),v(V2)|T2]) :- !. % (m>1) ENCE -> step4_c([v([e]),c(C1),v(V1),c(C2),v(V2)|T], [c(C1),v(V1),c(C2),v(V2)|T]) :- !. step4_c([v([e|T1]),c(C1),v(V1),c(C2),v(V2)|T2], [v(T1),c(C1),v(V1),c(C2),v(V2)|T2]) :- !. % Other switched cases step4_c(X, [v([e]),c([c,n])|X]). % Penultimate t % (m>1) ATE -> step4_t([v([e]),c([t]),v([a]),c(C1),v(V1),c(C2),v(V2)|T], [c(C1),v(V1),c(C2),v(V2)|T]) :- !. step4_t([v([e]),c([t]),v([a|T1]),c(C1),v(V1),c(C2),v(V2)|T2], [v(T1),c(C1),v(V1),c(C2),v(V2)|T2]) :- !. % (m>1) ITI -> step4_t([v([i]),c([t]),v([i]),c(C1),v(V1),c(C2),v(V2)|T], [c(C1),v(V1),c(C2),v(V2)|T]) :- !. step4_t([v([i]),c([t]),v([i|T1]),c(C1),v(V1),c(C2),v(V2)|T2], [v(T1),c(C1),v(V1),c(C2),v(V2)|T2]) :- !. % Other switched cases step4_t(X, X). % step5a(+In, -Out) % Applies Step 5a % (m>1) E -> step5a([v([e]),c(C1),v(V1),c(C2),v(V2)|T], [c(C1),v(V1),c(C2),v(V2)|T]) :- !. step5a([v([e|T1]),c(C1),v(V1),c(C2),v(V2)|T2], [v(T1),c(C1),v(V1),c(C2),v(V2)|T2]) :- !. % (m=1 and not *o) E -> step5a([v([e]),c(C1),v(V1)|T], [c(C1),v(V1)|T]) :- \+ step5a_x(C1,V1,T), !. step5a([v([e,V|T1]),c(C1),v(V1)], [v([V|T1]),c(C1),v(V1)]) :- !. step5a([v([e,V|T1]),c(C1),v(V1),c(C2)], [v([V|T1]),c(C1),v(V1),c(C2)]) :- !. % Everything else step5a(X,X). % Aux predicate, true when *o or m>1 step5a_x([C1], [_], [c(_)|_]) :- C1 \= 'w', C1 \= 'x', C1 \= 'y', !. step5a_x(_, _, [c(_),v(_)|_]). % step5b(+In, -Out) % Applies Step 5b % (m>1 and *d and *L) -> single letter step5b([c([l,l|T1]),v(V1),c(C1),v(V2)|T2], [c([l|T1]),v(V1),c(C1),v(V2)|T2]) :- !. % Everything else step5b(X,X). % yvowel(+Char) % Succeeds iff Char is a vowel or 'y'. yvowel(Char) :- vowel(Char). yvowel(y). % vowel(+Char) % Succeeds iff Char is a vowel but not 'y'. vowel(a). vowel(e). vowel(i). vowel(o). vowel(u). % consonant(+Char) % Succeeds iff Char is a consonant but not 'y'. consonant(Char) :- \+ yvowel(Char). % yconsonant(+Char) % Succeeds iff Char is a consonant including 'y'. yconsonant(Char) :- \+ vowel(Char).