package main

import "sync"

func LongestDupSubstringBruteforce(s string) string {
	strLen := len(s)
	if strLen < 2 {
		return ""
	}

	res := ""
	for ln := 1; ln < strLen; ln++ {
		for i := 0; i <= strLen-ln; i++ {
			word1 := s[i : i+ln]
			count := 0
			for j := 0; j <= strLen-ln; j++ {
				word2 := s[j : j+ln]
				if word1 == word2 {
					count++
					if count > 1 {
						res = word1
						break
					}
				}
			}
		}
	}

	return res
}

func LongestDupSubstringReversedBruteforce(s string) string {
	strLen := len(s)
	if strLen < 2 {
		return ""
	}

	for ln := strLen - 1; ln >= 1; ln-- {
		for i := strLen - ln; i >= 0; i-- {
			word1 := s[i : i+ln]
			count := 0
			for j := strLen - ln; j >= 0; j-- {
				word2 := s[j : j+ln]
				if word1 == word2 {
					count++
					if count > 1 {
						return word1
					}
				}
			}
		}
	}

	return ""
}

func LongestDupSubstringSmartforce(s string) string {
	strLen := len(s)
	if strLen < 2 {
		return ""
	}

	indicesCurrent := []int{}
	indicesNext := []int{}

	res := ""
	for i := 0; i < strLen-len(res); i++ {

		for j := 0; j < strLen; j++ {
			indicesCurrent = append(indicesCurrent, j)
		}

		for ln := 1; ln <= strLen-i; ln++ {
			word1 := s[i : i+ln]

			for _, index := range indicesCurrent {
				if index+ln > strLen {
					continue
				}
				word2 := s[index : index+ln]
				if (word1 == word2) && (index != i) {

					if len(res) < len(word1) {
						res = word1
					}
					indicesNext = append(indicesNext, index)
				}
			}

			indicesCurrent = indicesNext
			indicesNext = []int{}
		}
	}

	return res
}

func LongestDupSubstringSmartforceMark2(s string) string {
	strLen := len(s)
	if strLen < 2 {
		return ""
	}

	indicesNext := []int{}
	indicesAll := []int{}

	for j := 0; j < strLen; j++ {
		indicesAll = append(indicesAll, j)
	}

	res := ""
	for i := 0; i < strLen-len(res); i++ {
		indicesCurrent := indicesAll

		for ln := 1; ln <= strLen-i; ln++ {
			word1 := s[i : i+ln]

			for _, index := range indicesCurrent {
				if index+ln > strLen {
					continue
				}

				word2 := s[index : index+ln]
				if (index != i) && (word1 == word2) {
					if len(res) < len(word1) {
						res = word1
					}
					indicesNext = append(indicesNext, index)
				}
			}

			indicesCurrent = indicesNext
			indicesNext = []int{}
		}
	}

	return res
}

func LongestDupSubstringBruteforceReversedMark2(s string) string {
	strLen := len(s)
	if strLen < 2 {
		return ""
	}

	res := ""
	for ln := strLen - 1; ln > 0; ln-- {

		if ln < len(res) {
			break
		}

		for i := 0; i < strLen-ln; i++ {
			word1 := s[i : i+ln]
			for j := i + 1; j+ln <= strLen; j++ {
				word2 := s[j : j+ln]
				if word1 == word2 {
					if len(res) < len(word1) {
						res = word1
					}
					break
				}
			}
		}
	}
	return res
}

func LongestDupSubstringSmartforceMark3(s string) string {
	strLen := len(s)
	if strLen < 2 {
		return ""
	}

	indicesNext := []int{}
	indicesAll := []int{}

	for j := 0; j < strLen; j++ {
		indicesAll = append(indicesAll, j)
	}

	res := ""
	for i := 0; i < strLen-len(res); i++ {
		indicesCurrent := indicesAll[i+1:]

		for ln := 1; ln <= strLen-i; ln++ {

			word1 := s[i : i+ln]

			for _, index := range indicesCurrent {
				if index+ln > strLen {
					continue
				}

				if s[i+ln-1] == s[index+ln-1] {
					if len(res) < len(word1) {
						res = word1
					}
					indicesNext = append(indicesNext, index)
				}
			}

			indicesCurrent = indicesNext
			indicesNext = []int{}
		}
	}

	return res
}

func LongestDupSubstringSmartforceMark3Goroutine(s string, from int, to int) string {
	strLen := len(s)
	if strLen < 2 {
		return ""
	}

	indicesNext := make([]int, 0, len(s))
	indicesAll := make([]int, 0, len(s))
	indicesEmpty := make([]int, 0, len(s))

	for j := 0; j < strLen; j++ {
		indicesAll = append(indicesAll, j)
	}

	res := ""
	for i := from; (i < to) && (i < strLen-len(res)); i++ {
		indicesCurrent := indicesAll[i+1:]

		for ln := 1; ln <= strLen-i; ln++ {

			word1 := s[i : i+ln]

			for _, index := range indicesCurrent {
				if index+ln > strLen {
					continue
				}

				if s[i+ln-1] == s[index+ln-1] {
					if len(res) < len(word1) {
						res = word1
					}
					indicesNext = append(indicesNext, index)
				}
			}

			indicesCurrent = indicesNext
			indicesNext = indicesEmpty
		}
	}

	return res
}

func LongestDupSubstringGoroutines(s string) string {
	threads := 24
	results := make([]string, threads)
	interval := len(s) / threads

	if interval < (800 / threads) {
		return LongestDupSubstringSmartforceMark3Goroutine(s, 0, len(s))
	}

	wg := new(sync.WaitGroup)
	wg.Add(threads)

	for i := 0; i < threads-1; i++ {
		go func(input int) {
			results[input] = LongestDupSubstringSmartforceMark3Goroutine(s, input*interval, input*interval+interval)
			wg.Done()
		}(i)
	}

	go func(input int) {
		results[input] = LongestDupSubstringSmartforceMark3Goroutine(s, input*interval, len(s))
		wg.Done()
	}(threads - 1)

	wg.Wait()

	resLen := -1
	res := ""

	for _, w := range results {
		if len(w) > resLen {
			resLen = len(w)
			res = w
		}
	}

	return res
}

func DupSubstringLengthN(s string, n int) string {
	StringMap := map[string]interface{}{}

	for begin := 0; begin <= len(s)-n; begin++ {
		end := begin + n
		str := s[begin:end]

		_, ok := StringMap[str]
		if ok {
			return str
		}

		StringMap[str] = nil
	}

	return ""
}

func LongestDupSubstringBinarySearchAndMap(s string) string {
	maxLen := 0
	dups := map[int]string{}
	dups[0] = ""

	left := 0
	right := len(s)
	mid := 0

	for left < right {
		mid = (left + right) / 2
		res := DupSubstringLengthN(s, mid)

		if len(res) > 0 {
			dups[mid] = res
			left = mid + 1
			if mid > maxLen {
				maxLen = mid
			}
		} else {
			right = mid
		}
	}

	return dups[maxLen]
}

func LongestDupSubstring(s string) string {
	return LongestDupSubstringBinarySearchAndMap(s)
}
