import Data.Char
import Data.List
import qualified Data.Vector.Unboxed as U

insert_word :: [[Char]] -> [Char] -> [[Char]]
insert_word (a:tail) word
	| a==word 		= [(a)] ++ tail
	| otherwise 	= [(a)] ++ insert_word tail word
insert_word _ w = [(w)]

find_word :: [[Char]] -> [Char] -> Bool
find_word (a:tail) word
	| a==word		= True
	| otherwise 	= find_word tail word
find_word [] word = False

word_counter :: [[Char]] -> [Char] -> Integer
word_counter list word = find_word_counter list word 0

find_word_counter :: [[Char]] -> [Char] -> Integer -> Integer
find_word_counter (a:tail) word counter
	| a==word		= find_word_counter tail word counter+1
	| otherwise 	= find_word_counter tail word counter
find_word_counter [] word counter = counter

--count words from words table, ignore case with map
count_words ((a):tail) dictionary =
	let dict = (insert_word dictionary (map toLower a))
	in count_words tail dict
count_words [] dictionary = dictionary

--TRIM
-- check if letter is in "special charater" group
special_char letter
	| letter `elem` [',', '.', ':', '!', '?', ';'] = True
	| otherwise = False

--trim from word special characters defined above 	
trim_special word
	| special_char (last word) == True = trim_special (init word)
	| otherwise = word
	
--trim special characters from words table
trim_every_special ((a):tail) =	[(trim_special a)] ++ trim_every_special tail
trim_every_special [] = []

--word_to_lower :: [Char] -> [Char]
word_to_lower word@(a:tail) = map toLower word
-- converts "trim words from string" to ["trim", "words", "from", string"]
 --trim_words b = trim_every_special (words b)
trim_words b = map word_to_lower (map trim_special (words b))
-- /TRIM

dictionary_sum (word:tail) dict =
	if (find_word dict word)==False then [word] ++ dictionary_sum tail (dict++[word])
	else dictionary_sum tail dict
dictionary_sum [] dict = []

algebraic_string_sum string1 string2 =
	sort (dictionary_sum (trim_words string1 ++ trim_words string2) [])

-- FIXIT -> Fractional elements
string_vector (a:tail) string =
	[word_counter string a] ++ (string_vector tail string)
string_vector [] string = []

-- absolute sum of vector elements
get_vector_abs_sum (a:tail) =
	(abs a) + get_vector_abs_sum tail
get_vector_abs_sum [] = 0

-- module of vector -> (sum of elements module) / vector length
get_vector_module vector = 
	(fromIntegral (get_vector_abs_sum vector)) / (fromIntegral (length vector))

-- curse of floating numbers
isInt x = x == fromInteger (round x)
	
--vector1 - vector2
vector_minus (a:tail) (b:tail2) = 
	[a-b] ++ (vector_minus tail tail2)
vector_minus [] [] = []

-- Vector * Scalar
vector_times_scalar (a:tail) scalar =
	[ a * scalar ] ++ (vector_times_scalar tail scalar)
vector_times_scalar [] scalar = []

--get_multipier_vector :: (Integral a, Fractional b) => [a] -> [a] -> [b]
get_multipier_vector (a:tail) (b:tail2) =
	if (b/=0) then
		[ ceiling (fromIntegral a / fromIntegral b)] ++ get_multipier_vector tail tail2
	else
		(get_multipier_vector tail tail2)
get_multipier_vector [] [] = [0]

-- returns (vector1 - x*vector2)
linear_combination vector1 vector2 x =
	vector_minus vector1 (vector_times_scalar vector2 x)

-- FOR get_x_factor FUNCTION
--get_x :: Integral a => [a] -> [a] -> [a] -> a -> a
get_x vector1@(v1:t1) vector2@(v2:t2) mul_vec@(a:tail) minimum
	| get_vector_module (linear_combination vector1 vector2 a)< get_vector_module (linear_combination vector1 vector2 minimum) = get_x vector1 vector2 tail a	
	| otherwise = get_x vector1 vector2 tail minimum
get_x (v1:t1) (v2:t2) [] minimum = minimum


--get_x_factor :: (Fractional a, Integral a) => [a] -> [a] -> a
get_x_factor [] [] = 0
get_x_factor vector1 vector2 =
	get_x vector1 vector2 mul_vec min
	where
		mul_vec	= (get_multipier_vector vector1 vector2)
		min 	= mul_vec !! 0
		--(vector_minus vector1 (vector_times_scalar vector2 (mul_vec !! 0)))
		
--recipe
-- get two strings: str1 str2
-- trim words from strings: trim_words
-- create strings sum : algebraic_string_sum str1 str2
-- create string vector for str1 and str2: v1 = string_vector dict (trim_words str1)
-- get_x_factor from v1 v2 : x = get_x_factor v1 v2
-- Podobienstwo = 1 - f(x)/f(0)

podobienstwo string1 string2 =
	1.0 - (get_vector_module (linear_combination v1 v2 x)) / (get_vector_module v1)
	where 
		suma = (algebraic_string_sum string1 string2)
		-- longer-shorter for security reasons (for Integral x's)
		longer = if length string1>length string2 then string1 else string2
		shorter = if length string1>length string2 then string2 else string1
		v1 = string_vector suma (trim_words longer)
		v2 = string_vector suma (trim_words shorter)
		x = get_x_factor v1 v2

main :: IO()
main = do
	putStrLn (show "Podaj dwa teksty")
	string1 <- getLine
	string2 <- getLine --readLn reads any type you want
	let 
		suma = (algebraic_string_sum string1 string2)
		v1 = string_vector suma (trim_words string1)
		v2 = string_vector suma (trim_words string2)
		x = get_x_factor v1 v2
	putStrLn (show "Requested data: ")
	putStrLn (show ("Algebraic, sorted sum of strings: " ++ show suma))
	putStrLn (show (string1 ++ " <-- vector " ++ show v1))
	putStrLn (show (string2 ++ " <-- vector " ++ show v2))
	putStrLn (show ("Determined x factor : " ++ show x))
	putStrLn (show ("For known value of x linear combination equals = " ++ show (linear_combination v1 v2 x)))
	putStrLn (show ("Calculated similarity : " ++ show ((podobienstwo string1 string2) * 100.0) ++ show "%"))
		
		

