code: func SpatialEntropy(match match.Match, turns int, shiftCount int) float64 {
  	var s, d float64
  	if match.DictionaryName == "qwerty" || match.DictionaryName == "dvorak" {
  		//todo: verify qwerty and dvorak have the same length and degree
  		s = float64(len(adjacency.BuildQwerty().Graph))
  		d = adjacency.BuildQwerty().CalculateAvgDegree()
  	} else {
  		s = float64(keyPadStartingPositions)
  		d = keyPadAvgDegree
  	}
  
  	possibilities := float64(0)
  
  	length := float64(len(match.Token))
  
  	//TODO: Should this be <= or just < ?
  	//Estimate the number of possible patterns w/ length L or less with t turns or less
  	for i := float64(2); i <= length+1; i++ {
  		possibleTurns := math.Min(float64(turns), i-1)
  		for j := float64(1); j <= possibleTurns+1; j++ {
  			x := zxcvbnmath.NChoseK(i-1, j-1) * s * math.Pow(d, j)
  			possibilities += x
  		}
  	}
  
  	entropy := math.Log2(possibilities)
  	//add extra entropu for shifted keys. ( % instead of 5 A instead of a)
  	//Math is similar to extra entropy for uppercase letters in dictionary matches.
  
  	if S := float64(shiftCount); S > float64(0) {
  		possibilities = float64(0)
  		U := length - S
  
  		for i := float64(0); i < math.Min(S, U)+1; i++ {
  			possibilities += zxcvbnmath.NChoseK(S+U, i)
  		}
  
  		entropy += math.Log2(possibilities)
  	}
  
  	return entropy
  }
docs: // SpatialEntropy calculates the entropy for spatial matches