package main

import(
	"fmt"
	"os"
	"strings"
	"bufio"
	"strconv"
	"math"
	"sort"
)

var par_count int 
func main() {
	rand_flag := false
	com_flag := false
	help_flag := false
	max_count := -1
	num := int64(0)
	result := true
	max_order_index := int64(0)
	max_ope_index := int64(0)

	fmt.Println("\n\"Complete the formula.\"    in Go\n")
	fmt.Println("This program completes all the formulas when you input several parameters and one answer.\n")
	
	for i := 1; i < len(os.Args); i++ {
		if "/r" == strings.ToLower(strings.Replace(strings.Replace(os.Args[i], "--", "-", -1), "-", "/", -1)) {
			rand_flag = true
		} else if "/c" == strings.ToLower(strings.Replace(strings.Replace(os.Args[i], "--", "-", -1), "-", "/", -1)) {
			com_flag = true
		} else if "/h" == strings.Replace(strings.Replace(strings.ToLower(strings.Replace(strings.Replace(os.Args[i], "--", "-", -1), "-", "/", -1)), "help", "h", -1), "?", "h", -1) {
			help_flag = true
		} else {
			num, err := strconv.ParseInt(os.Args[i], 10, 64)
			if err != nil {
				fmt.Println("Command line option error")
				scanner := bufio.NewScanner(os.Stdin)
				fmt.Println("\nPlease press [enter] key.\a\n")
				scanner.Scan()
				os.Exit(1)
			} else {
				max_count = int(num)
			}
		}
	}

	if help_flag {
		fmt.Println("Command line option")
		fmt.Println("\"-h\"\t:\tShow help (this option)")
		fmt.Println("\"-r\"\t:\tRandomize the order of the parameters")
		fmt.Println("\"-c\"\t:\tMake the operator suitable for the computer")
		fmt.Println("number\t:\tMaximum number of candidates")
		fmt.Println("\nPlease press [enter] key.\a\n")
		bufio.NewScanner(os.Stdin).Scan()
		os.Exit(0)
	}
	
	fmt.Println("Please enter the numbers separated by spaces.")
	fmt.Println("num1 num2 num3 ... ans:\n")
	scanner := bufio.NewScanner(os.Stdin)
	scanner.Scan()
	num_strings := strings.Split(scanner.Text(), " ")
	if 3 > len(num_strings) {
		fmt.Println("Three or more numbers are required.")
		fmt.Println("\nPlease press [enter] key.\a\n")
		bufio.NewScanner(os.Stdin).Scan()
		os.Exit(1)
	}
	ans, err := strconv.Atoi(num_strings[len(num_strings) - 1])
	if err != nil {
		fmt.Println("Not numeric error")
		fmt.Println("\nPlease press [enter] key.\a\n")
		bufio.NewScanner(os.Stdin).Scan()
		os.Exit(1)
	}
	num_strings = num_strings[:len(num_strings) - 1]
	var nums []int
	for i := 0; i < len(num_strings); i++ {
		num, err := strconv.Atoi(num_strings[i])
		if err != nil {
			fmt.Println("Not numeric error")
			fmt.Println("\nPlease press [enter] key.\a\n")
			bufio.NewScanner(os.Stdin).Scan()
			os.Exit(1)
		} else {
			nums = append(nums, num)
		}
	}
	if rand_flag {
		sort.Ints(nums)
	}
	par_count = len(nums)

	min_item_number := int64(0)
	max_item_number := int64(0)
	for i := 0; i < par_count; i++ {
		num, result = mul_int(min_item_number, int64(par_count + 1))
		if result {
			num, result = add_int(num, int64(i + 1))
			if result {
				min_item_number = num
			} else {
				fmt.Println("Too many numbers.")
				fmt.Println("\nPlease press [enter] key.\a\n")
				bufio.NewScanner(os.Stdin).Scan()
				os.Exit(1)
			}
		} else {
			fmt.Println("Too many numbers.")
			fmt.Println("\nPlease press [enter] key.\a\n")
			bufio.NewScanner(os.Stdin).Scan()
			os.Exit(1)
		}
	}
	if rand_flag {
		for i := par_count - 1; i > -1; i-- {
			num, result = mul_int(max_item_number, int64(par_count + 1))
			if result {
				num, result = add_int(num, int64(i + 1))
				if result {
					max_item_number = num
				} else {
					fmt.Println("Too many numbers.")
					fmt.Println("\nPlease press [enter] key.\a\n")
					bufio.NewScanner(os.Stdin).Scan()
					os.Exit(1)
				}
			} else {
				fmt.Println("Too many numbers.")
				fmt.Println("\nPlease press [enter] key.\a\n")
				bufio.NewScanner(os.Stdin).Scan()
				os.Exit(1)
			}
		}
	} else {
		max_item_number = min_item_number
	}
	fmt.Println()
	
	var order_slice [][]int
	num, result = kai(int64(par_count - 1))
	if result {
		max_order_index = num
		order_slice = make([][]int, max_order_index)
		for i := range order_slice {
			order_slice[i] = make([]int, par_count - 1)
		}
	} else {
		fmt.Println("Too many numbers.")
		fmt.Println("\nPlease press [enter] key.\a\n")
		bufio.NewScanner(os.Stdin).Scan()
		os.Exit(1)
	}
	for i := 0; i < par_count - 1; i++ {
		order_index := 0
		l_max := int64(0)
		num, result = kai(int64(par_count - 2 - i))
		if result {
			l_max = num
		} else {
			os.Exit(1)
		}
		for j := int64(0); j < max_order_index / int64(par_count - 1 - i) / l_max; j++ {
			for k := 0; k < par_count - 1 - i; k++ {
				for l := int64(0); l < l_max; l++ {
					order_slice[order_index][i] = k
					order_index++
				}
			}
		}
	}
	fmt.Println("Slice 1 creation OK.")
	
	var ope_slice [][]int
	num, result = pow_int(int64(4), int64(par_count - 1))
	if result {
		max_ope_index = num
		ope_slice = make([][]int, max_ope_index)
		for i := range ope_slice {
			ope_slice[i] = make([]int, par_count - 1)
		}
	} else {
		fmt.Println("Too many numbers.")
		fmt.Println("\nPlease press [enter] key.\a\n")
		bufio.NewScanner(os.Stdin).Scan()
		os.Exit(1)
	}
	for i := 0; i < par_count - 1; i++ {
		ope_index := 0
		l_max := int64(0)
		times := int64(0)
		num, result = pow_int(int64(4), int64(par_count - 2 - i))
		if result {
			l_max = num
		} else {
			os.Exit(1)
		}
		num, result = pow_int(int64(4), int64(i))
		if result {
			times = num
		} else {
			os.Exit(1)
		}
		for j := int64(0); j < times; j++ {
			for k := 0; k < 4; k++ {
				for l := int64(0); l < l_max; l++ {
					ope_slice[ope_index][i] = k
					ope_index++
				}
			}
		}
	}
	fmt.Println("Slice 2 creation OK.")

	fmt.Println()
	fmt.Println("Calculation started ****************")
	par_list_str := ","
	for order_number := min_item_number; order_number < max_item_number + 1; order_number++ {
		if 0 == max_count {
			break
		}
		if select_all(order_number) {
			var par []int
			par = make([]int, par_count)
			var ope_num []int
			ope_num = make([]int, par_count - 1)
			var cal_str []string
			cal_str = make([]string, par_count)
			var cal []float64
			cal = make([]float64, par_count)
			var addition_or_subtraction []bool
			addition_or_subtraction = make([]bool, par_count)
			var division []bool
			division = make([]bool, par_count)
			x := order_number
			par_str := ""
			ans_str := ","
			for i := par_count - 1; i > -1; i-- {
				y := x % int64(par_count + 1)
				x /= int64(par_count + 1)
				par[i] = nums[int(y - 1)]
				par_str += strconv.Itoa(par[i]) + " "
			}
			par_str += ","
			if -1 != strings.Index(par_list_str, "," + par_str + ",") {
				continue
			} else {
				par_list_str += par_str
				ans_str = ","
			}
			for order_index := int64(0); order_index < max_order_index; order_index++ {
				if 0 == max_count {
					break
				}
				for ope_index := int64(0); ope_index < max_ope_index; ope_index++ {
					if 0 == max_count {
						break
					}
					for i := range(ope_num) {
						ope_num[i] = ope_slice[ope_index][i]
					}
					for i := range(cal) {
						cal[i] = float64(par[i])
						if 0 > par[i] {
							cal_str[i] = "(" + strconv.Itoa(par[i]) + ")"
						} else {
							cal_str[i] = strconv.Itoa(par[i])
						}
						addition_or_subtraction[i] = false
						division[i] = false
					}
					count := par_count
					for order := 0; order < par_count - 1; order++ {
						if 0 == max_count {
							break
						}
						point := order_slice[order_index][order]
						if 0 == ope_num[point] {
							cal_str[point] += "+" + cal_str[point + 1]
							num, result := add(cal[point], cal[point + 1])
							if result {
								cal[point] = num
								addition_or_subtraction[point] = true
								division[point] = false
							} else {
								//fmt.Println("Overflow")
								break
							}
						} else if 1 == ope_num[point] {
							if addition_or_subtraction[point + 1] {
								cal_str[point + 1] = "(" + cal_str[point + 1] + ")"
							}
							cal_str[point] += "-" + cal_str[point + 1]
							num, result := sub(cal[point], cal[point + 1])
							if result {
								cal[point] = num
								addition_or_subtraction[point] = true
								division[point] = false
							} else {
								//fmt.Println("Overflow")
								break
							}
						} else if 2 == ope_num[point] {
							if addition_or_subtraction[point] {
								cal_str[point] = "(" + cal_str[point] + ")"
							}
							if addition_or_subtraction[point + 1] {
								cal_str[point + 1] = "(" + cal_str[point + 1] + ")"
							}
							if com_flag {
								cal_str[point] += "*" + cal_str[point + 1]
							} else {
								cal_str[point] += "×" + cal_str[point + 1]
							}
							num, result := mul(cal[point], cal[point + 1])
							if result {
								cal[point] = num
								addition_or_subtraction[point] = false
								division[point] = false
							} else {
								//fmt.Println("Overflow")
								break
							}
						} else {
							if addition_or_subtraction[point] {
								cal_str[point] = "(" + cal_str[point] + ")"
							}
							if addition_or_subtraction[point + 1] {
								cal_str[point + 1] = "(" + cal_str[point + 1] + ")"
							}
							if division[point + 1] {
								cal_str[point + 1] = "(" + cal_str[point + 1] + ")"
							}
							if com_flag {
								cal_str[point] += "/" + cal_str[point + 1]
							} else {
								cal_str[point] += "÷" + cal_str[point + 1]
							}
							num, result := div(cal[point], cal[point + 1])
							if result {
								cal[point] = num
								addition_or_subtraction[point] = false
								division[point] = true
							} else {
								//fmt.Println("Divided by zero")
								break
							}
						}
						count--
						for i := point + 1; i < count; i++ {
							cal[i] = cal[i + 1]
							cal_str[i] = cal_str[i + 1]
							addition_or_subtraction[i] = addition_or_subtraction[i + 1]
							division[i] = division[i + 1]
						}
						for i := point; i < count - 1; i++ {
							ope_num[i] = ope_num[i + 1]
						}
					}
					
					if cal[0] == float64(ans) && 1 == count {
						if -1 == strings.Index(ans_str, "," + cal_str[0] + ",") {
							ans_str += cal_str[0] + ","
							fmt.Println(cal_str[0] + "=" + strconv.Itoa(ans))
							if 0 < max_count {
								max_count--
							}
						}
					}
				}
			}
		}
	}
	fmt.Println("Calculation complete ****************")
	fmt.Println("\nPlease press [enter] key.\a\n")
	bufio.NewScanner(os.Stdin).Scan()
	os.Exit(0)
/*
	if com_flag {
		fmt.Println("com")
	}
	fmt.Println(strconv.Itoa(max_count))
	
	for i := 0; i < len(nums); i++ {
		fmt.Println(strconv.Itoa(nums[i]))
	}
	fmt.Println("\n\t" + strconv.Itoa(ans))
	
	fmt.Println(min_item_number)
	fmt.Println(max_item_number)

	fmt.Println("order_slice")
	for i:= range order_slice {
		fmt.Print(i)
		for j := range order_slice[i] {
			fmt.Print("\t")
			fmt.Print(order_slice[i][j])
		}
		fmt.Print("\n")
	}
	
	fmt.Println("ope_slice")
	for i:= range ope_slice {
		fmt.Print(i)
		for j := range ope_slice[i] {
			fmt.Print("\t")
			fmt.Print(ope_slice[i][j])
		}
		fmt.Print("\n")
	}
*/
}

func add_int(a int64, b int64) (int64, bool) {
	a_float := float64(a)
	b_float := float64(b)
	num, result := add(a_float, b_float)
	if result {
		if math.MaxInt64 >= math.Abs(num) {
			return int64(num), true
		} else {
			return int64(0), false
		}
	} else {
		return int64(0), false
	}
}

func sub_int(a int64, b int64) (int64, bool) {
	a_float := float64(a)
	b_float := float64(b)
	num, result := sub(a_float, b_float)
	if result {
		if math.MaxInt64 >= math.Abs(num) {
			return int64(num), true
		} else {
			return int64(0), false
		}
	} else {
		return int64(0), false
	}
}

func mul_int(a int64, b int64) (int64, bool) {
	a_float := float64(a)
	b_float := float64(b)
	num, result := mul(a_float, b_float)
	if result {
		if math.MaxInt64 >= math.Abs(num) {
			return int64(num), true
		} else {
			return int64(0), false
		}
	} else {
		return int64(0), false
	}
}

func div_int(a int64, b int64) (int64, bool) {
	a_float := float64(a)
	b_float := float64(b)
	num, result := div(a_float, b_float)
	if result {
		if math.MaxInt64 >= math.Abs(num) {
			return int64(num), true
		} else {
			return int64(0), false
		}
	} else {
		return int64(0), false
	}
}

func add(a float64, b float64) (float64, bool) {
	if a == math.Abs(a) && b != math.Abs(b) {
		return a + b, true
	} else if a != math.Abs(a) && b == math.Abs(b) {
		return a + b, true
	} else if math.MaxFloat64 / 2.0 > math.Abs(a) / 2.0 + math.Abs(b) / 2.0 + 1.0 {
		return a + b, true
	} else {
		return float64(0.0), false
	}
}

func sub(a float64, b float64) (float64, bool) {
	if a == math.Abs(a) && b == math.Abs(b) {
		return a - b, true
	} else if a != math.Abs(a) && b != math.Abs(b) {
		return a - b, true
	} else if math.MaxFloat64 / 2.0 > math.Abs(a) / 2.0 + math.Abs(b) / 2.0 + 1.0 {
		return a - b, true
	} else {
		return float64(0.0), false
	}
}

func mul(a float64, b float64) (float64, bool) {
	if math.MaxFloat64 / 4.0 > (math.Abs(a) / 2.0 * 1.001) * (math.Abs(b) / 2.0 * 1.001) {
		return a * b, true
	} else {
		return float64(0.0), false
	}
}

func div(a float64, b float64) (float64, bool) {
	if 0 == b {
		return float64(0.0), false
	} else if math.MaxFloat64 / 4.0 > (math.Abs(a) / 2.0 * 1.001) / (math.Abs(b) * 2.0 * 0.999) {
		return a / b, true
	} else {
		return float64(0.0), false
	}
}

func pow_int(a int64, b int64) (int64, bool) {
	num := math.Pow(float64(a), float64(b))
	if math.MaxInt64 >= num {
		return int64(num), true
	} else {
		return int64(0), false
	}
}

func kai(n int64) (int64, bool) {
	x := int64(1)
	for i := int64(0); i < n; i++ {
		num, result := mul_int(x, i + 1)
		if result {
			x = num
		} else {
			return int64(0), false
		}
	}
	return x, true
}

func select_all(x int64) (bool) {
	var n []bool
	t := true
	n = make([]bool, par_count)
	for i := range n {
		n[i] = false
	}
	for i := len(n) - 1; i > -1; i-- {
		y := int(x % int64(par_count + 1))
		x /= int64(par_count + 1)
		for j := range n {
			if y == j + 1 {
				n[j] = true
				break
			}
		}
	}
	for i := range n {
		if !n[i] {
			t = false
			break
		}
	}
	return t
}
