<p>A <strong>good meal</strong> is a meal that contains <strong>exactly two different food items</strong> with a sum of deliciousness equal to a power of two.</p>

<p>You can pick <strong>any</strong> two different foods to make a good meal.</p>

<p>Given an array of integers <code>deliciousness</code> where <code>deliciousness[i]</code> is the deliciousness of the <code>i<sup>​​​​​​th</sup>​​​​</code>​​​​ item of food, return <em>the number of different <strong>good meals</strong> you can make from this list modulo</em> <code>10<sup>9</sup> + 7</code>.</p>

<p>Note that items with different indices are considered different even if they have the same deliciousness value.</p>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>

<pre>
<strong>Input:</strong> deliciousness = [1,3,5,7,9]
<strong>Output:</strong> 4
<strong>Explanation: </strong>The good meals are (1,3), (1,7), (3,5) and, (7,9).
Their respective sums are 4, 8, 8, and 16, all of which are powers of 2.
</pre>

<p><strong class="example">Example 2:</strong></p>

<pre>
<strong>Input:</strong> deliciousness = [1,1,1,3,3,3,7]
<strong>Output:</strong> 15
<strong>Explanation: </strong>The good meals are (1,1) with 3 ways, (1,3) with 9 ways, and (1,7) with 3 ways.</pre>

<p>&nbsp;</p>
<p><strong>Constraints:</strong></p>

<ul>
	<li><code>1 &lt;= deliciousness.length &lt;= 10<sup>5</sup></code></li>
	<li><code>0 &lt;= deliciousness[i] &lt;= 2<sup>20</sup></code></li>
</ul>

class Solution:
    def countPairs(self, deliciousness: List[int]) -> int:
        """
        brute force:
        iterate over each element of array i, then try to the number of delcious meal you can make with it arr[i:]
        runtime (n**2)

        improved:
        given the constraints 0 <= deliciousness[i] <= 2**20 there's are 22 delicious numbers (validVal)
        we can sort the array and use a two pointer approach.
        use the twoSum approach where the target is each value in deliciousVals
        do some preprocessing with a hashmap, key = deliciousness and value is an array of deliciousness w/ that value
        runtime: O(22 * n)
        """
        # 
        # 0... 2*21 (if there are 2 deliciousness[i] = 2**20)
        valid = set([2 ** i for i in range(22)])
        self.ans = 0
        self.modulo = 10 ** 9 + 7
        h = {}
        for i, d in enumerate(deliciousness):
            if h.get(d, None) is None:
                h[d] = set()
            h[d].add(i)
    
        for target in valid:
            used = set()
            for ingredient in deliciousness:
                # we already prepped a decilious meal with the current ingredient
                # or current ingredient exceeds target
                if ingredient in used or ingredient > target:
                    continue
                # n is number of current ingredient
                # m is the number of the counter ingredients
                used.add(ingredient)
                otherIngredient = target - ingredient
                # n choose 2
                n = len(h[ingredient])
                if ingredient == otherIngredient:
                    goodMeals = (n * (n-1)) // 2 # n choose 2 -> n * (n + 1) // 2
                    self.ans = (self.ans + goodMeals) % self.modulo
                    continue
                
                # try to get the other ingredients
                m = len(h[otherIngredient]) if h.get(otherIngredient) is not None else 0
                if m == 0:
                    continue
                # other wise it's nC1 * mC1 = n * m
                goodMeals = n * m
                self.ans = (self.ans + goodMeals) % self.modulo
                used.add(otherIngredient)
        
        return self.ans


961