Repository: f4str/number-theory-toolkit

Issue:
Binomial theorem support for both number and strings
The binomial theorem function does not work correctly when the string is being trimmed.

Change this function completely to return a valid output on the fly depending on if the inputs are numbers or strings.

Discussion:

Patch:
diff --git a/README.md b/README.md
index ccda3ce..0a88cfd 100644
--- a/README.md
+++ b/README.md
@@ -1,1 +1,94 @@
 # Number Theory Toolkit
+
+A toolkit of various algorithms used in number theory in pure Python. All algorithms are standalone and can be used out-of-the-box.
+
+A full list of all the available algorithms includes:
+
+* Prime Checking
+* Sieve of Eratosthenes
+* Binomial Coefficient
+* Binomial Theorem
+* Pascal's Triangle
+* Euclidean Algorithm
+* Bezout Coefficients
+* Linear Congruence Solver
+* Linear Congruence System Solver
+* Chinese Remainder Theorem
+* Public Key Encryption
+* Public Key Decryption
+
+## Installation
+
+Clone the repository.
+
+```bash
+git clone https://github.com/f4str/number-theory-toolkit
+```
+
+Change directories into the cloned repository.
+
+```bash
+cd number-theory-toolkit
+```
+
+Install Python and create a virtual environment.
+
+```bash
+python3 -m venv venv
+source venv/bin/activate
+```
+
+Install the dev dependencies using pip.
+
+```bash
+pip install -e .[dev]
+```
+
+## Usage
+
+All algorithms can be used by importing the corresponding function directly from the `number_theory_toolkit` package.
+
+```python
+from number_theory_toolkit import is_prime, linear_congruence
+
+print(is_prime(123))
+print(linear_congruence(2, 1, 5))
+```
+
+Each function has its different parameters. Refer to the individual function for more information on the expected inputs and outputs.
+
+## Development
+
+The `tox` library is used to run all tests and code formatting. This is automatically installed with the dev requirements. The available options are as follows.
+
+* Run linting checks using `flake8`.
+
+    ```bash
+    tox -e lint
+    ```
+
+* Run type checks using `mypy`.
+
+    ```bash
+    tox -e type
+    ```
+
+* Run unit tests `pytest`.
+
+    ```bash
+    tox -e test
+    ```
+
+* Run all three of the tests above.
+
+    ```bash
+    tox
+    ```
+
+* Format the code using `black` and `isort` to comply with linting conventions.
+
+    ```bash
+    tox -e format
+    ```
+
+Upon pull request, merge, or push to the `master` branch, the three tests with `tox` will be run using GitHub Actions. The workflow will fail if any of the tests fail. See `.github/workflows/python-package.yml` for more information on how the CI works.
diff --git a/number_theory_toolkit/binomial.py b/number_theory_toolkit/binomial.py
index dd97870..1dba22e 100644
--- a/number_theory_toolkit/binomial.py
+++ b/number_theory_toolkit/binomial.py
@@ -25,35 +25,39 @@ def pascal_triangle(n: int) -> List[List[int]]:
 
 
 def binomial_theorem(
-    x: Union[int, float, str], y: Union[int, float, str], n: int, trim=True
+    x: Union[int, float, str], y: Union[int, float, str], n: int, simplify=True
 ) -> Union[int, float, str]:
-    if isinstance(x, (int, float)) and isinstance(y, (int, float)):
-        total: Union[int, float] = 0
-        for k in range(n + 1):
-            coeff = binomial_coefficient(n, k) * (x ** (n - k)) * (y ** k)
-            total += coeff
-        return total
+    if simplify and isinstance(x, (int, float)) and isinstance(y, (int, float)):
+        return (x + y) ** n
     else:
         terms: List[str] = []
         for k in range(n + 1):
-            if trim:
-                terms = []
-                comb = binomial_coefficient(n, k)
-                if comb > 1:
-                    terms.append(str(comb))
-
-                if n - k == 1:
-                    terms.append(f'{x}')
-                elif n - k > 1:
-                    terms.append(f'{x}^{n - k}')
-
-                if k == 1:
-                    terms.append(f'{y}')
-                elif k > 1:
-                    terms.append(f'{y}^{k}')
-
-                term = ' '.join(terms)
+            coefficient: float = binomial_coefficient(n, k)
+            if simplify:
+                term_list: List[str] = []
+
+                if isinstance(x, (int, float)):
+                    coefficient *= x ** (n - k)
+                else:
+                    if n - k == 1:
+                        term_list.append(f'{x}')
+                    elif n - k > 1:
+                        term_list.append(f'{x}^{n - k}')
+
+                if isinstance(y, (int, float)):
+                    coefficient *= y ** k
+                else:
+                    if k == 1:
+                        term_list.append(f'{y}')
+                    elif k > 1:
+                        term_list.append(f'{y}^{k}')
+
+                if coefficient > 1:
+                    term_list.insert(0, str(coefficient))
+
+                term = ' * '.join(term_list)
             else:
-                term = f'{binomial_coefficient(n, k)} {x}^{n - k} {y}^{k}'
+                term = f'{coefficient} * {x}^{n - k} * {y}^{k}'
+
             terms.append(term)
         return ' + '.join(terms)
diff --git a/number_theory_toolkit/euclidean.py b/number_theory_toolkit/euclidean.py
index 17cc614..47a8fbf 100644
--- a/number_theory_toolkit/euclidean.py
+++ b/number_theory_toolkit/euclidean.py
@@ -14,9 +14,9 @@ def euclidean_algorithm(a: int, b: int, verbose=False) -> int:
         return euclidean_algorithm(b, a % b, verbose)
 
 
-def bezout_coefficients(a: int, b: int, verbose=False, even=False) -> Tuple[int, int, int]:
+def bezout_coefficients(a: int, b: int, verbose=False, *, even=False) -> Tuple[int, int, int]:
     if a < b:
-        return bezout_coefficients(b, a, verbose, not even)
+        return bezout_coefficients(b, a, verbose, even=not even)
     elif b == 0:
         if verbose:
             print(f'gcd = {a}')
@@ -24,7 +24,7 @@ def bezout_coefficients(a: int, b: int, verbose=False, even=False) -> Tuple[int,
     else:
         q = a // b
         r = a % b
-        x, y, g = bezout_coefficients(b, r, verbose, not even)
+        x, y, g = bezout_coefficients(b, r, verbose, even=not even)
         if verbose:
             if x != 0 and y != 0:
                 if even:
diff --git a/number_theory_toolkit/linear_congruences.py b/number_theory_toolkit/linear_congruences.py
index b130921..268e45b 100644
--- a/number_theory_toolkit/linear_congruences.py
+++ b/number_theory_toolkit/linear_congruences.py
@@ -31,16 +31,3 @@ def chinese_remainder_theorem(a: int, b: int, n: int, m: int) -> int:
     k = linear_congruence(n, b - a, m)[0]
     x = a + n * k
     return x % (n * m)
-
-
-if __name__ == '__main__':
-    print(linear_congruence(2, 1, 5))
-    print(linear_congruence(2, 1, 6))
-    print(linear_congruence(7, 4, 25))
-    print(linear_congruence(15, 10, 25))
-
-    print(linear_congruence_system(2, 3, 4, 25))
-    print(linear_congruence_system(4, 2, 15, 8))
-
-    print(chinese_remainder_theorem(2, 3, 4, 25))
-    print(chinese_remainder_theorem(4, 2, 15, 8))
diff --git a/pyproject.toml b/pyproject.toml
index d19c3c2..791d661 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -2,14 +2,12 @@
 line-length = 100
 skip-string-normalization = true
 target-version = ["py38"]
-exclude = '\.eggs|\.git|\.mypy_cache|\.tox|\.env|\.venv|env|venv|_build|buck-out|build|dist'
+exclude = '\.eggs|\.git|\.mypy_cache|\.tox|_build|buck-out|build|dist|\.env|\.venv|env|venv'
 
 [tool.isort]
 profile = "black"
 line_length = 100
-# Sort by name, don't cluster "from" vs "import"
 force_sort_within_sections = true
-# Combines "as" imports on the same line
 combine_as_imports = true
 
 [tool.mypy]
@@ -19,4 +17,4 @@ show_error_context = true
 show_column_numbers = true
 show_error_codes = true
 pretty = true
-exclude = '\.eggs|\.git|\.mypy_cache|\.tox|\.env|\.venv|env|venv|_build|buck-out|build|dist'
+exclude = '\.eggs|\.git|\.mypy_cache|\.tox|_build|buck-out|build|dist|\.env|\.venv|env|venv'
diff --git a/setup.py b/setup.py
index 5ac7a67..989c489 100644
--- a/setup.py
+++ b/setup.py
@@ -16,14 +16,18 @@ setup(
     description='Numerical Methods Toolkit',
     long_description=long_description,
     long_description_content_type='text/markdown',
+    url='https://github.com/f4str/number-theory-toolkit',
     license='MIT',
     author='Farhan Ahmed',
-    author_email='farhaahmed@cs.stonybrook.edu',
     keywords='',
     classifiers=[
+        'Programming Language :: Python',
         'Programming Language :: Python :: 3',
         'Programming Language :: Python :: 3.6',
-        'Programming Language :: Python',
+        'Programming Language :: Python :: 3.7',
+        'Programming Language :: Python :: 3.8',
+        'Programming Language :: Python :: 3.9',
+        'Programming Language :: Python :: 3.10',
     ],
     python_requires='>=3.6',
     packages=find_packages(),
diff --git a/tox.ini b/tox.ini
index 66674d9..516a9c7 100644
--- a/tox.ini
+++ b/tox.ini
@@ -24,7 +24,7 @@ skip_install = true
 deps =
     mypy
 commands =
-    mypy {posargs:.}
+    mypy --install-types --non-interactive {posargs:.}
 
 [testenv:format]
 skipsdist = true
@@ -37,7 +37,7 @@ commands =
     black {posargs:.}
 
 [testenv:test]
-extra = 
+extra =
     dev
 deps =
     pytest


Test patch:
diff --git a/tests/test_binomial.py b/tests/test_binomial.py
index 6eaa1f0..afa72a2 100644
--- a/tests/test_binomial.py
+++ b/tests/test_binomial.py
@@ -9,9 +9,14 @@ def test_binomial():
     triangle = [[1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1]]
     assert pascal_triangle(5) == triangle
 
-    assert binomial_theorem(1, 1, 4) == 16
+    assert binomial_theorem(1, 2, 4) == 81
 
-    poly1 = 'y^4 + y^4'
-    assert binomial_theorem('x', 'y', 4) == poly1
-    poly2 = '1 x^4 y^0 + 4 x^3 y^1 + 6 x^2 y^2 + 4 x^1 y^3 + 1 x^0 y^4'
-    assert binomial_theorem('x', 'y', 4, False) == poly2
+    poly1 = 'x^3 + 3 * x^2 * y + 3 * x * y^2 + y^3'
+    poly2 = 'x^3 + 6 * x^2 + 12 * x + 8'
+    poly3 = '125 + 75 * y + 15 * y^2 + y^3'
+    assert binomial_theorem('x', 'y', 3) == poly1
+    assert binomial_theorem('x', 2, 3) == poly2
+    assert binomial_theorem(5, 'y', 3) == poly3
+
+    poly4 = '1 * x^3 * y^0 + 3 * x^2 * y^1 + 3 * x^1 * y^2 + 1 * x^0 * y^3'
+    assert binomial_theorem('x', 'y', 3, False) == poly4