PageRenderTime 48ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 1ms

/main/src/com/google/refine/clustering/binning/Metaphone3.java

http://google-refine.googlecode.com/
Java | 7460 lines | 4716 code | 652 blank | 2092 comment | 2026 complexity | 8774dc1d2a94526d600a7cc2d818d021 MD5 | raw file
Possible License(s): JSON, LGPL-2.1, MIT, Apache-2.0, BSD-3-Clause
  1. /*
  2. Copyright 2010, Lawrence Philips
  3. All rights reserved.
  4. Redistribution and use in source and binary forms, with or without
  5. modification, are permitted provided that the following conditions are
  6. met:
  7. * Redistributions of source code must retain the above copyright
  8. notice, this list of conditions and the following disclaimer.
  9. * Redistributions in binary form must reproduce the above
  10. copyright notice, this list of conditions and the following disclaimer
  11. in the documentation and/or other materials provided with the
  12. distribution.
  13. * Neither the name of Google Inc. nor the names of its
  14. contributors may be used to endorse or promote products derived from
  15. this software without specific prior written permission.
  16. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  17. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  18. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  19. A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  20. OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  21. SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  22. LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23. DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24. THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  26. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. */
  28. /*
  29. * A request from the author: Please comment and sign any changes you make to
  30. * the Metaphone 3 reference implementation.
  31. * <br>
  32. * Please do NOT reformat this module to Google Refine's coding standard,
  33. * but instead keep the original format so that it can be more easily compare
  34. * to any modified fork of the original.
  35. */
  36. /**
  37. * Metaphone 3<br>
  38. * VERSION 2.1.3
  39. *
  40. * by Lawrence Philips<br>
  41. *
  42. * Metaphone 3 is designed to return an *approximate* phonetic key (and an alternate
  43. * approximate phonetic key when appropriate) that should be the same for English
  44. * words, and most names familiar in the United States, that are pronounced *similarly*.
  45. * The key value is *not* intended to be an *exact* phonetic, or even phonemic,
  46. * representation of the word. This is because a certain degree of 'fuzziness' has
  47. * proven to be useful in compensating for variations in pronunciation, as well as
  48. * misheard pronunciations. For example, although americans are not usually aware of it,
  49. * the letter 's' is normally pronounced 'z' at the end of words such as "sounds".<br><br>
  50. *
  51. * The 'approximate' aspect of the encoding is implemented according to the following rules:<br><br>
  52. *
  53. * (1) All vowels are encoded to the same value - 'A'. If the parameter encodeVowels
  54. * is set to false, only *initial* vowels will be encoded at all. If encodeVowels is set
  55. * to true, 'A' will be encoded at all places in the word that any vowels are normally
  56. * pronounced. 'W' as well as 'Y' are treated as vowels. Although there are differences in
  57. * the pronunciation of 'W' and 'Y' in different circumstances that lead to their being
  58. * classified as vowels under some circumstances and as consonants in others, for the purposes
  59. * of the 'fuzziness' component of the Soundex and Metaphone family of algorithms they will
  60. * be always be treated here as vowels.<br><br>
  61. *
  62. * (2) Voiced and un-voiced consonant pairs are mapped to the same encoded value. This
  63. * means that:<br>
  64. * 'D' and 'T' -> 'T'<br>
  65. * 'B' and 'P' -> 'P'<br>
  66. * 'G' and 'K' -> 'K'<br>
  67. * 'Z' and 'S' -> 'S'<br>
  68. * 'V' and 'F' -> 'F'<br><br>
  69. *
  70. * - In addition to the above voiced/unvoiced rules, 'CH' and 'SH' -> 'X', where 'X'
  71. * represents the "-SH-" and "-CH-" sounds in Metaphone 3 encoding.<br><br>
  72. *
  73. * - Also, the sound that is spelled as "TH" in English is encoded to '0' (zero symbol). (Although
  74. * Americans are not usually aware of it, "TH" is pronounced in a voiced (e.g. "that") as
  75. * well as an unvoiced (e.g. "theater") form, which are naturally mapped to the same encoding.)<br><br>
  76. *
  77. * The encodings in this version of Metaphone 3 are according to pronunciations common in the
  78. * United States. This means that they will be inaccurate for consonant pronunciations that
  79. * are different in the United Kingdom, for example "tube" -> "CHOOBE" -> XAP rather than american TAP.<br><br>
  80. *
  81. * Metaphone 3 was preceded by by Soundex, patented in 1919, and Metaphone and Double Metaphone,
  82. * developed by Lawrence Philips. All of these algorithms resulted in a significant number of
  83. * incorrect encodings. Metaphone3 was tested against a database of about 100 thousand English words,
  84. * names common in the United States, and non-English words found in publications in the United States,
  85. * with an emphasis on words that are commonly mispronounced, prepared by the Moby Words website,
  86. * but with the Moby Words 'phonetic' encodings algorithmically mapped to Double Metaphone encodings.
  87. * Metaphone3 increases the accuracy of encoding of english words, common names, and non-English
  88. * words found in american publications from the 89% for Double Metaphone, to over 98%.<br><br>
  89. *
  90. * DISCLAIMER:
  91. * Anthropomorphic Software LLC claims only that Metaphone 3 will return correct encodings,
  92. * within the 'fuzzy' definition of correct as above, for a very high percentage of correctly
  93. * spelled English and commonly recognized non-English words. Anthropomorphic Software LLC
  94. * warns the user that a number of words remain incorrectly encoded, that misspellings may not
  95. * be encoded 'properly', and that people often have differing ideas about the pronunciation
  96. * of a word. Therefore, Metaphone 3 is not guaranteed to return correct results every time, and
  97. * so a desired target word may very well be missed. Creators of commercial products should
  98. * keep in mind that systems like Metaphone 3 produce a 'best guess' result, and should
  99. * condition the expectations of end users accordingly.<br><br>
  100. *
  101. * METAPHONE3 IS PROVIDED "AS IS" WITHOUT
  102. * WARRANTY OF ANY KIND. LAWRENCE PHILIPS AND ANTHROPOMORPHIC SOFTWARE LLC
  103. * MAKE NO WARRANTIES, EXPRESS OR IMPLIED, THAT IT IS FREE OF ERROR,
  104. * OR ARE CONSISTENT WITH ANY PARTICULAR STANDARD OF MERCHANTABILITY,
  105. * OR THAT IT WILL MEET YOUR REQUIREMENTS FOR ANY PARTICULAR APPLICATION.
  106. * LAWRENCE PHILIPS AND ANTHROPOMORPHIC SOFTWARE LLC DISCLAIM ALL LIABILITY
  107. * FOR DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES RESULTING FROM USE
  108. * OF THIS SOFTWARE.
  109. *
  110. * @author Lawrence Philips
  111. *
  112. * Metaphone 3 is designed to return an <i>approximate</i> phonetic key (and an alternate
  113. * approximate phonetic key when appropriate) that should be the same for English
  114. * words, and most names familiar in the United States, that are pronounced "similarly".
  115. * The key value is <i>not</i> intended to be an exact phonetic, or even phonemic,
  116. * representation of the word. This is because a certain degree of 'fuzziness' has
  117. * proven to be useful in compensating for variations in pronunciation, as well as
  118. * misheard pronunciations. For example, although americans are not usually aware of it,
  119. * the letter 's' is normally pronounced 'z' at the end of words such as "sounds".<br><br>
  120. *
  121. * The 'approximate' aspect of the encoding is implemented according to the following rules:<br><br>
  122. *
  123. * (1) All vowels are encoded to the same value - 'A'. If the parameter encodeVowels
  124. * is set to false, only *initial* vowels will be encoded at all. If encodeVowels is set
  125. * to true, 'A' will be encoded at all places in the word that any vowels are normally
  126. * pronounced. 'W' as well as 'Y' are treated as vowels. Although there are differences in
  127. * the pronunciation of 'W' and 'Y' in different circumstances that lead to their being
  128. * classified as vowels under some circumstances and as consonants in others, for the purposes
  129. * of the 'fuzziness' component of the Soundex and Metaphone family of algorithms they will
  130. * be always be treated here as vowels.<br><br>
  131. *
  132. * (2) Voiced and un-voiced consonant pairs are mapped to the same encoded value. This
  133. * means that:<br>
  134. * 'D' and 'T' -> 'T'<br>
  135. * 'B' and 'P' -> 'P'<br>
  136. * 'G' and 'K' -> 'K'<br>
  137. * 'Z' and 'S' -> 'S'<br>
  138. * 'V' and 'F' -> 'F'<br><br>
  139. *
  140. * - In addition to the above voiced/unvoiced rules, 'CH' and 'SH' -> 'X', where 'X'
  141. * represents the "-SH-" and "-CH-" sounds in Metaphone 3 encoding.<br><br>
  142. *
  143. * - Also, the sound that is spelled as "TH" in English is encoded to '0' (zero symbol). (Although
  144. * americans are not usually aware of it, "TH" is pronounced in a voiced (e.g. "that") as
  145. * well as an unvoiced (e.g. "theater") form, which are naturally mapped to the same encoding.)<br><br>
  146. *
  147. * In the "Exact" encoding, voiced/unvoiced pairs are <i>not</i> mapped to the same encoding, except
  148. * for the voiced and unvoiced versions of 'TH', sounds such as 'CH' and 'SH', and for 'S' and 'Z',
  149. * so that the words whose metaph keys match will in fact be closer in pronunciation that with the
  150. * more approximate setting. Keep in mind that encoding settings for search strings should always
  151. * be exactly the same as the encoding settings of the stored metaph keys in your database!
  152. * Because of the considerably increased accuracy of Metaphone3, it is now possible to use this
  153. * setting and have a very good chance of getting a correct encoding.
  154. * <br><br>
  155. * In the Encode Vowels encoding, all non-initial vowels and diphthongs will be encoded to
  156. * 'A', and there will only be one such vowel encoding character between any two consonants.
  157. * It turns out that there are some surprising wrinkles to encoding non-initial vowels in
  158. * practice, pre-eminently in inversions between spelling and pronunciation such as e.g.
  159. * "wrinkle" => 'RANKAL', where the last two sounds are inverted when spelled.
  160. * <br><br>
  161. * The encodings in this version of Metaphone 3 are according to pronunciations common in the
  162. * United States. This means that they will be inaccurate for consonant pronunciations that
  163. * are different in the United Kingdom, for example "tube" -> "CHOOBE" -> XAP rather than american TAP.
  164. * <br><br>
  165. *
  166. */
  167. package com.google.refine.clustering.binning;
  168. public class Metaphone3 {
  169. /** Length of word sent in to be encoded, as
  170. * measured at beginning of encoding. */
  171. int m_length;
  172. /** Length of encoded key string. */
  173. int m_metaphLength;
  174. /** Flag whether or not to encode non-initial vowels. */
  175. boolean m_encodeVowels;
  176. /** Flag whether or not to encode consonants as exactly
  177. * as possible. */
  178. boolean m_encodeExact;
  179. /** Internal copy of word to be encoded, allocated separately
  180. * from string pointed to in incoming parameter. */
  181. String m_inWord;
  182. /** Running copy of primary key. */
  183. StringBuffer m_primary;
  184. /** Running copy of secondary key. */
  185. StringBuffer m_secondary;
  186. /** Index of character in m_inWord currently being
  187. * encoded. */
  188. int m_current;
  189. /** Index of last character in m_inWord. */
  190. int m_last;
  191. /** Flag that an AL inversion has already been done. */
  192. boolean flag_AL_inversion;
  193. /** Default size of key storage allocation */
  194. int MAX_KEY_ALLOCATION = 32;
  195. /** Default maximum length of encoded key. */
  196. int DEFAULT_MAX_KEY_LENGTH = 8;
  197. ////////////////////////////////////////////////////////////////////////////////
  198. // Metaphone3 class definition
  199. ////////////////////////////////////////////////////////////////////////////////
  200. /**
  201. * Constructor, default. This constructor is most convenient when
  202. * encoding more than one word at a time. New words to encode can
  203. * be set using SetWord(char *).
  204. *
  205. */
  206. Metaphone3()
  207. {
  208. m_primary = new StringBuffer();
  209. m_secondary = new StringBuffer();
  210. m_metaphLength = DEFAULT_MAX_KEY_LENGTH;
  211. m_encodeVowels = false;
  212. m_encodeExact = false;
  213. }
  214. /**
  215. * Constructor, parameterized. The Metaphone3 object will
  216. * be initialized with the incoming string, and can be called
  217. * on to encode this string. This constructor is most convenient
  218. * when only one word needs to be encoded.
  219. *
  220. * @param in pointer to char string of word to be encoded.
  221. *
  222. */
  223. Metaphone3(String in)
  224. {
  225. this();
  226. SetWord(in);
  227. }
  228. /**
  229. * Sets word to be encoded.
  230. *
  231. * @param in pointer to EXTERNALLY ALLOCATED char string of
  232. * the word to be encoded.
  233. *
  234. */
  235. void SetWord(String in)
  236. {
  237. m_inWord = in.toUpperCase();;
  238. m_length = m_inWord.length();
  239. }
  240. /**
  241. * Sets length allocated for output keys.
  242. * If incoming number is greater than maximum allowable
  243. * length returned by GetMaximumKeyLength(), set key length
  244. * to maximum key length and return false; otherwise, set key
  245. * length to parameter value and return true.
  246. *
  247. * @param inKeyLength new length of key.
  248. * @return true if able to set key length to requested value.
  249. *
  250. */
  251. boolean SetKeyLength(int inKeyLength)
  252. {
  253. if(inKeyLength < 1)
  254. {
  255. // can't have that -
  256. // no room for terminating null
  257. inKeyLength = 1;
  258. }
  259. if(inKeyLength > MAX_KEY_ALLOCATION)
  260. {
  261. m_metaphLength = MAX_KEY_ALLOCATION;
  262. return false;
  263. }
  264. m_metaphLength = inKeyLength;
  265. return true;
  266. }
  267. /**
  268. * Adds an encoding character to the encoded key value string - one parameter version.
  269. *
  270. * @param main primary encoding character to be added to encoded key string.
  271. */
  272. void MetaphAdd(String in)
  273. {
  274. if(!(in.equals("A")
  275. && (m_primary.length() > 0)
  276. && (m_primary.charAt(m_primary.length() - 1) == 'A')))
  277. {
  278. m_primary.append(in);
  279. }
  280. if(!(in.equals("A")
  281. && (m_secondary.length() > 0)
  282. && (m_secondary.charAt(m_secondary.length() - 1) == 'A')))
  283. {
  284. m_secondary.append(in);
  285. }
  286. }
  287. /**
  288. * Adds an encoding character to the encoded key value string - two parameter version
  289. *
  290. * @param main primary encoding character to be added to encoded key string
  291. * @param alt alternative encoding character to be added to encoded alternative key string
  292. *
  293. */
  294. void MetaphAdd(String main, String alt)
  295. {
  296. if(!(main.equals("A")
  297. && (m_primary.length() > 0)
  298. && (m_primary.charAt(m_primary.length() - 1) == 'A')))
  299. {
  300. m_primary.append(main);
  301. }
  302. if(!(alt.equals("A")
  303. && (m_secondary.length() > 0)
  304. && (m_secondary.charAt(m_secondary.length() - 1) == 'A')))
  305. {
  306. if(!alt.isEmpty())
  307. {
  308. m_secondary.append(alt);
  309. }
  310. }
  311. }
  312. /**
  313. * Adds an encoding character to the encoded key value string - Exact/Approx version
  314. *
  315. * @param mainExact primary encoding character to be added to encoded key string if
  316. * m_encodeExact is set
  317. *
  318. * @param altExact alternative encoding character to be added to encoded alternative
  319. * key string if m_encodeExact is set
  320. *
  321. * @param main primary encoding character to be added to encoded key string
  322. *
  323. * @param alt alternative encoding character to be added to encoded alternative key string
  324. *
  325. */
  326. void MetaphAddExactApprox(String mainExact, String altExact, String main, String alt)
  327. {
  328. if(m_encodeExact)
  329. {
  330. MetaphAdd(mainExact, altExact);
  331. }
  332. else
  333. {
  334. MetaphAdd(main, alt);
  335. }
  336. }
  337. /**
  338. * Adds an encoding character to the encoded key value string - Exact/Approx version
  339. *
  340. * @param mainExact primary encoding character to be added to encoded key string if
  341. * m_encodeExact is set
  342. *
  343. * @param main primary encoding character to be added to encoded key string
  344. *
  345. */
  346. void MetaphAddExactApprox(String mainExact, String main)
  347. {
  348. if(m_encodeExact)
  349. {
  350. MetaphAdd(mainExact);
  351. }
  352. else
  353. {
  354. MetaphAdd(main);
  355. }
  356. }
  357. /** Retrieves maximum number of characters currently allocated for encoded key.
  358. *
  359. * @return short integer representing the length allowed for the key.
  360. */
  361. int GetKeyLength(){return m_metaphLength;}
  362. /** Retrieves maximum number of characters allowed for encoded key.
  363. *
  364. * @return short integer representing the length of allocated storage for the key.
  365. */
  366. int GetMaximumKeyLength(){return (int)MAX_KEY_ALLOCATION;}
  367. /** Sets flag that causes Metaphone3 to encode non-initial vowels. However, even
  368. * if there are more than one vowel sound in a vowel sequence (i.e.
  369. * vowel diphthong, etc.), only one 'A' will be encoded before the next consonant or the
  370. * end of the word.
  371. *
  372. * @param inEncodeVowels Non-initial vowels encoded if true, not if false.
  373. */
  374. void SetEncodeVowels(boolean inEncodeVowels){m_encodeVowels = inEncodeVowels;}
  375. /** Retrieves setting determining whether or not non-initial vowels will be encoded.
  376. *
  377. * @return true if the Metaphone3 object has been set to encode non-initial vowels, false if not.
  378. */
  379. boolean GetEncodeVowels(){return m_encodeVowels;}
  380. /** Sets flag that causes Metaphone3 to encode consonants as exactly as possible.
  381. * This does not include 'S' vs. 'Z', since americans will pronounce 'S' at the
  382. * at the end of many words as 'Z', nor does it include "CH" vs. "SH". It does cause
  383. * a distinction to be made between 'B' and 'P', 'D' and 'T', 'G' and 'K', and 'V'
  384. * and 'F'.
  385. *
  386. * @param inEncodeExact consonants to be encoded "exactly" if true, not if false.
  387. */
  388. void SetEncodeExact(boolean inEncodeExact){m_encodeExact = inEncodeExact;}
  389. /** Retrieves setting determining whether or not consonants will be encoded "exactly".
  390. *
  391. * @return true if the Metaphone3 object has been set to encode "exactly", false if not.
  392. */
  393. boolean GetEncodeExact(){return m_encodeExact;}
  394. /** Retrieves primary encoded key.
  395. *
  396. * @return a character pointer to the primary encoded key
  397. */
  398. String GetMetaph()
  399. {
  400. String primary = new String(m_primary);
  401. return primary;
  402. }
  403. /** Retrieves alternate encoded key, if any.
  404. *
  405. * @return a character pointer to the alternate encoded key
  406. */
  407. String GetAlternateMetaph()
  408. {
  409. String secondary = new String(m_secondary);
  410. return secondary;
  411. }
  412. /**
  413. * Test for close front vowels
  414. *
  415. * @return true if close front vowel
  416. */
  417. boolean Front_Vowel(int at)
  418. {
  419. if(((CharAt(at) == 'E') || (CharAt(at) == 'I') || (CharAt(at) == 'Y')))
  420. {
  421. return true;
  422. }
  423. return false;
  424. }
  425. /**
  426. * Detect names or words that begin with spellings
  427. * typical of german or slavic words, for the purpose
  428. * of choosing alternate pronunciations correctly
  429. *
  430. */
  431. boolean SlavoGermanic()
  432. {
  433. if(StringAt(0, 3, "SCH", "")
  434. || StringAt(0, 2, "SW", "")
  435. || (CharAt(0) == 'J')
  436. || (CharAt(0) == 'W'))
  437. {
  438. return true;
  439. }
  440. return false;
  441. }
  442. /**
  443. * Tests if character is a vowel
  444. *
  445. * @param inChar character to be tested in string to be encoded
  446. * @return true if character is a vowel, false if not
  447. *
  448. */
  449. boolean IsVowel(char inChar)
  450. {
  451. if((inChar == 'A')
  452. || (inChar == 'E')
  453. || (inChar == 'I')
  454. || (inChar == 'O')
  455. || (inChar == 'U')
  456. || (inChar == 'Y')
  457. || (inChar == 'Ŕ')
  458. || (inChar == 'Á')
  459. || (inChar == 'Â')
  460. || (inChar == 'Ă')
  461. || (inChar == 'Ä')
  462. || (inChar == 'Ĺ')
  463. || (inChar == 'Ć')
  464. || (inChar == 'Č')
  465. || (inChar == 'É')
  466. || (inChar == 'Ę')
  467. || (inChar == 'Ë')
  468. || (inChar == 'Ě')
  469. || (inChar == 'Í')
  470. || (inChar == 'Î')
  471. || (inChar == 'Ď')
  472. || (inChar == 'Ň')
  473. || (inChar == 'Ó')
  474. || (inChar == 'Ô')
  475. || (inChar == 'Ő')
  476. || (inChar == 'Ö')
  477. || (inChar == '?')
  478. || (inChar == 'Ř')
  479. || (inChar == 'Ů')
  480. || (inChar == 'Ú')
  481. || (inChar == 'Ű')
  482. || (inChar == 'Ü')
  483. || (inChar == 'Ý')
  484. || (inChar == '?'))
  485. {
  486. return true;
  487. }
  488. return false;
  489. }
  490. /**
  491. * Tests if character in the input string is a vowel
  492. *
  493. * @param at position of character to be tested in string to be encoded
  494. * @return true if character is a vowel, false if not
  495. *
  496. */
  497. boolean IsVowel(int at)
  498. {
  499. if((at < 0) || (at >= m_length))
  500. {
  501. return false;
  502. }
  503. char it = CharAt(at);
  504. if(IsVowel(it))
  505. {
  506. return true;
  507. }
  508. return false;
  509. }
  510. /**
  511. * Skips over vowels in a string. Has exceptions for skipping consonants that
  512. * will not be encoded.
  513. *
  514. * @param at position, in string to be encoded, of character to start skipping from
  515. *
  516. * @return position of next consonant in string to be encoded
  517. */
  518. int SkipVowels(int at)
  519. {
  520. if(at < 0)
  521. {
  522. return 0;
  523. }
  524. if(at >= m_length)
  525. {
  526. return m_length;
  527. }
  528. char it = CharAt(at);
  529. while(IsVowel(it) || (it == 'W'))
  530. {
  531. if(StringAt(at, 4, "WICZ", "WITZ", "WIAK", "")
  532. || StringAt((at - 1), 5, "EWSKI", "EWSKY", "OWSKI", "OWSKY", "")
  533. || (StringAt(at, 5, "WICKI", "WACKI", "") && ((at + 4) == m_last)))
  534. {
  535. break;
  536. }
  537. at++;
  538. if(((CharAt(at - 1) == 'W') && (CharAt(at) == 'H'))
  539. && !(StringAt(at, 3, "HOP", "")
  540. || StringAt(at, 4, "HIDE", "HARD", "HEAD", "HAWK", "HERD", "HOOK", "HAND", "HOLE", "")
  541. || StringAt(at, 5, "HEART", "HOUSE", "HOUND", "")
  542. || StringAt(at, 6, "HAMMER", "")))
  543. {
  544. at++;
  545. }
  546. if(at > (m_length - 1))
  547. {
  548. break;
  549. }
  550. it = CharAt(at);
  551. }
  552. return at;
  553. }
  554. /**
  555. * Advanced counter m_current so that it indexes the next character to be encoded
  556. *
  557. * @param ifNotEncodeVowels number of characters to advance if not encoding internal vowels
  558. * @param ifEncodeVowels number of characters to advance if encoding internal vowels
  559. *
  560. */
  561. void AdvanceCounter(int ifNotEncodeVowels, int ifEncodeVowels)
  562. {
  563. if(!m_encodeVowels)
  564. {
  565. m_current += ifNotEncodeVowels;
  566. }
  567. else
  568. {
  569. m_current += ifEncodeVowels;
  570. }
  571. }
  572. /**
  573. * Subscript safe .charAt()
  574. *
  575. * @param at index of character to access
  576. * @return null if index out of bounds, .charAt() otherwise
  577. */
  578. char CharAt(int at)
  579. {
  580. // check substring bounds
  581. if((at < 0)
  582. || (at > (m_length - 1)))
  583. {
  584. return '\0';
  585. }
  586. return m_inWord.charAt(at);
  587. }
  588. /**
  589. * Tests whether the word is the root or a regular english inflection
  590. * of it, e.g. "ache", "achy", "aches", "ached", "aching", "achingly"
  591. * This is for cases where we want to match only the root and corresponding
  592. * inflected forms, and not completely different words which may have the
  593. * same substring in them.
  594. */
  595. boolean RootOrInflections(String inWord, String root)
  596. {
  597. int len = root.length();
  598. String test;
  599. test = root + "S";
  600. if((inWord.equals(root))
  601. || (inWord.equals(test)))
  602. {
  603. return true;
  604. }
  605. if(root.charAt(len - 1) != 'E')
  606. {
  607. test = root + "ES";
  608. }
  609. if(inWord.equals(test))
  610. {
  611. return true;
  612. }
  613. if(root.charAt(len - 1) != 'E')
  614. {
  615. test = root + "ED";
  616. }
  617. else
  618. {
  619. test = root + "D";
  620. }
  621. if(inWord.equals(test))
  622. {
  623. return true;
  624. }
  625. if(root.charAt(len - 1) == 'E')
  626. {
  627. root = root.substring(0, len - 1);
  628. }
  629. test = root + "ING";
  630. if(inWord.equals(test))
  631. {
  632. return true;
  633. }
  634. test = root + "INGLY";
  635. if(inWord.equals(test))
  636. {
  637. return true;
  638. }
  639. test = root + "Y";
  640. if(inWord.equals(test))
  641. {
  642. return true;
  643. }
  644. return false;
  645. }
  646. /**
  647. * Determines if one of the substrings sent in is the same as
  648. * what is at the specified position in the string being encoded.
  649. *
  650. * @param start
  651. * @param length
  652. * @param compareStrings
  653. * @return
  654. */
  655. boolean StringAt(int start, int length, String... compareStrings)
  656. {
  657. // check substring bounds
  658. if((start < 0)
  659. || (start > (m_length - 1))
  660. || ((start + length - 1) > (m_length - 1)))
  661. {
  662. return false;
  663. }
  664. String target = m_inWord.substring(start, (start + length));
  665. for(String strFragment : compareStrings)
  666. {
  667. if(target.equals(strFragment))
  668. {
  669. return true;
  670. }
  671. }
  672. return false;
  673. }
  674. /**
  675. * Encodes input string to one or two key values according to Metaphone 3 rules.
  676. *
  677. */
  678. void Encode()
  679. {
  680. flag_AL_inversion = false;
  681. m_current = 0;
  682. m_primary.setLength(0);
  683. m_secondary.setLength(0);
  684. if(m_length < 1)
  685. {
  686. return;
  687. }
  688. //zero based index
  689. m_last = m_length - 1;
  690. ///////////main loop//////////////////////////
  691. while(!(m_primary.length() > m_metaphLength) && !(m_secondary.length() > m_metaphLength))
  692. {
  693. if(m_current >= m_length)
  694. {
  695. break;
  696. }
  697. switch(CharAt(m_current))
  698. {
  699. case 'B':
  700. Encode_B();
  701. break;
  702. case 'ß':
  703. case 'Ç':
  704. MetaphAdd("S");
  705. m_current++;
  706. break;
  707. case 'C':
  708. Encode_C();
  709. break;
  710. case 'D':
  711. Encode_D();
  712. break;
  713. case 'F':
  714. Encode_F();
  715. break;
  716. case 'G':
  717. Encode_G();
  718. break;
  719. case 'H':
  720. Encode_H();
  721. break;
  722. case 'J':
  723. Encode_J();
  724. break;
  725. case 'K':
  726. Encode_K();
  727. break;
  728. case 'L':
  729. Encode_L();
  730. break;
  731. case 'M':
  732. Encode_M();
  733. break;
  734. case 'N':
  735. Encode_N();
  736. break;
  737. case 'Ń':
  738. MetaphAdd("N");
  739. m_current++;
  740. break;
  741. case 'P':
  742. Encode_P();
  743. break;
  744. case 'Q':
  745. Encode_Q();
  746. break;
  747. case 'R':
  748. Encode_R();
  749. break;
  750. case 'S':
  751. Encode_S();
  752. break;
  753. case 'T':
  754. Encode_T();
  755. break;
  756. case 'Đ': // eth
  757. case 'Ţ': // thorn
  758. MetaphAdd("0");
  759. m_current++;
  760. break;
  761. case 'V':
  762. Encode_V();
  763. break;
  764. case 'W':
  765. Encode_W();
  766. break;
  767. case 'X':
  768. Encode_X();
  769. break;
  770. case '?':
  771. MetaphAdd("X");
  772. m_current++;
  773. break;
  774. case '?':
  775. MetaphAdd("S");
  776. m_current++;
  777. break;
  778. case 'Z':
  779. Encode_Z();
  780. break;
  781. default:
  782. if(IsVowel(CharAt(m_current)))
  783. {
  784. Encode_Vowels();
  785. break;
  786. }
  787. m_current++;
  788. }
  789. }
  790. //only give back m_metaphLength number of chars in m_metaph
  791. if(m_primary.length() > m_metaphLength)
  792. {
  793. m_primary.setLength(m_metaphLength);
  794. }
  795. if(m_secondary.length() > m_metaphLength)
  796. {
  797. m_secondary.setLength(m_metaphLength);
  798. }
  799. // it is possible for the two metaphs to be the same
  800. // after truncation. lose the second one if so
  801. if((m_primary.toString()).equals(m_secondary.toString()))
  802. {
  803. m_secondary.setLength(0);
  804. }
  805. }
  806. /**
  807. * Encodes all initial vowels to A.
  808. *
  809. * Encodes non-initial vowels to A if m_encodeVowels is true
  810. *
  811. *
  812. */
  813. void Encode_Vowels()
  814. {
  815. if(m_current == 0)
  816. {
  817. // all init vowels map to 'A'
  818. // as of Double Metaphone
  819. MetaphAdd("A");
  820. }
  821. else if(m_encodeVowels)
  822. {
  823. if(CharAt(m_current) != 'E')
  824. {
  825. if(Skip_Silent_UE())
  826. {
  827. return;
  828. }
  829. if (O_Silent())
  830. {
  831. m_current++;
  832. return;
  833. }
  834. // encode all vowels and
  835. // diphthongs to the same value
  836. MetaphAdd("A");
  837. }
  838. else
  839. {
  840. Encode_E_Pronounced();
  841. }
  842. }
  843. if(!(!IsVowel(m_current - 2) && StringAt((m_current - 1), 4, "LEWA", "LEWO", "LEWI", "")))
  844. {
  845. m_current = SkipVowels(m_current);
  846. }
  847. else
  848. {
  849. m_current++;
  850. }
  851. }
  852. /**
  853. * Encodes cases where non-initial 'e' is pronounced, taking
  854. * care to detect unusual cases from the greek.
  855. *
  856. * Only executed if non initial vowel encoding is turned on
  857. *
  858. *
  859. */
  860. void Encode_E_Pronounced()
  861. {
  862. // special cases with two pronunciations
  863. // 'agape' 'lame' 'resume'
  864. if((StringAt(0, 4, "LAME", "SAKE", "PATE", "") && (m_length == 4))
  865. || (StringAt(0, 5, "AGAPE", "") && (m_length == 5))
  866. || ((m_current == 5) && StringAt(0, 6, "RESUME", "")))
  867. {
  868. MetaphAdd("", "A");
  869. return;
  870. }
  871. // special case "inge" => 'INGA', 'INJ'
  872. if(StringAt(0, 4, "INGE", "")
  873. && (m_length == 4))
  874. {
  875. MetaphAdd("A", "");
  876. return;
  877. }
  878. // special cases with two pronunciations
  879. // special handling due to the difference in
  880. // the pronunciation of the '-D'
  881. if((m_current == 5) && StringAt(0, 7, "BLESSED", "LEARNED", ""))
  882. {
  883. MetaphAddExactApprox("D", "AD", "T", "AT");
  884. m_current += 2;
  885. return;
  886. }
  887. // encode all vowels and diphthongs to the same value
  888. if((!E_Silent()
  889. && !flag_AL_inversion
  890. && !Silent_Internal_E())
  891. || E_Pronounced_Exceptions())
  892. {
  893. MetaphAdd("A");
  894. }
  895. // now that we've visited the vowel in question
  896. flag_AL_inversion = false;
  897. }
  898. /**
  899. * Tests for cases where non-initial 'o' is not pronounced
  900. * Only executed if non initial vowel encoding is turned on
  901. *
  902. * @return true if encoded as silent - no addition to m_metaph key
  903. *
  904. */
  905. boolean O_Silent()
  906. {
  907. // if "iron" at beginning or end of word and not "irony"
  908. if ((CharAt(m_current) == 'O')
  909. && StringAt((m_current - 2), 4, "IRON", ""))
  910. {
  911. if ((StringAt(0, 4, "IRON", "")
  912. || (StringAt((m_current - 2), 4, "IRON", "")
  913. && (m_last == (m_current + 1))))
  914. && !StringAt((m_current - 2), 6, "IRONIC", ""))
  915. {
  916. return true;
  917. }
  918. }
  919. return false;
  920. }
  921. /**
  922. * Tests and encodes cases where non-initial 'e' is never pronounced
  923. * Only executed if non initial vowel encoding is turned on
  924. *
  925. * @return true if encoded as silent - no addition to m_metaph key
  926. *
  927. */
  928. boolean E_Silent()
  929. {
  930. if(E_Pronounced_At_End())
  931. {
  932. return false;
  933. }
  934. // 'e' silent when last letter, altho
  935. if((m_current == m_last)
  936. // also silent if before plural 's'
  937. // or past tense or participle 'd', e.g.
  938. // 'grapes' and 'banished' => PNXT
  939. || ((StringAt(m_last, 1, "S", "D", "")
  940. && (m_current > 1)
  941. && ((m_current + 1) == m_last)
  942. // and not e.g. "nested", "rises", or "pieces" => RASAS
  943. && !(StringAt((m_current - 1), 3, "TED", "SES", "CES", "")
  944. || StringAt(0, 9, "ANTIPODES", "ANOPHELES", "")
  945. || StringAt(0, 8, "MOHAMMED", "MUHAMMED", "MOUHAMED", "")
  946. || StringAt(0, 7, "MOHAMED", "")
  947. || StringAt(0, 6, "NORRED", "MEDVED", "MERCED", "ALLRED", "KHALED", "RASHED", "MASJED", "")
  948. || StringAt(0, 5, "JARED", "AHMED", "HAMED", "JAVED", "")
  949. || StringAt(0, 4, "ABED", "IMED", ""))))
  950. // e.g. 'wholeness', 'boneless', 'barely'
  951. || (StringAt((m_current + 1), 4, "NESS", "LESS", "") && ((m_current + 4) == m_last))
  952. || (StringAt((m_current + 1), 2, "LY", "") && ((m_current + 2) == m_last)
  953. && !StringAt(0, 6, "CICELY", "")))
  954. {
  955. return true;
  956. }
  957. return false;
  958. }
  959. /**
  960. * Tests for words where an 'E' at the end of the word
  961. * is pronounced
  962. *
  963. * special cases, mostly from the greek, spanish, japanese,
  964. * italian, and french words normally having an acute accent.
  965. * also, pronouns and articles
  966. *
  967. * Many Thanks to ali, QuentinCompson, JeffCO, ToonScribe, Xan,
  968. * Trafalz, and VictorLaszlo, all of them atriots from the Eschaton,
  969. * for all their fine contributions!
  970. *
  971. * @return true if 'E' at end is pronounced
  972. *
  973. */
  974. boolean E_Pronounced_At_End()
  975. {
  976. if((m_current == m_last)
  977. && (StringAt((m_current - 6), 7, "STROPHE", "")
  978. // if a vowel is before the 'E', vowel eater will have eaten it.
  979. //otherwise, consonant + 'E' will need 'E' pronounced
  980. || (m_length == 2)
  981. || ((m_length == 3) && !IsVowel(0))
  982. // these german name endings can be relied on to have the 'e' pronounced
  983. || (StringAt((m_last - 2), 3, "BKE", "DKE", "FKE", "KKE", "LKE",
  984. "NKE", "MKE", "PKE", "TKE", "VKE", "ZKE", "")
  985. && !StringAt(0, 5, "FINKE", "FUNKE", "")
  986. && !StringAt(0, 6, "FRANKE", ""))
  987. || StringAt((m_last - 4), 5, "SCHKE", "")
  988. || (StringAt(0, 4, "ACME", "NIKE", "CAFE", "RENE", "LUPE", "JOSE", "ESME", "") && (m_length == 4))
  989. || (StringAt(0, 5, "LETHE", "CADRE", "TILDE", "SIGNE", "POSSE", "LATTE", "ANIME", "DOLCE", "CROCE",
  990. "ADOBE", "OUTRE", "JESSE", "JAIME", "JAFFE", "BENGE", "RUNGE",
  991. "CHILE", "DESME", "CONDE", "URIBE", "LIBRE", "ANDRE", "") && (m_length == 5))
  992. || (StringAt(0, 6, "HECATE", "PSYCHE", "DAPHNE", "PENSKE", "CLICHE", "RECIPE",
  993. "TAMALE", "SESAME", "SIMILE", "FINALE", "KARATE", "RENATE", "SHANTE",
  994. "OBERLE", "COYOTE", "KRESGE", "STONGE", "STANGE", "SWAYZE", "FUENTE",
  995. "SALOME", "URRIBE", "") && (m_length == 6))
  996. || (StringAt(0, 7, "ECHIDNE", "ARIADNE", "MEINEKE", "PORSCHE", "ANEMONE", "EPITOME",
  997. "SYNCOPE", "SOUFFLE", "ATTACHE", "MACHETE", "KARAOKE", "BUKKAKE",
  998. "VICENTE", "ELLERBE", "VERSACE", "") && (m_length == 7))
  999. || (StringAt(0, 8, "PENELOPE", "CALLIOPE", "CHIPOTLE", "ANTIGONE", "KAMIKAZE", "EURIDICE",
  1000. "YOSEMITE", "FERRANTE", "") && (m_length == 8))
  1001. || (StringAt(0, 9, "HYPERBOLE", "GUACAMOLE", "XANTHIPPE", "") && (m_length == 9))
  1002. || (StringAt(0, 10, "SYNECDOCHE", "") && (m_length == 10))))
  1003. {
  1004. return true;
  1005. }
  1006. return false;
  1007. }
  1008. /**
  1009. * Detect internal silent 'E's e.g. "roseman",
  1010. * "firestone"
  1011. *
  1012. */
  1013. boolean Silent_Internal_E()
  1014. {
  1015. // 'olesen' but not 'olen' RAKE BLAKE
  1016. if((StringAt(0, 3, "OLE", "")
  1017. && E_Silent_Suffix(3) && !E_Pronouncing_Suffix(3))
  1018. || (StringAt(0, 4, "BARE", "FIRE", "FORE", "GATE", "HAGE", "HAVE",
  1019. "HAZE", "HOLE", "CAPE", "HUSE", "LACE", "LINE",
  1020. "LIVE", "LOVE", "MORE", "MOSE", "MORE", "NICE",
  1021. "RAKE", "ROBE", "ROSE", "SISE", "SIZE", "WARE",
  1022. "WAKE", "WISE", "WINE", "")
  1023. && E_Silent_Suffix(4) && !E_Pronouncing_Suffix(4))
  1024. || (StringAt(0, 5, "BLAKE", "BRAKE", "BRINE", "CARLE", "CLEVE", "DUNNE",
  1025. "HEDGE", "HOUSE", "JEFFE", "LUNCE", "STOKE", "STONE",
  1026. "THORE", "WEDGE", "WHITE", "")
  1027. && E_Silent_Suffix(5) && !E_Pronouncing_Suffix(5))
  1028. || (StringAt(0, 6, "BRIDGE", "CHEESE", "")
  1029. && E_Silent_Suffix(6) && !E_Pronouncing_Suffix(6))
  1030. || StringAt((m_current - 5), 7, "CHARLES", ""))
  1031. {
  1032. return true;
  1033. }
  1034. return false;
  1035. }
  1036. /**
  1037. * Detect conditions required
  1038. * for the 'E' not to be pronounced
  1039. *
  1040. */
  1041. boolean E_Silent_Suffix(int at)
  1042. {
  1043. if((m_current == (at - 1))
  1044. && (m_length > (at + 1))
  1045. && (IsVowel((at + 1))
  1046. || (StringAt(at, 2, "ST", "SL", "")
  1047. && (m_length > (at + 2)))))
  1048. {
  1049. return true;
  1050. }
  1051. return false;
  1052. }
  1053. /**
  1054. * Detect endings that will
  1055. * cause the 'e' to be pronounced
  1056. *
  1057. */
  1058. boolean E_Pronouncing_Suffix(int at)
  1059. {
  1060. // e.g. 'bridgewood' - the other vowels will get eaten
  1061. // up so we need to put one in here
  1062. if((m_length == (at + 4)) && StringAt(at, 4, "WOOD", ""))
  1063. {
  1064. return true;
  1065. }
  1066. // same as above
  1067. if((m_length == (at + 5)) && StringAt(at, 5, "WATER", "WORTH", ""))
  1068. {
  1069. return true;
  1070. }
  1071. // e.g. 'bridgette'
  1072. if((m_length == (at + 3)) && StringAt(at, 3, "TTE", "LIA", "NOW", "ROS", "RAS", ""))
  1073. {
  1074. return true;
  1075. }
  1076. // e.g. 'olena'
  1077. if((m_length == (at + 2)) && StringAt(at, 2, "TA", "TT", "NA", "NO", "NE",
  1078. "RS", "RE", "LA", "AU", "RO", "RA", ""))
  1079. {
  1080. return true;
  1081. }
  1082. // e.g. 'bridget'
  1083. if((m_length == (at + 1)) && StringAt(at, 1, "T", "R", ""))
  1084. {
  1085. return true;
  1086. }
  1087. return false;
  1088. }
  1089. /**
  1090. * Exceptions where 'E' is pronounced where it
  1091. * usually wouldn't be, and also some cases
  1092. * where 'LE' transposition rules don't apply
  1093. * and the vowel needs to be encoded here
  1094. *
  1095. * @return true if 'E' pronounced
  1096. *
  1097. */
  1098. boolean E_Pronounced_Exceptions()
  1099. {
  1100. // greek names e.g. "herakles" or hispanic names e.g. "robles", where 'e' is pronounced, other exceptions
  1101. if((((m_current + 1) == m_last)
  1102. && (StringAt((m_current - 3), 5, "OCLES", "ACLES", "AKLES", "")
  1103. || StringAt(0, 4, "INES", "")
  1104. || StringAt(0, 5, "LOPES", "ESTES", "GOMES", "NUNES", "ALVES", "ICKES",
  1105. "INNES", "PERES", "WAGES", "NEVES", "BENES", "DONES", "")
  1106. || StringAt(0, 6, "CORTES", "CHAVES", "VALDES", "ROBLES", "TORRES", "FLORES", "BORGES",
  1107. "NIEVES", "MONTES", "SOARES", "VALLES", "GEDDES", "ANDRES", "VIAJES",
  1108. "CALLES", "FONTES", "HERMES", "ACEVES", "BATRES", "MATHES", "")
  1109. || StringAt(0, 7, "DELORES", "MORALES", "DOLORES", "ANGELES", "ROSALES", "MIRELES", "LINARES",
  1110. "PERALES", "PAREDES", "BRIONES", "SANCHES", "CAZARES", "REVELES", "ESTEVES",
  1111. "ALVARES", "MATTHES", "SOLARES", "CASARES", "CACERES", "STURGES", "RAMIRES",
  1112. "FUNCHES", "BENITES", "FUENTES", "PUENTES", "TABARES", "HENTGES", "VALORES", "")
  1113. || StringAt(0, 8, "GONZALES", "MERCEDES", "FAGUNDES", "JOHANNES", "GONSALES", "BERMUDES",
  1114. "CESPEDES", "BETANCES", "TERRONES", "DIOGENES", "CORRALES", "CABRALES",
  1115. "MARTINES", "GRAJALES", "")
  1116. || StringAt(0, 9, "CERVANTES", "FERNANDES", "GONCALVES", "BENEVIDES", "CIFUENTES", "SIFUENTES",
  1117. "SERVANTES", "HERNANDES", "BENAVIDES", "")
  1118. || StringAt(0, 10, "ARCHIMEDES", "CARRIZALES", "MAGALLANES", "")))
  1119. || StringAt(m_current - 2, 4, "FRED", "DGES", "DRED", "GNES", "")
  1120. || StringAt((m_current - 5), 7, "PROBLEM", "RESPLEN", "")
  1121. || StringAt((m_current - 4), 6, "REPLEN", "")
  1122. || StringAt((m_current - 3), 4, "SPLE", ""))
  1123. {
  1124. return true;
  1125. }
  1126. return false;
  1127. }
  1128. /**
  1129. * Encodes "-UE".
  1130. *
  1131. * @return true if encoding handled in this routine, false if not
  1132. */
  1133. boolean Skip_Silent_UE()
  1134. {
  1135. // always silent except for cases listed below
  1136. if((StringAt((m_current - 1), 3, "QUE", "GUE", "")
  1137. && !StringAt(0, 8, "BARBEQUE", "PALENQUE", "APPLIQUE", "")
  1138. // '-que' cases usually french but missing the acute accent
  1139. && !StringAt(0, 6, "RISQUE", "")
  1140. && !StringAt((m_current - 3), 5, "ARGUE", "SEGUE", "")
  1141. && !StringAt(0, 7, "PIROGUE", "ENRIQUE", "")
  1142. && !StringAt(0, 10, "COMMUNIQUE", ""))
  1143. && (m_current > 1)
  1144. && (((m_current + 1) == m_last)
  1145. || StringAt(0, 7, "JACQUES", "")))
  1146. {
  1147. m_current = SkipVowels(m_current);
  1148. return true;
  1149. }
  1150. return false;
  1151. }
  1152. /**
  1153. * Encodes 'B'
  1154. *
  1155. *
  1156. */
  1157. void Encode_B()
  1158. {
  1159. if(Encode_Silent_B())
  1160. {
  1161. return;
  1162. }
  1163. // "-mb", e.g", "dumb", already skipped over under
  1164. // 'M', altho it should really be handled here...
  1165. MetaphAddExactApprox("B", "P");
  1166. if((CharAt(m_current + 1) == 'B')
  1167. || ((CharAt(m_current + 1) == 'P')
  1168. && ((m_current + 1 < m_last) && (CharAt(m_current + 2) != 'H'))))
  1169. {
  1170. m_current += 2;
  1171. }
  1172. else
  1173. {
  1174. m_current++;
  1175. }
  1176. }
  1177. /**
  1178. * Encodes silent 'B' for cases not covered under "-mb-"
  1179. *
  1180. *
  1181. * @return true if encoding handled in this routine, false if not
  1182. *
  1183. */
  1184. boolean Encode_Silent_B()
  1185. {
  1186. //'debt', 'doubt', 'subtle'
  1187. if(StringAt((m_current - 2), 4, "DEBT", "")
  1188. || StringAt((m_current - 2), 5, "SUBTL", "")
  1189. || StringAt((m_current - 2), 6, "SUBTIL", "")
  1190. || StringAt((m_current - 3), 5, "DOUBT", ""))
  1191. {
  1192. MetaphAdd("T");
  1193. m_current += 2;
  1194. return true;
  1195. }
  1196. return false;
  1197. }
  1198. /**
  1199. * Encodes 'C'
  1200. *
  1201. */
  1202. void Encode_C()
  1203. {
  1204. if(Encode_Silent_C_At_Beginning()
  1205. || Encode_CA_To_S()
  1206. || Encode_CO_To_S()
  1207. || Encode_CH()
  1208. || Encode_CCIA()
  1209. || Encode_CC()
  1210. || Encode_CK_CG_CQ()
  1211. || Encode_C_Front_Vowel()
  1212. || Encode_Silent_C()
  1213. || Encode_CZ()
  1214. || Encode_CS())
  1215. {
  1216. return;
  1217. }
  1218. //else
  1219. if(!StringAt((m_current - 1), 1, "C", "K", "G", "Q", ""))
  1220. {
  1221. MetaphAdd("K");
  1222. }
  1223. //name sent in 'mac caffrey', 'mac gregor
  1224. if(StringAt((m_current + 1), 2, " C", " Q", " G", ""))
  1225. {
  1226. m_current += 2;
  1227. }
  1228. else
  1229. {
  1230. if(StringAt((m_current + 1), 1, "C", "K", "Q", "")
  1231. && !StringAt((m_current + 1), 2, "CE", "CI", ""))
  1232. {
  1233. m_current += 2;
  1234. // account for combinations such as Ro-ckc-liffe
  1235. if(StringAt((m_current), 1, "C", "K", "Q", "")
  1236. && !StringAt((m_current + 1), 2, "CE", "CI", ""))
  1237. {
  1238. m_current++;
  1239. }
  1240. }
  1241. else
  1242. {
  1243. m_current++;
  1244. }
  1245. }
  1246. }
  1247. /**
  1248. * Encodes cases where 'C' is silent at beginning of word
  1249. *
  1250. * @return true if encoding handled in this routine, false if not
  1251. *
  1252. */
  1253. boolean Encode_Silent_C_At_Beginning()
  1254. {
  1255. //skip these when at start of word
  1256. if((m_current == 0)
  1257. && StringAt(m_current, 2, "CT", "CN", ""))
  1258. {
  1259. m_current += 1;
  1260. return true;
  1261. }
  1262. return false;
  1263. }
  1264. /**
  1265. * Encodes exceptions where "-CA-" should encode to S
  1266. * instead of K including cases where the cedilla has not been used
  1267. *
  1268. * @return true if encoding handled in this routine, false if not
  1269. *
  1270. */
  1271. boolean Encode_CA_To_S()
  1272. {
  1273. // Special case: 'caesar'.
  1274. // Also, where cedilla not used, as in "linguica" => LNKS
  1275. if(((m_current == 0) && StringAt(m_current, 4, "CAES", "CAEC", "CAEM", ""))
  1276. || StringAt(0, 8, "FRANCAIS", "FRANCAIX", "LINGUICA", "")
  1277. || StringAt(0, 6, "FACADE", "")
  1278. || StringAt(0, 9, "GONCALVES", "PROVENCAL", ""))
  1279. {
  1280. MetaphAdd("S");
  1281. AdvanceCounter(2, 1);
  1282. return true;
  1283. }
  1284. return false;
  1285. }
  1286. /**
  1287. * Encodes exceptions where "-CO-" encodes to S instead of K
  1288. * including cases where the cedilla has not been used
  1289. *
  1290. * @return true if encoding handled in this routine, false if not
  1291. *
  1292. */
  1293. boolean Encode_CO_To_S()
  1294. {
  1295. // e.g. 'coelecanth' => SLKN0
  1296. if((StringAt(m_current, 4, "COEL", "")
  1297. && (IsVowel(m_current + 4) || ((m_current + 3) == m_last)))
  1298. || StringAt(m_current, 5, "COENA", "COENO", "")
  1299. || StringAt(0, 8, "FRANCOIS", "MELANCON", "")
  1300. || StringAt(0, 6, "GARCON", ""))
  1301. {
  1302. MetaphAdd("S");
  1303. AdvanceCounter(3, 1);
  1304. return true;
  1305. }
  1306. return false;
  1307. }
  1308. /**
  1309. * Encode "-CH-"
  1310. *
  1311. * @return true if encoding handled in this routine, false if not
  1312. *
  1313. */
  1314. boolean Encode_CH()
  1315. {
  1316. if(StringAt(m_current, 2, "CH", ""))
  1317. {
  1318. if(Encode_CHAE()
  1319. || Encode_CH_To_H()
  1320. || Encode_Silent_CH()
  1321. || Encode_ARCH()
  1322. // Encode_CH_To_X() should be
  1323. // called before the germanic
  1324. // and greek encoding functions
  1325. || Encode_CH_To_X()
  1326. || Encode_English_CH_To_K()
  1327. || Encode_Germanic_CH_To_K()
  1328. || Encode_Greek_CH_Initial()
  1329. || Encode_Greek_CH_Non_Initial())
  1330. {
  1331. return true;
  1332. }
  1333. if(m_current > 0)
  1334. {
  1335. if(StringAt(0, 2, "MC", "")
  1336. && (m_current == 1))
  1337. {
  1338. //e.g., "McHugh"
  1339. MetaphAdd("K");
  1340. }
  1341. else
  1342. {
  1343. MetaphAdd("X", "K");
  1344. }
  1345. }
  1346. else
  1347. {
  1348. MetaphAdd("X");
  1349. }
  1350. m_current += 2;
  1351. return true;
  1352. }
  1353. return false;
  1354. }
  1355. /**
  1356. * Encodes "-CHAE-"
  1357. *
  1358. * @return true if encoding handled in this routine, false if not
  1359. *
  1360. */
  1361. boolean Encode_CHAE()
  1362. {
  1363. // e.g. 'michael'
  1364. if(((m_current > 0) && StringAt((m_current + 2), 2, "AE", "")))
  1365. {
  1366. if(StringAt(0, 7, "RACHAEL", ""))
  1367. {
  1368. MetaphAdd("X");
  1369. }
  1370. else if(!StringAt((m_current - 1), 1, "C", "K", "G", "Q", ""))
  1371. {
  1372. MetaphAdd("K");
  1373. }
  1374. AdvanceCounter(4, 2);
  1375. return true;
  1376. }
  1377. return false;
  1378. }
  1379. /**
  1380. * Encdoes transliterations from the hebrew where the
  1381. * sound 'kh' is represented as "-CH-". The normal pronounciation
  1382. * of this in english is either 'h' or 'kh', and alternate
  1383. * spellings most often use "-H-"
  1384. *
  1385. * @return true if encoding handled in this routine, false if not
  1386. *
  1387. */
  1388. boolean Encode_CH_To_H()
  1389. {
  1390. // hebrew => 'H', e.g. 'channukah', 'chabad'
  1391. if(((m_current == 0)
  1392. && (StringAt((m_current + 2), 3, "AIM", "ETH", "ELM", "")
  1393. || StringAt((m_current + 2), 4, "ASID", "AZAN", "")
  1394. || StringAt((m_current + 2), 5, "UPPAH", "UTZPA", "ALLAH", "ALUTZ", "AMETZ", "")
  1395. || StringAt((m_current + 2), 6, "ESHVAN", "ADARIM", "ANUKAH", "")
  1396. || StringAt((m_current + 2), 7, "ALLLOTH", "ANNUKAH", "AROSETH", "")))
  1397. // and an irish name with the same encoding
  1398. || StringAt((m_current - 3), 7, "CLACHAN", ""))
  1399. {
  1400. MetaphAdd("H");
  1401. AdvanceCounter(3, 2);
  1402. return true;
  1403. }
  1404. return false;
  1405. }
  1406. /**
  1407. * Encodes cases where "-CH-" is not pronounced
  1408. *
  1409. * @return true if encoding handled in this routine, false if not
  1410. *
  1411. */
  1412. boolean Encode_Silent_CH()
  1413. {
  1414. // '-ch-' not pronounced
  1415. if(StringAt((m_current - 2), 7, "FUCHSIA", "")
  1416. || StringAt((m_current - 2), 5, "YACHT", "")
  1417. || StringAt(0, 8, "STRACHAN", "")
  1418. || StringAt(0, 8, "CRICHTON", "")
  1419. || (StringAt((m_current - 3), 6, "DRACHM", ""))
  1420. && !StringAt((m_current - 3), 7, "DRACHMA", ""))
  1421. {
  1422. m_current += 2;
  1423. return true;
  1424. }
  1425. return false;
  1426. }
  1427. /**
  1428. * Encodes "-CH-" to X
  1429. * English language patterns
  1430. *
  1431. * @return true if encoding handled in this routine, false if not
  1432. *
  1433. */
  1434. boolean Encode_CH_To_X()
  1435. {
  1436. // e.g. 'approach', 'beach'
  1437. if((StringAt((m_current - 2), 4, "OACH", "EACH", "EECH", "OUCH", "OOCH", "MUCH", "SUCH", "")
  1438. && !StringAt((m_current - 3), 5, "JOACH", ""))
  1439. // e.g. 'dacha', 'macho'
  1440. || (((m_current + 2) == m_last ) && StringAt((m_current - 1), 4, "ACHA", "ACHO", ""))
  1441. || (StringAt(m_current, 4, "CHOT", "CHOD", "CHAT", "") && ((m_current + 3) == m_last))
  1442. || ((StringAt((m_current - 1), 4, "OCHE", "") && ((m_current + 2) == m_last))
  1443. && !StringAt((m_current - 2), 5, "DOCHE", ""))
  1444. || StringAt((m_current - 4), 6, "ATTACH", "DETACH", "KOVACH", "")
  1445. || StringAt((m_current - 5), 7, "SPINACH", "")
  1446. || StringAt(0, 6, "MACHAU", "")
  1447. || StringAt((m_current - 4), 8, "PARACHUT", "")
  1448. || StringAt((m_current - 5), 8, "MASSACHU", "")
  1449. || (StringAt((m_current - 3), 5, "THACH", "") && !StringAt((m_current - 1), 4, "ACHE", ""))
  1450. || StringAt((m_current - 2), 6, "VACHON", "") )
  1451. {
  1452. MetaphAdd("X");
  1453. m_current += 2;
  1454. return true;
  1455. }
  1456. return false;
  1457. }
  1458. /**
  1459. * Encodes "-CH-" to K in contexts of
  1460. * initial "A" or "E" follwed by "CH"
  1461. *
  1462. * @return true if encoding handled in this routine, false if not
  1463. *
  1464. */
  1465. boolean Encode_English_CH_To_K()
  1466. {
  1467. //'ache', 'echo', alternate spelling of 'michael'
  1468. if(((m_current == 1) && RootOrInflections(m_inWord, "ACHE"))
  1469. || (((m_current > 3) && RootOrInflections(m_inWord.substring(m_current - 1), "ACHE"))
  1470. && (StringAt(0, 3, "EAR", "")
  1471. || StringAt(0, 4, "HEAD", "BACK", "")
  1472. || StringAt(0, 5, "HEART", "BELLY", "TOOTH", "")))
  1473. || StringAt((m_current - 1), 4, "ECHO", "")
  1474. || StringAt((m_current - 2), 7, "MICHEAL", "")
  1475. || StringAt((m_current - 4), 7, "JERICHO", "")
  1476. || StringAt((m_current - 5), 7, "LEPRECH", ""))
  1477. {
  1478. MetaphAdd("K", "X");
  1479. m_current += 2;
  1480. return true;
  1481. }
  1482. return false;
  1483. }
  1484. /**
  1485. * Encodes "-CH-" to K in mostly germanic context
  1486. * of internal "-ACH-", with exceptions
  1487. *
  1488. * @return true if encoding handled in this routine, false if not
  1489. *
  1490. */
  1491. boolean Encode_Germanic_CH_To_K()
  1492. {
  1493. // various germanic
  1494. // "<consonant><vowel>CH-"implies a german word where 'ch' => K
  1495. if(((m_current > 1)
  1496. && !IsVowel(m_current - 2)
  1497. && StringAt((m_current - 1), 3, "ACH", "")
  1498. && !StringAt((m_current - 2), 7, "MACHADO", "MACHUCA", "LACHANC", "LACHAPE", "KACHATU", "")
  1499. && !StringAt((m_current - 3), 7, "KHACHAT", "")
  1500. && ((CharAt(m_current + 2) != 'I')
  1501. && ((CharAt(m_current + 2) != 'E')
  1502. || StringAt((m_current - 2), 6, "BACHER", "MACHER", "MACHEN", "LACHER", "")) )
  1503. // e.g. 'brecht', 'fuchs'
  1504. || (StringAt((m_current + 2), 1, "T", "S", "")
  1505. && !(StringAt(0, 11, "WHICHSOEVER", "") || StringAt(0, 9, "LUNCHTIME", "") ))
  1506. // e.g. 'andromache'
  1507. || StringAt(0, 4, "SCHR", "")
  1508. || ((m_current > 2) && StringAt((m_current - 2), 5, "MACHE", ""))
  1509. || ((m_current == 2) && StringAt((m_current - 2), 4, "ZACH", ""))
  1510. || StringAt((m_current - 4), 6, "SCHACH", "")
  1511. || StringAt((m_current - 1), 5, "ACHEN", "")
  1512. || StringAt((m_current - 3), 5, "SPICH", "ZURCH", "BUECH", "")
  1513. || (StringAt((m_current - 3), 5, "KIRCH", "JOACH", "BLECH", "MALCH", "")
  1514. // "kirch" and "blech" both get 'X'
  1515. && !(StringAt((m_current - 3), 8, "KIRCHNER", "") || ((m_current + 1) == m_last)))
  1516. || (((m_current + 1) == m_last) && StringAt((m_current - 2), 4, "NICH", "LICH", "BACH", ""))
  1517. || (((m_current + 1) == m_last)
  1518. && StringAt((m_current - 3), 5, "URICH", "BRICH", "ERICH", "DRICH", "NRICH", "")
  1519. && !StringAt((m_current - 5), 7, "ALDRICH", "")
  1520. && !StringAt((m_current - 6), 8, "GOODRICH", "")
  1521. && !StringAt((m_current - 7), 9, "GINGERICH", "")))
  1522. || (((m_current + 1) == m_last) && StringAt((m_current - 4), 6, "ULRICH", "LFRICH", "LLRICH",
  1523. "EMRICH", "ZURICH", "EYRICH", ""))
  1524. // e.g., 'wachtler', 'wechsler', but not 'tichner'
  1525. || ((StringAt((m_current - 1), 1, "A", "O", "U", "E", "") || (m_current == 0))
  1526. && StringAt((m_current + 2), 1, "L", "R", "N", "M", "B", "H", "F", "V", "W", " ", "")))
  1527. {
  1528. // "CHR/L-" e.g. 'chris' do not get
  1529. // alt pronunciation of 'X'
  1530. if(StringAt((m_current + 2), 1, "R", "L", "")
  1531. || SlavoGermanic())
  1532. {
  1533. MetaphAdd("K");
  1534. }
  1535. else
  1536. {
  1537. MetaphAdd("K", "X");
  1538. }
  1539. m_current += 2;
  1540. return true;
  1541. }
  1542. return false;
  1543. }
  1544. /**
  1545. * Encode "-ARCH-". Some occurances are from greek roots and therefore encode
  1546. * to 'K', others are from english words and therefore encode to 'X'
  1547. *
  1548. * @return true if encoding handled in this routine, false if not
  1549. *
  1550. */
  1551. boolean Encode_ARCH()
  1552. {
  1553. if(StringAt((m_current - 2), 4, "ARCH", ""))
  1554. {
  1555. // "-ARCH-" has many combining forms where "-CH-" => K because of its
  1556. // derivation from the greek
  1557. if(((IsVowel(m_current + 2) && StringAt((m_current - 2), 5, "ARCHA", "ARCHI", "ARCHO", "ARCHU", "ARCHY", ""))
  1558. || StringAt((m_current - 2), 6, "ARCHEA", "ARCHEG", "ARCHEO", "ARCHET", "ARCHEL", "ARCHES", "ARCHEP",
  1559. "ARCHEM", "ARCHEN", "")
  1560. || (StringAt((m_current - 2), 4, "ARCH", "") && (((m_current + 1) == m_last)))
  1561. || StringAt(0, 7, "MENARCH", ""))
  1562. && (!RootOrInflections(m_inWord, "ARCH")
  1563. && !StringAt((m_current - 4), 6, "SEARCH", "POARCH", "")
  1564. && !StringAt(0, 9, "ARCHENEMY", "ARCHIBALD", "ARCHULETA", "ARCHAMBAU", "")
  1565. && !StringAt(0, 6, "ARCHER", "ARCHIE", "")
  1566. && !((((StringAt((m_current - 3), 5, "LARCH", "MARCH", "PARCH", "")
  1567. || StringAt((m_current - 4), 6, "STARCH", ""))
  1568. && !(StringAt(0, 6, "EPARCH", "")
  1569. || StringAt(0, 7, "NOMARCH", "")
  1570. || StringAt(0, 8, "EXILARCH", "HIPPARCH", "MARCHESE", "")
  1571. || StringAt(0, 9, "ARISTARCH", "")
  1572. || StringAt(0, 9, "MARCHETTI", "")) )
  1573. || RootOrInflections(m_inWord, "STARCH"))
  1574. && (!StringAt((m_current - 2), 5, "ARCHU", "ARCHY", "")
  1575. || StringAt(0, 7, "STARCHY", "")))))
  1576. {
  1577. MetaphAdd("K", "X");
  1578. }
  1579. else
  1580. {
  1581. MetaphAdd("X");
  1582. }
  1583. m_current += 2;
  1584. return true;
  1585. }
  1586. return false;
  1587. }
  1588. /**
  1589. * Encode "-CH-" to K when from greek roots
  1590. *
  1591. * @return true if encoding handled in this routine, false if not
  1592. *
  1593. */
  1594. boolean Encode_Greek_CH_Initial()
  1595. {
  1596. // greek roots e.g. 'chemistry', 'chorus', ch at beginning of root
  1597. if((StringAt(m_current, 6, "CHAMOM", "CHARAC", "CHARIS", "CHARTO", "CHARTU", "CHARYB", "CHRIST", "CHEMIC", "CHILIA", "")
  1598. || (StringAt(m_current, 5, "CHEMI", "CHEMO", "CHEMU", "CHEMY", "CHOND", "CHONA", "CHONI", "CHOIR", "CHASM",
  1599. "CHARO", "CHROM", "CHROI", "CHAMA", "CHALC", "CHALD", "CHAET","CHIRO", "CHILO", "CHELA", "CHOUS",
  1600. "CHEIL", "CHEIR", "CHEIM", "CHITI", "CHEOP", "")
  1601. && !(StringAt(m_current, 6, "CHEMIN", "") || StringAt((m_current - 2), 8, "ANCHONDO", "")))
  1602. || (StringAt(m_current, 5, "CHISM", "CHELI", "")
  1603. // exclude spanish "machismo"
  1604. && !(StringAt(0, 8, "MACHISMO", "")
  1605. // exclude some french words
  1606. || StringAt(0, 10, "REVANCHISM", "")
  1607. || StringAt(0, 9, "RICHELIEU", "")
  1608. || (StringAt(0, 5, "CHISM", "") && (m_length == 5))
  1609. || StringAt(0, 6, "MICHEL", "")))
  1610. // include e.g. "chorus", "chyme", "chaos"
  1611. || (StringAt(m_current, 4, "CHOR", "CHOL", "CHYM", "CHYL", "CHLO", "CHOS", "CHUS", "CHOE", "")
  1612. && !StringAt(0, 6, "CHOLLO", "CHOLLA", "CHORIZ", ""))
  1613. // "chaos" => K but not "chao"
  1614. || (StringAt(m_current, 4, "CHAO", "") && ((m_current + 3) != m_last))
  1615. // e.g. "abranchiate"
  1616. || (StringAt(m_current, 4, "CHIA", "") && !(StringAt(0, 10, "APPALACHIA", "") || StringAt(0, 7, "CHIAPAS", "")))
  1617. // e.g. "chimera"
  1618. || StringAt(m_current, 7, "CHIMERA", "CHIMAER", "CHIMERI", "")
  1619. // e.g. "chameleon"
  1620. || ((m_current == 0) && StringAt(m_current, 5, "CHAME", "CHELO", "CHITO", "") )
  1621. // e.g. "spirochete"
  1622. || ((((m_current + 4) == m_last) || ((m_current + 5) == m_last)) && StringAt((m_current - 1), 6, "OCHETE", "")))
  1623. // more exceptions where "-CH-" => X e.g. "chortle", "crocheter"
  1624. && !((StringAt(0, 5, "CHORE", "CHOLO", "CHOLA", "") && (m_length == 5))
  1625. || StringAt(m_current, 5, "CHORT", "CHOSE", "")
  1626. || StringAt((m_current - 3), 7, "CROCHET", "")
  1627. || StringAt(0, 7, "CHEMISE", "CHARISE", "CHARISS", "CHAROLE", "")) )
  1628. {
  1629. // "CHR/L-" e.g. 'christ', 'chlorine' do not get
  1630. // alt pronunciation of 'X'
  1631. if(StringAt((m_current + 2), 1, "R", "L", ""))
  1632. {
  1633. MetaphAdd("K");
  1634. }
  1635. else
  1636. {
  1637. MetaphAdd("K", "X");
  1638. }
  1639. m_current += 2;
  1640. return true;
  1641. }
  1642. return false;
  1643. }
  1644. /**
  1645. * Encode a variety of greek and some german roots where "-CH-" => K
  1646. *
  1647. * @return true if encoding handled in this routine, false if not
  1648. *
  1649. */
  1650. boolean Encode_Greek_CH_Non_Initial()
  1651. {
  1652. //greek & other roots e.g. 'tachometer', 'orchid', ch in middle or end of root
  1653. if(StringAt((m_current - 2), 6, "ORCHID", "NICHOL", "MECHAN", "LICHEN", "MACHIC", "PACHEL", "RACHIF", "RACHID",
  1654. "RACHIS", "RACHIC", "MICHAL", "")
  1655. || StringAt((m_current - 3), 5, "MELCH", "GLOCH", "TRACH", "TROCH", "BRACH", "SYNCH", "PSYCH",
  1656. "STICH", "PULCH", "EPOCH", "")
  1657. || (StringAt((m_current - 3), 5, "TRICH", "") && !StringAt((m_current - 5), 7, "OSTRICH", ""))
  1658. || (StringAt((m_current - 2), 4, "TYCH", "TOCH", "BUCH", "MOCH", "CICH", "DICH", "NUCH", "EICH", "LOCH",
  1659. "DOCH", "ZECH", "WYCH", "")
  1660. && !(StringAt((m_current - 4), 9, "INDOCHINA", "") || StringAt((m_current - 2), 6, "BUCHON", "")))
  1661. || StringAt((m_current - 2), 5, "LYCHN", "TACHO", "ORCHO", "ORCHI", "LICHO", "")
  1662. || (StringAt((m_current - 1), 5, "OCHER", "ECHIN", "ECHID", "") && ((m_current == 1) || (m_current == 2)))
  1663. || StringAt((m_current - 4), 6, "BRONCH", "STOICH", "STRYCH", "TELECH", "PLANCH", "CATECH", "MANICH", "MALACH",
  1664. "BIANCH", "DIDACH", "")
  1665. || (StringAt((m_current - 1), 4, "ICHA", "ICHN","") && (m_current == 1))
  1666. || StringAt((m_current - 2), 8, "ORCHESTR", "")
  1667. || StringAt((m_current - 4), 8, "BRANCHIO", "BRANCHIF", "")
  1668. || (StringAt((m_current - 1), 5, "ACHAB", "ACHAD", "ACHAN", "ACHAZ", "")
  1669. && !StringAt((m_current - 2), 7, "MACHADO", "LACHANC", ""))
  1670. || StringAt((m_current - 1), 6, "ACHISH", "ACHILL", "ACHAIA", "ACHENE", "")
  1671. || StringAt((m_current - 1), 7, "ACHAIAN", "ACHATES", "ACHIRAL", "ACHERON", "")
  1672. || StringAt((m_current - 1), 8, "ACHILLEA", "ACHIMAAS", "ACHILARY", "ACHELOUS", "ACHENIAL", "ACHERNAR", "")
  1673. || StringAt((m_current - 1), 9, "ACHALASIA", "ACHILLEAN", "ACHIMENES", "")
  1674. || StringAt((m_current - 1), 10, "ACHIMELECH", "ACHITOPHEL", "")
  1675. // e.g. 'inchoate'
  1676. || (((m_current - 2) == 0) && (StringAt((m_current - 2), 6, "INCHOA", "")
  1677. // e.g. 'ischemia'
  1678. || StringAt(0, 4, "ISCH", "")) )
  1679. // e.g. 'ablimelech', 'antioch', 'pentateuch'
  1680. || (((m_current + 1) == m_last) && StringAt((m_current - 1), 1, "A", "O", "U", "E", "")
  1681. && !(StringAt(0, 7, "DEBAUCH", "")
  1682. || StringAt((m_current - 2), 4, "MUCH", "SUCH", "KOCH", "")
  1683. || StringAt((m_current - 5), 7, "OODRICH", "ALDRICH", ""))))
  1684. {
  1685. MetaphAdd("K", "X");
  1686. m_current += 2;
  1687. return true;
  1688. }
  1689. return false;
  1690. }
  1691. /**
  1692. * Encodes reliably italian "-CCIA-"
  1693. *
  1694. * @return true if encoding handled in this routine, false if not
  1695. *
  1696. */
  1697. boolean Encode_CCIA()
  1698. {
  1699. //e.g., 'focaccia'
  1700. if(StringAt((m_current + 1), 3, "CIA", ""))
  1701. {
  1702. MetaphAdd("X", "S");
  1703. m_current += 2;
  1704. return true;
  1705. }
  1706. return false;
  1707. }
  1708. /**
  1709. * Encode "-CC-"
  1710. *
  1711. * @return true if encoding handled in this routine, false if not
  1712. *
  1713. */
  1714. boolean Encode_CC()
  1715. {
  1716. //double 'C', but not if e.g. 'McClellan'
  1717. if(StringAt(m_current, 2, "CC", "") && !((m_current == 1) && (CharAt(0) == 'M')))
  1718. {
  1719. // exception
  1720. if (StringAt((m_current - 3), 7, "FLACCID", ""))
  1721. {
  1722. MetaphAdd("S");
  1723. AdvanceCounter(3, 2);
  1724. return true;
  1725. }
  1726. //'bacci', 'bertucci', other italian
  1727. if((((m_current + 2) == m_last) && StringAt((m_current + 2), 1, "I", ""))
  1728. || StringAt((m_current + 2), 2, "IO", "")
  1729. || (((m_current + 4) == m_last) && StringAt((m_current + 2), 3, "INO", "INI", "")))
  1730. {
  1731. MetaphAdd("X");
  1732. AdvanceCounter(3, 2);
  1733. return true;
  1734. }
  1735. //'accident', 'accede' 'succeed'
  1736. if(StringAt((m_current + 2), 1, "I", "E", "Y", "")
  1737. //except 'bellocchio','bacchus', 'soccer' get K
  1738. && !((CharAt(m_current + 2) == 'H')
  1739. || StringAt((m_current - 2), 6, "SOCCER", "")))
  1740. {
  1741. MetaphAdd("KS");
  1742. AdvanceCounter(3, 2);
  1743. return true;
  1744. }
  1745. else
  1746. {
  1747. //Pierce's rule
  1748. MetaphAdd("K");
  1749. m_current += 2;
  1750. return true;
  1751. }
  1752. }
  1753. return false;
  1754. }
  1755. /**
  1756. * Encode cases where the consonant following "C" is redundant
  1757. *
  1758. * @return true if encoding handled in this routine, false if not
  1759. *
  1760. */
  1761. boolean Encode_CK_CG_CQ()
  1762. {
  1763. if(StringAt(m_current, 2, "CK", "CG", "CQ", ""))
  1764. {
  1765. // eastern european spelling e.g. 'gorecki' == 'goresky'
  1766. if(StringAt(m_current, 3, "CKI", "CKY", "")
  1767. && ((m_current + 2) == m_last)
  1768. && (m_length > 6))
  1769. {
  1770. MetaphAdd("K", "SK");
  1771. }
  1772. else
  1773. {
  1774. MetaphAdd("K");
  1775. }
  1776. m_current += 2;
  1777. if(StringAt(m_current, 1, "K", "G", "Q", ""))
  1778. {
  1779. m_current++;
  1780. }
  1781. return true;
  1782. }
  1783. return false;
  1784. }
  1785. /**
  1786. * Encode cases where "C" preceeds a front vowel such as "E", "I", or "Y".
  1787. * These cases most likely => S or X
  1788. *
  1789. * @return true if encoding handled in this routine, false if not
  1790. *
  1791. */
  1792. boolean Encode_C_Front_Vowel()
  1793. {
  1794. if(StringAt(m_current, 2, "CI", "CE", "CY", ""))
  1795. {
  1796. if(Encode_British_Silent_CE()
  1797. || Encode_CE()
  1798. || Encode_CI()
  1799. || Encode_Latinate_Suffixes())
  1800. {
  1801. AdvanceCounter(2, 1);
  1802. return true;
  1803. }
  1804. MetaphAdd("S");
  1805. AdvanceCounter(2, 1);
  1806. return true;
  1807. }
  1808. return false;
  1809. }
  1810. /**
  1811. *
  1812. * @return true if encoding handled in this routine, false if not
  1813. *
  1814. */
  1815. boolean Encode_British_Silent_CE()
  1816. {
  1817. // english place names like e.g.'gloucester' pronounced glo-ster
  1818. if((StringAt((m_current + 1), 5, "ESTER", "") && ((m_current + 5) == m_last))
  1819. || StringAt((m_current + 1), 10, "ESTERSHIRE", ""))
  1820. {
  1821. return true;
  1822. }
  1823. return false;
  1824. }
  1825. /**
  1826. *
  1827. * @return true if encoding handled in this routine, false if not
  1828. *
  1829. */
  1830. boolean Encode_CE()
  1831. {
  1832. // 'ocean', 'commercial', 'provincial', 'cello', 'fettucini', 'medici'
  1833. if((StringAt((m_current + 1), 3, "EAN", "") && IsVowel(m_current - 1))
  1834. // e.g. 'rosacea'
  1835. || (StringAt((m_current - 1), 4, "ACEA", "")
  1836. && ((m_current + 2) == m_last)
  1837. && !StringAt(0, 7, "PANACEA", ""))
  1838. // e.g. 'botticelli', 'concerto'
  1839. || StringAt((m_current + 1), 4, "ELLI", "ERTO", "EORL", "")
  1840. // some italian names familiar to americans
  1841. || (StringAt((m_current - 3), 5, "CROCE", "") && ((m_current + 1) == m_last))
  1842. || StringAt((m_current - 3), 5, "DOLCE", "")
  1843. // e.g. 'cello'
  1844. || (StringAt((m_current + 1), 4, "ELLO", "")
  1845. && ((m_current + 4) == m_last)))
  1846. {
  1847. MetaphAdd("X", "S");
  1848. return true;
  1849. }
  1850. return false;
  1851. }
  1852. /**
  1853. *
  1854. * @return true if encoding handled in this routine, false if not
  1855. *
  1856. */
  1857. boolean Encode_CI()
  1858. {
  1859. // with consonant before C
  1860. // e.g. 'fettucini', but exception for the americanized pronunciation of 'mancini'
  1861. if(((StringAt((m_current + 1), 3, "INI", "") && !StringAt(0, 7, "MANCINI", "")) && ((m_current + 3) == m_last))
  1862. // e.g. 'medici'
  1863. || (StringAt((m_current - 1), 3, "ICI", "") && ((m_current + 1) == m_last))
  1864. // e.g. "commercial', 'provincial', 'cistercian'
  1865. || StringAt((m_current - 1), 5, "RCIAL", "NCIAL", "RCIAN", "UCIUS", "")
  1866. // special cases
  1867. || StringAt((m_current - 3), 6, "MARCIA", "")
  1868. || StringAt((m_current - 2), 7, "ANCIENT", ""))
  1869. {
  1870. MetaphAdd("X", "S");
  1871. return true;
  1872. }
  1873. // with vowel before C (or at beginning?)
  1874. if(((StringAt(m_current, 3, "CIO", "CIE", "CIA", "")
  1875. && IsVowel(m_current - 1))
  1876. // e.g. "ciao"
  1877. || StringAt((m_current + 1), 3, "IAO", ""))
  1878. && !StringAt((m_current - 4), 8, "COERCION", ""))
  1879. {
  1880. if((StringAt(m_current, 4, "CIAN", "CIAL", "CIAO", "CIES", "CIOL", "CION", "")
  1881. // exception - "glacier" => 'X' but "spacier" = > 'S'
  1882. || StringAt((m_current - 3), 7, "GLACIER", "")
  1883. || StringAt(m_current, 5, "CIENT", "CIENC", "CIOUS", "CIATE", "CIATI", "CIATO", "CIABL", "CIARY", "")
  1884. || (((m_current + 2) == m_last) && StringAt(m_current, 3, "CIA", "CIO", ""))
  1885. || (((m_current + 3) == m_last) && StringAt(m_current, 3, "CIAS", "CIOS", "")))
  1886. // exceptions
  1887. && !(StringAt((m_current - 4), 11, "ASSOCIATION", "")
  1888. || StringAt(0, 4, "OCIE", "")
  1889. // exceptions mostly because these names are usually from
  1890. // the spanish rather than the italian in america
  1891. || StringAt((m_current - 2), 5, "LUCIO", "")
  1892. || StringAt((m_current - 2), 6, "MACIAS", "")
  1893. || StringAt((m_current - 3), 6, "GRACIE", "GRACIA", "")
  1894. || StringAt((m_current - 2), 7, "LUCIANO", "")
  1895. || StringAt((m_current - 3), 8, "MARCIANO", "")
  1896. || StringAt((m_current - 4), 7, "PALACIO", "")
  1897. || StringAt((m_current - 4), 9, "FELICIANO", "")
  1898. || StringAt((m_current - 5), 8, "MAURICIO", "")
  1899. || StringAt((m_current - 7), 11, "ENCARNACION", "")
  1900. || StringAt((m_current - 4), 8, "POLICIES", "")
  1901. || StringAt((m_current - 2), 8, "HACIENDA", "")
  1902. || StringAt((m_current - 6), 9, "ANDALUCIA", "")
  1903. || StringAt((m_current - 2), 5, "SOCIO", "SOCIE", "")))
  1904. {
  1905. MetaphAdd("X", "S");
  1906. }
  1907. else
  1908. {
  1909. MetaphAdd("S", "X");
  1910. }
  1911. return true;
  1912. }
  1913. // exception
  1914. if(StringAt((m_current - 4), 8, "COERCION", ""))
  1915. {
  1916. MetaphAdd("J");
  1917. return true;
  1918. }
  1919. return false;
  1920. }
  1921. /**
  1922. *
  1923. * @return true if encoding handled in this routine, false if not
  1924. *
  1925. */
  1926. boolean Encode_Latinate_Suffixes()
  1927. {
  1928. if(StringAt((m_current + 1), 4, "EOUS", "IOUS", ""))
  1929. {
  1930. MetaphAdd("X", "S");
  1931. return true;
  1932. }
  1933. return false;
  1934. }
  1935. /**
  1936. * Encodes some exceptions where "C" is silent
  1937. *
  1938. * @return true if encoding handled in this routine, false if not
  1939. *
  1940. */
  1941. boolean Encode_Silent_C()
  1942. {
  1943. if(StringAt((m_current + 1), 1, "T", "S", ""))
  1944. {
  1945. if (StringAt(0, 11, "CONNECTICUT", "")
  1946. || StringAt(0, 6, "INDICT", "TUCSON", ""))
  1947. {
  1948. m_current++;
  1949. return true;
  1950. }
  1951. }
  1952. return false;
  1953. }
  1954. /**
  1955. * Encodes slavic spellings or transliterations
  1956. * written as "-CZ-"
  1957. *
  1958. * @return true if encoding handled in this routine, false if not
  1959. *
  1960. */
  1961. boolean Encode_CZ()
  1962. {
  1963. if(StringAt((m_current + 1), 1, "Z", "")
  1964. && !StringAt((m_current - 1), 6, "ECZEMA", ""))
  1965. {
  1966. if(StringAt(m_current, 4, "CZAR", ""))
  1967. {
  1968. MetaphAdd("S");
  1969. }
  1970. // otherwise most likely a czech word...
  1971. else
  1972. {
  1973. MetaphAdd("X");
  1974. }
  1975. m_current += 2;
  1976. return true;
  1977. }
  1978. return false;
  1979. }
  1980. /**
  1981. * "-CS" special cases
  1982. *
  1983. * @return true if encoding handled in this routine, false if not
  1984. *
  1985. */
  1986. boolean Encode_CS()
  1987. {
  1988. // give an 'etymological' 2nd
  1989. // encoding for "kovacs" so
  1990. // that it matches "kovach"
  1991. if(StringAt(0, 6, "KOVACS", ""))
  1992. {
  1993. MetaphAdd("KS", "X");
  1994. m_current += 2;
  1995. return true;
  1996. }
  1997. if(StringAt((m_current - 1), 3, "ACS", "")
  1998. && ((m_current + 1) == m_last)
  1999. && !StringAt((m_current - 4), 6, "ISAACS", ""))
  2000. {
  2001. MetaphAdd("X");
  2002. m_current += 2;
  2003. return true;
  2004. }
  2005. return false;
  2006. }
  2007. /**
  2008. * Encode "-D-"
  2009. *
  2010. */
  2011. void Encode_D()
  2012. {
  2013. if(Encode_DG()
  2014. || Encode_DJ()
  2015. || Encode_DT_DD()
  2016. || Encode_D_To_J()
  2017. || Encode_DOUS()
  2018. || Encode_Silent_D())
  2019. {
  2020. return;
  2021. }
  2022. if(m_encodeExact)
  2023. {
  2024. // "final de-voicing" in this case
  2025. // e.g. 'missed' == 'mist'
  2026. if((m_current == m_last)
  2027. && StringAt((m_current - 3), 4, "SSED", ""))
  2028. {
  2029. MetaphAdd("T");
  2030. }
  2031. else
  2032. {
  2033. MetaphAdd("D");
  2034. }
  2035. }
  2036. else
  2037. {
  2038. MetaphAdd("T");
  2039. }
  2040. m_current++;
  2041. }
  2042. /**
  2043. * Encode "-DG-"
  2044. *
  2045. * @return true if encoding handled in this routine, false if not
  2046. *
  2047. */
  2048. boolean Encode_DG()
  2049. {
  2050. if(StringAt(m_current, 2, "DG", ""))
  2051. {
  2052. // excludes exceptions e.g. 'edgar',
  2053. // or cases where 'g' is first letter of combining form
  2054. // e.g. 'handgun', 'waldglas'
  2055. if(StringAt((m_current + 2), 1, "A", "O", "")
  2056. // e.g. "midgut"
  2057. || StringAt((m_current + 1), 3, "GUN", "GUT", "")
  2058. // e.g. "handgrip"
  2059. || StringAt((m_current + 1), 4, "GEAR", "GLAS", "GRIP", "GREN", "GILL", "GRAF", "")
  2060. // e.g. "mudgard"
  2061. || StringAt((m_current + 1), 5, "GUARD", "GUILT", "GRAVE", "GRASS", "")
  2062. // e.g. "woodgrouse"
  2063. || StringAt((m_current + 1), 6, "GROUSE", ""))
  2064. {
  2065. MetaphAddExactApprox("DG", "TK");
  2066. }
  2067. else
  2068. {
  2069. //e.g. "edge", "abridgment"
  2070. MetaphAdd("J");
  2071. }
  2072. m_current += 2;
  2073. return true;
  2074. }
  2075. return false;
  2076. }
  2077. /**
  2078. * Encode "-DJ-"
  2079. *
  2080. * @return true if encoding handled in this routine, false if not
  2081. *
  2082. */
  2083. boolean Encode_DJ()
  2084. {
  2085. // e.g. "adjacent"
  2086. if(StringAt(m_current, 2, "DJ", ""))
  2087. {
  2088. MetaphAdd("J");
  2089. m_current += 2;
  2090. return true;
  2091. }
  2092. return false;
  2093. }
  2094. /**
  2095. * Encode "-DD-" and "-DT-"
  2096. *
  2097. * @return true if encoding handled in this routine, false if not
  2098. *
  2099. */
  2100. boolean Encode_DT_DD()
  2101. {
  2102. // eat redundant 'T' or 'D'
  2103. if(StringAt(m_current, 2, "DT", "DD", ""))
  2104. {
  2105. if(StringAt(m_current, 3, "DTH", ""))
  2106. {
  2107. MetaphAddExactApprox("D0", "T0");
  2108. m_current += 3;
  2109. }
  2110. else
  2111. {
  2112. if(m_encodeExact)
  2113. {
  2114. // devoice it
  2115. if(StringAt(m_current, 2, "DT", ""))
  2116. {
  2117. MetaphAdd("T");
  2118. }
  2119. else
  2120. {
  2121. MetaphAdd("D");
  2122. }
  2123. }
  2124. else
  2125. {
  2126. MetaphAdd("T");
  2127. }
  2128. m_current += 2;
  2129. }
  2130. return true;
  2131. }
  2132. return false;
  2133. }
  2134. /**
  2135. * Encode cases where "-DU-" "-DI-", and "-DI-" => J
  2136. *
  2137. * @return true if encoding handled in this routine, false if not
  2138. *
  2139. */
  2140. boolean Encode_D_To_J()
  2141. {
  2142. // e.g. "module", "adulate"
  2143. if((StringAt(m_current, 3, "DUL", "")
  2144. && (IsVowel(m_current - 1) && IsVowel(m_current + 3)))
  2145. // e.g. "soldier", "grandeur", "procedure"
  2146. || (((m_current + 3) == m_last)
  2147. && StringAt((m_current - 1) , 5, "LDIER", "NDEUR", "EDURE", "RDURE", ""))
  2148. || StringAt((m_current - 3), 7, "CORDIAL", "")
  2149. // e.g. "pendulum", "education"
  2150. || StringAt((m_current - 1), 5, "NDULA", "NDULU", "EDUCA", "")
  2151. // e.g. "individual", "individual", "residuum"
  2152. || StringAt((m_current - 1), 4, "ADUA", "IDUA", "IDUU", ""))
  2153. {
  2154. MetaphAddExactApprox("J", "D", "J", "T");
  2155. AdvanceCounter(2, 1);
  2156. return true;
  2157. }
  2158. return false;
  2159. }
  2160. /**
  2161. * Encode latinate suffix "-DOUS" where 'D' is pronounced as J
  2162. *
  2163. * @return true if encoding handled in this routine, false if not
  2164. *
  2165. */
  2166. boolean Encode_DOUS()
  2167. {
  2168. // e.g. "assiduous", "arduous"
  2169. if(StringAt((m_current + 1), 4, "UOUS", ""))
  2170. {
  2171. MetaphAddExactApprox("J", "D", "J", "T");
  2172. AdvanceCounter(4, 1);
  2173. return true;
  2174. }
  2175. return false;
  2176. }
  2177. /**
  2178. * Encode silent "-D-"
  2179. *
  2180. * @return true if encoding handled in this routine, false if not
  2181. *
  2182. */
  2183. boolean Encode_Silent_D()
  2184. {
  2185. // silent 'D' e.g. 'wednesday', 'handsome'
  2186. if(StringAt((m_current - 2), 9, "WEDNESDAY", "")
  2187. || StringAt((m_current - 3), 7, "HANDKER", "HANDSOM", "WINDSOR", "")
  2188. // french silent D at end in words or names familiar to americans
  2189. || StringAt((m_current - 5), 6, "PERNOD", "ARTAUD", "RENAUD", "")
  2190. || StringAt((m_current - 6), 7, "RIMBAUD", "MICHAUD", "BICHAUD", ""))
  2191. {
  2192. m_current++;
  2193. return true;
  2194. }
  2195. return false;
  2196. }
  2197. /**
  2198. * Encode "-F-"
  2199. *
  2200. */
  2201. void Encode_F()
  2202. {
  2203. // Encode cases where "-FT-" => "T" is usually silent
  2204. // e.g. 'often', 'soften'
  2205. // This should really be covered under "T"!
  2206. if(StringAt((m_current - 1), 5, "OFTEN", ""))
  2207. {
  2208. MetaphAdd("F", "FT");
  2209. m_current += 2;
  2210. return;
  2211. }
  2212. // eat redundant 'F'
  2213. if(CharAt(m_current + 1) == 'F')
  2214. {
  2215. m_current += 2;
  2216. }
  2217. else
  2218. {
  2219. m_current++;
  2220. }
  2221. MetaphAdd("F");
  2222. }
  2223. /**
  2224. * Encode "-G-"
  2225. *
  2226. */
  2227. void Encode_G()
  2228. {
  2229. if(Encode_Silent_G_At_Beginning()
  2230. || Encode_GG()
  2231. || Encode_GK()
  2232. || Encode_GH()
  2233. || Encode_Silent_G()
  2234. || Encode_GN()
  2235. || Encode_GL()
  2236. || Encode_Initial_G_Front_Vowel()
  2237. || Encode_NGER()
  2238. || Encode_GER()
  2239. || Encode_GEL()
  2240. || Encode_Non_Initial_G_Front_Vowel()
  2241. || Encode_GA_To_J())
  2242. {
  2243. return;
  2244. }
  2245. if(!StringAt((m_current - 1), 1, "C", "K", "G", "Q", ""))
  2246. {
  2247. MetaphAddExactApprox("G", "K");
  2248. }
  2249. m_current++;
  2250. }
  2251. /**
  2252. * Encode cases where 'G' is silent at beginning of word
  2253. *
  2254. * @return true if encoding handled in this routine, false if not
  2255. *
  2256. */
  2257. boolean Encode_Silent_G_At_Beginning()
  2258. {
  2259. //skip these when at start of word
  2260. if((m_current == 0)
  2261. && StringAt(m_current, 2, "GN", ""))
  2262. {
  2263. m_current += 1;
  2264. return true;
  2265. }
  2266. return false;
  2267. }
  2268. /**
  2269. * Encode "-GG-"
  2270. *
  2271. * @return true if encoding handled in this routine, false if not
  2272. *
  2273. */
  2274. boolean Encode_GG()
  2275. {
  2276. if(CharAt(m_current + 1) == 'G')
  2277. {
  2278. // italian e.g, 'loggia', 'caraveggio', also 'suggest' and 'exaggerate'
  2279. if(StringAt((m_current - 1), 5, "AGGIA", "OGGIA", "AGGIO", "EGGIO", "EGGIA", "IGGIO", "")
  2280. // 'ruggiero' but not 'snuggies'
  2281. || (StringAt((m_current - 1), 5, "UGGIE", "") && !(((m_current + 3) == m_last) || ((m_current + 4) == m_last)))
  2282. || (((m_current + 2) == m_last) && StringAt((m_current - 1), 4, "AGGI", "OGGI", ""))
  2283. || StringAt((m_current - 2), 6, "SUGGES", "XAGGER", "REGGIE", ""))
  2284. {
  2285. // expection where "-GG-" => KJ
  2286. if (StringAt((m_current - 2), 7, "SUGGEST", ""))
  2287. {
  2288. MetaphAddExactApprox("G", "K");
  2289. }
  2290. MetaphAdd("J");
  2291. AdvanceCounter(3, 2);
  2292. }
  2293. else
  2294. {
  2295. MetaphAddExactApprox("G", "K");
  2296. m_current += 2;
  2297. }
  2298. return true;
  2299. }
  2300. return false;
  2301. }
  2302. /**
  2303. * Encode "-GK-"
  2304. *
  2305. * @return true if encoding handled in this routine, false if not
  2306. *
  2307. */
  2308. boolean Encode_GK()
  2309. {
  2310. // 'gingko'
  2311. if(CharAt(m_current + 1) == 'K')
  2312. {
  2313. MetaphAdd("K");
  2314. m_current += 2;
  2315. return true;
  2316. }
  2317. return false;
  2318. }
  2319. /**
  2320. * Encode "-GH-"
  2321. *
  2322. * @return true if encoding handled in this routine, false if not
  2323. *
  2324. */
  2325. boolean Encode_GH()
  2326. {
  2327. if(CharAt(m_current + 1) == 'H')
  2328. {
  2329. if(Encode_GH_After_Consonant()
  2330. || Encode_Initial_GH()
  2331. || Encode_GH_To_J()
  2332. || Encode_GH_To_H()
  2333. || Encode_UGHT()
  2334. || Encode_GH_H_Part_Of_Other_Word()
  2335. || Encode_Silent_GH()
  2336. || Encode_GH_To_F())
  2337. {
  2338. return true;
  2339. }
  2340. MetaphAddExactApprox("G", "K");
  2341. m_current += 2;
  2342. return true;
  2343. }
  2344. return false;
  2345. }
  2346. /**
  2347. *
  2348. * @return true if encoding handled in this routine, false if not
  2349. *
  2350. */
  2351. boolean Encode_GH_After_Consonant()
  2352. {
  2353. // e.g. 'burgher', 'bingham'
  2354. if((m_current > 0)
  2355. && !IsVowel(m_current - 1)
  2356. // not e.g. 'greenhalgh'
  2357. && !(StringAt((m_current - 3), 5, "HALGH", "")
  2358. && ((m_current + 1) == m_last)))
  2359. {
  2360. MetaphAddExactApprox("G", "K");
  2361. m_current += 2;
  2362. return true;
  2363. }
  2364. return false;
  2365. }
  2366. /**
  2367. *
  2368. * @return true if encoding handled in this routine, false if not
  2369. *
  2370. */
  2371. boolean Encode_Initial_GH()
  2372. {
  2373. if(m_current < 3)
  2374. {
  2375. // e.g. "ghislane", "ghiradelli"
  2376. if(m_current == 0)
  2377. {
  2378. if(CharAt(m_current + 2) == 'I')
  2379. {
  2380. MetaphAdd("J");
  2381. }
  2382. else
  2383. {
  2384. MetaphAddExactApprox("G", "K");
  2385. }
  2386. m_current += 2;
  2387. return true;
  2388. }
  2389. }
  2390. return false;
  2391. }
  2392. /**
  2393. *
  2394. * @return true if encoding handled in this routine, false if not
  2395. *
  2396. */
  2397. boolean Encode_GH_To_J()
  2398. {
  2399. // e.g., 'greenhalgh', 'dunkenhalgh', english names
  2400. if(StringAt((m_current - 2), 4, "ALGH", "") && ((m_current + 1) == m_last))
  2401. {
  2402. MetaphAdd("J", "");
  2403. m_current += 2;
  2404. return true;
  2405. }
  2406. return false;
  2407. }
  2408. /**
  2409. *
  2410. * @return true if encoding handled in this routine, false if not
  2411. *
  2412. */
  2413. boolean Encode_GH_To_H()
  2414. {
  2415. // special cases
  2416. // e.g., 'donoghue', 'donaghy'
  2417. if((StringAt((m_current - 4), 4, "DONO", "DONA", "") && IsVowel(m_current + 2))
  2418. || StringAt((m_current - 5), 9, "CALLAGHAN", ""))
  2419. {
  2420. MetaphAdd("H");
  2421. m_current += 2;
  2422. return true;
  2423. }
  2424. return false;
  2425. }
  2426. /**
  2427. *
  2428. * @return true if encoding handled in this routine, false if not
  2429. *
  2430. */
  2431. boolean Encode_UGHT()
  2432. {
  2433. //e.g. "ought", "aught", "daughter", "slaughter"
  2434. if(StringAt((m_current - 1), 4, "UGHT", ""))
  2435. {
  2436. if ((StringAt((m_current - 3), 5, "LAUGH", "")
  2437. && !(StringAt((m_current - 4), 7, "SLAUGHT", "")
  2438. || StringAt((m_current - 3), 7, "LAUGHTO", "")))
  2439. || StringAt((m_current - 4), 6, "DRAUGH", ""))
  2440. {
  2441. MetaphAdd("FT");
  2442. }
  2443. else
  2444. {
  2445. MetaphAdd("T");
  2446. }
  2447. m_current += 3;
  2448. return true;
  2449. }
  2450. return false;
  2451. }
  2452. /**
  2453. *
  2454. * @return true if encoding handled in this routine, false if not
  2455. *
  2456. */
  2457. boolean Encode_GH_H_Part_Of_Other_Word()
  2458. {
  2459. // if the 'H' is the beginning of another word or syllable
  2460. if (StringAt((m_current + 1), 4, "HOUS", "HEAD", "HOLE", "HORN", "HARN", ""))
  2461. {
  2462. MetaphAddExactApprox("G", "K");
  2463. m_current += 2;
  2464. return true;
  2465. }
  2466. return false;
  2467. }
  2468. /**
  2469. *
  2470. * @return true if encoding handled in this routine, false if not
  2471. *
  2472. */
  2473. boolean Encode_Silent_GH()
  2474. {
  2475. //Parker's rule (with some further refinements) - e.g., 'hugh'
  2476. if(((((m_current > 1) && StringAt((m_current - 2), 1, "B", "H", "D", "G", "L", "") )
  2477. //e.g., 'bough'
  2478. || ((m_current > 2)
  2479. && StringAt((m_current - 3), 1, "B", "H", "D", "K", "W", "N", "P", "V", "")
  2480. && !StringAt(0, 6, "ENOUGH", ""))
  2481. //e.g., 'broughton'
  2482. || ((m_current > 3) && StringAt((m_current - 4), 1, "B", "H", "") )
  2483. //'plough', 'slaugh'
  2484. || ((m_current > 3) && StringAt((m_current - 4), 2, "PL", "SL", "") )
  2485. || ((m_current > 0)
  2486. // 'sigh', 'light'
  2487. && ((CharAt(m_current - 1) == 'I')
  2488. || StringAt(0, 4, "PUGH", "")
  2489. // e.g. 'MCDONAGH', 'MURTAGH', 'CREAGH'
  2490. || (StringAt((m_current - 1), 3, "AGH", "")
  2491. && ((m_current + 1) == m_last))
  2492. || StringAt((m_current - 4), 6, "GERAGH", "DRAUGH", "")
  2493. || (StringAt((m_current - 3), 5, "GAUGH", "GEOGH", "MAUGH", "")
  2494. && !StringAt(0, 9, "MCGAUGHEY", ""))
  2495. // exceptions to 'tough', 'rough', 'lough'
  2496. || (StringAt((m_current - 2), 4, "OUGH", "")
  2497. && (m_current > 3)
  2498. && !StringAt((m_current - 4), 6, "CCOUGH", "ENOUGH", "TROUGH", "CLOUGH", "")))))
  2499. // suffixes starting w/ vowel where "-GH-" is usually silent
  2500. && (StringAt((m_current - 3), 5, "VAUGH", "FEIGH", "LEIGH", "")
  2501. || StringAt((m_current - 2), 4, "HIGH", "TIGH", "")
  2502. || ((m_current + 1) == m_last)
  2503. || (StringAt((m_current + 2), 2, "IE", "EY", "ES", "ER", "ED", "TY", "")
  2504. && ((m_current + 3) == m_last)
  2505. && !StringAt((m_current - 5), 9, "GALLAGHER", ""))
  2506. || (StringAt((m_current + 2), 1, "Y", "") && ((m_current + 2) == m_last))
  2507. || (StringAt((m_current + 2), 3, "ING", "OUT", "") && ((m_current + 4) == m_last))
  2508. || (StringAt((m_current + 2), 4, "ERTY", "") && ((m_current + 5) == m_last))
  2509. || (!IsVowel(m_current + 2)
  2510. || StringAt((m_current - 3), 5, "GAUGH", "GEOGH", "MAUGH", "")
  2511. || StringAt((m_current - 4), 8, "BROUGHAM", ""))))
  2512. // exceptions where '-g-' pronounced
  2513. && !(StringAt(0, 6, "BALOGH", "SABAGH", "")
  2514. || StringAt((m_current - 2), 7, "BAGHDAD", "")
  2515. || StringAt((m_current - 3), 5, "WHIGH", "")
  2516. || StringAt((m_current - 5), 7, "SABBAGH", "AKHLAGH", "")))
  2517. {
  2518. // silent - do nothing
  2519. m_current += 2;
  2520. return true;
  2521. }
  2522. return false;
  2523. }
  2524. /**
  2525. *
  2526. * @return true if encoding handled in this routine, false if not
  2527. *
  2528. */
  2529. boolean Encode_GH_Special_Cases()
  2530. {
  2531. boolean handled = false;
  2532. // special case: 'hiccough' == 'hiccup'
  2533. if(StringAt((m_current - 6), 8, "HICCOUGH", ""))
  2534. {
  2535. MetaphAdd("P");
  2536. handled = true;
  2537. }
  2538. // special case: 'lough' alt spelling for scots 'loch'
  2539. else if(StringAt(0, 5, "LOUGH", ""))
  2540. {
  2541. MetaphAdd("K");
  2542. handled = true;
  2543. }
  2544. // hungarian
  2545. else if(StringAt(0, 6, "BALOGH", ""))
  2546. {
  2547. MetaphAddExactApprox("G", "", "K", "");
  2548. handled = true;
  2549. }
  2550. // "maclaughlin"
  2551. else if(StringAt((m_current - 3), 8, "LAUGHLIN", "COUGHLAN", "LOUGHLIN", ""))
  2552. {
  2553. MetaphAdd("K", "F");
  2554. handled = true;
  2555. }
  2556. else if(StringAt((m_current - 3), 5, "GOUGH", "")
  2557. || StringAt((m_current - 7), 9, "COLCLOUGH", ""))
  2558. {
  2559. MetaphAdd("", "F");
  2560. handled = true;
  2561. }
  2562. if(handled)
  2563. {
  2564. m_current += 2;
  2565. return true;
  2566. }
  2567. return false;
  2568. }
  2569. /**
  2570. *
  2571. * @return true if encoding handled in this routine, false if not
  2572. *
  2573. */
  2574. boolean Encode_GH_To_F()
  2575. {
  2576. // the cases covered here would fall under
  2577. // the GH_To_F rule below otherwise
  2578. if(Encode_GH_Special_Cases())
  2579. {
  2580. return true;
  2581. }
  2582. else
  2583. {
  2584. //e.g., 'laugh', 'cough', 'rough', 'tough'
  2585. if((m_current > 2)
  2586. && (CharAt(m_current - 1) == 'U')
  2587. && IsVowel(m_current - 2)
  2588. && StringAt((m_current - 3), 1, "C", "G", "L", "R", "T", "N", "S", "")
  2589. && !StringAt((m_current - 4), 8, "BREUGHEL", "FLAUGHER", ""))
  2590. {
  2591. MetaphAdd("F");
  2592. m_current += 2;
  2593. return true;
  2594. }
  2595. }
  2596. return false;
  2597. }
  2598. /**
  2599. * Encode some contexts where "g" is silent
  2600. *
  2601. * @return true if encoding handled in this routine, false if not
  2602. *
  2603. */
  2604. boolean Encode_Silent_G()
  2605. {
  2606. // e.g. "phlegm", "apothegm", "voigt"
  2607. if((((m_current + 1) == m_last)
  2608. && (StringAt((m_current - 1), 3, "EGM", "IGM", "AGM", "")
  2609. || StringAt(m_current, 2, "GT", "")))
  2610. || (StringAt(0, 5, "HUGES", "") && (m_length == 5)))
  2611. {
  2612. m_current++;
  2613. return true;
  2614. }
  2615. // vietnamese names e.g. "Nguyen" but not "Ng"
  2616. if(StringAt(0, 2, "NG", "") && (m_current != m_last))
  2617. {
  2618. m_current++;
  2619. return true;
  2620. }
  2621. return false;
  2622. }
  2623. /**
  2624. * ENcode "-GN-"
  2625. *
  2626. * @return true if encoding handled in this routine, false if not
  2627. *
  2628. */
  2629. boolean Encode_GN()
  2630. {
  2631. if(CharAt(m_current + 1) == 'N')
  2632. {
  2633. // 'align' 'sign', 'resign' but not 'resignation'
  2634. // also 'impugn', 'impugnable', but not 'repugnant'
  2635. if(((m_current > 1)
  2636. && ((StringAt((m_current - 1), 1, "I", "U", "E", "")
  2637. || StringAt((m_current - 3), 9, "LORGNETTE", "")
  2638. || StringAt((m_current - 2), 9, "LAGNIAPPE", "")
  2639. || StringAt((m_current - 2), 6, "COGNAC", "")
  2640. || StringAt((m_current - 3), 7, "CHAGNON", "")
  2641. || StringAt((m_current - 5), 9, "COMPAGNIE", "")
  2642. || StringAt((m_current - 4), 6, "BOLOGN", ""))
  2643. // Exceptions: following are cases where 'G' is pronounced
  2644. // in "assign" 'g' is silent, but not in "assignation"
  2645. && !(StringAt((m_current + 2), 5, "ATION", "")
  2646. || StringAt((m_current + 2), 4, "ATOR", "")
  2647. || StringAt((m_current + 2), 3, "ATE", "ITY", "")
  2648. // exception to exceptions, not pronounced:
  2649. || (StringAt((m_current + 2), 2, "AN", "AC", "IA", "UM", "")
  2650. && !(StringAt((m_current - 3), 8, "POIGNANT", "")
  2651. || StringAt((m_current - 2), 6, "COGNAC", "")))
  2652. || StringAt(0, 7, "SPIGNER", "STEGNER", "")
  2653. || (StringAt(0, 5, "SIGNE", "") && (m_length == 5))
  2654. || StringAt((m_current - 2), 5, "LIGNI", "LIGNO", "REGNA", "DIGNI", "WEGNE",
  2655. "TIGNE", "RIGNE", "REGNE", "TIGNO", "")
  2656. || StringAt((m_current - 2), 6, "SIGNAL", "SIGNIF", "SIGNAT", "")
  2657. || StringAt((m_current - 1), 5, "IGNIT", ""))
  2658. && !StringAt((m_current - 2), 6, "SIGNET", "LIGNEO", "") ))
  2659. //not e.g. 'cagney', 'magna'
  2660. || (((m_current + 2) == m_last)
  2661. && StringAt(m_current, 3, "GNE", "GNA", "")
  2662. && !StringAt((m_current - 2), 5, "SIGNA", "MAGNA", "SIGNE", "")))
  2663. {
  2664. MetaphAddExactApprox("N", "GN", "N", "KN");
  2665. }
  2666. else
  2667. {
  2668. MetaphAddExactApprox("GN", "KN");
  2669. }
  2670. m_current += 2;
  2671. return true;
  2672. }
  2673. return false;
  2674. }
  2675. /**
  2676. * Encode "-GL-"
  2677. *
  2678. * @return true if encoding handled in this routine, false if not
  2679. *
  2680. */
  2681. boolean Encode_GL()
  2682. {
  2683. //'tagliaro', 'puglia' BUT add K in alternative
  2684. // since americans sometimes do this
  2685. if(StringAt((m_current + 1), 3, "LIA", "LIO", "LIE", "")
  2686. && IsVowel(m_current - 1))
  2687. {
  2688. MetaphAddExactApprox("L", "GL", "L", "KL");
  2689. m_current += 2;
  2690. return true;
  2691. }
  2692. return false;
  2693. }
  2694. /**
  2695. *
  2696. * @return true if encoding handled in this routine, false if not
  2697. *
  2698. */
  2699. boolean Initial_G_Soft()
  2700. {
  2701. if(((StringAt((m_current + 1), 2, "EL", "EM", "EN", "EO", "ER", "ES", "IA", "IN", "IO", "IP", "IU", "YM", "YN", "YP", "YR", "EE", "")
  2702. || StringAt((m_current + 1), 3, "IRA", "IRO", ""))
  2703. // except for smaller set of cases where => K, e.g. "gerber"
  2704. && !(StringAt((m_current + 1), 3, "ELD", "ELT", "ERT", "INZ", "ERH", "ITE", "ERD", "ERL", "ERN",
  2705. "INT", "EES", "EEK", "ELB", "EER", "")
  2706. || StringAt((m_current + 1), 4, "ERSH", "ERST", "INSB", "INGR", "EROW", "ERKE", "EREN", "")
  2707. || StringAt((m_current + 1), 5, "ELLER", "ERDIE", "ERBER", "ESUND", "ESNER", "INGKO", "INKGO",
  2708. "IPPER", "ESELL", "IPSON", "EEZER", "ERSON", "ELMAN", "")
  2709. || StringAt((m_current + 1), 6, "ESTALT", "ESTAPO", "INGHAM", "ERRITY", "ERRISH", "ESSNER", "ENGLER", "")
  2710. || StringAt((m_current + 1), 7, "YNAECOL", "YNECOLO", "ENTHNER", "ERAGHTY", "")
  2711. || StringAt((m_current + 1), 8, "INGERICH", "EOGHEGAN", "")))
  2712. ||(IsVowel(m_current + 1)
  2713. && (StringAt((m_current + 1), 3, "EE ", "EEW", "")
  2714. || (StringAt((m_current + 1), 3, "IGI", "IRA", "IBE", "AOL", "IDE", "IGL", "")
  2715. && !StringAt((m_current + 1), 5, "IDEON", "") )
  2716. || StringAt((m_current + 1), 4, "ILES", "INGI", "ISEL", "")
  2717. || (StringAt((m_current + 1), 5, "INGER", "") && !StringAt((m_current + 1), 8, "INGERICH", ""))
  2718. || StringAt((m_current + 1), 5, "IBBER", "IBBET", "IBLET", "IBRAN", "IGOLO", "IRARD", "IGANT", "")
  2719. || StringAt((m_current + 1), 6, "IRAFFE", "EEWHIZ","")
  2720. || StringAt((m_current + 1), 7, "ILLETTE", "IBRALTA", ""))))
  2721. {
  2722. return true;
  2723. }
  2724. return false;
  2725. }
  2726. /**
  2727. * Encode cases where 'G' is at start of word followed
  2728. * by a "front" vowel e.g. 'E', 'I', 'Y'
  2729. *
  2730. * @return true if encoding handled in this routine, false if not
  2731. *
  2732. */
  2733. boolean Encode_Initial_G_Front_Vowel()
  2734. {
  2735. // 'g' followed by vowel at beginning
  2736. if((m_current == 0) && Front_Vowel(m_current + 1))
  2737. {
  2738. // special case "gila" as in "gila monster"
  2739. if(StringAt((m_current + 1), 3, "ILA", "")
  2740. && (m_length == 4))
  2741. {
  2742. MetaphAdd("H");
  2743. }
  2744. else if(Initial_G_Soft())
  2745. {
  2746. MetaphAddExactApprox("J", "G", "J", "K");
  2747. }
  2748. else
  2749. {
  2750. // only code alternate 'J' if front vowel
  2751. if((m_inWord.charAt(m_current + 1) == 'E') || (m_inWord.charAt(m_current + 1) == 'I'))
  2752. {
  2753. MetaphAddExactApprox("G", "J", "K", "J");
  2754. }
  2755. else
  2756. {
  2757. MetaphAddExactApprox("G", "K");
  2758. }
  2759. }
  2760. AdvanceCounter(2, 1);
  2761. return true;
  2762. }
  2763. return false;
  2764. }
  2765. /**
  2766. * Encode "-NGER-"
  2767. *
  2768. * @return true if encoding handled in this routine, false if not
  2769. *
  2770. */
  2771. boolean Encode_NGER()
  2772. {
  2773. if((m_current > 1)
  2774. && StringAt((m_current - 1), 4, "NGER", ""))
  2775. {
  2776. // default 'G' => J such as 'ranger', 'stranger', 'manger', 'messenger', 'orangery', 'granger'
  2777. // 'boulanger', 'challenger', 'danger', 'changer', 'harbinger', 'lounger', 'ginger', 'passenger'
  2778. // except for these the following
  2779. if(!(RootOrInflections(m_inWord, "ANGER")
  2780. || RootOrInflections(m_inWord, "LINGER")
  2781. || RootOrInflections(m_inWord, "MALINGER")
  2782. || RootOrInflections(m_inWord, "FINGER")
  2783. || (StringAt((m_current - 3), 4, "HUNG", "FING", "BUNG", "WING", "RING", "DING", "ZENG",
  2784. "ZING", "JUNG", "LONG", "PING", "CONG", "MONG", "BANG",
  2785. "GANG", "HANG", "LANG", "SANG", "SING", "WANG", "ZANG", "")
  2786. // exceptions to above where 'G' => J
  2787. && !(StringAt((m_current - 6), 7, "BOULANG", "SLESING", "KISSING", "DERRING", "")
  2788. || StringAt((m_current - 8), 9, "SCHLESING", "")
  2789. || StringAt((m_current - 5), 6, "SALING", "BELANG", "")
  2790. || StringAt((m_current - 6), 7, "BARRING", "")
  2791. || StringAt((m_current - 6), 9, "PHALANGER", "")
  2792. || StringAt((m_current - 4), 5, "CHANG", "")))
  2793. || StringAt((m_current - 4), 5, "STING", "YOUNG", "")
  2794. || StringAt((m_current - 5), 6, "STRONG", "")
  2795. || StringAt(0, 3, "UNG", "ENG", "ING", "")
  2796. || StringAt(m_current, 6, "GERICH", "")
  2797. || StringAt(0, 6, "SENGER", "")
  2798. || StringAt((m_current - 3), 6, "WENGER", "MUNGER", "SONGER", "KINGER", "")
  2799. || StringAt((m_current - 4), 7, "FLINGER", "SLINGER", "STANGER", "STENGER", "KLINGER", "CLINGER", "")
  2800. || StringAt((m_current - 5), 8, "SPRINGER", "SPRENGER", "")
  2801. || StringAt((m_current - 3), 7, "LINGERF", "")
  2802. || StringAt((m_current - 2), 7, "ANGERLY", "ANGERBO", "INGERSO", "") ))
  2803. {
  2804. MetaphAddExactApprox("J", "G", "J", "K");
  2805. }
  2806. else
  2807. {
  2808. MetaphAddExactApprox("G", "J", "K", "J");
  2809. }
  2810. AdvanceCounter(2, 1);
  2811. return true;
  2812. }
  2813. return false;
  2814. }
  2815. /**
  2816. * Encode "-GER-"
  2817. *
  2818. * @return true if encoding handled in this routine, false if not
  2819. *
  2820. */
  2821. boolean Encode_GER()
  2822. {
  2823. if((m_current > 0)
  2824. && StringAt((m_current + 1), 2, "ER", ""))
  2825. {
  2826. // Exceptions to 'GE' where 'G' => K
  2827. // e.g. "JAGER", "TIGER", "LIGER", "LAGER", "LUGER", "AUGER", "EAGER", "HAGER", "SAGER"
  2828. if((((m_current == 2) && IsVowel(m_current - 1) && !IsVowel(m_current - 2)
  2829. && !(StringAt((m_current - 2), 5, "PAGER", "WAGER", "NIGER", "ROGER", "LEGER", "CAGER", ""))
  2830. || StringAt((m_current - 2), 5, "AUGER", "EAGER", "INGER", "YAGER", ""))
  2831. || StringAt((m_current - 3), 6, "SEEGER", "JAEGER", "GEIGER", "KRUGER", "SAUGER", "BURGER",
  2832. "MEAGER", "MARGER", "RIEGER", "YAEGER", "STEGER", "PRAGER", "SWIGER",
  2833. "YERGER", "TORGER", "FERGER", "HILGER", "ZEIGER", "YARGER",
  2834. "COWGER", "CREGER", "KROGER", "KREGER", "GRAGER", "STIGER", "BERGER", "")
  2835. // 'berger' but not 'bergerac'
  2836. || (StringAt((m_current - 3), 6, "BERGER", "") && ((m_current + 2) == m_last))
  2837. || StringAt((m_current - 4), 7, "KREIGER", "KRUEGER", "METZGER", "KRIEGER", "KROEGER", "STEIGER",
  2838. "DRAEGER", "BUERGER", "BOERGER", "FIBIGER", "")
  2839. // e.g. 'harshbarger', 'winebarger'
  2840. || (StringAt((m_current - 3), 6, "BARGER", "") && (m_current > 4))
  2841. // e.g. 'weisgerber'
  2842. || (StringAt(m_current, 6, "GERBER", "") && (m_current > 0))
  2843. || StringAt((m_current - 5), 8, "SCHWAGER", "LYBARGER", "SPRENGER", "GALLAGER", "WILLIGER", "")
  2844. || StringAt(0, 4, "HARGER", "")
  2845. || (StringAt(0, 4, "AGER", "EGER", "") && (m_length == 4))
  2846. || StringAt((m_current - 1), 6, "YGERNE", "")
  2847. || StringAt((m_current - 6), 9, "SCHWEIGER", ""))
  2848. && !(StringAt((m_current - 5), 10, "BELLIGEREN", "")
  2849. || StringAt(0, 7, "MARGERY", "")
  2850. || StringAt((m_current - 3), 8, "BERGERAC", "")))
  2851. {
  2852. if(SlavoGermanic())
  2853. {
  2854. MetaphAddExactApprox("G", "K");
  2855. }
  2856. else
  2857. {
  2858. MetaphAddExactApprox("G", "J", "K", "J");
  2859. }
  2860. }
  2861. else
  2862. {
  2863. MetaphAddExactApprox("J", "G", "J", "K");
  2864. }
  2865. AdvanceCounter(2, 1);
  2866. return true;
  2867. }
  2868. return false;
  2869. }
  2870. /**
  2871. * ENcode "-GEL-"
  2872. *
  2873. * @return true if encoding handled in this routine, false if not
  2874. *
  2875. */
  2876. boolean Encode_GEL()
  2877. {
  2878. // more likely to be "-GEL-" => JL
  2879. if(StringAt((m_current + 1), 2, "EL", "")
  2880. && (m_current > 0))
  2881. {
  2882. // except for
  2883. // "BAGEL", "HEGEL", "HUGEL", "KUGEL", "NAGEL", "VOGEL", "FOGEL", "PAGEL"
  2884. if(((m_length == 5)
  2885. && IsVowel(m_current - 1)
  2886. && !IsVowel(m_current - 2)
  2887. && !StringAt((m_current - 2), 5, "NIGEL", "RIGEL", ""))
  2888. // or the following as combining forms
  2889. || StringAt((m_current - 2), 5, "ENGEL", "HEGEL", "NAGEL", "VOGEL", "")
  2890. || StringAt((m_current - 3), 6, "MANGEL", "WEIGEL", "FLUGEL", "RANGEL", "HAUGEN", "RIEGEL", "VOEGEL", "")
  2891. || StringAt((m_current - 4), 7, "SPEIGEL", "STEIGEL", "WRANGEL", "SPIEGEL", "")
  2892. || StringAt((m_current - 4), 8, "DANEGELD", ""))
  2893. {
  2894. if(SlavoGermanic())
  2895. {
  2896. MetaphAddExactApprox("G", "K");
  2897. }
  2898. else
  2899. {
  2900. MetaphAddExactApprox("G", "J", "K", "J");
  2901. }
  2902. }
  2903. else
  2904. {
  2905. MetaphAddExactApprox("J", "G", "J", "K");
  2906. }
  2907. AdvanceCounter(2, 1);
  2908. return true;
  2909. }
  2910. return false;
  2911. }
  2912. /**
  2913. * Encode "-G-" followed by a vowel when non-initial leter.
  2914. * Default for this is a 'J' sound, so check exceptions where
  2915. * it is pronounced 'G'
  2916. *
  2917. * @return true if encoding handled in this routine, false if not
  2918. *
  2919. */
  2920. boolean Encode_Non_Initial_G_Front_Vowel()
  2921. {
  2922. // -gy-, gi-, ge-
  2923. if(StringAt((m_current + 1), 1, "E", "I", "Y", ""))
  2924. {
  2925. // '-ge' at end
  2926. // almost always 'j 'sound
  2927. if(StringAt(m_current, 2, "GE", "") && (m_current == (m_last - 1)))
  2928. {
  2929. if(Hard_GE_At_End())
  2930. {
  2931. if(SlavoGermanic())
  2932. {
  2933. MetaphAddExactApprox("G", "K");
  2934. }
  2935. else
  2936. {
  2937. MetaphAddExactApprox("G", "J", "K", "J");
  2938. }
  2939. }
  2940. else
  2941. {
  2942. MetaphAdd("J");
  2943. }
  2944. }
  2945. else
  2946. {
  2947. if(Internal_Hard_G())
  2948. {
  2949. // don't encode KG or KK if e.g. "mcgill"
  2950. if(!((m_current == 2) && StringAt(0, 2, "MC", ""))
  2951. || ((m_current == 3) && StringAt(0, 3, "MAC", "")))
  2952. {
  2953. if(SlavoGermanic())
  2954. {
  2955. MetaphAddExactApprox("G", "K");
  2956. }
  2957. else
  2958. {
  2959. MetaphAddExactApprox("G", "J", "K", "J");
  2960. }
  2961. }
  2962. }
  2963. else
  2964. {
  2965. MetaphAddExactApprox("J", "G", "J", "K");
  2966. }
  2967. }
  2968. AdvanceCounter(2, 1);
  2969. return true;
  2970. }
  2971. return false;
  2972. }
  2973. /*
  2974. * Detect german names and other words that have
  2975. * a 'hard' 'g' in the context of "-ge" at end
  2976. *
  2977. * @return true if encoding handled in this routine, false if not
  2978. */
  2979. boolean Hard_GE_At_End()
  2980. {
  2981. if(StringAt(0, 6, "RENEGE", "STONGE", "STANGE", "PRANGE", "KRESGE", "")
  2982. || StringAt(0, 5, "BYRGE", "BIRGE", "BERGE", "HAUGE", "")
  2983. || StringAt(0, 4, "HAGE", "")
  2984. || StringAt(0, 5, "LANGE", "SYNGE", "BENGE", "RUNGE", "HELGE", "")
  2985. || StringAt(0, 4, "INGE", "LAGE", ""))
  2986. {
  2987. return true;
  2988. }
  2989. return false;
  2990. }
  2991. /**
  2992. * Exceptions to default encoding to 'J':
  2993. * encode "-G-" to 'G' in "-g<frontvowel>-" words
  2994. * where we are not at "-GE" at the end of the word
  2995. *
  2996. * @return true if encoding handled in this routine, false if not
  2997. *
  2998. */
  2999. boolean Internal_Hard_G()
  3000. {
  3001. // if not "-GE" at end
  3002. if(!(((m_current + 1) == m_last) && (CharAt(m_current + 1) == 'E') )
  3003. && (Internal_Hard_NG()
  3004. || Internal_Hard_GEN_GIN_GET_GIT()
  3005. || Internal_Hard_G_Open_Syllable()
  3006. || Internal_Hard_G_Other()))
  3007. {
  3008. return true;
  3009. }
  3010. return false;
  3011. }
  3012. /**
  3013. * Detect words where "-ge-" or "-gi-" get a 'hard' 'g'
  3014. * even though this is usually a 'soft' 'g' context
  3015. *
  3016. * @return true if 'hard' 'g' detected
  3017. *
  3018. */
  3019. boolean Internal_Hard_G_Other()
  3020. {
  3021. if((StringAt(m_current, 4, "GETH", "GEAR", "GEIS", "GIRL", "GIVI", "GIVE", "GIFT",
  3022. "GIRD", "GIRT", "GILV", "GILD", "GELD", "")
  3023. && !StringAt((m_current - 3), 6, "GINGIV", "") )
  3024. // "gish" but not "largish"
  3025. || (StringAt((m_current + 1), 3, "ISH", "") && (m_current > 0) && !StringAt(0, 4, "LARG", ""))
  3026. || (StringAt((m_current - 2), 5, "MAGED", "MEGID", "") && !((m_current + 2) == m_last))
  3027. || StringAt(m_current, 3, "GEZ", "")
  3028. || StringAt(0, 4, "WEGE", "HAGE", "")
  3029. || (StringAt((m_current - 2), 6, "ONGEST", "UNGEST", "")
  3030. && ((m_current + 3) == m_last)
  3031. && !StringAt((m_current - 3), 7, "CONGEST", ""))
  3032. || StringAt(0, 5, "VOEGE", "BERGE", "HELGE", "")
  3033. || (StringAt(0, 4, "ENGE", "BOGY", "") && (m_length == 4))
  3034. || StringAt(m_current, 6, "GIBBON", "")
  3035. || StringAt(0, 10, "CORREGIDOR", "")
  3036. || StringAt(0, 8, "INGEBORG", "")
  3037. || (StringAt(m_current, 4, "GILL", "")
  3038. && (((m_current + 3) == m_last) || ((m_current + 4) == m_last))
  3039. && !StringAt(0, 8, "STURGILL", "")))
  3040. {
  3041. return true;
  3042. }
  3043. return false;
  3044. }
  3045. /**
  3046. * Detect words where "-gy-", "-gie-", "-gee-",
  3047. * or "-gio-" get a 'hard' 'g' even though this is
  3048. * usually a 'soft' 'g' context
  3049. *
  3050. * @return true if 'hard' 'g' detected
  3051. *
  3052. */
  3053. boolean Internal_Hard_G_Open_Syllable()
  3054. {
  3055. if(StringAt((m_current + 1), 3, "EYE", "")
  3056. || StringAt((m_current - 2), 4, "FOGY", "POGY", "YOGI", "")
  3057. || StringAt((m_current - 2), 5, "MAGEE", "MCGEE", "HAGIO", "")
  3058. || StringAt((m_current - 1), 4, "RGEY", "OGEY", "")
  3059. || StringAt((m_current - 3), 5, "HOAGY", "STOGY", "PORGY", "")
  3060. || StringAt((m_current - 5), 8, "CARNEGIE", "")
  3061. || (StringAt((m_current - 1), 4, "OGEY", "OGIE", "") && ((m_current + 2) == m_last)))
  3062. {
  3063. return true;
  3064. }
  3065. return false;
  3066. }
  3067. /**
  3068. * Detect a number of contexts, mostly german names, that
  3069. * take a 'hard' 'g'.
  3070. *
  3071. * @return true if 'hard' 'g' detected, false if not
  3072. *
  3073. */
  3074. boolean Internal_Hard_GEN_GIN_GET_GIT()
  3075. {
  3076. if((StringAt((m_current - 3), 6, "FORGET", "TARGET", "MARGIT", "MARGET", "TURGEN",
  3077. "BERGEN", "MORGEN", "JORGEN", "HAUGEN", "JERGEN",
  3078. "JURGEN", "LINGEN", "BORGEN", "LANGEN", "KLAGEN", "STIGER", "BERGER", "")
  3079. && !StringAt(m_current, 7, "GENETIC", "GENESIS", "")
  3080. && !StringAt((m_current - 4), 8, "PLANGENT", ""))
  3081. || (StringAt((m_current - 3), 6, "BERGIN", "FEAGIN", "DURGIN", "") && ((m_current + 2) == m_last))
  3082. || (StringAt((m_current - 2), 5, "ENGEN", "") && !StringAt((m_current + 3), 3, "DER", "ETI", "ESI", ""))
  3083. || StringAt((m_current - 4), 7, "JUERGEN", "")
  3084. || StringAt(0, 5, "NAGIN", "MAGIN", "HAGIN", "")
  3085. || (StringAt(0, 5, "ENGIN", "DEGEN", "LAGEN", "MAGEN", "NAGIN", "") && (m_length == 5))
  3086. || (StringAt((m_current - 2), 5, "BEGET", "BEGIN", "HAGEN", "FAGIN",
  3087. "BOGEN", "WIGIN", "NTGEN", "EIGEN",
  3088. "WEGEN", "WAGEN", "")
  3089. && !StringAt((m_current - 5), 8, "OSPHAGEN", "")))
  3090. {
  3091. return true;
  3092. }
  3093. return false;
  3094. }
  3095. /**
  3096. * Detect a number of contexts of '-ng-' that will
  3097. * take a 'hard' 'g' despite being followed by a
  3098. * front vowel.
  3099. *
  3100. * @return true if 'hard' 'g' detected, false if not
  3101. *
  3102. */
  3103. boolean Internal_Hard_NG()
  3104. {
  3105. if((StringAt((m_current - 3), 4, "DANG", "FANG", "SING", "")
  3106. // exception to exception
  3107. && !StringAt((m_current - 5), 8, "DISINGEN", "") )
  3108. || StringAt(0, 5, "INGEB", "ENGEB", "")
  3109. || (StringAt((m_current - 3), 4, "RING", "WING", "HANG", "LONG", "")
  3110. && !(StringAt((m_current - 4), 5, "CRING", "FRING", "ORANG", "TWING", "CHANG", "PHANG", "")
  3111. || StringAt((m_current - 5), 6, "SYRING", "")
  3112. || StringAt((m_current - 3), 7, "RINGENC", "RINGENT", "LONGITU", "LONGEVI", "")
  3113. // e.g. 'longino', 'mastrangelo'
  3114. || (StringAt(m_current, 4, "GELO", "GINO", "") && ((m_current + 3) == m_last))))
  3115. || (StringAt((m_current - 1), 3, "NGY", "")
  3116. // exceptions to exception
  3117. && !(StringAt((m_current - 3), 5, "RANGY", "MANGY", "MINGY", "")
  3118. || StringAt((m_current - 4), 6, "SPONGY", "STINGY", ""))))
  3119. {
  3120. return true;
  3121. }
  3122. return false;
  3123. }
  3124. /**
  3125. * Encode special case where "-GA-" => J
  3126. *
  3127. * @return true if encoding handled in this routine, false if not
  3128. *
  3129. */
  3130. boolean Encode_GA_To_J()
  3131. {
  3132. // 'margary', 'margarine'
  3133. if((StringAt((m_current - 3), 7, "MARGARY", "MARGARI", "")
  3134. // but not in spanish forms such as "margatita"
  3135. && !StringAt((m_current - 3), 8, "MARGARIT", ""))
  3136. || StringAt(0, 4, "GAOL", "")
  3137. || StringAt((m_current - 2), 5, "ALGAE", ""))
  3138. {
  3139. MetaphAddExactApprox("J", "G", "J", "K");
  3140. AdvanceCounter(2, 1);
  3141. return true;
  3142. }
  3143. return false;
  3144. }
  3145. /**
  3146. * Encode 'H'
  3147. *
  3148. *
  3149. */
  3150. void Encode_H()
  3151. {
  3152. if(Encode_Initial_Silent_H()
  3153. || Encode_Initial_HS()
  3154. || Encode_Initial_HU_HW()
  3155. || Encode_Non_Initial_Silent_H())
  3156. {
  3157. return;
  3158. }
  3159. //only keep if first & before vowel or btw. 2 vowels
  3160. if(!Encode_H_Pronounced())
  3161. {
  3162. //also takes care of 'HH'
  3163. m_current++;
  3164. }
  3165. }
  3166. /**
  3167. * Encode cases where initial 'H' is not pronounced (in American)
  3168. *
  3169. * @return true if encoding handled in this routine, false if not
  3170. *
  3171. */
  3172. boolean Encode_Initial_Silent_H()
  3173. {
  3174. //'hour', 'herb', 'heir', 'honor'
  3175. if(StringAt((m_current + 1), 3, "OUR", "ERB", "EIR", "")
  3176. || StringAt((m_current + 1), 4, "ONOR", "")
  3177. || StringAt((m_current + 1), 5, "ONOUR", "ONEST", ""))
  3178. {
  3179. // british pronounce H in this word
  3180. // americans give it 'H' for the name,
  3181. // no 'H' for the plant
  3182. if((m_current == 0) && StringAt(m_current, 4, "HERB", ""))
  3183. {
  3184. if(m_encodeVowels)
  3185. {
  3186. MetaphAdd("HA", "A");
  3187. }
  3188. else
  3189. {
  3190. MetaphAdd("H", "A");
  3191. }
  3192. }
  3193. else if((m_current == 0) || m_encodeVowels)
  3194. {
  3195. MetaphAdd("A");
  3196. }
  3197. m_current++;
  3198. // don't encode vowels twice
  3199. m_current = SkipVowels(m_current);
  3200. return true;
  3201. }
  3202. return false;
  3203. }
  3204. /**
  3205. * Encode "HS-"
  3206. *
  3207. * @return true if encoding handled in this routine, false if not
  3208. *
  3209. */
  3210. boolean Encode_Initial_HS()
  3211. {
  3212. // old chinese pinyin transliteration
  3213. // e.g., 'HSIAO'
  3214. if ((m_current == 0) && StringAt(0, 2, "HS", ""))
  3215. {
  3216. MetaphAdd("X");
  3217. m_current += 2;
  3218. return true;
  3219. }
  3220. return false;
  3221. }
  3222. /**
  3223. * Encode cases where "HU-" is pronounced as part of a vowel dipthong
  3224. *
  3225. * @return true if encoding handled in this routine, false if not
  3226. *
  3227. */
  3228. boolean Encode_Initial_HU_HW()
  3229. {
  3230. // spanish spellings and chinese pinyin transliteration
  3231. if (StringAt(0, 3, "HUA", "HUE", "HWA", ""))
  3232. {
  3233. if(!StringAt(m_current, 4, "HUEY", ""))
  3234. {
  3235. MetaphAdd("A");
  3236. if(!m_encodeVowels)
  3237. {
  3238. m_current += 3;
  3239. }
  3240. else
  3241. {
  3242. m_current++;
  3243. // don't encode vowels twice
  3244. while(IsVowel(m_current) || (CharAt(m_current) == 'W'))
  3245. {
  3246. m_current++;
  3247. }
  3248. }
  3249. return true;
  3250. }
  3251. }
  3252. return false;
  3253. }
  3254. /**
  3255. * Encode cases where 'H' is silent between vowels
  3256. *
  3257. * @return true if encoding handled in this routine, false if not
  3258. *
  3259. */
  3260. boolean Encode_Non_Initial_Silent_H()
  3261. {
  3262. //exceptions - 'h' not pronounced
  3263. // "PROHIB" BUT NOT "PROHIBIT"
  3264. if(StringAt((m_current - 2), 5, "NIHIL", "VEHEM", "LOHEN", "NEHEM",
  3265. "MAHON", "MAHAN", "COHEN", "GAHAN", "")
  3266. || StringAt((m_current - 3), 6, "GRAHAM", "PROHIB", "FRAHER",
  3267. "TOOHEY", "TOUHEY", "")
  3268. || StringAt((m_current - 3), 5, "TOUHY", "")
  3269. || StringAt(0, 9, "CHIHUAHUA", ""))
  3270. {
  3271. if(!m_encodeVowels)
  3272. {
  3273. m_current += 2;
  3274. }
  3275. else
  3276. {
  3277. m_current++;
  3278. // don't encode vowels twice
  3279. m_current = SkipVowels(m_current);
  3280. }
  3281. return true;
  3282. }
  3283. return false;
  3284. }
  3285. /**
  3286. * Encode cases where 'H' is pronounced
  3287. *
  3288. * @return true if encoding handled in this routine, false if not
  3289. *
  3290. */
  3291. boolean Encode_H_Pronounced()
  3292. {
  3293. if((((m_current == 0)
  3294. || IsVowel(m_current - 1)
  3295. || ((m_current > 0)
  3296. && (CharAt(m_current - 1) == 'W')))
  3297. && IsVowel(m_current + 1))
  3298. // e.g. 'alWahhab'
  3299. || ((CharAt(m_current + 1) == 'H') && IsVowel(m_current + 2)))
  3300. {
  3301. MetaphAdd("H");
  3302. AdvanceCounter(2, 1);
  3303. return true;
  3304. }
  3305. return false;
  3306. }
  3307. /**
  3308. * Encode 'J'
  3309. *
  3310. */
  3311. void Encode_J()
  3312. {
  3313. if(Encode_Spanish_J()
  3314. || Encode_Spanish_OJ_UJ())
  3315. {
  3316. return;
  3317. }
  3318. Encode_Other_J();
  3319. }
  3320. /**
  3321. * Encode cases where initial or medial "j" is in a spanish word or name
  3322. *
  3323. * @return true if encoding handled in this routine, false if not
  3324. *
  3325. */
  3326. boolean Encode_Spanish_J()
  3327. {
  3328. //obvious spanish, e.g. "jose", "san jacinto"
  3329. if((StringAt((m_current + 1), 3, "UAN", "ACI", "ALI", "EFE", "ICA", "IME", "OAQ", "UAR", "")
  3330. && !StringAt(m_current, 8, "JIMERSON", "JIMERSEN", ""))
  3331. || (StringAt((m_current + 1), 3, "OSE", "") && ((m_current + 3) == m_last))
  3332. || StringAt((m_current + 1), 4, "EREZ", "UNTA", "AIME", "AVIE", "AVIA", "")
  3333. || StringAt((m_current + 1), 6, "IMINEZ", "ARAMIL", "")
  3334. || (((m_current + 2) == m_last) && StringAt((m_current - 2), 5, "MEJIA", ""))
  3335. || StringAt((m_current - 2), 5, "TEJED", "TEJAD", "LUJAN", "FAJAR", "BEJAR", "BOJOR", "CAJIG",
  3336. "DEJAS", "DUJAR", "DUJAN", "MIJAR", "MEJOR", "NAJAR",
  3337. "NOJOS", "RAJED", "RIJAL", "REJON", "TEJAN", "UIJAN", "")
  3338. || StringAt((m_current - 3), 8, "ALEJANDR", "GUAJARDO", "TRUJILLO", "")
  3339. || (StringAt((m_current - 2), 5, "RAJAS", "") && (m_current > 2))
  3340. || (StringAt((m_current - 2), 5, "MEJIA", "") && !StringAt((m_current - 2), 6, "MEJIAN", ""))
  3341. || StringAt((m_current - 1), 5, "OJEDA", "")
  3342. || StringAt((m_current - 3), 5, "LEIJA", "MINJA", "")
  3343. || StringAt((m_current - 3), 6, "VIAJES", "GRAJAL", "")
  3344. || StringAt(m_current, 8, "JAUREGUI", "")
  3345. || StringAt((m_current - 4), 8, "HINOJOSA", "")
  3346. || StringAt(0, 4, "SAN ", "")
  3347. || (((m_current + 1) == m_last)
  3348. && (CharAt(m_current + 1) == 'O')
  3349. // exceptions
  3350. && !(StringAt(0, 4, "TOJO", "")
  3351. || StringAt(0, 5, "BANJO", "")
  3352. || StringAt(0, 6, "MARYJO", ""))))
  3353. {
  3354. // americans pronounce "juan" as 'wan'
  3355. // and "marijuana" and "tijuana" also
  3356. // do not get the 'H' as in spanish, so
  3357. // just treat it like a vowel in these cases
  3358. if(!(StringAt(m_current, 4, "JUAN", "") || StringAt(m_current, 4, "JOAQ", "")))
  3359. {
  3360. MetaphAdd("H");
  3361. }
  3362. else
  3363. {
  3364. if(m_current == 0)
  3365. {
  3366. MetaphAdd("A");
  3367. }
  3368. }
  3369. AdvanceCounter(2, 1);
  3370. return true;
  3371. }
  3372. // Jorge gets 2nd HARHA. also JULIO, JESUS
  3373. if(StringAt((m_current + 1), 4, "ORGE", "ULIO", "ESUS", "")
  3374. && !StringAt(0, 6, "JORGEN", ""))
  3375. {
  3376. // get both consonants for "jorge"
  3377. if(((m_current + 4) == m_last) && StringAt((m_current + 1), 4, "ORGE", ""))
  3378. {
  3379. if(m_encodeVowels)
  3380. {
  3381. MetaphAdd("JARJ", "HARHA");
  3382. }
  3383. else
  3384. {
  3385. MetaphAdd("JRJ", "HRH");
  3386. }
  3387. AdvanceCounter(5, 5);
  3388. return true;
  3389. }
  3390. MetaphAdd("J", "H");
  3391. AdvanceCounter(2, 1);
  3392. return true;
  3393. }
  3394. return false;
  3395. }
  3396. /**
  3397. * Encode cases where 'J' is clearly in a german word or name
  3398. * that americans pronounce in the german fashion
  3399. *
  3400. * @return true if encoding handled in this routine, false if not
  3401. *
  3402. */
  3403. boolean Encode_German_J()
  3404. {
  3405. if(StringAt((m_current + 1), 2, "AH", "")
  3406. || (StringAt((m_current + 1), 5, "OHANN", "") && ((m_current + 5) == m_last))
  3407. || (StringAt((m_current + 1), 3, "UNG", "") && !StringAt((m_current + 1), 4, "UNGL", ""))
  3408. || StringAt((m_current + 1), 3, "UGO", ""))
  3409. {
  3410. MetaphAdd("A");
  3411. AdvanceCounter(2, 1);
  3412. return true;
  3413. }
  3414. return false;
  3415. }
  3416. /**
  3417. * Encode "-JOJ-" and "-JUJ-" as spanish words
  3418. *
  3419. * @return true if encoding handled in this routine, false if not
  3420. *
  3421. */
  3422. boolean Encode_Spanish_OJ_UJ()
  3423. {
  3424. if(StringAt((m_current + 1), 5, "OJOBA", "UJUY ", ""))
  3425. {
  3426. if(m_encodeVowels)
  3427. {
  3428. MetaphAdd("HAH");
  3429. }
  3430. else
  3431. {
  3432. MetaphAdd("HH");
  3433. }
  3434. AdvanceCounter(4, 3);
  3435. return true;
  3436. }
  3437. return false;
  3438. }
  3439. /**
  3440. * Encode 'J' => J
  3441. *
  3442. * @return true if encoding handled in this routine, false if not
  3443. *
  3444. */
  3445. boolean Encode_J_To_J()
  3446. {
  3447. if(IsVowel(m_current + 1))
  3448. {
  3449. if((m_current == 0)
  3450. && Names_Beginning_With_J_That_Get_Alt_Y())
  3451. {
  3452. // 'Y' is a vowel so encode
  3453. // is as 'A'
  3454. if(m_encodeVowels)
  3455. {
  3456. MetaphAdd("JA", "A");
  3457. }
  3458. else
  3459. {
  3460. MetaphAdd("J", "A");
  3461. }
  3462. }
  3463. else
  3464. {
  3465. if(m_encodeVowels)
  3466. {
  3467. MetaphAdd("JA");
  3468. }
  3469. else
  3470. {
  3471. MetaphAdd("J");
  3472. }
  3473. }
  3474. m_current++;
  3475. m_current = SkipVowels(m_current);
  3476. return false;
  3477. }
  3478. else
  3479. {
  3480. MetaphAdd("J");
  3481. m_current++;
  3482. return true;
  3483. }
  3484. // return false;
  3485. }
  3486. /**
  3487. * Encode 'J' toward end in spanish words
  3488. *
  3489. * @return true if encoding handled in this routine, false if not
  3490. *
  3491. */
  3492. boolean Encode_Spanish_J_2()
  3493. {
  3494. // spanish forms e.g. "brujo", "badajoz"
  3495. if((((m_current - 2) == 0)
  3496. && StringAt((m_current - 2), 4, "BOJA", "BAJA", "BEJA", "BOJO", "MOJA", "MOJI", "MEJI", ""))
  3497. || (((m_current - 3) == 0)
  3498. && StringAt((m_current - 3), 5, "FRIJO", "BRUJO", "BRUJA", "GRAJE", "GRIJA", "LEIJA", "QUIJA", ""))
  3499. || (((m_current + 3) == m_last)
  3500. && StringAt((m_current - 1), 5, "AJARA", ""))
  3501. || (((m_current + 2) == m_last)
  3502. && StringAt((m_current - 1), 4, "AJOS", "EJOS", "OJAS", "OJOS", "UJON", "AJOZ", "AJAL", "UJAR", "EJON", "EJAN", ""))
  3503. || (((m_current + 1) == m_last)
  3504. && (StringAt((m_current - 1), 3, "OJA", "EJA", "") && !StringAt(0, 4, "DEJA", ""))))
  3505. {
  3506. MetaphAdd("H");
  3507. AdvanceCounter(2, 1);
  3508. return true;
  3509. }
  3510. return false;
  3511. }
  3512. /**
  3513. * Encode 'J' as vowel in some exception cases
  3514. *
  3515. * @return true if encoding handled in this routine, false if not
  3516. *
  3517. */
  3518. boolean Encode_J_As_Vowel()
  3519. {
  3520. if(StringAt(m_current, 5, "JEWSK", ""))
  3521. {
  3522. MetaphAdd("J", "");
  3523. return true;
  3524. }
  3525. // e.g. "stijl", "sejm" - dutch, scandanavian, and eastern european spellings
  3526. if((StringAt((m_current + 1), 1, "L", "T", "K", "S", "N", "M", "")
  3527. // except words from hindi and arabic
  3528. && !StringAt((m_current + 2), 1, "A", ""))
  3529. || StringAt(0, 9, "HALLELUJA", "LJUBLJANA", "")
  3530. || StringAt(0, 4, "LJUB", "BJOR", "")
  3531. || StringAt(0, 5, "HAJEK", "")
  3532. || StringAt(0, 3, "WOJ", "")
  3533. // e.g. 'fjord'
  3534. || StringAt(0, 2, "FJ", "")
  3535. // e.g. 'rekjavik', 'blagojevic'
  3536. || StringAt(m_current, 5, "JAVIK", "JEVIC", "")
  3537. || (((m_current + 1) == m_last) && StringAt(0, 5, "SONJA", "TANJA", "TONJA", "")))
  3538. {
  3539. return true;
  3540. }
  3541. return false;
  3542. }
  3543. /**
  3544. * Call routines to encode 'J', in proper order
  3545. *
  3546. */
  3547. void Encode_Other_J()
  3548. {
  3549. if(m_current == 0)
  3550. {
  3551. if(Encode_German_J())
  3552. {
  3553. return;
  3554. }
  3555. else
  3556. {
  3557. if(Encode_J_To_J())
  3558. {
  3559. return;
  3560. }
  3561. }
  3562. }
  3563. else
  3564. {
  3565. if(Encode_Spanish_J_2())
  3566. {
  3567. return;
  3568. }
  3569. else if(!Encode_J_As_Vowel())
  3570. {
  3571. MetaphAdd("J");
  3572. }
  3573. //it could happen! e.g. "hajj"
  3574. // eat redundant 'J'
  3575. if(CharAt(m_current + 1) == 'J')
  3576. {
  3577. m_current += 2;
  3578. }
  3579. else
  3580. {
  3581. m_current++;
  3582. }
  3583. }
  3584. }
  3585. /**
  3586. * Encode 'K'
  3587. *
  3588. *
  3589. */
  3590. void Encode_K()
  3591. {
  3592. if(!Encode_Silent_K())
  3593. {
  3594. MetaphAdd("K");
  3595. // eat redundant 'K's and 'Q's
  3596. if((CharAt(m_current + 1) == 'K')
  3597. || (CharAt(m_current + 1) == 'Q'))
  3598. {
  3599. m_current += 2;
  3600. }
  3601. else
  3602. {
  3603. m_current++;
  3604. }
  3605. }
  3606. }
  3607. /**
  3608. * Encode cases where 'K' is not pronounced
  3609. *
  3610. * @return true if encoding handled in this routine, false if not
  3611. *
  3612. */
  3613. boolean Encode_Silent_K()
  3614. {
  3615. //skip this except for special cases
  3616. if((m_current == 0)
  3617. && StringAt(m_current, 2, "KN", ""))
  3618. {
  3619. if(!(StringAt((m_current + 2), 5, "ESSET", "IEVEL", "") || StringAt((m_current + 2), 3, "ISH", "") ))
  3620. {
  3621. m_current += 1;
  3622. return true;
  3623. }
  3624. }
  3625. // e.g. "know", "knit", "knob"
  3626. if((StringAt((m_current + 1), 3, "NOW", "NIT", "NOT", "NOB", "")
  3627. // exception, "slipknot" => SLPNT but "banknote" => PNKNT
  3628. && !StringAt(0, 8, "BANKNOTE", ""))
  3629. || StringAt((m_current + 1), 4, "NOCK", "NUCK", "NIFE", "NACK", "")
  3630. || StringAt((m_current + 1), 5, "NIGHT", ""))
  3631. {
  3632. // N already encoded before
  3633. // e.g. "penknife"
  3634. if ((m_current > 0) && CharAt(m_current - 1) == 'N')
  3635. {
  3636. m_current += 2;
  3637. }
  3638. else
  3639. {
  3640. m_current++;
  3641. }
  3642. return true;
  3643. }
  3644. return false;
  3645. }
  3646. /**
  3647. * Encode 'L'
  3648. *
  3649. * Includes special vowel transposition
  3650. * encoding, where 'LE' => AL
  3651. *
  3652. */
  3653. void Encode_L()
  3654. {
  3655. // logic below needs to know this
  3656. // after 'm_current' variable changed
  3657. int save_current = m_current;
  3658. Interpolate_Vowel_When_Cons_L_At_End();
  3659. if(Encode_LELY_To_L()
  3660. || Encode_COLONEL()
  3661. || Encode_French_AULT()
  3662. || Encode_French_EUIL()
  3663. || Encode_French_OULX()
  3664. || Encode_Silent_L_In_LM()
  3665. || Encode_Silent_L_In_LK_LV()
  3666. || Encode_Silent_L_In_OULD())
  3667. {
  3668. return;
  3669. }
  3670. if(Encode_LL_As_Vowel_Cases())
  3671. {
  3672. return;
  3673. }
  3674. Encode_LE_Cases(save_current);
  3675. }
  3676. /**
  3677. * Cases where an L follows D, G, or T at the
  3678. * end have a schwa pronounced before the L
  3679. *
  3680. */
  3681. void Interpolate_Vowel_When_Cons_L_At_End()
  3682. {
  3683. if(m_encodeVowels == true)
  3684. {
  3685. // e.g. "ertl", "vogl"
  3686. if((m_current == m_last)
  3687. && StringAt((m_current - 1), 1, "D", "G", "T", ""))
  3688. {
  3689. MetaphAdd("A");
  3690. }
  3691. }
  3692. }
  3693. /**
  3694. * Catch cases where 'L' spelled twice but pronounced
  3695. * once, e.g., 'DOCILELY' => TSL
  3696. *
  3697. * @return true if encoding handled in this routine, false if not
  3698. *
  3699. */
  3700. boolean Encode_LELY_To_L()
  3701. {
  3702. // e.g. "agilely", "docilely"
  3703. if(StringAt((m_current - 1), 5, "ILELY", "")
  3704. && ((m_current + 3) == m_last))
  3705. {
  3706. MetaphAdd("L");
  3707. m_current += 3;
  3708. return true;
  3709. }
  3710. return false;
  3711. }
  3712. /**
  3713. * Encode special case "colonel" => KRNL. Can somebody tell
  3714. * me how this pronounciation came to be?
  3715. *
  3716. * @return true if encoding handled in this routine, false if not
  3717. *
  3718. */
  3719. boolean Encode_COLONEL()
  3720. {
  3721. if(StringAt((m_current - 2), 7, "COLONEL", ""))
  3722. {
  3723. MetaphAdd("R");
  3724. m_current += 2;
  3725. return true;
  3726. }
  3727. return false;
  3728. }
  3729. /**
  3730. * Encode "-AULT-", found in a french names
  3731. *
  3732. * @return true if encoding handled in this routine, false if not
  3733. *
  3734. */
  3735. boolean Encode_French_AULT()
  3736. {
  3737. // e.g. "renault" and "foucault", well known to americans, but not "fault"
  3738. if((m_current > 3)
  3739. && (StringAt((m_current - 3), 5, "RAULT", "NAULT", "BAULT", "SAULT", "GAULT", "CAULT", "")
  3740. || StringAt((m_current - 4), 6, "REAULT", "RIAULT", "NEAULT", "BEAULT", ""))
  3741. && !(RootOrInflections(m_inWord, "ASSAULT")
  3742. || StringAt((m_current - 8), 10, "SOMERSAULT","")
  3743. || StringAt((m_current - 9), 11, "SUMMERSAULT", "")))
  3744. {
  3745. m_current += 2;
  3746. return true;
  3747. }
  3748. return false;
  3749. }
  3750. /**
  3751. * Encode "-EUIL-", always found in a french word
  3752. *
  3753. * @return true if encoding handled in this routine, false if not
  3754. *
  3755. */
  3756. boolean Encode_French_EUIL()
  3757. {
  3758. // e.g. "auteuil"
  3759. if(StringAt((m_current - 3), 4, "EUIL", "") && (m_current == m_last))
  3760. {
  3761. m_current++;
  3762. return true;
  3763. }
  3764. return false;
  3765. }
  3766. /**
  3767. * Encode "-OULX", always found in a french word
  3768. *
  3769. * @return true if encoding handled in this routine, false if not
  3770. *
  3771. */
  3772. boolean Encode_French_OULX()
  3773. {
  3774. // e.g. "proulx"
  3775. if(StringAt((m_current - 2), 4, "OULX", "") && ((m_current + 1) == m_last))
  3776. {
  3777. m_current += 2;
  3778. return true;
  3779. }
  3780. return false;
  3781. }
  3782. /**
  3783. * Encodes contexts where 'L' is not pronounced in "-LM-"
  3784. *
  3785. * @return true if encoding handled in this routine, false if not
  3786. *
  3787. */
  3788. boolean Encode_Silent_L_In_LM()
  3789. {
  3790. if(StringAt(m_current, 2, "LM", "LN", ""))
  3791. {
  3792. // e.g. "lincoln", "holmes", "psalm", "salmon"
  3793. if((StringAt((m_current - 2), 4, "COLN", "CALM", "BALM", "MALM", "PALM", "")
  3794. || (StringAt((m_current - 1), 3, "OLM", "") && ((m_current + 1) == m_last))
  3795. || StringAt((m_current - 3), 5, "PSALM", "QUALM", "")
  3796. || StringAt((m_current - 2), 6, "SALMON", "HOLMES", "")
  3797. || StringAt((m_current - 1), 6, "ALMOND", "")
  3798. || ((m_current == 1) && StringAt((m_current - 1), 4, "ALMS", "") ))
  3799. && (!StringAt((m_current + 2), 1, "A", "")
  3800. && !StringAt((m_current - 2), 5, "BALMO", "")
  3801. && !StringAt((m_current - 2), 6, "PALMER", "PALMOR", "BALMER", "")
  3802. && !StringAt((m_current - 3), 5, "THALM", "")))
  3803. {
  3804. m_current++;
  3805. return true;
  3806. }
  3807. else
  3808. {
  3809. MetaphAdd("L");
  3810. m_current++;
  3811. return true;
  3812. }
  3813. }
  3814. return false;
  3815. }
  3816. /**
  3817. * Encodes contexts where '-L-' is silent in 'LK', 'LV'
  3818. *
  3819. * @return true if encoding handled in this routine, false if not
  3820. *
  3821. */
  3822. boolean Encode_Silent_L_In_LK_LV()
  3823. {
  3824. if((StringAt((m_current - 2), 4, "WALK", "YOLK", "FOLK", "HALF", "TALK", "CALF", "BALK", "CALK", "")
  3825. || (StringAt((m_current - 2), 4, "POLK", "")
  3826. && !StringAt((m_current - 2), 5, "POLKA", "WALKO", ""))
  3827. || (StringAt((m_current - 2), 4, "HALV", "")
  3828. && !StringAt((m_current - 2), 5, "HALVA", "HALVO", ""))
  3829. || (StringAt((m_current - 3), 5, "CAULK", "CHALK", "BAULK", "FAULK", "")
  3830. && !StringAt((m_current - 4), 6, "SCHALK", ""))
  3831. || (StringAt((m_current - 2), 5, "SALVE", "CALVE", "")
  3832. || StringAt((m_current - 2), 6, "SOLDER", ""))
  3833. // exceptions to above cases where 'L' is usually pronounced
  3834. && !StringAt((m_current - 2), 6, "SALVER", "CALVER", ""))
  3835. && !StringAt((m_current - 5), 9, "GONSALVES", "GONCALVES", "")
  3836. && !StringAt((m_current - 2), 6, "BALKAN", "TALKAL", "")
  3837. && !StringAt((m_current - 3), 5, "PAULK", "CHALF", ""))
  3838. {
  3839. m_current++;
  3840. return true;
  3841. }
  3842. return false;
  3843. }
  3844. /**
  3845. * Encode 'L' in contexts of "-OULD-" where it is silent
  3846. *
  3847. * @return true if encoding handled in this routine, false if not
  3848. *
  3849. */
  3850. boolean Encode_Silent_L_In_OULD()
  3851. {
  3852. //'would', 'could'
  3853. if(StringAt((m_current - 3), 5, "WOULD", "COULD", "")
  3854. || (StringAt((m_current - 4), 6, "SHOULD", "")
  3855. && !StringAt((m_current - 4), 8, "SHOULDER", "")))
  3856. {
  3857. MetaphAddExactApprox("D", "T");
  3858. m_current += 2;
  3859. return true;
  3860. }
  3861. return false;
  3862. }
  3863. /**
  3864. * Encode "-ILLA-" and "-ILLE-" in spanish and french
  3865. * contexts were americans know to pronounce it as a 'Y'
  3866. *
  3867. * @return true if encoding handled in this routine, false if not
  3868. *
  3869. */
  3870. boolean Encode_LL_As_Vowel_Special_Cases()
  3871. {
  3872. if(StringAt((m_current - 5), 8, "TORTILLA", "")
  3873. || StringAt((m_current - 8), 11, "RATATOUILLE", "")
  3874. // e.g. 'guillermo', "veillard"
  3875. || (StringAt(0, 5, "GUILL", "VEILL", "GAILL", "")
  3876. // 'guillotine' usually has '-ll-' pronounced as 'L' in english
  3877. && !(StringAt((m_current - 3), 7, "GUILLOT", "GUILLOR", "GUILLEN", "")
  3878. || (StringAt(0, 5, "GUILL", "") && (m_length == 5))))
  3879. // e.g. "brouillard", "gremillion"
  3880. || StringAt(0, 7, "BROUILL", "GREMILL", "ROBILL", "")
  3881. // e.g. 'mireille'
  3882. || (StringAt((m_current - 2), 5, "EILLE", "")
  3883. && ((m_current + 2) == m_last)
  3884. // exception "reveille" usually pronounced as 're-vil-lee'
  3885. && !StringAt((m_current - 5), 8, "REVEILLE", "")))
  3886. {
  3887. m_current += 2;
  3888. return true;
  3889. }
  3890. return false;
  3891. }
  3892. /**
  3893. * Encode other spanish cases where "-LL-" is pronounced as 'Y'
  3894. *
  3895. * @return true if encoding handled in this routine, false if not
  3896. *
  3897. */
  3898. boolean Encode_LL_As_Vowel()
  3899. {
  3900. //spanish e.g. "cabrillo", "gallegos" but also "gorilla", "ballerina" -
  3901. // give both pronounciations since an american might pronounce "cabrillo"
  3902. // in the spanish or the american fashion.
  3903. if((((m_current + 3) == m_length)
  3904. && StringAt((m_current - 1), 4, "ILLO", "ILLA", "ALLE", ""))
  3905. || (((StringAt((m_last - 1), 2, "AS", "OS", "")
  3906. || StringAt(m_last, 2, "AS", "OS", "")
  3907. || StringAt(m_last, 1, "A", "O", ""))
  3908. && StringAt((m_current - 1), 2, "AL", "IL", ""))
  3909. && !StringAt((m_current - 1), 4, "ALLA", ""))
  3910. || StringAt(0, 5, "VILLE", "VILLA", "")
  3911. || StringAt(0, 8, "GALLARDO", "VALLADAR", "MAGALLAN", "CAVALLAR", "BALLASTE", "")
  3912. || StringAt(0, 3, "LLA", ""))
  3913. {
  3914. MetaphAdd("L", "");
  3915. m_current += 2;
  3916. return true;
  3917. }
  3918. return false;
  3919. }
  3920. /**
  3921. * Call routines to encode "-LL-", in proper order
  3922. *
  3923. * @return true if encoding handled in this routine, false if not
  3924. *
  3925. */
  3926. boolean Encode_LL_As_Vowel_Cases()
  3927. {
  3928. if(CharAt(m_current + 1) == 'L')
  3929. {
  3930. if(Encode_LL_As_Vowel_Special_Cases())
  3931. {
  3932. return true;
  3933. }
  3934. else if(Encode_LL_As_Vowel())
  3935. {
  3936. return true;
  3937. }
  3938. m_current += 2;
  3939. }
  3940. else
  3941. {
  3942. m_current++;
  3943. }
  3944. return false;
  3945. }
  3946. /**
  3947. * Encode vowel-encoding cases where "-LE-" is pronounced "-EL-"
  3948. *
  3949. * @return true if encoding handled in this routine, false if not
  3950. *
  3951. */
  3952. boolean Encode_Vowel_LE_Transposition(int save_current)
  3953. {
  3954. // transposition of vowel sound and L occurs in many words,
  3955. // e.g. "bristle", "dazzle", "goggle" => KAKAL
  3956. if(m_encodeVowels
  3957. && (save_current > 1)
  3958. && !IsVowel(save_current - 1)
  3959. && (CharAt(save_current + 1) == 'E')
  3960. && (CharAt(save_current - 1) != 'L')
  3961. && (CharAt(save_current - 1) != 'R')
  3962. // lots of exceptions to this:
  3963. && !IsVowel(save_current + 2)
  3964. && !StringAt(0, 7, "ECCLESI", "COMPLEC", "COMPLEJ", "ROBLEDO", "")
  3965. && !StringAt(0, 5, "MCCLE", "MCLEL", "")
  3966. && !StringAt(0, 6, "EMBLEM", "KADLEC", "")
  3967. && !(((save_current + 2) == m_last) && StringAt(save_current, 3, "LET", ""))
  3968. && !StringAt(save_current, 7, "LETTING", "")
  3969. && !StringAt(save_current, 6, "LETELY", "LETTER", "LETION", "LETIAN", "LETING", "LETORY", "")
  3970. && !StringAt(save_current, 5, "LETUS", "LETIV", "")
  3971. && !StringAt(save_current, 4, "LESS", "LESQ", "LECT", "LEDG", "LETE", "LETH", "LETS", "LETT", "")
  3972. && !StringAt(save_current, 3, "LEG", "LER", "LEX", "")
  3973. // e.g. "complement" !=> KAMPALMENT
  3974. && !(StringAt(save_current, 6, "LEMENT", "")
  3975. && !(StringAt((m_current - 5), 6, "BATTLE", "TANGLE", "PUZZLE", "RABBLE", "BABBLE", "")
  3976. || StringAt((m_current - 4), 5, "TABLE", "")))
  3977. && !(((save_current + 2) == m_last) && StringAt((save_current - 2), 5, "OCLES", "ACLES", "AKLES", ""))
  3978. && !StringAt((save_current - 3), 5, "LISLE", "AISLE", "")
  3979. && !StringAt(0, 4, "ISLE", "")
  3980. && !StringAt(0, 6, "ROBLES", "")
  3981. && !StringAt((save_current - 4), 7, "PROBLEM", "RESPLEN", "")
  3982. && !StringAt((save_current - 3), 6, "REPLEN", "")
  3983. && !StringAt((save_current - 2), 4, "SPLE", "")
  3984. && (CharAt(save_current - 1) != 'H')
  3985. && (CharAt(save_current - 1) != 'W'))
  3986. {
  3987. MetaphAdd("AL");
  3988. flag_AL_inversion = true;
  3989. // eat redundant 'L'
  3990. if(CharAt(save_current + 2) == 'L')
  3991. {
  3992. m_current = save_current + 3;
  3993. }
  3994. return true;
  3995. }
  3996. return false;
  3997. }
  3998. /**
  3999. * Encode special vowel-encoding cases where 'E' is not
  4000. * silent at the end of a word as is the usual case
  4001. *
  4002. * @return true if encoding handled in this routine, false if not
  4003. *
  4004. */
  4005. boolean Encode_Vowel_Preserve_Vowel_After_L(int save_current)
  4006. {
  4007. // an example of where the vowel would NOT need to be preserved
  4008. // would be, say, "hustled", where there is no vowel pronounced
  4009. // between the 'l' and the 'd'
  4010. if(m_encodeVowels
  4011. && !IsVowel(save_current - 1)
  4012. && (CharAt(save_current + 1) == 'E')
  4013. && (save_current > 1)
  4014. && ((save_current + 1) != m_last)
  4015. && !(StringAt((save_current + 1), 2, "ES", "ED", "")
  4016. && ((save_current + 2) == m_last))
  4017. && !StringAt((save_current - 1), 5, "RLEST", "") )
  4018. {
  4019. MetaphAdd("LA");
  4020. m_current = SkipVowels(m_current);
  4021. return true;
  4022. }
  4023. return false;
  4024. }
  4025. /**
  4026. * Call routines to encode "-LE-", in proper order
  4027. *
  4028. * @param save_current index of actual current letter
  4029. *
  4030. */
  4031. void Encode_LE_Cases(int save_current)
  4032. {
  4033. if(Encode_Vowel_LE_Transposition(save_current))
  4034. {
  4035. return;
  4036. }
  4037. else
  4038. {
  4039. if(Encode_Vowel_Preserve_Vowel_After_L(save_current))
  4040. {
  4041. return;
  4042. }
  4043. else
  4044. {
  4045. MetaphAdd("L");
  4046. }
  4047. }
  4048. }
  4049. /**
  4050. * Encode "-M-"
  4051. *
  4052. */
  4053. void Encode_M()
  4054. {
  4055. if(Encode_Silent_M_At_Beginning()
  4056. || Encode_MR_And_MRS()
  4057. || Encode_MAC()
  4058. || Encode_MPT())
  4059. {
  4060. return;
  4061. }
  4062. // Silent 'B' should really be handled
  4063. // under 'B", not here under 'M'!
  4064. Encode_MB();
  4065. MetaphAdd("M");
  4066. }
  4067. /**
  4068. * Encode cases where 'M' is silent at beginning of word
  4069. *
  4070. * @return true if encoding handled in this routine, false if not
  4071. *
  4072. */
  4073. boolean Encode_Silent_M_At_Beginning()
  4074. {
  4075. //skip these when at start of word
  4076. if((m_current == 0)
  4077. && StringAt(m_current, 2, "MN", ""))
  4078. {
  4079. m_current += 1;
  4080. return true;
  4081. }
  4082. return false;
  4083. }
  4084. /**
  4085. * Encode special cases "Mr." and "Mrs."
  4086. *
  4087. * @return true if encoding handled in this routine, false if not
  4088. *
  4089. */
  4090. boolean Encode_MR_And_MRS()
  4091. {
  4092. if((m_current == 0) && StringAt(m_current, 2, "MR", ""))
  4093. {
  4094. // exceptions for "mr." and "mrs."
  4095. if((m_length == 2) && StringAt(m_current, 2, "MR", ""))
  4096. {
  4097. if(m_encodeVowels)
  4098. {
  4099. MetaphAdd("MASTAR");
  4100. }
  4101. else
  4102. {
  4103. MetaphAdd("MSTR");
  4104. }
  4105. m_current += 2;
  4106. return true;
  4107. }
  4108. else if((m_length == 3) && StringAt(m_current, 3, "MRS", ""))
  4109. {
  4110. if(m_encodeVowels)
  4111. {
  4112. MetaphAdd("MASAS");
  4113. }
  4114. else
  4115. {
  4116. MetaphAdd("MSS");
  4117. }
  4118. m_current += 3;
  4119. return true;
  4120. }
  4121. }
  4122. return false;
  4123. }
  4124. /**
  4125. * Encode "Mac-" and "Mc-"
  4126. *
  4127. * @return true if encoding handled in this routine, false if not
  4128. *
  4129. */
  4130. boolean Encode_MAC()
  4131. {
  4132. // should only find irish and
  4133. // scottish names e.g. 'macintosh'
  4134. if((m_current == 0)
  4135. && (StringAt(0, 7, "MACIVER", "MACEWEN", "")
  4136. || StringAt(0, 8, "MACELROY", "MACILROY", "")
  4137. || StringAt(0, 9, "MACINTOSH", "")
  4138. || StringAt(0, 2, "MC", "") ))
  4139. {
  4140. if(m_encodeVowels)
  4141. {
  4142. MetaphAdd("MAK");
  4143. }
  4144. else
  4145. {
  4146. MetaphAdd("MK");
  4147. }
  4148. if(StringAt(0, 2, "MC", ""))
  4149. {
  4150. if(StringAt((m_current + 2), 1, "K", "G", "Q", "")
  4151. // watch out for e.g. "McGeorge"
  4152. && !StringAt((m_current + 2), 4, "GEOR", ""))
  4153. {
  4154. m_current += 3;
  4155. }
  4156. else
  4157. {
  4158. m_current += 2;
  4159. }
  4160. }
  4161. else
  4162. {
  4163. m_current += 3;
  4164. }
  4165. return true;
  4166. }
  4167. return false;
  4168. }
  4169. /**
  4170. * Encode silent 'M' in context of "-MPT-"
  4171. *
  4172. * @return true if encoding handled in this routine, false if not
  4173. *
  4174. */
  4175. boolean Encode_MPT()
  4176. {
  4177. if(StringAt((m_current - 2), 8, "COMPTROL", "")
  4178. || StringAt((m_current - 4), 7, "ACCOMPT", ""))
  4179. {
  4180. MetaphAdd("N");
  4181. m_current += 2;
  4182. return true;
  4183. }
  4184. return false;
  4185. }
  4186. /**
  4187. * Test if 'B' is silent in these contexts
  4188. *
  4189. * @return true if 'B' is silent in this context
  4190. *
  4191. */
  4192. boolean Test_Silent_MB_1()
  4193. {
  4194. // e.g. "LAMB", "COMB", "LIMB", "DUMB", "BOMB"
  4195. // Handle combining roots first
  4196. if (((m_current == 3)
  4197. && StringAt((m_current - 3), 5, "THUMB", ""))
  4198. || ((m_current == 2)
  4199. && StringAt((m_current - 2), 4, "DUMB", "BOMB", "DAMN", "LAMB", "NUMB", "TOMB", "") ))
  4200. {
  4201. return true;
  4202. }
  4203. return false;
  4204. }
  4205. /**
  4206. * Test if 'B' is pronounced in this context
  4207. *
  4208. * @return true if 'B' is pronounced in this context
  4209. *
  4210. */
  4211. boolean Test_Pronounced_MB()
  4212. {
  4213. if (StringAt((m_current - 2), 6, "NUMBER", "")
  4214. || (StringAt((m_current + 2), 1, "A", "")
  4215. && !StringAt((m_current - 2), 7, "DUMBASS", ""))
  4216. || StringAt((m_current + 2), 1, "O", "")
  4217. || StringAt((m_current - 2), 6, "LAMBEN", "LAMBER", "LAMBET", "TOMBIG", "LAMBRE", ""))
  4218. {
  4219. return true;
  4220. }
  4221. return false;
  4222. }
  4223. /**
  4224. * Test whether "-B-" is silent in these contexts
  4225. *
  4226. * @return true if 'B' is silent in this context
  4227. *
  4228. */
  4229. boolean Test_Silent_MB_2()
  4230. {
  4231. // 'M' is the current letter
  4232. if ((CharAt(m_current + 1) == 'B') && (m_current > 1)
  4233. && (((m_current + 1) == m_last)
  4234. // other situations where "-MB-" is at end of root
  4235. // but not at end of word. The tests are for standard
  4236. // noun suffixes.
  4237. // e.g. "climbing" => KLMNK
  4238. || StringAt((m_current + 2), 3, "ING", "ABL", "")
  4239. || StringAt((m_current + 2), 4, "LIKE", "")
  4240. || ((CharAt(m_current + 2) == 'S') && ((m_current + 2) == m_last))
  4241. || StringAt((m_current - 5), 7, "BUNCOMB", "")
  4242. // e.g. "bomber",
  4243. || (StringAt((m_current + 2), 2, "ED", "ER", "")
  4244. && ((m_current + 3) == m_last)
  4245. && (StringAt(0, 5, "CLIMB", "PLUMB", "")
  4246. // e.g. "beachcomber"
  4247. || !StringAt((m_current - 1), 5, "IMBER", "AMBER", "EMBER", "UMBER", ""))
  4248. // exceptions
  4249. && !StringAt((m_current - 2), 6, "CUMBER", "SOMBER", "") ) ) )
  4250. {
  4251. return true;
  4252. }
  4253. return false;
  4254. }
  4255. /**
  4256. * Test if 'B' is pronounced in these "-MB-" contexts
  4257. *
  4258. * @return true if "-B-" is pronounced in these contexts
  4259. *
  4260. */
  4261. boolean Test_Pronounced_MB_2()
  4262. {
  4263. // e.g. "bombastic", "umbrage", "flamboyant"
  4264. if (StringAt((m_current - 1), 5, "OMBAS", "OMBAD", "UMBRA", "")
  4265. || StringAt((m_current - 3), 4, "FLAM", "") )
  4266. {
  4267. return true;
  4268. }
  4269. return false;
  4270. }
  4271. /**
  4272. * Tests for contexts where "-N-" is silent when after "-M-"
  4273. *
  4274. * @return true if "-N-" is silent in these contexts
  4275. *
  4276. */
  4277. boolean Test_MN()
  4278. {
  4279. if ((CharAt(m_current + 1) == 'N')
  4280. && (((m_current + 1) == m_last)
  4281. // or at the end of a word but followed by suffixes
  4282. || (StringAt((m_current + 2), 3, "ING", "EST", "") && ((m_current + 4) == m_last))
  4283. || ((CharAt(m_current + 2) == 'S') && ((m_current + 2) == m_last))
  4284. || (StringAt((m_current + 2), 2, "LY", "ER", "ED", "")
  4285. && ((m_current + 3) == m_last))
  4286. || StringAt((m_current - 2), 9, "DAMNEDEST", "")
  4287. || StringAt((m_current - 5), 9, "GODDAMNIT", "") ))
  4288. {
  4289. return true;
  4290. }
  4291. return false;
  4292. }
  4293. /**
  4294. * Call routines to encode "-MB-", in proper order
  4295. *
  4296. */
  4297. void Encode_MB()
  4298. {
  4299. if(Test_Silent_MB_1())
  4300. {
  4301. if(Test_Pronounced_MB())
  4302. {
  4303. m_current++;
  4304. }
  4305. else
  4306. {
  4307. m_current += 2;
  4308. }
  4309. }
  4310. else if(Test_Silent_MB_2())
  4311. {
  4312. if(Test_Pronounced_MB_2())
  4313. {
  4314. m_current++;
  4315. }
  4316. else
  4317. {
  4318. m_current += 2;
  4319. }
  4320. }
  4321. else if(Test_MN())
  4322. {
  4323. m_current += 2;
  4324. }
  4325. else
  4326. {
  4327. // eat redundant 'M'
  4328. if (CharAt(m_current + 1) == 'M')
  4329. {
  4330. m_current += 2;
  4331. }
  4332. else
  4333. {
  4334. m_current++;
  4335. }
  4336. }
  4337. }
  4338. /**
  4339. * Encode "-N-"
  4340. *
  4341. */
  4342. void Encode_N()
  4343. {
  4344. if(Encode_NCE())
  4345. {
  4346. return;
  4347. }
  4348. // eat redundant 'N'
  4349. if(CharAt(m_current + 1) == 'N')
  4350. {
  4351. m_current += 2;
  4352. }
  4353. else
  4354. {
  4355. m_current++;
  4356. }
  4357. if (!StringAt((m_current - 3), 8, "MONSIEUR", "")
  4358. // e.g. "aloneness",
  4359. && !StringAt((m_current - 3), 6, "NENESS", ""))
  4360. {
  4361. MetaphAdd("N");
  4362. }
  4363. }
  4364. /**
  4365. * Encode "-NCE-" and "-NSE-"
  4366. * "entrance" is pronounced exactly the same as "entrants"
  4367. *
  4368. * @return true if encoding handled in this routine, false if not
  4369. *
  4370. */
  4371. boolean Encode_NCE()
  4372. {
  4373. //'acceptance', 'accountancy'
  4374. if(StringAt((m_current + 1), 1, "C", "S", "")
  4375. && StringAt((m_current + 2), 1, "E", "Y", "I", "")
  4376. && (((m_current + 2) == m_last)
  4377. || (((m_current + 3) == m_last))
  4378. && (CharAt(m_current + 3) == 'S')))
  4379. {
  4380. MetaphAdd("NTS");
  4381. m_current += 2;
  4382. return true;
  4383. }
  4384. return false;
  4385. }
  4386. /**
  4387. * Encode "-P-"
  4388. *
  4389. */
  4390. void Encode_P()
  4391. {
  4392. if(Encode_Silent_P_At_Beginning()
  4393. || Encode_PT()
  4394. || Encode_PH()
  4395. || Encode_PPH()
  4396. || Encode_RPS()
  4397. || Encode_COUP()
  4398. || Encode_PNEUM()
  4399. || Encode_PSYCH()
  4400. || Encode_PSALM())
  4401. {
  4402. return;
  4403. }
  4404. Encode_PB();
  4405. MetaphAdd("P");
  4406. }
  4407. /**
  4408. * Encode cases where "-P-" is silent at the start of a word
  4409. *
  4410. * @return true if encoding handled in this routine, false if not
  4411. *
  4412. */
  4413. boolean Encode_Silent_P_At_Beginning()
  4414. {
  4415. //skip these when at start of word
  4416. if((m_current == 0)
  4417. && StringAt(m_current, 2, "PN", "PF", "PS", "PT", ""))
  4418. {
  4419. m_current += 1;
  4420. return true;
  4421. }
  4422. return false;
  4423. }
  4424. /**
  4425. * Encode cases where "-P-" is silent before "-T-"
  4426. *
  4427. * @return true if encoding handled in this routine, false if not
  4428. *
  4429. */
  4430. boolean Encode_PT()
  4431. {
  4432. // 'pterodactyl', 'receipt', 'asymptote'
  4433. if((CharAt(m_current + 1) == 'T'))
  4434. {
  4435. if (((m_current == 0) && StringAt(m_current, 5, "PTERO", ""))
  4436. || StringAt((m_current - 5), 7, "RECEIPT", "")
  4437. || StringAt((m_current - 4), 8, "ASYMPTOT", ""))
  4438. {
  4439. MetaphAdd("T");
  4440. m_current += 2;
  4441. return true;
  4442. }
  4443. }
  4444. return false;
  4445. }
  4446. /**
  4447. * Encode "-PH-", usually as F, with exceptions for
  4448. * cases where it is silent, or where the 'P' and 'T'
  4449. * are pronounced seperately because they belong to
  4450. * two different words in a combining form
  4451. *
  4452. * @return true if encoding handled in this routine, false if not
  4453. *
  4454. */
  4455. boolean Encode_PH()
  4456. {
  4457. if(CharAt(m_current + 1) == 'H')
  4458. {
  4459. // 'PH' silent in these contexts
  4460. if (StringAt(m_current, 9, "PHTHALEIN", "")
  4461. || ((m_current == 0) && StringAt(m_current, 4, "PHTH", ""))
  4462. || StringAt((m_current - 3), 10, "APOPHTHEGM", ""))
  4463. {
  4464. MetaphAdd("0");
  4465. m_current += 4;
  4466. }
  4467. // combining forms
  4468. //'sheepherd', 'upheaval', 'cupholder'
  4469. else if((m_current > 0)
  4470. && (StringAt((m_current + 2), 3, "EAD", "OLE", "ELD", "ILL", "OLD", "EAP", "ERD",
  4471. "ARD", "ANG", "ORN", "EAV", "ART", "")
  4472. || StringAt((m_current + 2), 4, "OUSE", "")
  4473. || (StringAt((m_current + 2), 2, "AM", "") && !StringAt((m_current -1), 5, "LPHAM", ""))
  4474. || StringAt((m_current + 2), 5, "AMMER", "AZARD", "UGGER", "")
  4475. || StringAt((m_current + 2), 6, "OLSTER", ""))
  4476. && !StringAt((m_current - 3), 5, "LYMPH", "NYMPH", ""))
  4477. {
  4478. MetaphAdd("P");
  4479. AdvanceCounter(3, 2);
  4480. }
  4481. else
  4482. {
  4483. MetaphAdd("F");
  4484. m_current += 2;
  4485. }
  4486. return true;
  4487. }
  4488. return false;
  4489. }
  4490. /**
  4491. * Encode "-PPH-". I don't know why the greek poet's
  4492. * name is transliterated this way...
  4493. *
  4494. * @return true if encoding handled in this routine, false if not
  4495. *
  4496. */
  4497. boolean Encode_PPH()
  4498. {
  4499. // 'sappho'
  4500. if((CharAt(m_current + 1) == 'P')
  4501. && ((m_current + 2) < m_length) && (CharAt(m_current + 2) == 'H'))
  4502. {
  4503. MetaphAdd("F");
  4504. m_current += 3;
  4505. return true;
  4506. }
  4507. return false;
  4508. }
  4509. /**
  4510. * Encode "-CORPS-" where "-PS-" not pronounced
  4511. * since the cognate is here from the french
  4512. *
  4513. * @return true if encoding handled in this routine, false if not
  4514. *
  4515. */
  4516. boolean Encode_RPS()
  4517. {
  4518. //'-corps-', 'corpsman'
  4519. if(StringAt((m_current - 3), 5, "CORPS", "")
  4520. && !StringAt((m_current - 3), 6, "CORPSE", ""))
  4521. {
  4522. m_current += 2;
  4523. return true;
  4524. }
  4525. return false;
  4526. }
  4527. /**
  4528. * Encode "-COUP-" where "-P-" is not pronounced
  4529. * since the word is from the french
  4530. *
  4531. * @return true if encoding handled in this routine, false if not
  4532. *
  4533. */
  4534. boolean Encode_COUP()
  4535. {
  4536. //'coup'
  4537. if((m_current == m_last)
  4538. && StringAt((m_current - 3), 4, "COUP", "")
  4539. && !StringAt((m_current - 5), 6, "RECOUP", ""))
  4540. {
  4541. m_current++;
  4542. return true;
  4543. }
  4544. return false;
  4545. }
  4546. /**
  4547. * Encode 'P' in non-initial contexts of "-PNEUM-"
  4548. * where is also silent
  4549. *
  4550. * @return true if encoding handled in this routine, false if not
  4551. *
  4552. */
  4553. boolean Encode_PNEUM()
  4554. {
  4555. //'-pneum-'
  4556. if(StringAt((m_current + 1), 4, "NEUM", ""))
  4557. {
  4558. MetaphAdd("N");
  4559. m_current += 2;
  4560. return true;
  4561. }
  4562. return false;
  4563. }
  4564. /**
  4565. * Encode special case "-PSYCH-" where two encodings need to be
  4566. * accounted for in one syllable, one for the 'PS' and one for
  4567. * the 'CH'
  4568. *
  4569. * @return true if encoding handled in this routine, false if not
  4570. *
  4571. */
  4572. boolean Encode_PSYCH()
  4573. {
  4574. //'-psych-'
  4575. if(StringAt((m_current + 1), 4, "SYCH", ""))
  4576. {
  4577. if(m_encodeVowels)
  4578. {
  4579. MetaphAdd("SAK");
  4580. }
  4581. else
  4582. {
  4583. MetaphAdd("SK");
  4584. }
  4585. m_current += 5;
  4586. return true;
  4587. }
  4588. return false;
  4589. }
  4590. /**
  4591. * Encode 'P' in context of "-PSALM-", where it has
  4592. * become silent
  4593. *
  4594. * @return true if encoding handled in this routine, false if not
  4595. *
  4596. */
  4597. boolean Encode_PSALM()
  4598. {
  4599. //'-psalm-'
  4600. if(StringAt((m_current + 1), 4, "SALM", ""))
  4601. {
  4602. // go ahead and encode entire word
  4603. if(m_encodeVowels)
  4604. {
  4605. MetaphAdd("SAM");
  4606. }
  4607. else
  4608. {
  4609. MetaphAdd("SM");
  4610. }
  4611. m_current += 5;
  4612. return true;
  4613. }
  4614. return false;
  4615. }
  4616. /**
  4617. * Eat redundant 'B' or 'P'
  4618. *
  4619. */
  4620. void Encode_PB()
  4621. {
  4622. // e.g. "campbell", "raspberry"
  4623. // eat redundant 'P' or 'B'
  4624. if(StringAt((m_current + 1), 1, "P", "B", ""))
  4625. {
  4626. m_current += 2;
  4627. }
  4628. else
  4629. {
  4630. m_current++;
  4631. }
  4632. }
  4633. /**
  4634. * Encode "-Q-"
  4635. *
  4636. */
  4637. void Encode_Q()
  4638. {
  4639. // current pinyin
  4640. if(StringAt(m_current, 3, "QIN", ""))
  4641. {
  4642. MetaphAdd("X");
  4643. m_current++;
  4644. return;
  4645. }
  4646. // eat redundant 'Q'
  4647. if(CharAt(m_current + 1) == 'Q')
  4648. {
  4649. m_current += 2;
  4650. }
  4651. else
  4652. {
  4653. m_current++;
  4654. }
  4655. MetaphAdd("K");
  4656. }
  4657. /**
  4658. * Encode "-R-"
  4659. *
  4660. */
  4661. void Encode_R()
  4662. {
  4663. if(Encode_RZ())
  4664. {
  4665. return;
  4666. }
  4667. if(!Test_Silent_R())
  4668. {
  4669. if(!Encode_Vowel_RE_Transposition())
  4670. {
  4671. MetaphAdd("R");
  4672. }
  4673. }
  4674. // eat redundant 'R'; also skip 'S' as well as 'R' in "poitiers"
  4675. if((CharAt(m_current + 1) == 'R') || StringAt((m_current - 6), 8, "POITIERS", ""))
  4676. {
  4677. m_current += 2;
  4678. }
  4679. else
  4680. {
  4681. m_current++;
  4682. }
  4683. }
  4684. /**
  4685. * Encode "-RZ-" according
  4686. * to american and polish pronunciations
  4687. *
  4688. * @return true if encoding handled in this routine, false if not
  4689. *
  4690. */
  4691. boolean Encode_RZ()
  4692. {
  4693. if(StringAt((m_current - 2), 4, "GARZ", "KURZ", "MARZ", "MERZ", "HERZ", "PERZ", "WARZ", "")
  4694. || StringAt(m_current, 5, "RZANO", "RZOLA", "")
  4695. || StringAt((m_current - 1), 4, "ARZA", "ARZN", ""))
  4696. {
  4697. return false;
  4698. }
  4699. // 'yastrzemski' usually has 'z' silent in
  4700. // united states, but should get 'X' in poland
  4701. if(StringAt((m_current - 4), 11, "YASTRZEMSKI", ""))
  4702. {
  4703. MetaphAdd("R", "X");
  4704. m_current += 2;
  4705. return true;
  4706. }
  4707. // 'BRZEZINSKI' gets two pronunciations
  4708. // in the united states, neither of which
  4709. // are authentically polish
  4710. if(StringAt((m_current - 1), 10, "BRZEZINSKI", ""))
  4711. {
  4712. MetaphAdd("RS", "RJ");
  4713. // skip over 2nd 'Z'
  4714. m_current += 4;
  4715. return true;
  4716. }
  4717. // 'z' in 'rz after voiceless consonant gets 'X'
  4718. // in alternate polish style pronunciation
  4719. else if(StringAt((m_current - 1), 3, "TRZ", "PRZ", "KRZ", "")
  4720. || (StringAt(m_current, 2, "RZ", "")
  4721. && (IsVowel(m_current - 1) || (m_current == 0))))
  4722. {
  4723. MetaphAdd("RS", "X");
  4724. m_current += 2;
  4725. return true;
  4726. }
  4727. // 'z' in 'rz after voiceled consonant, vowel, or at
  4728. // beginning gets 'J' in alternate polish style pronunciation
  4729. else if(StringAt((m_current - 1), 3, "BRZ", "DRZ", "GRZ", ""))
  4730. {
  4731. MetaphAdd("RS", "J");
  4732. m_current += 2;
  4733. return true;
  4734. }
  4735. return false;
  4736. }
  4737. /**
  4738. * Test whether 'R' is silent in this context
  4739. *
  4740. * @return true if 'R' is silent in this context
  4741. *
  4742. */
  4743. boolean Test_Silent_R()
  4744. {
  4745. // test cases where 'R' is silent, either because the
  4746. // word is from the french or because it is no longer pronounced.
  4747. // e.g. "rogier", "monsieur", "surburban"
  4748. if(((m_current == m_last)
  4749. // reliably french word ending
  4750. && StringAt((m_current - 2), 3, "IER", "")
  4751. // e.g. "metier"
  4752. && (StringAt((m_current - 5), 3, "MET", "VIV", "LUC", "")
  4753. // e.g. "cartier", "bustier"
  4754. || StringAt((m_current - 6), 4, "CART", "DOSS", "FOUR", "OLIV", "BUST", "DAUM", "ATEL",
  4755. "SONN", "CORM", "MERC", "PELT", "POIR", "BERN", "FORT", "GREN",
  4756. "SAUC", "GAGN", "GAUT", "GRAN", "FORC", "MESS", "LUSS", "MEUN",
  4757. "POTH", "HOLL", "CHEN", "")
  4758. // e.g. "croupier"
  4759. || StringAt((m_current - 7), 5, "CROUP", "TORCH", "CLOUT", "FOURN", "GAUTH", "TROTT",
  4760. "DEROS", "CHART", "")
  4761. // e.g. "chevalier"
  4762. || StringAt((m_current - 8), 6, "CHEVAL", "LAVOIS", "PELLET", "SOMMEL", "TREPAN", "LETELL", "COLOMB", "")
  4763. || StringAt((m_current - 9), 7, "CHARCUT", "")
  4764. || StringAt((m_current - 10), 8, "CHARPENT", "")))
  4765. || StringAt((m_current - 2), 7, "SURBURB", "WORSTED", "")
  4766. || StringAt((m_current - 2), 9, "WORCESTER", "")
  4767. || StringAt((m_current - 7), 8, "MONSIEUR", "")
  4768. || StringAt((m_current - 6), 8, "POITIERS", "") )
  4769. {
  4770. return true;
  4771. }
  4772. return false;
  4773. }
  4774. /**
  4775. * Encode '-re-" as 'AR' in contexts
  4776. * where this is the correct pronunciation
  4777. *
  4778. * @return true if encoding handled in this routine, false if not
  4779. *
  4780. */
  4781. boolean Encode_Vowel_RE_Transposition()
  4782. {
  4783. // -re inversion is just like
  4784. // -le inversion
  4785. // e.g. "fibre" => FABAR or "centre" => SANTAR
  4786. if((m_encodeVowels)
  4787. && (CharAt(m_current + 1) == 'E')
  4788. && (m_length > 3)
  4789. && !StringAt(0, 5, "OUTRE", "LIBRE", "ANDRE", "")
  4790. && !(StringAt(0, 4, "FRED", "TRES", "") && (m_length == 4))
  4791. && !StringAt((m_current - 2), 5, "LDRED", "LFRED", "NDRED", "NFRED", "NDRES", "TRES", "IFRED", "")
  4792. && !IsVowel(m_current - 1)
  4793. && (((m_current + 1) == m_last)
  4794. || (((m_current + 2) == m_last)
  4795. && StringAt((m_current + 2), 1, "D", "S", ""))))
  4796. {
  4797. MetaphAdd("AR");
  4798. return true;
  4799. }
  4800. return false;
  4801. }
  4802. /**
  4803. * Encode "-S-"
  4804. *
  4805. */
  4806. void Encode_S()
  4807. {
  4808. if(Encode_SKJ()
  4809. || Encode_Special_SW()
  4810. || Encode_SJ()
  4811. || Encode_Silent_French_S_Final()
  4812. || Encode_Silent_French_S_Internal()
  4813. || Encode_ISL()
  4814. || Encode_STL()
  4815. || Encode_Christmas()
  4816. || Encode_STHM()
  4817. || Encode_ISTEN()
  4818. || Encode_Sugar()
  4819. || Encode_SH()
  4820. || Encode_SCH()
  4821. || Encode_SUR()
  4822. || Encode_SU()
  4823. || Encode_SSIO()
  4824. || Encode_SS()
  4825. || Encode_SIA()
  4826. || Encode_SIO()
  4827. || Encode_Anglicisations()
  4828. || Encode_SC()
  4829. || Encode_SEA_SUI_SIER()
  4830. || Encode_SEA())
  4831. {
  4832. return;
  4833. }
  4834. MetaphAdd("S");
  4835. if(StringAt((m_current + 1), 1, "S", "Z", "")
  4836. && !StringAt((m_current + 1), 2, "SH", ""))
  4837. {
  4838. m_current += 2;
  4839. }
  4840. else
  4841. {
  4842. m_current++;
  4843. }
  4844. }
  4845. /**
  4846. * Encode a couple of contexts where scandinavian, slavic
  4847. * or german names should get an alternate, native
  4848. * pronunciation of 'SV' or 'XV'
  4849. *
  4850. * @return true if handled
  4851. *
  4852. */
  4853. boolean Encode_Special_SW()
  4854. {
  4855. if(m_current == 0)
  4856. {
  4857. //
  4858. if(Names_Beginning_With_SW_That_Get_Alt_SV())
  4859. {
  4860. MetaphAdd("S", "SV");
  4861. m_current += 2;
  4862. return true;
  4863. }
  4864. //
  4865. if(Names_Beginning_With_SW_That_Get_Alt_XV())
  4866. {
  4867. MetaphAdd("S", "XV");
  4868. m_current += 2;
  4869. return true;
  4870. }
  4871. }
  4872. return false;
  4873. }
  4874. /**
  4875. * Encode "-SKJ-" as X ("sh"), since americans pronounce
  4876. * the name Dag Hammerskjold as "hammer-shold"
  4877. *
  4878. * @return true if encoding handled in this routine, false if not
  4879. *
  4880. */
  4881. boolean Encode_SKJ()
  4882. {
  4883. // scandinavian
  4884. if(StringAt(m_current, 4, "SKJO", "SKJU", "")
  4885. && IsVowel(m_current + 3))
  4886. {
  4887. MetaphAdd("X");
  4888. m_current += 3;
  4889. return true;
  4890. }
  4891. return false;
  4892. }
  4893. /**
  4894. * Encode initial swedish "SJ-" as X ("sh")
  4895. *
  4896. * @return true if encoding handled in this routine, false if not
  4897. *
  4898. */
  4899. boolean Encode_SJ()
  4900. {
  4901. if(StringAt(0, 2, "SJ", ""))
  4902. {
  4903. MetaphAdd("X");
  4904. m_current += 2;
  4905. return true;
  4906. }
  4907. return false;
  4908. }
  4909. /**
  4910. * Encode final 'S' in words from the french, where they
  4911. * are not pronounced
  4912. *
  4913. * @return true if encoding handled in this routine, false if not
  4914. *
  4915. */
  4916. boolean Encode_Silent_French_S_Final()
  4917. {
  4918. // "louis" is an exception because it gets two pronuncuations
  4919. if(StringAt(0, 5, "LOUIS", "") && (m_current == m_last))
  4920. {
  4921. MetaphAdd("S", "");
  4922. m_current++;
  4923. return true;
  4924. }
  4925. // french words familiar to americans where final s is silent
  4926. if((m_current == m_last)
  4927. && (StringAt(0, 4, "YVES", "")
  4928. || (StringAt(0, 4, "HORS", "") && (m_current == 3))
  4929. || StringAt((m_current - 4), 5, "CAMUS", "YPRES", "")
  4930. || StringAt((m_current - 5), 6, "MESNES", "DEBRIS", "BLANCS", "INGRES", "CANNES", "")
  4931. || StringAt((m_current - 6), 7, "CHABLIS", "APROPOS", "JACQUES", "ELYSEES", "OEUVRES",
  4932. "GEORGES", "DESPRES", "")
  4933. || StringAt(0, 8, "ARKANSAS", "FRANCAIS", "CRUDITES", "BRUYERES", "")
  4934. || StringAt(0, 9, "DESCARTES", "DESCHUTES", "DESCHAMPS", "DESROCHES", "DESCHENES", "")
  4935. || StringAt(0, 10, "RENDEZVOUS", "")
  4936. || StringAt(0, 11, "CONTRETEMPS", "DESLAURIERS", ""))
  4937. || ((m_current == m_last)
  4938. && StringAt((m_current - 2), 2, "AI", "OI", "UI", "")
  4939. && !StringAt(0, 4, "LOIS", "LUIS", "")))
  4940. {
  4941. m_current++;
  4942. return true;
  4943. }
  4944. return false;
  4945. }
  4946. /**
  4947. * Encode non-final 'S' in words from the french where they
  4948. * are not pronounced.
  4949. *
  4950. * @return true if encoding handled in this routine, false if not
  4951. *
  4952. */
  4953. boolean Encode_Silent_French_S_Internal()
  4954. {
  4955. // french words familiar to americans where internal s is silent
  4956. if(StringAt((m_current - 2), 9, "DESCARTES", "")
  4957. || StringAt((m_current - 2), 7, "DESCHAM", "DESPRES", "DESROCH", "DESROSI", "DESJARD", "DESMARA",
  4958. "DESCHEN", "DESHOTE", "DESLAUR", "")
  4959. || StringAt((m_current - 2), 6, "MESNES", "")
  4960. || StringAt((m_current - 5), 8, "DUQUESNE", "DUCHESNE", "")
  4961. || StringAt((m_current - 7), 10, "BEAUCHESNE", "")
  4962. || StringAt((m_current - 3), 7, "FRESNEL", "")
  4963. || StringAt((m_current - 3), 9, "GROSVENOR", "")
  4964. || StringAt((m_current - 4), 10, "LOUISVILLE", "")
  4965. || StringAt((m_current - 7), 10, "ILLINOISAN", ""))
  4966. {
  4967. m_current++;
  4968. return true;
  4969. }
  4970. return false;
  4971. }
  4972. /**
  4973. * Encode silent 'S' in context of "-ISL-"
  4974. *
  4975. * @return true if encoding handled in this routine, false if not
  4976. *
  4977. */
  4978. boolean Encode_ISL()
  4979. {
  4980. //special cases 'island', 'isle', 'carlisle', 'carlysle'
  4981. if((StringAt((m_current - 2), 4, "LISL", "LYSL", "AISL", "")
  4982. && !StringAt((m_current - 3), 7, "PAISLEY", "BAISLEY", "ALISLAM", "ALISLAH", "ALISLAA", ""))
  4983. || ((m_current == 1)
  4984. && ((StringAt((m_current - 1), 4, "ISLE", "")
  4985. || StringAt((m_current - 1), 5, "ISLAN", ""))
  4986. && !StringAt((m_current - 1), 5, "ISLEY", "ISLER", ""))))
  4987. {
  4988. m_current++;
  4989. return true;
  4990. }
  4991. return false;
  4992. }
  4993. /**
  4994. * Encode "-STL-" in contexts where the 'T' is silent. Also
  4995. * encode "-USCLE-" in contexts where the 'C' is silent
  4996. *
  4997. * @return true if encoding handled in this routine, false if not
  4998. *
  4999. */
  5000. boolean Encode_STL()
  5001. {
  5002. //'hustle', 'bustle', 'whistle'
  5003. if((StringAt(m_current, 4, "STLE", "STLI", "")
  5004. && !StringAt((m_current + 2), 4, "LESS", "LIKE", "LINE", ""))
  5005. || StringAt((m_current - 3), 7, "THISTLY", "BRISTLY", "GRISTLY", "")
  5006. // e.g. "corpuscle"
  5007. || StringAt((m_current - 1), 5, "USCLE", ""))
  5008. {
  5009. // KRISTEN, KRYSTLE, CRYSTLE, KRISTLE all pronounce the 't'
  5010. // also, exceptions where "-LING" is a nominalizing suffix
  5011. if(StringAt(0, 7, "KRISTEN", "KRYSTLE", "CRYSTLE", "KRISTLE", "")
  5012. || StringAt(0, 11, "CHRISTENSEN", "CHRISTENSON", "")
  5013. || StringAt((m_current - 3), 9, "FIRSTLING", "")
  5014. || StringAt((m_current - 2), 8, "NESTLING", "WESTLING", ""))
  5015. {
  5016. MetaphAdd("ST");
  5017. m_current += 2;
  5018. }
  5019. else
  5020. {
  5021. if(m_encodeVowels
  5022. && (CharAt(m_current + 3) == 'E')
  5023. && (CharAt(m_current + 4) != 'R')
  5024. && !StringAt((m_current + 3), 4, "ETTE", "ETTA", "")
  5025. && !StringAt((m_current + 3), 2, "EY", ""))
  5026. {
  5027. MetaphAdd("SAL");
  5028. flag_AL_inversion = true;
  5029. }
  5030. else
  5031. {
  5032. MetaphAdd("SL");
  5033. }
  5034. m_current += 3;
  5035. }
  5036. return true;
  5037. }
  5038. return false;
  5039. }
  5040. /**
  5041. * Encode "christmas". Americans always pronounce this as "krissmuss"
  5042. *
  5043. * @return true if encoding handled in this routine, false if not
  5044. *
  5045. */
  5046. boolean Encode_Christmas()
  5047. {
  5048. //'christmas'
  5049. if(StringAt((m_current - 4), 8, "CHRISTMA", ""))
  5050. {
  5051. MetaphAdd("SM");
  5052. m_current += 3;
  5053. return true;
  5054. }
  5055. return false;
  5056. }
  5057. /**
  5058. * Encode "-STHM-" in contexts where the 'TH'
  5059. * is silent.
  5060. *
  5061. * @return true if encoding handled in this routine, false if not
  5062. *
  5063. */
  5064. boolean Encode_STHM()
  5065. {
  5066. //'asthma', 'isthmus'
  5067. if(StringAt(m_current, 4, "STHM", ""))
  5068. {
  5069. MetaphAdd("SM");
  5070. m_current += 4;
  5071. return true;
  5072. }
  5073. return false;
  5074. }
  5075. /**
  5076. * Encode "-ISTEN-" and "-STNT-" in contexts
  5077. * where the 'T' is silent
  5078. *
  5079. * @return true if encoding handled in this routine, false if not
  5080. *
  5081. */
  5082. boolean Encode_ISTEN()
  5083. {
  5084. // 't' is silent in verb, pronounced in name
  5085. if(StringAt(0, 8, "CHRISTEN", ""))
  5086. {
  5087. // the word itself
  5088. if(RootOrInflections(m_inWord, "CHRISTEN")
  5089. || StringAt(0, 11, "CHRISTENDOM", ""))
  5090. {
  5091. MetaphAdd("S", "ST");
  5092. }
  5093. else
  5094. {
  5095. // e.g. 'christenson', 'christene'
  5096. MetaphAdd("ST");
  5097. }
  5098. m_current += 2;
  5099. return true;
  5100. }
  5101. //e.g. 'glisten', 'listen'
  5102. if(StringAt((m_current - 2), 6, "LISTEN", "RISTEN", "HASTEN", "FASTEN", "MUSTNT", "")
  5103. || StringAt((m_current - 3), 7, "MOISTEN", ""))
  5104. {
  5105. MetaphAdd("S");
  5106. m_current += 2;
  5107. return true;
  5108. }
  5109. return false;
  5110. }
  5111. /**
  5112. * Encode special case "sugar"
  5113. *
  5114. * @return true if encoding handled in this routine, false if not
  5115. *
  5116. */
  5117. boolean Encode_Sugar()
  5118. {
  5119. //special case 'sugar-'
  5120. if(StringAt(m_current, 5, "SUGAR", ""))
  5121. {
  5122. MetaphAdd("X");
  5123. m_current++;
  5124. return true;
  5125. }
  5126. return false;
  5127. }
  5128. /**
  5129. * Encode "-SH-" as X ("sh"), except in cases
  5130. * where the 'S' and 'H' belong to different combining
  5131. * roots and are therefore pronounced seperately
  5132. *
  5133. * @return true if encoding handled in this routine, false if not
  5134. *
  5135. */
  5136. boolean Encode_SH()
  5137. {
  5138. if(StringAt(m_current, 2, "SH", ""))
  5139. {
  5140. // exception
  5141. if(StringAt((m_current - 2), 8, "CASHMERE", ""))
  5142. {
  5143. MetaphAdd("J");
  5144. m_current += 2;
  5145. return true;
  5146. }
  5147. //combining forms, e.g. 'clotheshorse', 'woodshole'
  5148. if((m_current > 0)
  5149. // e.g. "mishap"
  5150. && ((StringAt((m_current + 1), 3, "HAP", "") && ((m_current + 3) == m_last))
  5151. // e.g. "hartsheim", "clothshorse"
  5152. || StringAt((m_current + 1), 4, "HEIM", "HOEK", "HOLM", "HOLZ", "HOOD", "HEAD", "HEID",
  5153. "HAAR", "HORS", "HOLE", "HUND", "HELM", "HAWK", "HILL", "")
  5154. // e.g. "dishonor"
  5155. || StringAt((m_current + 1), 5, "HEART", "HATCH", "HOUSE", "HOUND", "HONOR", "")
  5156. // e.g. "mishear"
  5157. || (StringAt((m_current + 2), 3, "EAR", "") && ((m_current + 4) == m_last))
  5158. // e.g. "hartshorn"
  5159. || (StringAt((m_current + 2), 3, "ORN", "") && !StringAt((m_current - 2), 7, "UNSHORN", ""))
  5160. // e.g. "newshour" but not "bashour", "manshour"
  5161. || (StringAt((m_current + 1), 4, "HOUR", "")
  5162. && !(StringAt(0, 7, "BASHOUR", "") || StringAt(0, 8, "MANSHOUR", "") || StringAt(0, 6, "ASHOUR", "") ))
  5163. // e.g. "dishonest", "grasshopper"
  5164. || StringAt((m_current + 2), 5, "ARMON", "ONEST", "ALLOW", "OLDER", "OPPER", "EIMER", "ANDLE", "ONOUR", "")
  5165. // e.g. "dishabille", "transhumance"
  5166. || StringAt((m_current + 2), 6, "ABILLE", "UMANCE", "ABITUA", "")))
  5167. {
  5168. if (!StringAt((m_current - 1), 1, "S", ""))
  5169. MetaphAdd("S");
  5170. }
  5171. else
  5172. {
  5173. MetaphAdd("X");
  5174. }
  5175. m_current += 2;
  5176. return true;
  5177. }
  5178. return false;
  5179. }
  5180. /**
  5181. * Encode "-SCH-" in cases where the 'S' is pronounced
  5182. * seperately from the "CH", in words from the dutch, italian,
  5183. * and greek where it can be pronounced SK, and german words
  5184. * where it is pronounced X ("sh")
  5185. *
  5186. * @return true if encoding handled in this routine, false if not
  5187. *
  5188. */
  5189. boolean Encode_SCH()
  5190. {
  5191. // these words were combining forms many centuries ago
  5192. if(StringAt((m_current + 1), 2, "CH", ""))
  5193. {
  5194. if((m_current > 0)
  5195. // e.g. "mischief", "escheat"
  5196. && (StringAt((m_current + 3), 3, "IEF", "EAT", "")
  5197. // e.g. "mischance"
  5198. || StringAt((m_current + 3), 4, "ANCE", "ARGE", "")
  5199. // e.g. "eschew"
  5200. || StringAt(0, 6, "ESCHEW", "")))
  5201. {
  5202. MetaphAdd("S");
  5203. m_current++;
  5204. return true;
  5205. }
  5206. //Schlesinger's rule
  5207. //dutch, danish, italian, greek origin, e.g. "school", "schooner", "schiavone", "schiz-"
  5208. if((StringAt((m_current + 3), 2, "OO", "ER", "EN", "UY", "ED", "EM", "IA", "IZ", "IS", "OL", "")
  5209. && !StringAt(m_current, 6, "SCHOLT", "SCHISL", "SCHERR", ""))
  5210. || StringAt((m_current + 3), 3, "ISZ", "")
  5211. || (StringAt((m_current - 1), 6, "ESCHAT", "ASCHIN", "ASCHAL", "ISCHAE", "ISCHIA", "")
  5212. && !StringAt((m_current - 2), 8, "FASCHING", ""))
  5213. || (StringAt((m_current - 1), 5, "ESCHI", "") && ((m_current + 3) == m_last))
  5214. || (CharAt(m_current + 3) == 'Y'))
  5215. {
  5216. // e.g. "schermerhorn", "schenker", "schistose"
  5217. if(StringAt((m_current + 3), 2, "ER", "EN", "IS", "")
  5218. && (((m_current + 4) == m_last)
  5219. || StringAt((m_current + 3), 3, "ENK", "ENB", "IST", "")))
  5220. {
  5221. MetaphAdd("X", "SK");
  5222. }
  5223. else
  5224. {
  5225. MetaphAdd("SK");
  5226. }
  5227. m_current += 3;
  5228. return true;
  5229. }
  5230. else
  5231. {
  5232. MetaphAdd("X");
  5233. m_current += 3;
  5234. return true;
  5235. }
  5236. }
  5237. return false;
  5238. }
  5239. /**
  5240. * Encode "-SUR<E,A,Y>-" to J, unless it is at the beginning,
  5241. * or preceeded by 'N', 'K', or "NO"
  5242. *
  5243. * @return true if encoding handled in this routine, false if not
  5244. *
  5245. */
  5246. boolean Encode_SUR()
  5247. {
  5248. // 'erasure', 'usury'
  5249. if(StringAt((m_current + 1), 3, "URE", "URA", "URY", ""))
  5250. {
  5251. //'sure', 'ensure'
  5252. if ((m_current == 0)
  5253. || StringAt((m_current - 1), 1, "N", "K", "")
  5254. || StringAt((m_current - 2), 2, "NO", ""))
  5255. {
  5256. MetaphAdd("X");
  5257. }
  5258. else
  5259. {
  5260. MetaphAdd("J");
  5261. }
  5262. AdvanceCounter(2, 1);
  5263. return true;
  5264. }
  5265. return false;
  5266. }
  5267. /**
  5268. * Encode "-SU<O,A>-" to X ("sh") unless it is preceeded by
  5269. * an 'R', in which case it is encoded to S, or it is
  5270. * preceeded by a vowel, in which case it is encoded to J
  5271. *
  5272. * @return true if encoding handled in this routine, false if not
  5273. *
  5274. */
  5275. boolean Encode_SU()
  5276. {
  5277. //'sensuous', 'consensual'
  5278. if(StringAt((m_current + 1), 2, "UO", "UA", "") && (m_current != 0))
  5279. {
  5280. // exceptions e.g. "persuade"
  5281. if(StringAt((m_current - 1), 4, "RSUA", ""))
  5282. {
  5283. MetaphAdd("S");
  5284. }
  5285. // exceptions e.g. "casual"
  5286. else if(IsVowel(m_current - 1))
  5287. {
  5288. MetaphAdd("J", "S");
  5289. }
  5290. else
  5291. {
  5292. MetaphAdd("X", "S");
  5293. }
  5294. AdvanceCounter(3, 1);
  5295. return true;
  5296. }
  5297. return false;
  5298. }
  5299. /**
  5300. * Encodes "-SSIO-" in contexts where it is pronounced
  5301. * either J or X ("sh")
  5302. *
  5303. * @return true if encoding handled in this routine, false if not
  5304. *
  5305. */
  5306. boolean Encode_SSIO()
  5307. {
  5308. if(StringAt((m_current + 1), 4, "SION", ""))
  5309. {
  5310. //"abcission"
  5311. if (StringAt((m_current - 2), 2, "CI", ""))
  5312. {
  5313. MetaphAdd("J");
  5314. }
  5315. //'mission'
  5316. else
  5317. {
  5318. if (IsVowel(m_current - 1))
  5319. {
  5320. MetaphAdd("X");
  5321. }
  5322. }
  5323. AdvanceCounter(4, 2);
  5324. return true;
  5325. }
  5326. return false;
  5327. }
  5328. /**
  5329. * Encode "-SS-" in contexts where it is pronounced X ("sh")
  5330. *
  5331. * @return true if encoding handled in this routine, false if not
  5332. *
  5333. */
  5334. boolean Encode_SS()
  5335. {
  5336. // e.g. "russian", "pressure"
  5337. if(StringAt((m_current - 1), 5, "USSIA", "ESSUR", "ISSUR", "ISSUE", "")
  5338. // e.g. "hessian", "assurance"
  5339. || StringAt((m_current - 1), 6, "ESSIAN", "ASSURE", "ASSURA", "ISSUAB", "ISSUAN", "ASSIUS", ""))
  5340. {
  5341. MetaphAdd("X");
  5342. AdvanceCounter(3, 2);
  5343. return true;
  5344. }
  5345. return false;
  5346. }
  5347. /**
  5348. * Encodes "-SIA-" in contexts where it is pronounced
  5349. * as X ("sh"), J, or S
  5350. *
  5351. * @return true if encoding handled in this routine, false if not
  5352. *
  5353. */
  5354. boolean Encode_SIA()
  5355. {
  5356. // e.g. "controversial", also "fuchsia", "ch" is silent
  5357. if(StringAt((m_current - 2), 5, "CHSIA", "")
  5358. || StringAt((m_current - 1), 5, "RSIAL", ""))
  5359. {
  5360. MetaphAdd("X");
  5361. AdvanceCounter(3, 1);
  5362. return true;
  5363. }
  5364. // names generally get 'X' where terms, e.g. "aphasia" get 'J'
  5365. if((StringAt(0, 6, "ALESIA", "ALYSIA", "ALISIA", "STASIA", "")
  5366. && (m_current == 3)
  5367. && !StringAt(0, 9, "ANASTASIA", ""))
  5368. || StringAt((m_current - 5), 9, "DIONYSIAN", "")
  5369. || StringAt((m_current - 5), 8, "THERESIA", ""))
  5370. {
  5371. MetaphAdd("X", "S");
  5372. AdvanceCounter(3, 1);
  5373. return true;
  5374. }
  5375. if((StringAt(m_current, 3, "SIA", "") && ((m_current + 2) == m_last))
  5376. || (StringAt(m_current, 4, "SIAN", "") && ((m_current + 3) == m_last))
  5377. || StringAt((m_current - 5), 9, "AMBROSIAL", ""))
  5378. {
  5379. if ((IsVowel(m_current - 1) || StringAt((m_current - 1), 1, "R", ""))
  5380. // exclude compounds based on names, or french or greek words
  5381. && !(StringAt(0, 5, "JAMES", "NICOS", "PEGAS", "PEPYS", "")
  5382. || StringAt(0, 6, "HOBBES", "HOLMES", "JAQUES", "KEYNES", "")
  5383. || StringAt(0, 7, "MALTHUS", "HOMOOUS", "")
  5384. || StringAt(0, 8, "MAGLEMOS", "HOMOIOUS", "")
  5385. || StringAt(0, 9, "LEVALLOIS", "TARDENOIS", "")
  5386. || StringAt((m_current - 4), 5, "ALGES", "") ))
  5387. {
  5388. MetaphAdd("J");
  5389. }
  5390. else
  5391. {
  5392. MetaphAdd("S");
  5393. }
  5394. AdvanceCounter(2, 1);
  5395. return true;
  5396. }
  5397. return false;
  5398. }
  5399. /**
  5400. * Encodes "-SIO-" in contexts where it is pronounced
  5401. * as J or X ("sh")
  5402. *
  5403. * @return true if encoding handled in this routine, false if not
  5404. *
  5405. */
  5406. boolean Encode_SIO()
  5407. {
  5408. // special case, irish name
  5409. if(StringAt(0, 7, "SIOBHAN", ""))
  5410. {
  5411. MetaphAdd("X");
  5412. AdvanceCounter(3, 1);
  5413. return true;
  5414. }
  5415. if(StringAt((m_current + 1), 3, "ION", ""))
  5416. {
  5417. // e.g. "vision", "version"
  5418. if (IsVowel(m_current - 1) || StringAt((m_current - 2), 2, "ER", "UR", ""))
  5419. {
  5420. MetaphAdd("J");
  5421. }
  5422. else // e.g. "declension"
  5423. {
  5424. MetaphAdd("X");
  5425. }
  5426. AdvanceCounter(3, 1);
  5427. return true;
  5428. }
  5429. return false;
  5430. }
  5431. /**
  5432. * Encode cases where "-S-" might well be from a german name
  5433. * and add encoding of german pronounciation in alternate m_metaph
  5434. * so that it can be found in a genealogical search
  5435. *
  5436. * @return true if encoding handled in this routine, false if not
  5437. *
  5438. */
  5439. boolean Encode_Anglicisations()
  5440. {
  5441. //german & anglicisations, e.g. 'smith' match 'schmidt', 'snider' match 'schneider'
  5442. //also, -sz- in slavic language altho in hungarian it is pronounced 's'
  5443. if(((m_current == 0)
  5444. && StringAt((m_current + 1), 1, "M", "N", "L", ""))
  5445. || StringAt((m_current + 1), 1, "Z", ""))
  5446. {
  5447. MetaphAdd("S", "X");
  5448. // eat redundant 'Z'
  5449. if(StringAt((m_current + 1), 1, "Z", ""))
  5450. {
  5451. m_current += 2;
  5452. }
  5453. else
  5454. {
  5455. m_current++;
  5456. }
  5457. return true;
  5458. }
  5459. return false;
  5460. }
  5461. /**
  5462. * Encode "-SC<vowel>-" in contexts where it is silent,
  5463. * or pronounced as X ("sh"), S, or SK
  5464. *
  5465. * @return true if encoding handled in this routine, false if not
  5466. *
  5467. */
  5468. boolean Encode_SC()
  5469. {
  5470. if(StringAt(m_current, 2, "SC", ""))
  5471. {
  5472. // exception 'viscount'
  5473. if(StringAt((m_current - 2), 8, "VISCOUNT", ""))
  5474. {
  5475. m_current += 1;
  5476. return true;
  5477. }
  5478. // encode "-SC<front vowel>-"
  5479. if(StringAt((m_current + 2), 1, "I", "E", "Y", ""))
  5480. {
  5481. // e.g. "conscious"
  5482. if(StringAt((m_current + 2), 4, "IOUS", "")
  5483. // e.g. "prosciutto"
  5484. || StringAt((m_current + 2), 3, "IUT", "")
  5485. || StringAt((m_current - 4), 9, "OMNISCIEN", "")
  5486. // e.g. "conscious"
  5487. || StringAt((m_current - 3), 8, "CONSCIEN", "CRESCEND", "CONSCION", "")
  5488. || StringAt((m_current - 2), 6, "FASCIS", ""))
  5489. {
  5490. MetaphAdd("X");
  5491. }
  5492. else if(StringAt(m_current, 7, "SCEPTIC", "SCEPSIS", "")
  5493. || StringAt(m_current, 5, "SCIVV", "SCIRO", "")
  5494. // commonly pronounced this way in u.s.
  5495. || StringAt(m_current, 6, "SCIPIO", "")
  5496. || StringAt((m_current - 2), 10, "PISCITELLI", ""))
  5497. {
  5498. MetaphAdd("SK");
  5499. }
  5500. else
  5501. {
  5502. MetaphAdd("S");
  5503. }
  5504. m_current += 2;
  5505. return true;
  5506. }
  5507. MetaphAdd("SK");
  5508. m_current += 2;
  5509. return true;
  5510. }
  5511. return false;
  5512. }
  5513. /**
  5514. * Encode "-S<EA,UI,IER>-" in contexts where it is pronounced
  5515. * as J
  5516. *
  5517. * @return true if encoding handled in this routine, false if not
  5518. *
  5519. */
  5520. boolean Encode_SEA_SUI_SIER()
  5521. {
  5522. // "nausea" by itself has => NJ as a more likely encoding. Other forms
  5523. // using "nause-" (see Encode_SEA()) have X or S as more familiar pronounciations
  5524. if((StringAt((m_current - 3), 6, "NAUSEA", "") && ((m_current + 2) == m_last))
  5525. // e.g. "casuistry", "frasier", "hoosier"
  5526. || StringAt((m_current - 2), 5, "CASUI", "")
  5527. || (StringAt((m_current - 1), 5, "OSIER", "ASIER", "")
  5528. && !(StringAt(0, 6, "EASIER","")
  5529. || StringAt(0, 5, "OSIER","")
  5530. || StringAt((m_current - 2), 6, "ROSIER", "MOSIER", ""))))
  5531. {
  5532. MetaphAdd("J", "X");
  5533. AdvanceCounter(3, 1);
  5534. return true;
  5535. }
  5536. return false;
  5537. }
  5538. /**
  5539. * Encode cases where "-SE-" is pronounced as X ("sh")
  5540. *
  5541. * @return true if encoding handled in this routine, false if not
  5542. *
  5543. */
  5544. boolean Encode_SEA()
  5545. {
  5546. if((StringAt(0, 4, "SEAN", "") && ((m_current + 3) == m_last))
  5547. || (StringAt((m_current - 3), 6, "NAUSEO", "")
  5548. && !StringAt((m_current - 3), 7, "NAUSEAT", "")))
  5549. {
  5550. MetaphAdd("X");
  5551. AdvanceCounter(3, 1);
  5552. return true;
  5553. }
  5554. return false;
  5555. }
  5556. /**
  5557. * Encode "-T-"
  5558. *
  5559. */
  5560. void Encode_T()
  5561. {
  5562. if(Encode_T_Initial()
  5563. || Encode_TCH()
  5564. || Encode_Silent_French_T()
  5565. || Encode_TUN_TUL_TUA_TUO()
  5566. || Encode_TUE_TEU_TEOU_TUL_TIE()
  5567. || Encode_TUR_TIU_Suffixes()
  5568. || Encode_TI()
  5569. || Encode_TIENT()
  5570. || Encode_TSCH()
  5571. || Encode_TZSCH()
  5572. || Encode_TH_Pronounced_Separately()
  5573. || Encode_TTH()
  5574. || Encode_TH())
  5575. {
  5576. return;
  5577. }
  5578. // eat redundant 'T' or 'D'
  5579. if(StringAt((m_current + 1), 1, "T", "D", ""))
  5580. {
  5581. m_current += 2;
  5582. }
  5583. else
  5584. {
  5585. m_current++;
  5586. }
  5587. MetaphAdd("T");
  5588. }
  5589. /**
  5590. * Encode some exceptions for initial 'T'
  5591. *
  5592. * @return true if encoding handled in this routine, false if not
  5593. *
  5594. */
  5595. boolean Encode_T_Initial()
  5596. {
  5597. if(m_current == 0)
  5598. {
  5599. // americans usually pronounce "tzar" as "zar"
  5600. if (StringAt((m_current + 1), 3, "SAR", "ZAR", ""))
  5601. {
  5602. m_current++;
  5603. return true;
  5604. }
  5605. // old 'École française d'Extręme-Orient' chinese pinyin where 'ts-' => 'X'
  5606. if (((m_length == 3) && StringAt((m_current + 1), 2, "SO", "SA", "SU", ""))
  5607. || ((m_length == 4) && StringAt((m_current + 1), 3, "SAO", "SAI", ""))
  5608. || ((m_length == 5) && StringAt((m_current + 1), 4, "SING", "SANG", "")))
  5609. {
  5610. MetaphAdd("X");
  5611. AdvanceCounter(3, 2);
  5612. return true;
  5613. }
  5614. // "TS<vowel>-" at start can be pronounced both with and without 'T'
  5615. if (StringAt((m_current + 1), 1, "S", "") && IsVowel(m_current + 2))
  5616. {
  5617. MetaphAdd("TS", "S");
  5618. AdvanceCounter(3, 2);
  5619. return true;
  5620. }
  5621. // e.g. "Tjaarda"
  5622. if (StringAt((m_current + 1), 1, "J", ""))
  5623. {
  5624. MetaphAdd("X");
  5625. AdvanceCounter(3, 2);
  5626. return true;
  5627. }
  5628. // cases where initial "TH-" is pronounced as T and not 0 ("th")
  5629. if ((StringAt((m_current + 1), 2, "HU", "") && (m_length == 3))
  5630. || StringAt((m_current + 1), 3, "HAI", "HUY", "HAO", "")
  5631. || StringAt((m_current + 1), 4, "HYME", "HYMY", "HANH", "")
  5632. || StringAt((m_current + 1), 5, "HERES", ""))
  5633. {
  5634. MetaphAdd("T");
  5635. AdvanceCounter(3, 2);
  5636. return true;
  5637. }
  5638. }
  5639. return false;
  5640. }
  5641. /**
  5642. * Encode "-TCH-", reliably X ("sh", or in this case, "ch")
  5643. *
  5644. * @return true if encoding handled in this routine, false if not
  5645. *
  5646. */
  5647. boolean Encode_TCH()
  5648. {
  5649. if(StringAt((m_current + 1), 2, "CH", ""))
  5650. {
  5651. MetaphAdd("X");
  5652. m_current += 3;
  5653. return true;
  5654. }
  5655. return false;
  5656. }
  5657. /**
  5658. * Encode the many cases where americans are aware that a certain word is
  5659. * french and know to not pronounce the 'T'
  5660. *
  5661. * @return true if encoding handled in this routine, false if not
  5662. * TOUCHET CHABOT BENOIT
  5663. */
  5664. boolean Encode_Silent_French_T()
  5665. {
  5666. // french silent T familiar to americans
  5667. if(((m_current == m_last) && StringAt((m_current - 4), 5, "MONET", "GENET", "CHAUT", ""))
  5668. || StringAt((m_current - 2), 9, "POTPOURRI", "")
  5669. || StringAt((m_current - 3), 9, "BOATSWAIN", "")
  5670. || StringAt((m_current - 3), 8, "MORTGAGE", "")
  5671. || (StringAt((m_current - 4), 5, "BERET", "BIDET", "FILET", "DEBUT", "DEPOT", "PINOT", "TAROT", "")
  5672. || StringAt((m_current - 5), 6, "BALLET", "BUFFET", "CACHET", "CHALET", "ESPRIT", "RAGOUT", "GOULET",
  5673. "CHABOT", "BENOIT", "")
  5674. || StringAt((m_current - 6), 7, "GOURMET", "BOUQUET", "CROCHET", "CROQUET", "PARFAIT", "PINCHOT",
  5675. "CABARET", "PARQUET", "RAPPORT", "TOUCHET", "COURBET", "DIDEROT", "")
  5676. || StringAt((m_current - 7), 8, "ENTREPOT", "CABERNET", "DUBONNET", "MASSENET", "MUSCADET", "RICOCHET", "ESCARGOT", "")
  5677. || StringAt((m_current - 8), 9, "SOBRIQUET", "CABRIOLET", "CASSOULET", "OUBRIQUET", "CAMEMBERT", ""))
  5678. && !StringAt((m_current + 1), 2, "AN", "RY", "IC", "OM", "IN", ""))
  5679. {
  5680. m_current++;
  5681. return true;
  5682. }
  5683. return false;
  5684. }
  5685. /**
  5686. * Encode "-TU<N,L,A,O>-" in cases where it is pronounced
  5687. * X ("sh", or in this case, "ch")
  5688. *
  5689. * @return true if encoding handled in this routine, false if not
  5690. *
  5691. */
  5692. boolean Encode_TUN_TUL_TUA_TUO()
  5693. {
  5694. // e.g. "fortune", "fortunate"
  5695. if(StringAt((m_current - 3), 6, "FORTUN", "")
  5696. // e.g. "capitulate"
  5697. || (StringAt(m_current, 3, "TUL", "")
  5698. && (IsVowel(m_current - 1) && IsVowel(m_current + 3)))
  5699. // e.g. "obituary", "barbituate"
  5700. || StringAt((m_current - 2), 5, "BITUA", "BITUE", "")
  5701. // e.g. "actual"
  5702. || ((m_current > 1) && StringAt(m_current, 3, "TUA", "TUO", "")))
  5703. {
  5704. MetaphAdd("X", "T");
  5705. m_current++;
  5706. return true;
  5707. }
  5708. return false;
  5709. }
  5710. /**
  5711. * Encode "-T<vowel>-" forms where 'T' is pronounced as X
  5712. * ("sh", or in this case "ch")
  5713. *
  5714. * @return true if encoding handled in this routine, false if not
  5715. *
  5716. */
  5717. boolean Encode_TUE_TEU_TEOU_TUL_TIE()
  5718. {
  5719. // 'constituent', 'pasteur'
  5720. if(StringAt((m_current + 1), 4, "UENT", "")
  5721. || StringAt((m_current - 4), 9, "RIGHTEOUS", "")
  5722. || StringAt((m_current - 3), 7, "STATUTE", "")
  5723. || StringAt((m_current - 3), 7, "AMATEUR", "")
  5724. // e.g. "blastula", "pasteur"
  5725. || (StringAt((m_current - 1), 5, "NTULE", "NTULA", "STULE", "STULA", "STEUR", ""))
  5726. // e.g. "statue"
  5727. || (((m_current + 2) == m_last) && StringAt(m_current, 3, "TUE", ""))
  5728. // e.g. "constituency"
  5729. || StringAt(m_current, 5, "TUENC", "")
  5730. // e.g. "statutory"
  5731. || StringAt((m_current - 3), 8, "STATUTOR", "")
  5732. // e.g. "patience"
  5733. || (((m_current + 5) == m_last) && StringAt(m_current, 6, "TIENCE", "")))
  5734. {
  5735. MetaphAdd("X", "T");
  5736. AdvanceCounter(2, 1);
  5737. return true;
  5738. }
  5739. return false;
  5740. }
  5741. /**
  5742. * Encode "-TU-" forms in suffixes where it is usually
  5743. * pronounced as X ("sh")
  5744. *
  5745. * @return true if encoding handled in this routine, false if not
  5746. *
  5747. */
  5748. boolean Encode_TUR_TIU_Suffixes()
  5749. {
  5750. // 'adventure', 'musculature'
  5751. if((m_current > 0) && StringAt((m_current + 1), 3, "URE", "URA", "URI", "URY", "URO", "IUS", ""))
  5752. {
  5753. // exceptions e.g. 'tessitura', mostly from romance languages
  5754. if ((StringAt((m_current + 1), 3, "URA", "URO", "")
  5755. //&& !StringAt((m_current + 1), 4, "URIA", "")
  5756. && ((m_current + 3) == m_last))
  5757. && !StringAt((m_current - 3), 7, "VENTURA", "")
  5758. // e.g. "kachaturian", "hematuria"
  5759. || StringAt((m_current + 1), 4, "URIA", ""))
  5760. {
  5761. MetaphAdd("T");
  5762. }
  5763. else
  5764. {
  5765. MetaphAdd("X", "T");
  5766. }
  5767. AdvanceCounter(2, 1);
  5768. return true;
  5769. }
  5770. return false;
  5771. }
  5772. /**
  5773. * Encode "-TI<O,A,U>-" as X ("sh"), except
  5774. * in cases where it is part of a combining form,
  5775. * or as J
  5776. *
  5777. * @return true if encoding handled in this routine, false if not
  5778. *
  5779. */
  5780. boolean Encode_TI()
  5781. {
  5782. // '-tio-', '-tia-', '-tiu-'
  5783. // except combining forms where T already pronounced e.g 'rooseveltian'
  5784. if((StringAt((m_current + 1), 2, "IO", "") && !StringAt((m_current - 1), 5, "ETIOL", ""))
  5785. || StringAt((m_current + 1), 3, "IAL", "")
  5786. || StringAt((m_current - 1), 5, "RTIUM", "ATIUM", "")
  5787. || ((StringAt((m_current + 1), 3, "IAN", "") && (m_current > 0))
  5788. && !(StringAt((m_current - 4), 8, "FAUSTIAN", "")
  5789. || StringAt((m_current - 5), 9, "PROUSTIAN", "")
  5790. || StringAt((m_current - 2), 7, "TATIANA", "")
  5791. ||(StringAt((m_current - 3), 7, "KANTIAN", "GENTIAN", "")
  5792. || StringAt((m_current - 8), 12, "ROOSEVELTIAN", "")))
  5793. || (((m_current + 2) == m_last)
  5794. && StringAt(m_current, 3, "TIA", "")
  5795. // exceptions to above rules where the pronounciation is usually X
  5796. && !(StringAt((m_current - 3), 6, "HESTIA", "MASTIA", "")
  5797. || StringAt((m_current - 2), 5, "OSTIA", "")
  5798. || StringAt(0, 3, "TIA", "")
  5799. || StringAt((m_current - 5), 8, "IZVESTIA", "")))
  5800. || StringAt((m_current + 1), 4, "IATE", "IATI", "IABL", "IATO", "IARY", "")
  5801. || StringAt((m_current - 5), 9, "CHRISTIAN", "")))
  5802. {
  5803. if(((m_current == 2) && StringAt(0, 4, "ANTI", ""))
  5804. || StringAt(0, 5, "PATIO", "PITIA", "DUTIA", ""))
  5805. {
  5806. MetaphAdd("T");
  5807. }
  5808. else if(StringAt((m_current - 4), 8, "EQUATION", ""))
  5809. {
  5810. MetaphAdd("J");
  5811. }
  5812. else
  5813. {
  5814. if(StringAt(m_current, 4, "TION", ""))
  5815. {
  5816. MetaphAdd("X");
  5817. }
  5818. else if(StringAt(0, 5, "KATIA", "LATIA", ""))
  5819. {
  5820. MetaphAdd("T", "X");
  5821. }
  5822. else
  5823. {
  5824. MetaphAdd("X", "T");
  5825. }
  5826. }
  5827. AdvanceCounter(3, 1);
  5828. return true;
  5829. }
  5830. return false;
  5831. }
  5832. /**
  5833. * Encode "-TIENT-" where "TI" is pronounced X ("sh")
  5834. *
  5835. * @return true if encoding handled in this routine, false if not
  5836. *
  5837. */
  5838. boolean Encode_TIENT()
  5839. {
  5840. // e.g. 'patient'
  5841. if(StringAt((m_current + 1), 4, "IENT", ""))
  5842. {
  5843. MetaphAdd("X", "T");
  5844. AdvanceCounter(3, 1);
  5845. return true;
  5846. }
  5847. return false;
  5848. }
  5849. /**
  5850. * Encode "-TSCH-" as X ("ch")
  5851. *
  5852. * @return true if encoding handled in this routine, false if not
  5853. *
  5854. */
  5855. boolean Encode_TSCH()
  5856. {
  5857. //'deutsch'
  5858. if(StringAt(m_current, 4, "TSCH", "")
  5859. // combining forms in german where the 'T' is pronounced seperately
  5860. && !StringAt((m_current - 3), 4, "WELT", "KLAT", "FEST", ""))
  5861. {
  5862. // pronounced the same as "ch" in "chit" => X
  5863. MetaphAdd("X");
  5864. m_current += 4;
  5865. return true;
  5866. }
  5867. return false;
  5868. }
  5869. /**
  5870. * Encode "-TZSCH-" as X ("ch")
  5871. *
  5872. * "Neitzsche is peachy"
  5873. *
  5874. * @return true if encoding handled in this routine, false if not
  5875. *
  5876. */
  5877. boolean Encode_TZSCH()
  5878. {
  5879. //'neitzsche'
  5880. if(StringAt(m_current, 5, "TZSCH", ""))
  5881. {
  5882. MetaphAdd("X");
  5883. m_current += 5;
  5884. return true;
  5885. }
  5886. return false;
  5887. }
  5888. /**
  5889. * Encodes cases where the 'H' in "-TH-" is the beginning of
  5890. * another word in a combining form, special cases where it is
  5891. * usually pronounced as 'T', and a special case where it has
  5892. * become pronounced as X ("sh", in this case "ch")
  5893. *
  5894. * @return true if encoding handled in this routine, false if not
  5895. *
  5896. */
  5897. boolean Encode_TH_Pronounced_Separately()
  5898. {
  5899. //'adulthood', 'bithead', 'apartheid'
  5900. if(((m_current > 0)
  5901. && StringAt((m_current + 1), 4, "HOOD", "HEAD", "HEID", "HAND", "HILL", "HOLD",
  5902. "HAWK", "HEAP", "HERD", "HOLE", "HOOK", "HUNT",
  5903. "HUMO", "HAUS", "HOFF", "HARD", "")
  5904. && !StringAt((m_current - 3), 5, "SOUTH", "NORTH", ""))
  5905. || StringAt((m_current + 1), 5, "HOUSE", "HEART", "HASTE", "HYPNO", "HEQUE", "")
  5906. // watch out for greek root "-thallic"
  5907. || (StringAt((m_current + 1), 4, "HALL", "")
  5908. && ((m_current + 4) == m_last)
  5909. && !StringAt((m_current - 3), 5, "SOUTH", "NORTH", ""))
  5910. || (StringAt((m_current + 1), 3, "HAM", "")
  5911. && ((m_current + 3) == m_last)
  5912. && !(StringAt(0, 6, "GOTHAM", "WITHAM", "LATHAM", "")
  5913. || StringAt(0, 7, "BENTHAM", "WALTHAM", "WORTHAM", "")
  5914. || StringAt(0, 8, "GRANTHAM", "")))
  5915. || (StringAt((m_current + 1), 5, "HATCH", "")
  5916. && !((m_current == 0) || StringAt((m_current - 2), 8, "UNTHATCH", "")))
  5917. || StringAt((m_current - 3), 7, "WARTHOG", "")
  5918. // and some special cases where "-TH-" is usually pronounced 'T'
  5919. || StringAt((m_current - 2), 6, "ESTHER", "")
  5920. || StringAt((m_current - 3), 6, "GOETHE", "")
  5921. || StringAt((m_current - 2), 8, "NATHALIE", ""))
  5922. {
  5923. // special case
  5924. if (StringAt((m_current - 3), 7, "POSTHUM", ""))
  5925. {
  5926. MetaphAdd("X");
  5927. }
  5928. else
  5929. {
  5930. MetaphAdd("T");
  5931. }
  5932. m_current += 2;
  5933. return true;
  5934. }
  5935. return false;
  5936. }
  5937. /**
  5938. * Encode the "-TTH-" in "matthew", eating the redundant 'T'
  5939. *
  5940. * @return true if encoding handled in this routine, false if not
  5941. *
  5942. */
  5943. boolean Encode_TTH()
  5944. {
  5945. // 'matthew' vs. 'outthink'
  5946. if(StringAt(m_current, 3, "TTH", ""))
  5947. {
  5948. if (StringAt((m_current - 2), 5, "MATTH", ""))
  5949. {
  5950. MetaphAdd("0");
  5951. }
  5952. else
  5953. {
  5954. MetaphAdd("T0");
  5955. }
  5956. m_current += 3;
  5957. return true;
  5958. }
  5959. return false;
  5960. }
  5961. /**
  5962. * Encode "-TH-". 0 (zero) is used in Metaphone to encode this sound
  5963. * when it is pronounced as a dipthong, either voiced or unvoiced
  5964. *
  5965. * @return true if encoding handled in this routine, false if not
  5966. *
  5967. */
  5968. boolean Encode_TH()
  5969. {
  5970. if(StringAt(m_current, 2, "TH", "") )
  5971. {
  5972. //'-clothes-'
  5973. if(StringAt((m_current - 3), 7, "CLOTHES", ""))
  5974. {
  5975. // vowel already encoded so skip right to S
  5976. m_current += 3;
  5977. return true;
  5978. }
  5979. //special case "thomas", "thames", "beethoven" or germanic words
  5980. if(StringAt((m_current + 2), 4, "OMAS", "OMPS", "OMPK", "OMSO", "OMSE",
  5981. "AMES", "OVEN", "OFEN", "ILDA", "ILDE", "")
  5982. || (StringAt(0, 4, "THOM", "") && (m_length == 4))
  5983. || (StringAt(0, 5, "THOMS", "") && (m_length == 5))
  5984. || StringAt(0, 4, "VAN ", "VON ", "")
  5985. || StringAt(0, 3, "SCH", ""))
  5986. {
  5987. MetaphAdd("T");
  5988. }
  5989. else
  5990. {
  5991. // give an 'etymological' 2nd
  5992. // encoding for "smith"
  5993. if(StringAt(0, 2, "SM", ""))
  5994. {
  5995. MetaphAdd("0", "T");
  5996. }
  5997. else
  5998. {
  5999. MetaphAdd("0");
  6000. }
  6001. }
  6002. m_current += 2;
  6003. return true;
  6004. }
  6005. return false;
  6006. }
  6007. /**
  6008. * Encode "-V-"
  6009. *
  6010. */
  6011. void Encode_V()
  6012. {
  6013. // eat redundant 'V'
  6014. if(CharAt(m_current + 1) == 'V')
  6015. {
  6016. m_current += 2;
  6017. }
  6018. else
  6019. {
  6020. m_current++;
  6021. }
  6022. MetaphAddExactApprox("V", "F");
  6023. }
  6024. /**
  6025. * Encode "-W-"
  6026. *
  6027. */
  6028. void Encode_W()
  6029. {
  6030. if(Encode_Silent_W_At_Beginning()
  6031. || Encode_WITZ_WICZ()
  6032. || Encode_WR()
  6033. || Encode_Initial_W_Vowel()
  6034. || Encode_WH()
  6035. || Encode_Eastern_European_W())
  6036. {
  6037. return;
  6038. }
  6039. // e.g. 'zimbabwe'
  6040. if(m_encodeVowels
  6041. && StringAt(m_current, 2, "WE", "")
  6042. && ((m_current + 1) == m_last))
  6043. {
  6044. MetaphAdd("A");
  6045. }
  6046. //else skip it
  6047. m_current++;
  6048. }
  6049. /**
  6050. * Encode cases where 'W' is silent at beginning of word
  6051. *
  6052. * @return true if encoding handled in this routine, false if not
  6053. *
  6054. */
  6055. boolean Encode_Silent_W_At_Beginning()
  6056. {
  6057. //skip these when at start of word
  6058. if((m_current == 0)
  6059. && StringAt(m_current, 2, "WR", ""))
  6060. {
  6061. m_current += 1;
  6062. return true;
  6063. }
  6064. return false;
  6065. }
  6066. /**
  6067. * Encode polish patronymic suffix, mapping
  6068. * alternate spellings to the same encoding,
  6069. * and including easern european pronounciation
  6070. * to the american so that both forms can
  6071. * be found in a genealogy search
  6072. *
  6073. * @return true if encoding handled in this routine, false if not
  6074. *
  6075. */
  6076. boolean Encode_WITZ_WICZ()
  6077. {
  6078. //polish e.g. 'filipowicz'
  6079. if(((m_current + 3) == m_last) && StringAt(m_current, 4, "WICZ", "WITZ", ""))
  6080. {
  6081. if(m_encodeVowels)
  6082. {
  6083. if((m_primary.length() > 0)
  6084. && m_primary.charAt(m_primary.length() - 1) == 'A')
  6085. {
  6086. MetaphAdd("TS", "FAX");
  6087. }
  6088. else
  6089. {
  6090. MetaphAdd("ATS", "FAX");
  6091. }
  6092. }
  6093. else
  6094. {
  6095. MetaphAdd("TS", "FX");
  6096. }
  6097. m_current += 4;
  6098. return true;
  6099. }
  6100. return false;
  6101. }
  6102. /**
  6103. * Encode "-WR-" as R ('W' always effectively silent)
  6104. *
  6105. * @return true if encoding handled in this routine, false if not
  6106. *
  6107. */
  6108. boolean Encode_WR()
  6109. {
  6110. //can also be in middle of word
  6111. if(StringAt(m_current, 2, "WR", ""))
  6112. {
  6113. MetaphAdd("R");
  6114. m_current += 2;
  6115. return true;
  6116. }
  6117. return false;
  6118. }
  6119. /**
  6120. * Encode "W-", adding central and eastern european
  6121. * pronounciations so that both forms can be found
  6122. * in a genealogy search
  6123. *
  6124. * @return true if encoding handled in this routine, false if not
  6125. *
  6126. */
  6127. boolean Encode_Initial_W_Vowel()
  6128. {
  6129. if((m_current == 0) && IsVowel(m_current + 1))
  6130. {
  6131. //Witter should match Vitter
  6132. if(Germanic_Or_Slavic_Name_Beginning_With_W())
  6133. {
  6134. if(m_encodeVowels)
  6135. {
  6136. MetaphAddExactApprox("A", "VA", "A", "FA");
  6137. }
  6138. else
  6139. {
  6140. MetaphAddExactApprox("A", "V", "A", "F");
  6141. }
  6142. }
  6143. else
  6144. {
  6145. MetaphAdd("A");
  6146. }
  6147. m_current++;
  6148. // don't encode vowels twice
  6149. m_current = SkipVowels(m_current);
  6150. return true;
  6151. }
  6152. return false;
  6153. }
  6154. /**
  6155. * Encode "-WH-" either as H, or close enough to 'U' to be
  6156. * considered a vowel
  6157. *
  6158. * @return true if encoding handled in this routine, false if not
  6159. *
  6160. */
  6161. boolean Encode_WH()
  6162. {
  6163. if(StringAt(m_current, 2, "WH", ""))
  6164. {
  6165. // cases where it is pronounced as H
  6166. // e.g. 'who', 'whole'
  6167. if((CharAt(m_current + 2) == 'O')
  6168. // exclude cases where it is pronounced like a vowel
  6169. && !(StringAt((m_current + 2), 4, "OOSH", "")
  6170. || StringAt((m_current + 2), 3, "OOP", "OMP", "ORL", "ORT", "")
  6171. || StringAt((m_current + 2), 2, "OA", "OP", "")))
  6172. {
  6173. MetaphAdd("H");
  6174. AdvanceCounter(3, 2);
  6175. return true;
  6176. }
  6177. else
  6178. {
  6179. // combining forms, e.g. 'hollowhearted', 'rawhide'
  6180. if(StringAt((m_current + 2), 3, "IDE", "ARD", "EAD", "AWK", "ERD",
  6181. "OOK", "AND", "OLE", "OOD", "")
  6182. || StringAt((m_current + 2), 4, "EART", "OUSE", "OUND", "")
  6183. || StringAt((m_current + 2), 5, "AMMER", ""))
  6184. {
  6185. MetaphAdd("H");
  6186. m_current += 2;
  6187. return true;
  6188. }
  6189. else if(m_current == 0)
  6190. {
  6191. MetaphAdd("A");
  6192. m_current += 2;
  6193. // don't encode vowels twice
  6194. m_current = SkipVowels(m_current);
  6195. return true;
  6196. }
  6197. }
  6198. m_current += 2;
  6199. return true;
  6200. }
  6201. return false;
  6202. }
  6203. /**
  6204. * Encode "-W-" when in eastern european names, adding
  6205. * the eastern european pronounciation to the american so
  6206. * that both forms can be found in a genealogy search
  6207. *
  6208. * @return true if encoding handled in this routine, false if not
  6209. *
  6210. */
  6211. boolean Encode_Eastern_European_W()
  6212. {
  6213. //Arnow should match Arnoff
  6214. if(((m_current == m_last) && IsVowel(m_current - 1))
  6215. || StringAt((m_current - 1), 5, "EWSKI", "EWSKY", "OWSKI", "OWSKY", "")
  6216. || (StringAt(m_current, 5, "WICKI", "WACKI", "") && ((m_current + 4) == m_last))
  6217. || StringAt(m_current, 4, "WIAK", "") && ((m_current + 3) == m_last)
  6218. || StringAt(0, 3, "SCH", ""))
  6219. {
  6220. MetaphAddExactApprox("", "V", "", "F");
  6221. m_current++;
  6222. return true;
  6223. }
  6224. return false;
  6225. }
  6226. /**
  6227. * Encode "-X-"
  6228. *
  6229. */
  6230. void Encode_X()
  6231. {
  6232. if(Encode_Initial_X()
  6233. || Encode_Greek_X()
  6234. || Encode_X_Special_Cases()
  6235. || Encode_X_To_H()
  6236. || Encode_X_Vowel()
  6237. || Encode_French_X_Final())
  6238. {
  6239. return;
  6240. }
  6241. // eat redundant 'X' or other redundant cases
  6242. if(StringAt((m_current + 1), 1, "X", "Z", "S", "")
  6243. // e.g. "excite", "exceed"
  6244. || StringAt((m_current + 1), 2, "CI", "CE", ""))
  6245. {
  6246. m_current += 2;
  6247. }
  6248. else
  6249. {
  6250. m_current++;
  6251. }
  6252. }
  6253. /**
  6254. * Encode initial X where it is usually pronounced as S
  6255. *
  6256. * @return true if encoding handled in this routine, false if not
  6257. *
  6258. */
  6259. boolean Encode_Initial_X()
  6260. {
  6261. // current chinese pinyin spelling
  6262. if(StringAt(0, 3, "XIA", "XIO", "XIE", "")
  6263. || StringAt(0, 2, "XU", ""))
  6264. {
  6265. MetaphAdd("X");
  6266. m_current++;
  6267. return true;
  6268. }
  6269. // else
  6270. if((m_current == 0))
  6271. {
  6272. MetaphAdd("S");
  6273. m_current++;
  6274. return true;
  6275. }
  6276. return false;
  6277. }
  6278. /**
  6279. * Encode X when from greek roots where it is usually pronounced as S
  6280. *
  6281. * @return true if encoding handled in this routine, false if not
  6282. *
  6283. */
  6284. boolean Encode_Greek_X()
  6285. {
  6286. // 'xylophone', xylem', 'xanthoma', 'xeno-'
  6287. if(StringAt((m_current + 1), 3, "YLO", "YLE", "ENO", "")
  6288. || StringAt((m_current + 1), 4, "ANTH", ""))
  6289. {
  6290. MetaphAdd("S");
  6291. m_current++;
  6292. return true;
  6293. }
  6294. return false;
  6295. }
  6296. /**
  6297. * Encode special cases, "LUXUR-", "Texeira"
  6298. *
  6299. * @return true if encoding handled in this routine, false if not
  6300. *
  6301. */
  6302. boolean Encode_X_Special_Cases()
  6303. {
  6304. // 'luxury'
  6305. if(StringAt((m_current - 2), 5, "LUXUR", ""))
  6306. {
  6307. MetaphAddExactApprox("GJ", "KJ");
  6308. m_current++;
  6309. return true;
  6310. }
  6311. // 'texeira' portuguese/galician name
  6312. if(StringAt(0, 7, "TEXEIRA", "")
  6313. || StringAt(0, 8, "TEIXEIRA", ""))
  6314. {
  6315. MetaphAdd("X");
  6316. m_current++;
  6317. return true;
  6318. }
  6319. return false;
  6320. }
  6321. /**
  6322. * Encode special case where americans know the
  6323. * proper mexican indian pronounciation of this name
  6324. *
  6325. * @return true if encoding handled in this routine, false if not
  6326. *
  6327. */
  6328. boolean Encode_X_To_H()
  6329. {
  6330. // TODO: look for other mexican indian words
  6331. // where 'X' is usually pronounced this way
  6332. if(StringAt((m_current - 2), 6, "OAXACA", "")
  6333. || StringAt((m_current - 3), 7, "QUIXOTE", ""))
  6334. {
  6335. MetaphAdd("H");
  6336. m_current++;
  6337. return true;
  6338. }
  6339. return false;
  6340. }
  6341. /**
  6342. * Encode "-X-" in vowel contexts where it is usually
  6343. * pronounced KX ("ksh")
  6344. * account also for BBC pronounciation of => KS
  6345. *
  6346. * @return true if encoding handled in this routine, false if not
  6347. *
  6348. */
  6349. boolean Encode_X_Vowel()
  6350. {
  6351. // e.g. "sexual", "connexion" (british), "noxious"
  6352. if(StringAt((m_current + 1), 3, "UAL", "ION", "IOU", ""))
  6353. {
  6354. MetaphAdd("KX", "KS");
  6355. AdvanceCounter(3, 1);
  6356. return true;
  6357. }
  6358. return false;
  6359. }
  6360. /**
  6361. * Encode cases of "-X", encoding as silent when part
  6362. * of a french word where it is not pronounced
  6363. *
  6364. * @return true if encoding handled in this routine, false if not
  6365. *
  6366. */
  6367. boolean Encode_French_X_Final()
  6368. {
  6369. //french e.g. "breaux", "paix"
  6370. if(!((m_current == m_last)
  6371. && (StringAt((m_current - 3), 3, "IAU", "EAU", "IEU", "")
  6372. || StringAt((m_current - 2), 2, "AI", "AU", "OU", "OI", "EU", ""))) )
  6373. {
  6374. MetaphAdd("KS");
  6375. }
  6376. return false;
  6377. }
  6378. /**
  6379. * Encode "-Z-"
  6380. *
  6381. */
  6382. void Encode_Z()
  6383. {
  6384. if(Encode_ZZ()
  6385. || Encode_ZU_ZIER_ZS()
  6386. || Encode_French_EZ()
  6387. || Encode_German_Z())
  6388. {
  6389. return;
  6390. }
  6391. if(Encode_ZH())
  6392. {
  6393. return;
  6394. }
  6395. else
  6396. {
  6397. MetaphAdd("S");
  6398. }
  6399. // eat redundant 'Z'
  6400. if(CharAt(m_current + 1) == 'Z')
  6401. {
  6402. m_current += 2;
  6403. }
  6404. else
  6405. {
  6406. m_current++;
  6407. }
  6408. }
  6409. /**
  6410. * Encode cases of "-ZZ-" where it is obviously part
  6411. * of an italian word where "-ZZ-" is pronounced as TS
  6412. *
  6413. * @return true if encoding handled in this routine, false if not
  6414. *
  6415. */
  6416. boolean Encode_ZZ()
  6417. {
  6418. // "abruzzi", 'pizza'
  6419. if((CharAt(m_current + 1) == 'Z')
  6420. && ((StringAt((m_current + 2), 1, "I", "O", "A", "")
  6421. && ((m_current + 2) == m_last))
  6422. || StringAt((m_current - 2), 9, "MOZZARELL", "PIZZICATO", "PUZZONLAN", "")))
  6423. {
  6424. MetaphAdd("TS", "S");
  6425. m_current += 2;
  6426. return true;
  6427. }
  6428. return false;
  6429. }
  6430. /**
  6431. * Encode special cases where "-Z-" is pronounced as J
  6432. *
  6433. * @return true if encoding handled in this routine, false if not
  6434. *
  6435. */
  6436. boolean Encode_ZU_ZIER_ZS()
  6437. {
  6438. if(((m_current == 1) && StringAt((m_current - 1), 4, "AZUR", ""))
  6439. || (StringAt(m_current, 4, "ZIER", "")
  6440. && !StringAt((m_current - 2), 6, "VIZIER", ""))
  6441. || StringAt(m_current, 3, "ZSA", ""))
  6442. {
  6443. MetaphAdd("J", "S");
  6444. if(StringAt(m_current, 3, "ZSA", ""))
  6445. {
  6446. m_current += 2;
  6447. }
  6448. else
  6449. {
  6450. m_current++;
  6451. }
  6452. return true;
  6453. }
  6454. return false;
  6455. }
  6456. /**
  6457. * Encode cases where americans recognize "-EZ" as part
  6458. * of a french word where Z not pronounced
  6459. *
  6460. * @return true if encoding handled in this routine, false if not
  6461. *
  6462. */
  6463. boolean Encode_French_EZ()
  6464. {
  6465. if(((m_current == 3) && StringAt((m_current - 3), 4, "CHEZ", ""))
  6466. || StringAt((m_current - 5), 6, "RENDEZ", ""))
  6467. {
  6468. m_current++;
  6469. return true;
  6470. }
  6471. return false;
  6472. }
  6473. /**
  6474. * Encode cases where "-Z-" is in a german word
  6475. * where Z => TS in german
  6476. *
  6477. * @return true if encoding handled in this routine, false if not
  6478. *
  6479. */
  6480. boolean Encode_German_Z()
  6481. {
  6482. if(((m_current == 2) && ((m_current + 1) == m_last) && StringAt((m_current - 2), 4, "NAZI", ""))
  6483. || StringAt((m_current - 2), 6, "NAZIFY", "MOZART", "")
  6484. || StringAt((m_current - 3), 4, "HOLZ", "HERZ", "MERZ", "FITZ", "")
  6485. || (StringAt((m_current - 3), 4, "GANZ", "") && !IsVowel(m_current + 1))
  6486. || StringAt((m_current - 4), 5, "STOLZ", "PRINZ", "")
  6487. || StringAt((m_current - 4), 7, "VENEZIA", "")
  6488. || StringAt((m_current - 3), 6, "HERZOG", "")
  6489. // german words beginning with "sch-" but not schlimazel, schmooze
  6490. || (m_inWord.contains("SCH") && !(StringAt((m_last - 2), 3, "IZE", "OZE", "ZEL", "")))
  6491. || ((m_current > 0) && StringAt(m_current, 4, "ZEIT", ""))
  6492. || StringAt((m_current - 3), 4, "WEIZ", ""))
  6493. {
  6494. if((m_current > 0) && m_inWord.charAt(m_current - 1) == 'T')
  6495. {
  6496. MetaphAdd("S");
  6497. }
  6498. else
  6499. {
  6500. MetaphAdd("TS");
  6501. }
  6502. m_current++;
  6503. return true;
  6504. }
  6505. return false;
  6506. }
  6507. /**
  6508. * Encode "-ZH-" as J
  6509. *
  6510. * @return true if encoding handled in this routine, false if not
  6511. *
  6512. */
  6513. boolean Encode_ZH()
  6514. {
  6515. //chinese pinyin e.g. 'zhao', also english "phonetic spelling"
  6516. if(CharAt(m_current + 1) == 'H')
  6517. {
  6518. MetaphAdd("J");
  6519. m_current += 2;
  6520. return true;
  6521. }
  6522. return false;
  6523. }
  6524. /**
  6525. * Test for names derived from the swedish,
  6526. * dutch, or slavic that should get an alternate
  6527. * pronunciation of 'SV' to match the native
  6528. * version
  6529. *
  6530. * @return true if swedish, dutch, or slavic derived name
  6531. */
  6532. boolean Names_Beginning_With_SW_That_Get_Alt_SV()
  6533. {
  6534. if(StringAt(0, 7, "SWANSON", "SWENSON", "SWINSON", "SWENSEN",
  6535. "SWOBODA", "")
  6536. || StringAt(0, 9, "SWIDERSKI", "SWARTHOUT", "")
  6537. || StringAt(0, 10, "SWEARENGIN", ""))
  6538. {
  6539. return true;
  6540. }
  6541. return false;
  6542. }
  6543. /**
  6544. * Test for names derived from the german
  6545. * that should get an alternate pronunciation
  6546. * of 'XV' to match the german version spelled
  6547. * "schw-"
  6548. *
  6549. * @return true if german derived name
  6550. */
  6551. boolean Names_Beginning_With_SW_That_Get_Alt_XV()
  6552. {
  6553. if(StringAt(0, 5, "SWART", "")
  6554. || StringAt(0, 6, "SWARTZ", "SWARTS", "SWIGER", "")
  6555. || StringAt(0, 7, "SWITZER", "SWANGER", "SWIGERT",
  6556. "SWIGART", "SWIHART", "")
  6557. || StringAt(0, 8, "SWEITZER", "SWATZELL", "SWINDLER", "")
  6558. || StringAt(0, 9, "SWINEHART", "")
  6559. || StringAt(0, 10, "SWEARINGEN", ""))
  6560. {
  6561. return true;
  6562. }
  6563. return false;
  6564. }
  6565. /**
  6566. * Test whether the word in question
  6567. * is a name of germanic or slavic origin, for
  6568. * the purpose of determining whether to add an
  6569. * alternate encoding of 'V'
  6570. *
  6571. * @return true if germanic or slavic name
  6572. */
  6573. boolean Germanic_Or_Slavic_Name_Beginning_With_W()
  6574. {
  6575. if(StringAt(0, 3, "WEE", "WIX", "WAX", "")
  6576. || StringAt(0, 4, "WOLF", "WEIS", "WAHL", "WALZ", "WEIL", "WERT",
  6577. "WINE", "WILK", "WALT", "WOLL", "WADA", "WULF",
  6578. "WEHR", "WURM", "WYSE", "WENZ", "WIRT", "WOLK",
  6579. "WEIN", "WYSS", "WASS", "WANN", "WINT", "WINK",
  6580. "WILE", "WIKE", "WIER", "WELK", "WISE", "")
  6581. || StringAt(0, 5, "WIRTH", "WIESE", "WITTE", "WENTZ", "WOLFF", "WENDT",
  6582. "WERTZ", "WILKE", "WALTZ", "WEISE", "WOOLF", "WERTH",
  6583. "WEESE", "WURTH", "WINES", "WARGO", "WIMER", "WISER",
  6584. "WAGER", "WILLE", "WILDS", "WAGAR", "WERTS", "WITTY",
  6585. "WIENS", "WIEBE", "WIRTZ", "WYMER", "WULFF", "WIBLE",
  6586. "WINER", "WIEST", "WALKO", "WALLA", "WEBRE", "WEYER",
  6587. "WYBLE", "WOMAC", "WILTZ", "WURST", "WOLAK", "WELKE",
  6588. "WEDEL", "WEIST", "WYGAN", "WUEST", "WEISZ", "WALCK",
  6589. "WEITZ", "WYDRA", "WANDA", "WILMA", "WEBER", "")
  6590. || StringAt(0, 6, "WETZEL", "WEINER", "WENZEL", "WESTER", "WALLEN", "WENGER",
  6591. "WALLIN", "WEILER", "WIMMER", "WEIMER", "WYRICK", "WEGNER",
  6592. "WINNER", "WESSEL", "WILKIE", "WEIGEL", "WOJCIK", "WENDEL",
  6593. "WITTER", "WIENER", "WEISER", "WEXLER", "WACKER", "WISNER",
  6594. "WITMER", "WINKLE", "WELTER", "WIDMER", "WITTEN", "WINDLE",
  6595. "WASHER", "WOLTER", "WILKEY", "WIDNER", "WARMAN", "WEYANT",
  6596. "WEIBEL", "WANNER", "WILKEN", "WILTSE", "WARNKE", "WALSER",
  6597. "WEIKEL", "WESNER", "WITZEL", "WROBEL", "WAGNON", "WINANS",
  6598. "WENNER", "WOLKEN", "WILNER", "WYSONG", "WYCOFF", "WUNDER",
  6599. "WINKEL", "WIDMAN", "WELSCH", "WEHNER", "WEIGLE", "WETTER",
  6600. "WUNSCH", "WHITTY", "WAXMAN", "WILKER", "WILHAM", "WITTIG",
  6601. "WITMAN", "WESTRA", "WEHRLE", "WASSER", "WILLER", "WEGMAN",
  6602. "WARFEL", "WYNTER", "WERNER", "WAGNER", "WISSER", "")
  6603. || StringAt(0, 7, "WISEMAN", "WINKLER", "WILHELM", "WELLMAN", "WAMPLER", "WACHTER",
  6604. "WALTHER", "WYCKOFF", "WEIDNER", "WOZNIAK", "WEILAND", "WILFONG",
  6605. "WIEGAND", "WILCHER", "WIELAND", "WILDMAN", "WALDMAN", "WORTMAN",
  6606. "WYSOCKI", "WEIDMAN", "WITTMAN", "WIDENER", "WOLFSON", "WENDELL",
  6607. "WEITZEL", "WILLMAN", "WALDRUP", "WALTMAN", "WALCZAK", "WEIGAND",
  6608. "WESSELS", "WIDEMAN", "WOLTERS", "WIREMAN", "WILHOIT", "WEGENER",
  6609. "WOTRING", "WINGERT", "WIESNER", "WAYMIRE", "WHETZEL", "WENTZEL",
  6610. "WINEGAR", "WESTMAN", "WYNKOOP", "WALLICK", "WURSTER", "WINBUSH",
  6611. "WILBERT", "WALLACH", "WYNKOOP", "WALLICK", "WURSTER", "WINBUSH",
  6612. "WILBERT", "WALLACH", "WEISSER", "WEISNER", "WINDERS", "WILLMON",
  6613. "WILLEMS", "WIERSMA", "WACHTEL", "WARNICK", "WEIDLER", "WALTRIP",
  6614. "WHETSEL", "WHELESS", "WELCHER", "WALBORN", "WILLSEY", "WEINMAN",
  6615. "WAGAMAN", "WOMMACK", "WINGLER", "WINKLES", "WIEDMAN", "WHITNER",
  6616. "WOLFRAM", "WARLICK", "WEEDMAN", "WHISMAN", "WINLAND", "WEESNER",
  6617. "WARTHEN", "WETZLER", "WENDLER", "WALLNER", "WOLBERT", "WITTMER",
  6618. "WISHART", "WILLIAM", "")
  6619. || StringAt(0, 8, "WESTPHAL", "WICKLUND", "WEISSMAN", "WESTLUND", "WOLFGANG", "WILLHITE",
  6620. "WEISBERG", "WALRAVEN", "WOLFGRAM", "WILHOITE", "WECHSLER", "WENDLING",
  6621. "WESTBERG", "WENDLAND", "WININGER", "WHISNANT", "WESTRICK", "WESTLING",
  6622. "WESTBURY", "WEITZMAN", "WEHMEYER", "WEINMANN", "WISNESKI", "WHELCHEL",
  6623. "WEISHAAR", "WAGGENER", "WALDROUP", "WESTHOFF", "WIEDEMAN", "WASINGER",
  6624. "WINBORNE", "")
  6625. || StringAt(0, 9, "WHISENANT", "WEINSTEIN", "WESTERMAN", "WASSERMAN", "WITKOWSKI", "WEINTRAUB",
  6626. "WINKELMAN", "WINKFIELD", "WANAMAKER", "WIECZOREK", "WIECHMANN", "WOJTOWICZ",
  6627. "WALKOWIAK", "WEINSTOCK", "WILLEFORD", "WARKENTIN", "WEISINGER", "WINKLEMAN",
  6628. "WILHEMINA", "")
  6629. || StringAt(0, 10, "WISNIEWSKI", "WUNDERLICH", "WHISENHUNT", "WEINBERGER", "WROBLEWSKI",
  6630. "WAGUESPACK", "WEISGERBER", "WESTERVELT", "WESTERLUND", "WASILEWSKI",
  6631. "WILDERMUTH", "WESTENDORF", "WESOLOWSKI", "WEINGARTEN", "WINEBARGER",
  6632. "WESTERBERG", "WANNAMAKER", "WEISSINGER", "")
  6633. || StringAt(0, 11, "WALDSCHMIDT", "WEINGARTNER", "WINEBRENNER", "")
  6634. || StringAt(0, 12, "WOLFENBARGER", "")
  6635. || StringAt(0, 13, "WOJCIECHOWSKI", ""))
  6636. {
  6637. return true;
  6638. }
  6639. return false;
  6640. }
  6641. /**
  6642. * Test whether the word in question
  6643. * is a name starting with 'J' that should
  6644. * match names starting with a 'Y' sound.
  6645. * All forms of 'John', 'Jane', etc, get
  6646. * and alt to match e.g. 'Ian', 'Yana'. Joelle
  6647. * should match 'Yael', 'Joseph' should match
  6648. * 'Yusef'. German and slavic last names are
  6649. * also included.
  6650. *
  6651. * @return true if name starting with 'J' that
  6652. * should get an alternate encoding as a vowel
  6653. */
  6654. boolean Names_Beginning_With_J_That_Get_Alt_Y()
  6655. {
  6656. if(StringAt(0, 3, "JAN", "JON", "JAN", "JIN", "JEN", "")
  6657. || StringAt(0, 4, "JUHL", "JULY", "JOEL", "JOHN", "JOSH",
  6658. "JUDE", "JUNE", "JONI", "JULI", "JENA",
  6659. "JUNG", "JINA", "JANA", "JENI", "JOEL",
  6660. "JANN", "JONA", "JENE", "JULE", "JANI",
  6661. "JONG", "JOHN", "JEAN", "JUNG", "JONE",
  6662. "JARA", "JUST", "JOST", "JAHN", "JACO",
  6663. "JANG", "JUDE", "JONE", "")
  6664. || StringAt(0, 5, "JOANN", "JANEY", "JANAE", "JOANA", "JUTTA",
  6665. "JULEE", "JANAY", "JANEE", "JETTA", "JOHNA",
  6666. "JOANE", "JAYNA", "JANES", "JONAS", "JONIE",
  6667. "JUSTA", "JUNIE", "JUNKO", "JENAE", "JULIO",
  6668. "JINNY", "JOHNS", "JACOB", "JETER", "JAFFE",
  6669. "JESKE", "JANKE", "JAGER", "JANIK", "JANDA",
  6670. "JOSHI", "JULES", "JANTZ", "JEANS", "JUDAH",
  6671. "JANUS", "JENNY", "JENEE", "JONAH", "JONAS",
  6672. "JACOB", "JOSUE", "JOSEF", "JULES", "JULIE",
  6673. "JULIA", "JANIE", "JANIS", "JENNA", "JANNA",
  6674. "JEANA", "JENNI", "JEANE", "JONNA", "")
  6675. || StringAt(0, 6, "JORDAN", "JORDON", "JOSEPH", "JOSHUA", "JOSIAH",
  6676. "JOSPEH", "JUDSON", "JULIAN", "JULIUS", "JUNIOR",
  6677. "JUDITH", "JOESPH", "JOHNIE", "JOANNE", "JEANNE",
  6678. "JOANNA", "JOSEFA", "JULIET", "JANNIE", "JANELL",
  6679. "JASMIN", "JANINE", "JOHNNY", "JEANIE", "JEANNA",
  6680. "JOHNNA", "JOELLE", "JOVITA", "JOSEPH", "JONNIE",
  6681. "JANEEN", "JANINA", "JOANIE", "JAZMIN", "JOHNIE",
  6682. "JANENE", "JOHNNY", "JONELL", "JENELL", "JANETT",
  6683. "JANETH", "JENINE", "JOELLA", "JOEANN", "JULIAN",
  6684. "JOHANA", "JENICE", "JANNET", "JANISE", "JULENE",
  6685. "JOSHUA", "JANEAN", "JAIMEE", "JOETTE", "JANYCE",
  6686. "JENEVA", "JORDAN", "JACOBS", "JENSEN", "JOSEPH",
  6687. "JANSEN", "JORDON", "JULIAN", "JAEGER", "JACOBY",
  6688. "JENSON", "JARMAN", "JOSLIN", "JESSEN", "JAHNKE",
  6689. "JACOBO", "JULIEN", "JOSHUA", "JEPSON", "JULIUS",
  6690. "JANSON", "JACOBI", "JUDSON", "JARBOE", "JOHSON",
  6691. "JANZEN", "JETTON", "JUNKER", "JONSON", "JAROSZ",
  6692. "JENNER", "JAGGER", "JASMIN", "JEPSEN", "JORDEN",
  6693. "JANNEY", "JUHASZ", "JERGEN", "JAKOB", "")
  6694. || StringAt(0, 7, "JOHNSON", "JOHNNIE", "JASMINE", "JEANNIE", "JOHANNA",
  6695. "JANELLE", "JANETTE", "JULIANA", "JUSTINA", "JOSETTE",
  6696. "JOELLEN", "JENELLE", "JULIETA", "JULIANN", "JULISSA",
  6697. "JENETTE", "JANETTA", "JOSELYN", "JONELLE", "JESENIA",
  6698. "JANESSA", "JAZMINE", "JEANENE", "JOANNIE", "JADWIGA",
  6699. "JOLANDA", "JULIANE", "JANUARY", "JEANICE", "JANELLA",
  6700. "JEANETT", "JENNINE", "JOHANNE", "JOHNSIE", "JANIECE",
  6701. "JOHNSON", "JENNELL", "JAMISON", "JANSSEN", "JOHNSEN",
  6702. "JARDINE", "JAGGERS", "JURGENS", "JOURDAN", "JULIANO",
  6703. "JOSEPHS", "JHONSON", "JOZWIAK", "JANICKI", "JELINEK",
  6704. "JANSSON", "JOACHIM", "JANELLE", "JACOBUS", "JENNING",
  6705. "JANTZEN", "JOHNNIE", "")
  6706. || StringAt(0, 8, "JOSEFINA", "JEANNINE", "JULIANNE", "JULIANNA", "JONATHAN",
  6707. "JONATHON", "JEANETTE", "JANNETTE", "JEANETTA", "JOHNETTA",
  6708. "JENNEFER", "JULIENNE", "JOSPHINE", "JEANELLE", "JOHNETTE",
  6709. "JULIEANN", "JOSEFINE", "JULIETTA", "JOHNSTON", "JACOBSON",
  6710. "JACOBSEN", "JOHANSEN", "JOHANSON", "JAWORSKI", "JENNETTE",
  6711. "JELLISON", "JOHANNES", "JASINSKI", "JUERGENS", "JARNAGIN",
  6712. "JEREMIAH", "JEPPESEN", "JARNIGAN", "JANOUSEK", "")
  6713. || StringAt(0, 9, "JOHNATHAN", "JOHNATHON", "JORGENSEN", "JEANMARIE", "JOSEPHINA",
  6714. "JEANNETTE", "JOSEPHINE", "JEANNETTA", "JORGENSON", "JANKOWSKI",
  6715. "JOHNSTONE", "JABLONSKI", "JOSEPHSON", "JOHANNSEN", "JURGENSEN",
  6716. "JIMMERSON", "JOHANSSON", "")
  6717. || StringAt(0, 10, "JAKUBOWSKI", ""))
  6718. {
  6719. return true;
  6720. }
  6721. return false;
  6722. }
  6723. /**
  6724. * @param args
  6725. */
  6726. public static void main(String[] args)
  6727. {
  6728. // example code
  6729. Metaphone3 m3 = new Metaphone3();
  6730. //m3.SetEncodeVowels(true);
  6731. //m3.SetEncodeExact(true);
  6732. m3.SetWord("iron");
  6733. m3.Encode();
  6734. System.out.println("iron : " + m3.GetMetaph());
  6735. System.out.println("iron : (alt) " + m3.GetAlternateMetaph());
  6736. m3.SetWord("witz");
  6737. m3.Encode();
  6738. System.out.println("witz : " + m3.GetMetaph());
  6739. System.out.println("witz : (alt) " + m3.GetAlternateMetaph());
  6740. m3.SetWord("");
  6741. m3.Encode();
  6742. System.out.println("BLANK : " + m3.GetMetaph());
  6743. System.out.println("BLANK : (alt) " + m3.GetAlternateMetaph());
  6744. // these settings default to false
  6745. m3.SetEncodeExact(true);
  6746. m3.SetEncodeVowels(true);
  6747. String test = new String("Guillermo");
  6748. m3.SetWord(test);
  6749. m3.Encode();
  6750. System.out.println(test + " : " + m3.GetMetaph());
  6751. System.out.println(test + " : (alt) " + m3.GetAlternateMetaph());
  6752. test = "VILLASENOR";
  6753. m3.SetWord(test);
  6754. m3.Encode();
  6755. System.out.println(test + " : " + m3.GetMetaph());
  6756. System.out.println(test + " : (alt) " + m3.GetAlternateMetaph());
  6757. test = "GUILLERMINA";
  6758. m3.SetWord(test);
  6759. m3.Encode();
  6760. System.out.println(test + " : " + m3.GetMetaph());
  6761. System.out.println(test + " : (alt) " + m3.GetAlternateMetaph());
  6762. test = "PADILLA";
  6763. m3.SetWord(test);
  6764. m3.Encode();
  6765. System.out.println(test + " : " + m3.GetMetaph());
  6766. System.out.println(test + " : (alt) " + m3.GetAlternateMetaph());
  6767. test = "BJORK";
  6768. m3.SetWord(test);
  6769. m3.Encode();
  6770. System.out.println(test + " : " + m3.GetMetaph());
  6771. System.out.println(test + " : (alt) " + m3.GetAlternateMetaph());
  6772. test = "belle";
  6773. m3.SetWord(test);
  6774. m3.Encode();
  6775. System.out.println(test + " : " + m3.GetMetaph());
  6776. System.out.println(test + " : (alt) " + m3.GetAlternateMetaph());
  6777. test = "ERICH";
  6778. m3.SetWord(test);
  6779. m3.Encode();
  6780. System.out.println(test + " : " + m3.GetMetaph());
  6781. System.out.println(test + " : (alt) " + m3.GetAlternateMetaph());
  6782. test = "CROCE";
  6783. m3.SetWord(test);
  6784. m3.Encode();
  6785. System.out.println(test + " : " + m3.GetMetaph());
  6786. System.out.println(test + " : (alt) " + m3.GetAlternateMetaph());
  6787. test = "GLOWACKI";
  6788. m3.SetWord(test);
  6789. m3.Encode();
  6790. System.out.println(test + " : " + m3.GetMetaph());
  6791. System.out.println(test + " : (alt) " + m3.GetAlternateMetaph());
  6792. test = "qing";
  6793. m3.SetWord(test);
  6794. m3.Encode();
  6795. System.out.println(test + " : " + m3.GetMetaph());
  6796. System.out.println(test + " : (alt) " + m3.GetAlternateMetaph());
  6797. test = "tsing";
  6798. m3.SetWord(test);
  6799. m3.Encode();
  6800. System.out.println(test + " : " + m3.GetMetaph());
  6801. System.out.println(test + " : (alt) " + m3.GetAlternateMetaph());
  6802. }
  6803. }