Repository: lk-geimfari/mimesis

Issue:
Restructure Numbers provider
# Feature request

## Thesis

While I was implementing the ``matrix()`` function in the ``Numbers`` provider and I was thinking about some changes that we could make in this provider:

- Add a function ``complex(start, end, length)`` that return a random array of complex numbers
- Make the API uniform, so that every function in the ``Numbers`` provider has the arguments ``start, end, length`` (where possible). Maybe in the ``complex()`` function we can add ``start_real, end_real, start_imaginary, end_imaginary`` ?
- Remove the function ``ranting()`` and add an argument ``decimal_digits`` in the function ``floats()`` to specify the number of decimal digits to keep.

## Reasoning

I think these changes would make the provider more uniform and easy to use.



Discussion:
@ceccoemi I like this idea. You can implement it.
@ceccoemi And yeah you're right — `rating` is not really useful function. Just do not forget about changelogs and inform there what users must use instead of it. Thank you!

Resolving patch:
diff --git a/mimesis/providers/numbers.py b/mimesis/providers/numbers.py
--- a/mimesis/providers/numbers.py
+++ b/mimesis/providers/numbers.py
@@ -17,18 +17,20 @@ class Meta:
 
         name = 'numbers'
 
-    def floats(self, n: int = 2) -> List[float]:
+    def floats(self, start: float = 0, end: float = 1, n: int = 10,
+               rounding: int = 15) -> List[float]:
         """Generate a list of random float numbers.
 
-        :param n: Raise 10 to the 'n' power.
+        :param start: Start range.
+        :param end: End range.
+        :param n: Length of the list.
+        :param rounding: Max number of decimal digits.
         :return: The list of floating-point numbers.
         """
-        nums = [self.random.random()
-                for _ in range(10 ** int(n))]
-        return nums
+        return [self.random.uniform(start, end, rounding) for _ in range(n)]
 
     def integers(self, start: int = 0, end: int = 10,
-                 length: int = 10) -> List[int]:
+                 n: int = 10) -> List[int]:
         """Generate a list of random integers.
 
         Integers can be negative or positive numbers.
@@ -36,14 +38,33 @@ def integers(self, start: int = 0, end: int = 10,
 
         :param start: Start.
         :param end: End.
-        :param length: Length of list.
+        :param n: Length of list.
         :return: List of integers.
 
         :Example:
             [-20, -19, -18, -17]
         """
-        return self.random.randints(
-            length, start, end)
+        return self.random.randints(n, start, end)
+
+    def complexes(self, start_real: float = 0, end_real: float = 1,
+                  start_imag: float = 0, end_imag: float = 1,
+                  rounding_real: int = 15, rounding_imag: int = 15,
+                  n: int = 10) -> List[complex]:
+        """Generate a list of random complex numbers.
+
+        :param start_real: Start real range.
+        :param end_real: End real range.
+        :param start_imag: Start imaginary range.
+        :param end_imag: End imaginary range.
+        :param rounding_real: Rounding real part.
+        :param rounding_imag: Roungind imaginary part.
+        :param n: Length of the list.
+        :return: A list of random complex numbers.
+        """
+        return [
+            complex(self.random.uniform(start_real, end_real, rounding_real),
+                    self.random.uniform(start_imag, end_imag, rounding_imag))
+            for _ in range(n)]
 
     @staticmethod
     def primes(start: int = 1, end: int = 999) -> List[int]:
@@ -99,14 +120,3 @@ def between(self, minimum: int = 1, maximum: int = 1000) -> int:
         :return: Number.
         """
         return self.random.randint(minimum, maximum)
-
-    def rating(self, maximum: float = 5.0) -> float:
-        """Generate a random rating for something.
-
-        :param maximum: Maximum value (default is 5.0).
-        :return: Rating.
-
-        :Example:
-            4.7
-        """
-        return self.random.uniform(0, maximum, 1)


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
@@ -17,14 +17,26 @@ def numbers(self):
     def test_str(self, numbers):
         assert re.match(patterns.PROVIDER_STR_REGEX, str(numbers))
 
-    def test_floats(self, numbers):
-        result = numbers.floats()
-        assert len(result) == 100
+    @pytest.mark.parametrize(
+        'start, end', [
+            (1.2, 10),
+            (10.4, 20.0),
+            (20.3, 30.8),
+        ],
+    )
+    def test_floats(self, numbers, start, end):
+        result = numbers.floats(start, end)
+        assert max(result) <= end
+        assert min(result) >= start
+        assert len(result) == 10
         assert isinstance(result, list)
 
-        result = numbers.floats(n=3)
+        result = numbers.floats(n=1000)
         assert len(result) == 1000
-        assert isinstance(result, list)
+
+        result = numbers.floats(rounding=4)
+        for e in result:
+            assert len(str(e).split('.')[1]) <= 4
 
     @pytest.mark.parametrize(
         'start, end', [
@@ -43,6 +55,32 @@ def test_integers(self, numbers, start, end):
         element = numbers.random.choice(result)
         assert isinstance(element, int)
 
+    @pytest.mark.parametrize(
+        'start_real, end_real, start_imag, end_imag', [
+            (1.2, 10, 1, 2.4),
+            (10.4, 20.0, 2.3, 10),
+            (20.3, 30.8, 2.4, 4.5),
+        ],
+    )
+    def test_complexes(self, numbers,
+                         start_real, end_real, start_imag, end_imag):
+        result = numbers.complexes(start_real, end_real,
+                                     start_imag, end_imag)
+        assert max(e.real for e in result) <= end_real
+        assert min(e.real for e in result) >= start_real
+        assert max(e.imag for e in result) <= end_imag
+        assert min(e.imag for e in result) >= start_imag
+        assert len(result) == 10
+        assert isinstance(result, list)
+
+        result = numbers.complexes(n=1000)
+        assert len(result) == 1000
+
+        result = numbers.complexes(rounding_real=4, rounding_imag=6)
+        for e in result:
+            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
@@ -73,11 +111,6 @@ def test_between(self, numbers):
         assert result >= 90
         assert result <= 100
 
-    def test_rating(self, numbers):
-        result = numbers.rating(maximum=5.0)
-        assert isinstance(result, float)
-        assert (result >= 0) and (result <= 5.0)
-
 
 class TestSeededNumbers(object):
 
@@ -95,8 +128,12 @@ def test_floats(self, n1, n2):
 
     def test_integers(self, n1, n2):
         assert n1.integers() == n2.integers()
-        assert n1.integers(start=-999, end=999, length=10) == \
-            n2.integers(start=-999, end=999, length=10)
+        assert n1.integers(start=-999, end=999, n=10) == \
+            n2.integers(start=-999, end=999, n=10)
+
+    def test_complexes(self, n1, n2):
+        assert n1.complexes() == n2.complexes()
+        assert n1.complexes(n=5) == n2.complexes(n=5)
 
     def test_digit(self, n1, n2):
         assert n1.digit() == n2.digit()
@@ -107,10 +144,6 @@ def test_between(self, n1, n2):
         assert n1.between(minimum=42, maximum=2048) == \
             n2.between(minimum=42, maximum=2048)
 
-    def test_rating(self, n1, n2):
-        assert n1.rating() == n2.rating()
-        assert n1.rating(maximum=10.0) == n2.rating(maximum=10.0)
-
     @pytest.mark.skip(reason='Method refactoring needed.')
     def test_primes(self, n1, n2):
         assert n1.primes() == n2.primes()