# ----------------------------------------------------------
# AdvaS Advanced Search 
# module for phonetic algorithms
#
# (C) 2002 - 2005 Frank Hofmann, Chemnitz, Germany
# email fh@efho.de
# ----------------------------------------------------------

# changed 2005-01-24

import string
import re

def soundex (term):
	"Return the soundex value to a string argument."

	# Create and compare soundex codes of English words.
	#
	# Soundex is an algorithm that hashes English strings into
	# alpha-numerical value that represents what the word sounds
	# like. For more information on soundex and some notes on the
	# differences in implemenations visit:
	# http://www.bluepoof.com/Soundex/info.html
	#
	# This version modified by Nathan Heagy at Front Logic Inc., to be
	# compatible with php's soundexing and much faster.
	#
	# eAndroid / Nathan Heagy / Jul 29 2000
	# changes by Frank Hofmann / Jan 02 2005

	# generate translation table only once. used to translate into soundex numbers
	#table = string.maketrans('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', '0123012002245501262301020201230120022455012623010202')
	table = string.maketrans('ABCDEFGHIJKLMNOPQRSTUVWXYZ', '01230120022455012623010202')

	# check parameter
	if not term:
		return "0000" # could be Z000 for compatibility with other implementations
	# end if

    	# convert into uppercase letters
	term = string.upper(term)
    	first_char = term[0]

	# translate the string into soundex code according to the table above
	term = string.translate(term[1:], table)
	
	# remove all 0s
	term = term.replace("0", "")

    	# remove duplicate numbers in-a-row
    	str2 = first_char
	for x in term:
		if x != str2[-1]:
	   		str2 = str2 + x
		# end if
	# end for

	# pad with zeros
	str2 = str2+"0"*len(str2)

	# take the first four letters
	return_value = str2[:4]

	# return value
	return return_value

def metaphone (term):
	"returns metaphone code for a given string"

	# implementation of the original algorithm from Lawrence Philips
	# extended/rewritten by M. Kuhn
	# improvements with thanks to John Machin <sjmachin@lexicon.net>

	# define return value
	code = ""

	i = 0
	term_length = len(term)

	if (term_length == 0):
		# empty string ?
		return code
	# end if

	# extension #1 (added 2005-01-28)
	# convert to lowercase
	term = term.lower()
	
	# extension #2 (added 2005-01-28)
	# remove all non-english characters, first
	term = re.sub(r'[^a-z]', '', term)
	if len(term) == 0:
		# nothing left
		return code
	# end if
		
	# extension #3 (added 2005-01-24)
	# conflate repeated letters
	firstChar = term[0]
    	str2 = firstChar
	for x in term:
		if x != str2[-1]:
	   		str2 = str2 + x
		# end if
	# end for
	
	# extension #4 (added 2005-01-24)
	# remove any vowels unless a vowel is the first letter
	firstChar = str2[0]
	str3 = firstChar
	for x in str2[1:]:
		if (re.search(r'[^aeiou]', x)):
			str3 = str3 + x
		# end if
	# end for
	
	term = str3
	term_length = len(term)
	if term_length == 0:
		# nothing left
		return code
	# end if
	
	# check for exceptions
	if (term_length > 1):
		# get first two characters
		first_chars = term[0:2]

		# build translation table
		table = {
		    "ae":"e",
		    "gn":"n",
		    "kn":"n",
		    "pn":"n",
		    "wr":"n",
		    "wh":"w"
		}
		
		if first_chars in table.keys():
			term = term[2:]
			code = table[first_chars]
			term_length = len(term)
		# end if
		
	elif (term[0] == "x"):
		term = ""
		code = "s"
		term_length = 0
	# end if

	# define standard translation table
	st_trans = {
		"b":"b",
		"c":"k",
		"d":"t",
		"g":"k",
		"h":"h",
		"k":"k",
		"p":"p",
		"q":"k",
		"s":"s",
		"t":"t",
		"v":"f",
		"w":"w",
		"x":"ks",
		"y":"y",
		"z":"s"
	}

	i = 0
	while (i<term_length):
		# init character to add, init basic patterns
		add_char = ""
		part_n_2 = ""
		part_n_3 = ""
		part_n_4 = ""
		part_c_2 = ""
		part_c_3 = ""

		# extract a number of patterns, if possible
		if (i < (term_length - 1)):
			part_n_2 = term[i:i+2]

			if (i>0):
				part_c_2 = term[i-1:i+1]
				part_c_3 = term[i-1:i+2]
			# end if
		# end if

		if (i < (term_length - 2)):
			part_n_3 = term[i:i+3]
		# end if

		if (i < (term_length - 3)):
			part_n_4 = term[i:i+4]
		# end if

		# use table with conditions for translations
		if (term[i] == "b"):
			add_char = st_trans["b"]
			if (i == (term_length - 1)):
				if (i>0):
					if (term[i-1] == "m"):
						add_char = ""
					# end if
				# end if
			# end if
		elif (term[i] == "c"):
			add_char = st_trans["c"]
			if (part_n_2 == "ch"):
				add_char = "x"
			elif (re.search(r'c[iey]', part_n_2)):
				add_char = "s"
			# end if

			if (part_n_3 == "cia"):
				add_char = "x"
			# end if

			if (re.search(r'sc[iey]', part_c_3)):
				add_char = ""
			# end if

		elif (term[i] == "d"):
			add_char = st_trans["d"]
			if (re.search(r'dg[eyi]', part_n_3)):
				add_char = "j"
			# end if

		elif (term[i] == "g"):
			add_char = st_trans["g"]

			if (part_n_2 == "gh"):
				if (i == (term_length - 2)):
					add_char = ""
				# end if
			elif (re.search(r'gh[aeiouy]', part_n_3)):
				add_char = ""
			elif (part_n_2 == "gn"):
				add_char = ""
			elif (part_n_4 == "gned"):
				add_char = ""
			elif (re.search(r'dg[eyi]',part_c_3)):
				add_char = ""
			elif (part_n_2 == "gi"):
				if (part_c_3 != "ggi"):
					add_char = "j"
				# end if
			elif (part_n_2 == "ge"):
				if (part_c_3 != "gge"):
					add_char = "j"
				# end if
			elif (part_n_2 == "gy"):
				if (part_c_3 != "ggy"):
					add_char = "j"
				# end if
			elif (part_n_2 == "gg"):
				add_char = ""
			# end if
		elif (term[i] == "h"):
			add_char = st_trans["h"]
			if (re.search(r'[aeiouy]h[^aeiouy]', part_c_3)):
				add_char = ""
			elif (re.search(r'[csptg]h', part_c_2)):
				add_char = ""
			# end if
		elif (term[i] == "k"):
			add_char = st_trans["k"]
			if (part_c_2 == "ck"):
				add_char = ""
			# end if
		elif (term[i] == "p"):
			add_char = st_trans["p"]
			if (part_n_2 == "ph"):
				add_char = "f"
			# end if
		elif (term[i] == "q"):
			add_char = st_trans["q"]
		elif (term[i] == "s"):
			add_char = st_trans["s"]
			if (part_n_2 == "sh"):
				add_char = "x"
			# end if

			if (re.search(r'si[ao]', part_n_3)):
				add_char = "x"
			# end if
		elif (term[i] == "t"):
			add_char = st_trans["t"]
			if (part_n_2 == "th"):
				add_char = "0"
			# end if

			if (re.search(r'ti[ao]', part_n_3)):
				add_char = "x"
			# end if
		elif (term[i] == "v"):
			add_char = st_trans["v"]
		elif (term[i] == "w"):
			add_char = st_trans["w"]
			if (re.search(r'w[^aeiouy]', part_n_2)):
				add_char = ""
			# end if
		elif (term[i] == "x"):
			add_char = st_trans["x"]
		elif (term[i] == "y"):
			add_char = st_trans["y"]
		elif (term[i] == "z"):
			add_char = st_trans["z"]
		else:
			# alternative
			add_char = term[i]
		# end if

		code = code + add_char
		i += 1
	# end while

	# return metaphone code
	return code

def nysiis (term):
	"returns New York State Identification and Intelligence Algorithm (NYSIIS) code for the given term"

	code = ""

	i = 0
	term_length = len(term)

	if (term_length == 0):
		# empty string ?
		return code
	# end if

	# build translation table for the first characters
	table = {
		"mac":"mcc",
		"ph":"ff",
		"kn":"nn",
		"pf":"ff",
		"k":"c",
		"sch":"sss"
	}

	for table_entry in table.keys():
		table_value = table[table_entry]	# get table value
		table_value_len = len(table_value)	# calculate its length
		first_chars = term[0:table_value_len]
		if (first_chars == table_entry):
			term = table_value + term[table_value_len:]
			break
		# end if
	# end for

	# build translation table for the last characters
	table = {
		"ee":"y",
		"ie":"y",
		"dt":"d",
		"rt":"d",
		"rd":"d",
		"nt":"d",
		"nd":"d",
	}

	for table_entry in table.keys():
		table_value = table[table_entry]	# get table value
		table_entry_len = len(table_entry)	# calculate its length
		last_chars = term[(0 - table_entry_len):]
		#print last_chars, ", ", table_entry, ", ", table_value
		if (last_chars == table_entry):
			term = term[:(0 - table_value_len + 1)] + table_value
			break
		# end if
	# end for

	# initialize code
	code = term

	# transform ev->af
	code = re.sub(r'ev', r'af', code)

	# transform a,e,i,o,u->a
	code = re.sub(r'[aeiouy]', r'a', code)
	
	# transform q->g
	code = re.sub(r'q', r'g', code)
	
	# transform z->s
	code = re.sub(r'z', r's', code)

	# transform m->n
	code = re.sub(r'm', r'n', code)

	# transform kn->n
	code = re.sub(r'kn', r'n', code)

	# transform k->c
	code = re.sub(r'k', r'c', code)

	# transform sch->sss
	code = re.sub(r'sch', r'sss', code)

	# transform ph->ff
	code = re.sub(r'ph', r'ff', code)

	# transform h-> if previous or next is nonvowel -> previous
	occur = re.findall(r'([a-z]{0,1}?)h([a-z]{0,1}?)', code)
	#print occur
	for occur_group in occur:
		occur_item_previous = occur_group[0]
		occur_item_next = occur_group[1]

		if ((re.match(r'[^aeiouy]', occur_item_previous)) or (re.match(r'[^aeiouy]', occur_item_next))):
			if (occur_item_previous != ""):
				# make substitution
				code = re.sub (occur_item_previous + "h", occur_item_previous * 2, code, 1)
			# end if
		# end if
	# end for
	
	# transform w-> if previous is vowel -> previous
	occur = re.findall(r'([aeiouy]{1}?)w', code)
	#print occur
	for occur_group in occur:
		occur_item_previous = occur_group[0]
		# make substitution
		code = re.sub (occur_item_previous + "w", occur_item_previous * 2, code, 1)
	# end for
	
	# check last character
	# -s, remove
	code = re.sub (r's$', r'', code)
	# -ay, replace by -y
	code = re.sub (r'ay$', r'y', code)
	# -a, remove
	code = re.sub (r'a$', r'', code)
	
	# return nysiis code
	return code

def caverphone (term):
	"returns the language key using the caverphone algorithm 2.0"

	# Developed at the University of Otago, New Zealand.
	# Project: Caversham Project (http://caversham.otago.ac.nz)
	# Developer: David Hood, University of Otago, New Zealand
	# Contact: caversham@otago.ac.nz
	# Project Technical Paper: http://caversham.otago.ac.nz/files/working/ctp150804.pdf
	# Version 2.0 (2004-08-15)

	code = ""

	i = 0
	term_length = len(term)

	if (term_length == 0):
		# empty string ?
		return code
	# end if

	# convert to lowercase
	code = term.lower()

	# remove anything not in the standard alphabet (a-z)
	code = re.sub(r'[^a-z]', '', code)

	# remove final e
	if code.endswith("e"):
		code = code[:-1]

	# if the name starts with cough, rough, tough, enough or trough -> cou2f (rou2f, tou2f, enou2f, trough)
	code = re.sub(r'^([crt]|(en)|(tr))ough', r'\1ou2f', code)

	# if the name starts with gn -> 2n
	code = re.sub(r'^gn', r'2n', code)

	# if the name ends with mb -> m2
	code = re.sub(r'mb$', r'm2', code)

	# replace cq -> 2q
	code = re.sub(r'cq', r'2q', code)
	
	# replace c[i,e,y] -> s[i,e,y]
	code = re.sub(r'c([iey])', r's\1', code)
	
	# replace tch -> 2ch
	code = re.sub(r'tch', r'2ch', code)
	
	# replace c,q,x -> k
	code = re.sub(r'[cqx]', r'k', code)
	
	# replace v -> f
	code = re.sub(r'v', r'f', code)
	
	# replace dg -> 2g
	code = re.sub(r'dg', r'2g', code)
	
	# replace ti[o,a] -> si[o,a]
	code = re.sub(r'ti([oa])', r'si\1', code)
	
	# replace d -> t
	code = re.sub(r'd', r't', code)
	
	# replace ph -> fh
	code = re.sub(r'ph', r'fh', code)

	# replace b -> p
	code = re.sub(r'b', r'p', code)
	
	# replace sh -> s2
	code = re.sub(r'sh', r's2', code)
	
	# replace z -> s
	code = re.sub(r'z', r's', code)

	# replace initial vowel [aeiou] -> A
	code = re.sub(r'^[aeiou]', r'A', code)

	# replace all other vowels [aeiou] -> 3
	code = re.sub(r'[aeiou]', r'3', code)

	# replace j -> y
	code = re.sub(r'j', r'y', code)

	# replace an initial y3 -> Y3
	code = re.sub(r'^y3', r'Y3', code)
	
	# replace an initial y -> A
	code = re.sub(r'^y', r'A', code)

	# replace y -> 3
	code = re.sub(r'y', r'3', code)
	
	# replace 3gh3 -> 3kh3
	code = re.sub(r'3gh3', r'3kh3', code)
	
	# replace gh -> 22
	code = re.sub(r'gh', r'22', code)

	# replace g -> k
	code = re.sub(r'g', r'k', code)

	# replace groups of s,t,p,k,f,m,n by its single, upper-case equivalent
	for single_letter in ["s", "t", "p", "k", "f", "m", "n"]:
		otherParts = re.split(single_letter + "+", code)
		code = string.join(otherParts, string.upper(single_letter))
	
	# replace w[3,h3] by W[3,h3]
	code = re.sub(r'w(h?3)', r'W\1', code)

	# replace final w with 3
	code = re.sub(r'w$', r'3', code)

	# replace w -> 2
	code = re.sub(r'w', r'2', code)

	# replace h at the beginning with an A
	code = re.sub(r'^h', r'A', code)

	# replace all other occurrences of h with a 2
	code = re.sub(r'h', r'2', code)

	# replace r3 with R3
	code = re.sub(r'r3', r'R3', code)

	# replace final r -> 3
	code = re.sub(r'r$', r'3', code)

	# replace r with 2
	code = re.sub(r'r', r'2', code)

	# replace l3 with L3
	code = re.sub(r'l3', r'L3', code)
	
	# replace final l -> 3
	code = re.sub(r'l$', r'3', code)
	
	# replace l with 2
	code = re.sub(r'l', r'2', code)

	# remove all 2's
	code = re.sub(r'2', r'', code)

	# replace the final 3 -> A
	code = re.sub(r'3$', r'A', code)
	
	# remove all 3's
	code = re.sub(r'3', r'', code)

	# extend the code by 10 '1' (one)
	code += '1' * 10
	
	# take the first 10 characters
	caverphoneCode = code[:10]
	
	# return caverphone code
	return caverphoneCode

