<p>You are given two numeric strings <code>num1</code> and <code>num2</code> and two integers <code>max_sum</code> and <code>min_sum</code>. We denote an integer <code>x</code> to be <em>good</em> if:</p>

<ul>
	<li><code>num1 &lt;= x &lt;= num2</code></li>
	<li><code>min_sum &lt;= digit_sum(x) &lt;= max_sum</code>.</li>
</ul>

<p>Return <em>the number of good integers</em>. Since the answer may be large, return it modulo <code>10<sup>9</sup> + 7</code>.</p>

<p>Note that <code>digit_sum(x)</code> denotes the sum of the digits of <code>x</code>.</p>

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

<pre>
<strong>Input:</strong> num1 = &quot;1&quot;, num2 = &quot;12&quot;, <code>min_sum</code> = 1, max_sum = 8
<strong>Output:</strong> 11
<strong>Explanation:</strong> There are 11 integers whose sum of digits lies between 1 and 8 are 1,2,3,4,5,6,7,8,10,11, and 12. Thus, we return 11.
</pre>

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

<pre>
<strong>Input:</strong> num1 = &quot;1&quot;, num2 = &quot;5&quot;, <code>min_sum</code> = 1, max_sum = 5
<strong>Output:</strong> 5
<strong>Explanation:</strong> The 5 integers whose sum of digits lies between 1 and 5 are 1,2,3,4, and 5. Thus, we return 5.
</pre>

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

<ul>
	<li><code>1 &lt;= num1 &lt;= num2 &lt;= 10<sup>22</sup></code></li>
	<li><code>1 &lt;= min_sum &lt;= max_sum &lt;= 400</code></li>
</ul>



# import scipy

# class Solution:
#     def count(self, num1: str, num2: str, min_sum: int, max_sum: int) -> int:
#         def f(num):
#             tot = 0 
#             lead = int(num[0])
#             num = num[1:]
#             for s in range(min_sum, max_sum+1):
#                 for j in range(1, lead):
#                     ts = s - j
#                     if ts < 0:
#                         break
#                     N = ts
#                     r = 9
#                     n = len(num)

#                     min_q = math.ceil((N+n)/(r+1))
#                     for q in range(min_q,n+1):
#                         tot += (-1)**(n-q)*math.comb(n,q)*math.comb(q*(r+1)-1-N,n-1)
#                 @
#             return tot
#         return 0
# #     @cache
#     #     def f(n, l, r, cur_dig_sum):
#     #         if len(n) == 0:
#     #             return 1
            
#     #         for i in range(int(n[0])+1):
#     #             if 

#     #     tot =  0
        
#     #     return f(num2, min_sum, max_sum,0) - f(num1, min_sum, max_sum,0)
#     # # 1 12
#     # #   1010010105102
#     # # 100101001010100
#     # # 1 5


import numpy as np
from numpy.polynomial.polynomial import polymul, polypow

M = 10**9 + 7
p2 = [[1]]
for _ in range(22):
    p2.append( np.mod( polymul(p2[-1], [1] * 10).astype('int64'), M ) )

class Solution:
    def count(self, num1: str, num2: str, min_sum: int, max_sum: int) -> int:
        cnt = 0
        p1 = [0] + [1] * 9
    
        for n in range(len(num1), len(num2) + 1):
            coeff = np.mod(polymul(p1, p2[n-1]).astype('int64'), M)
            cnt += coeff[min_sum:max_sum+1].sum() % M
            cnt %= M
        
        for j, A in enumerate([num1, num2]):
            n = len(A)
            l, r = min_sum, max_sum + 1
            for i in range(n):
                x = ord(A[i]) - ord('0')
                if j == 0:
                    p1 = [int(i != 0 and x > 0)] + [1] * (x - 1)
                else:
                    p1 = [0] * (x + 1) + [1] * (9 - x)
                coeff = np.mod(polymul(p1, p2[n-i-1]).astype('int64'), M)
                cnt -= coeff[l:r].sum() % M
                l, r = max(0, l - x), max(0, r - x)
                
        return cnt % M
247