package common

// CartesianProduct takes map of lists and returns list of unique tuples
func CartesianProduct(mapOfLists map[string][]interface{}) []map[string]interface{} {
	listNames := make([]string, 0)
	lists := make([][]interface{}, 0)
	for k, v := range mapOfLists {
		listNames = append(listNames, k)
		lists = append(lists, v)
	}

	listCart := cartN(lists...)

	rtn := make([]map[string]interface{}, 0)
	for _, list := range listCart {
		vMap := make(map[string]interface{})
		for i, v := range list {
			vMap[listNames[i]] = v
		}
		rtn = append(rtn, vMap)
	}
	return rtn
}

func cartN(a ...[]interface{}) [][]interface{} {
	c := 1
	for _, a := range a {
		c *= len(a)
	}
	if c == 0 || len(a) == 0 {
		return nil
	}
	p := make([][]interface{}, c)
	b := make([]interface{}, c*len(a))
	n := make([]int, len(a))
	s := 0
	for i := range p {
		e := s + len(a)
		pi := b[s:e]
		p[i] = pi
		s = e
		for j, n := range n {
			pi[j] = a[j][n]
		}
		for j := len(n) - 1; j >= 0; j-- {
			n[j]++
			if n[j] < len(a[j]) {
				break
			}
			n[j] = 0
		}
	}
	return p
}
