# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""This is a simple geometry module containing basic elements."""


# To allow roundoff error
TOLERANCE = 0.00000001


def about_eq(f1, f2):
    """Determine if two numbers are about equal within the TOLERANCE.

    @param f1: float number 1
    @param f2: float number 2
    """
    return abs(f1 - f2) < TOLERANCE


class Point:
    """A point class."""
    def __init__(self, x=None, y=None):
        """Initialize a point.

        @param x: x coordinate
        @param y: y coordinate
        """
        self.x = x if x is None else float(x)
        self.y = y if y is None else float(y)

    def __bool__(self):
        """A boolean indicating if this point is defined."""
        return self.x is not None and self.y is not None

    def __eq__(self, p):
        """Determine if this point is equal to the specified point, p.

        @param p: a point
        """
        return about_eq(self.x, p.x) and about_eq(self.y, p.y)

    def __hash__(self):
        """Redefine the hash function to meet the consistency requirement.

        In order to put an item into a set, it needs to be hashable.
        To make an object hashable, it must meet the consistency requirement:
            a == b must imply hash(a) == hash(b)
        """
        return hash((self.x, self.y))

    def __str__(self):
        """The string representation of the point value."""
        return 'Point: (%.4f, %.4f)' % (self.x, self.y)

    def distance(self, p):
        """Calculate the distance between p and this point.

        @param p: a point
        """
        dist_x = p.x - self.x
        dist_y = p.y - self.y
        return (dist_x ** 2 + dist_y ** 2 ) ** 0.5

    def value(self):
        """Return the point coordinates."""
        return (self.x, self.y)

    # __bool__ is used in Python 3.x and __nonzero__ in Python 2.x
    __nonzero__ = __bool__


class Circle:
    """A circle class."""
    def __init__(self, center, radius):
        """Initialize a circle.

        @param center: the center point of the circle
        @param radius: the radius of the circle
        """
        self.center = center
        self.radius = radius

    def __bool__(self):
        """A boolean indicating if this circle is defined."""
        return self.center is not None and self.radius is not None

    def __contains__(self, p):
        """Determine if p is enclosed in the circle.

        @param p: a point
        """
        return self.center.distance(p) <= self.radius + TOLERANCE

    def __eq__(self, c):
        """Determine if this circle is equal to the specified circle, c.

        @param c: a circle
        """
        return self.center == c.center and about_eq(self.radius, c.radius)

    def __hash__(self):
        """Redefine the hash function to meet the consistency requirement.

        In order to put an item into a sets.Set, it needs to be hashable.
        To make an object hashable, it must meet the consistency requirement:
            a == b must imply hash(a) == hash(b)
        """
        return hash((self.center, self.radius))

    def __str__(self):
        """The string representation of the circle value."""
        return ('Center: %s, %s' % (str(self.center),
                                    'Radius: %.4f' % self.radius)
                if self else 'Circle is None')

    # __bool__ is used in Python 3.x and __nonzero__ in Python 2.x
    __nonzero__ = __bool__