#!/usr/bin/env python

"""###################################################################
# Author: Cooper Bell                                                #
# Class: Programming Languages COSC 4480                             #
# Due Date: 10/13/2014                                               #
# Summary: Create a monte-carlo simulation for the calculation of pi #
#          using a quadrant of a circle in the cartesian coordinate  #
#          plane.                                                    #
#                                                                    #
# Installation: in Debian-based systems:                             #
#               "sudo apt-get install matplotlib" should do.         #
#                                                                    #
#               see http://matplotlib.org/users/installation.html    #
#               for more info.                                       #
#                                                                    #
# Documentation: typing the following into the python interpreter    #
# will generate the documentation for the class.                     #
# >> import pi_monte_carlo as pi                                     #
# >> help(pi.PiCalc)                                                 #
###################################################################"""

import numpy as np
import matplotlib.pyplot as plt


class PiCalc(object):
    """
    This class will generate a graph based on an initial sample
    size given by the user. The graph will show a quarter-circle
    in the first quadrant; a horizontal and vertical line representing
    the cartesian product [0,1]x[0,1]; the random sample points representing
    random points chosen by the monte-carlo experiment and the labels showing
    the sample size and the approximation of pi.


    Instance Variables:
    size - sample size for calculation
    x_rands - random values for the x coordinates
    y_rands - random values for the y coordinates
    """


    def __init__(self):
        """
        Default constructor - initializes the sample size from user input
        and the randomly generated point arrays
        """

        # get the sample size from the user
        self.size = self.get_sample_size()

        # generate two lists of random variables for point coordinates
        self.x_rands = np.random.random(self.size)
        self.y_rands = np.random.random(self.size)


    @classmethod
    def init_matplotlib(cls):
        """
        Initializes matplotlib and plots the quarter-circle and horizontal
        and vertical lines representing the cartesian product [0,1]x[0,1]
        """

        # plot the quarter-circle
        fig = plt.gcf()
        circle = plt.Circle((0, 0), 1, color='k', fill=False, lw=2.0)
        fig.gca().add_artist(circle)

        # plot the lines
        plt.plot((0.0, 1.0), (1.0, 1.0), 'k-', lw=2.0)
        plt.plot((1.0, 1.0), (0.0, 1.0), 'k-', lw=2.0)

        # set the axis ranges
        plt.axis([0, 1.3, 0, 1.3])


    @classmethod
    def get_sample_size(cls):
        """
        Gets the random point sample size from the user.

        return - the inputted sample size.
        """

        try:
            return int(raw_input('Enter the sample size: '))
        except ValueError:
            print 'Input not an integer!'


    def pi_calc(self):
        """
        Calculates pi from the randomly generated arrays xrands and y_rands

        return - the approximation of pi.
        """

        in_circles = 0
        for x_coord in self.x_rands:
            for y_coord in self.y_rands:
                if x_coord**2 + y_coord**2 <= 1.0:
                    in_circles += 1

        return 4 * (float(in_circles)/self.size**2)


    def plot_pi(self):
        """
        Generates the entire plot, including the randomly generated points
        and the sample size and pi approximation text
        """

        # initialize matplotlib with circle and lines
        self.init_matplotlib()

        # plot the points
        plt.scatter(self.x_rands, self.y_rands)

        plt.text(.2, 1.2, 'N = ' + str(self.size))
        plt.text(.2, 1.1, u'\u03C0' + ' = ' + str(self.pi_calc()))

        # show the plot
        plt.show()


def main():
    """ Main Method """

    pi_calc = PiCalc()
    pi_calc.plot_pi()


if __name__ == '__main__':
    main()
