Repository: lk-geimfari/mimesis

Issue:
The matrix generators for provider Numbers()
# Feature request

<!-- Thanks for coming up with a proposal! -->

## Thesis
The matrix can be useful in working in Jupyter Notebook. So, it would be awesome to support generators of matrix in provider `Numbers()`.

I sketched a rough implementation example:

```python
import random

from typing import Tuple, Optional, Union, List

ValuesRange = Union[
    List[int],
    Tuple[int, int],
]


def matrix(m: int = 3, n: int = 3, values_range: Optional[ValuesRange] = None) -> list:
    """Creates an 𝑚×𝑛 matrix which has 𝑚 rows and 𝑛 columns.

    :param m: Rows count.
    :param n: Columns count.
    :param values_range: Range of values of matrix items.
    :return: Matrix 𝑚×𝑛.
    """
    if values_range is None:
        values_range = (-100, 100)

    if len(values_range) > 2:
        raise ValueError('Invalid items range! Usage example: items_range=(-10, 10)')

    result = [
        [random.randint(*values_range) for _ in range(m)]
        for _ in range(n)
    ]

    return result

matrix(m=5, n=4, values_range=[-50, 50])
```

Output:
```python
[[29, 7, 17, -24, -29],
 [-26, 13, 29, -25, 6],
 [-5, 16, 13, -45, 49],
 [23, 20, -25, 14, -25]]
```


Discussion:
hi @lk-geimfari I'd like to work on this?
@anshulxyz Great! Don't forget to read `CONTRIBUTING.rst`.

Current implementation won't work when m or n is equal to 0. You need to check this out too.

Thanks and happy hacking!
Awesome! Can you assign this issue to me here: 

![Screenshot from 2019-10-01 13-22-16](https://user-images.githubusercontent.com/6121530/65944079-916e0780-e44e-11e9-8e39-7c98e93d39ba.png)

That would help me in finding all the issues assigned to me.

Also anything else I should know before starting?
@anshulxyz Done! That's it. Just read this: https://mimesis.readthedocs.io/contributing.html
@anshulxyz Also, a provider `Numbers()` is located here: https://github.com/lk-geimfari/mimesis/blob/master/mimesis/providers/numbers.py
I have unassigned this issue because of a lack of any activity in 10 days. So, anyone can take this issue for Hacktoberfest.
@lk-geimfari  Can I take this?
@LilJohny Sure you can. Feel free to create PR. You can also create a method for generating tensors.
@lk-geimfari Hi! I'd like to work on it. Kindly allow me an issue here.

@gupta-kartik20 Sure! I'll wait for PR.

Resolving patch:
diff --git a/mimesis/enums.py b/mimesis/enums.py
--- a/mimesis/enums.py
+++ b/mimesis/enums.py
@@ -216,3 +216,14 @@ class UnitName(Enum):
     INDUCTANCE = ('henry', 'H')
     TEMPERATURE = ('Celsius', '°C')
     RADIOACTIVITY = ('becquerel', 'Bq')
+
+
+class NumTypes(Enum):
+    """Provide number types.
+
+    An argument for :meth:`~mimesis.Numbers.matrix()`.
+    """
+
+    FLOATS = 'floats'
+    INTEGERS = 'integers'
+    COMPLEXES = 'complexes'
diff --git a/mimesis/providers/numbers.py b/mimesis/providers/numbers.py
--- a/mimesis/providers/numbers.py
+++ b/mimesis/providers/numbers.py
@@ -2,8 +2,9 @@
 
 """Provides data related to numbers."""
 
-from typing import List, Union
+from typing import Callable, List, Union
 
+from mimesis.enums import NumTypes
 from mimesis.providers.base import BaseProvider
 
 __all__ = ['Numbers']
@@ -66,6 +67,21 @@ def complexes(self, start_real: float = 0, end_real: float = 1,
                     self.random.uniform(start_imag, end_imag, rounding_imag))
             for _ in range(n)]
 
+    def matrix(self, m: int = 10, n: int = 10,
+               num_type: NumTypes = NumTypes.FLOATS, **kwargs) -> List[List]:
+        """Generate a m x n matrix with random numbers.
+
+        :param m: Number of rows.
+        :param n: Number of columns.
+        :param num_type: NumTypes enum object.
+        :param **kwargs: Other specific arguments.
+        :return: A matrix of random numbers.
+        """
+        key = self._validate_enum(num_type, NumTypes)
+        kwargs.update({'n': n})
+        method = getattr(self, key)
+        return [method(**kwargs) for _ in range(m)]
+
     @staticmethod
     def primes(start: int = 1, end: int = 999) -> List[int]:
         """Generate a list of prime numbers.


Test patch:
diff --git a/tests/test_providers/test_numbers.py b/tests/test_providers/test_numbers.py
--- a/tests/test_providers/test_numbers.py
+++ b/tests/test_providers/test_numbers.py
@@ -4,6 +4,8 @@
 import pytest
 
 from mimesis import Numbers
+from mimesis.enums import NumTypes
+from mimesis.exceptions import NonEnumerableError
 
 from . import patterns
 
@@ -81,6 +83,36 @@ def test_complexes(self, numbers,
             assert len(str(e.real).split('.')[1]) <= 4
             assert len(str(e.imag).split('.')[1]) <= 6
 
+    def test_matrix(self, numbers):
+
+        with pytest.raises(NonEnumerableError):
+            numbers.matrix(num_type='int')
+
+        result = numbers.matrix(rounding=4)
+        assert len(result) == 10
+        for row in result:
+            assert len(row) == 10
+            for e in row:
+                assert isinstance(e, float)
+                assert len(str(e).split('.')[1]) <= 4
+
+        result = numbers.matrix(m=5, n=5, num_type=NumTypes.INTEGERS, start=5)
+        assert len(result) == 5
+        for row in result:
+            assert len(row) == 5
+            assert min(row) >= 5
+            for e in row:
+                assert isinstance(e, int)
+
+        result = numbers.matrix(
+            num_type=NumTypes.COMPLEXES, rounding_real=4, rounding_imag=6)
+        assert len(result) == 10
+        for row in result:
+            assert len(row) == 10
+            for e in row:
+                assert len(str(e.real).split('.')[1]) <= 4
+                assert len(str(e.imag).split('.')[1]) <= 6
+
     def test_primes(self, numbers):
         result = numbers.primes()
         assert len(result) == 168
@@ -135,6 +167,10 @@ def test_complexes(self, n1, n2):
         assert n1.complexes() == n2.complexes()
         assert n1.complexes(n=5) == n2.complexes(n=5)
 
+    def test_matrix(self, n1, n2):
+        assert n1.matrix() == n2.matrix()
+        assert n1.matrix(n=5) == n2.matrix(n=5)
+
     def test_digit(self, n1, n2):
         assert n1.digit() == n2.digit()
         assert n1.digit(to_bin=True) == n2.digit(to_bin=True)