def test_edge_cases_visualization_best_fit_lines(self):
        """`visualization.Visualization.best_fit_lines`: Edge Case Validator.

        Tests the behavior of `Visualization.best_fit_lines` with edge cases.

        Raises:
            Exception: If at least one `Exception` raised is not of the expected
                kind.

        """
        x = _compose(list, _np.random.uniform)(0.0,
                                               100.0,
                                               size=self.data_shape[0])
        """list of float: Random x-values."""
        values = {
            name: _compose(list, _np.random.uniform)(0.0, 100.0,
                                                     size=self.data_shape[0]) \
                  for name in ["observations", "predictions"]
        }
        """:obj:`list of float`: Contains all y-values."""

        v = Visualization("Title", (1, 1))
        """Visualization: Plotter instance."""

        with self.assertRaises(TypeError):
            # Empty `x`-values.
            v._best_fit_lines([], values, 0)

        with self.assertRaises(ValueError):
            # Empty `y`-values.
            v._best_fit_lines(x, {}, 0)

        with self.assertRaises(IndexError):
            # Suplot index out of range.
            v._best_fit_lines(x, values, 1)

        v.close()
    def test_random_visualization_best_fit_lines(self):
        """`visualization.Visualization.best_fit_lines`: Randomized Validator.

        Tests the behavior of `Visualization.best_fit_lines` by feeding it
        randomly generated arguments.

        Raises:
            AssertionError: If `Visualization.best_fit_lines` needs debugging.

        """
        for i in range(self.n_tests):
            v = Visualization("Title", (3, 3))
            """Visualization: Plotter instance."""

            for i in range(9):
                x = _compose(list, _np.random.uniform)(0.0,
                                                       100.0,
                                                       size=self.data_shape[0])
                """list of float: Random x-values."""
                values = {
                    name: _compose(list,
                                   _np.random.uniform)(0.0, 100.0,
                                                       size=self.data_shape[0]) \
                          for name in ["observations", "predictions"]
                }
                """:obj:`list of float`: Contains all y-values."""

                lines = v._best_fit_lines(x, values, i)
                """list of matplotlib.lines.Line2D: Test input."""

                # Best-fit lines should be a list of Line2D instances.
                self.assertIsInstance(lines, list)
                map(_appendargs(self.assertIsInstance, _Line2D), lines)

                unique_x = _compose(list, _np.unique)(x)
                """list of float: X-values with duplicates removed."""

                for line in lines:
                    x_data = line.get_xdata()
                    """list of float: X-values in actual best-fit line."""
                    y_data = line.get_ydata()
                    """list of float: Y-values in actual best-fit line."""

                    # Number of x- and y-values in all lines should match number
                    # of unique values in `x`.
                    self.assertEqual(*map(_np.linalg.norm, [x_data, unique_x]))
                    self.assertEqual(*map(len, [y_data, unique_x]))

                    m = (y_data[1] - y_data[0]) / (x_data[1] - x_data[0])
                    """float: Best-fit line gradient."""
                    b = y_data[0] - m * x_data[0]
                    """float: Y-intercept."""

                    computed_y_vals = map(lambda val: m * val + b, x_data)
                    """list of float: Y-values computed analytically."""

                    # All lines should be rewritable in linear terms.
                    self.assertAlmostEqual(
                        *map(_np.linalg.norm, [computed_y_vals, y_data]))

            v.close()
    def test_invalid_args_visualization_best_fit_lines(self):
        """`visualization.Visualization.best_fit_lines`: Argument Validator.

        Tests the behavior of `Visualization.best_fit_lines` with invalid
        argument counts and values.

        Raises:
            Exception: If at least one `Exception` raised is not of the expected
                kind.

        """
        x = _compose(list, _np.random.uniform)(0.0,
                                               100.0,
                                               size=self.data_shape[0])
        """list of float: Random x-values."""
        values = {
            name: _compose(list, _np.random.uniform)(0.0, 100.0,
                                                     size=self.data_shape[0]) \
                  for name in ["observations", "predictions"]
        }
        """:obj:`list of float`: Contains all y-values."""

        v = Visualization("Title", (1, 1))
        """Visualization: Plotter instance."""

        with self.assertRaises(TypeError):
            # No arguments.
            v._best_fit_lines()

        with self.assertRaises(TypeError):
            # Only one argument.
            v._best_fit_lines(x)

        with self.assertRaises(TypeError):
            # Only two arguments.
            v._best_fit_lines(x, values)

        with self.assertRaises(TypeError):
            # Too many arguments.
            v._best_fit_lines(x, values, 0, "extra")

        with self.assertRaises(TypeError):
            # With keyword.
            v._best_fit_lines(x, values, 0, key="value")

        with self.assertRaises(TypeError):
            # `None` instead of list `x`.
            v._best_fit_lines(None, values, 0)

        with self.assertRaises(TypeError):
            # np.matrix instead of list `x`.
            v._best_fit_lines(_np.matrix(x), values, 0)

        with self.assertRaises(TypeError):
            # `None` instead of dict of lists `values`.
            v._best_fit_lines(x, None, 0)

        with self.assertRaises(TypeError):
            value_list = {k: _np.matrix(val) for k, val in values.iteritems()}
            """:obj:`np.matrix`: Lists in `values` mapped to matrices."""

            # Dict of np.matrix instead of dict of lists `values`.
            v._best_fit_lines(x, value_list, 0)

        with self.assertRaises(TypeError):
            # List instead of dict of lists `values`.
            v._best_fit_lines(x, x, 0)

        with self.assertRaises(TypeError):
            # `None` instead of int `subplot`.
            v._best_fit_lines(x, values, None)

        with self.assertRaises(TypeError):
            # Float instead of int `subplot`.
            v._best_fit_lines(x, values, 5.6)