""" Pi - calculate pi over time via the monte carlo method. idea borrowed from #linode Copyright (C) 2010 Brian S. Stephan This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ import math import random import re import MySQLdb as mdb from Module import Module class Pi(Module): """Use the Monte Carlo method to approximate pi. Each time this method is called, it calculates a random point inside a square on the xy plane. If that point also falls within a circle bound within that square, it is added to the count of points inside. Over time, 4 * count_inside / count approaches pi. Idea from #linode on OFTC. Code from http://www.eveandersson.com/pi/monte-carlo-circle """ def db_init(self): """Initialize database tables.""" # init the database if pi table doesn't exist version = self.db_module_registered(self.__class__.__name__) if version == None: # create tables db = self.get_db() try: version = 1 cur = db.cursor(mdb.cursors.DictCursor) cur.execute(''' CREATE TABLE pi_log ( id SERIAL, count_inside INTEGER NOT NULL, count_total INTEGER NOT NULL, time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin ''') cur.execute(''' CREATE VIEW pi_latest_pi AS SELECT count_inside, count_total FROM pi_log ORDER BY id DESC ''') db.commit() self.db_register_module_version(self.__class__.__name__, version) except mdb.Error as e: db.rollback() self.log.error("database error trying to create tables") self.log.exception(e) raise finally: cur.close() def do(self, connection, event, nick, userhost, what, admin_unlocked): match = re.search('^!pi$', what) if match: db = self.get_db() try: cur = db.cursor(mdb.cursors.DictCursor) cur.execute('SELECT * FROM pi_latest_pi') datum = cur.fetchone() if datum == None: count_inside = 0 count = 0 else: # load values count_inside = datum['count_inside'] count = datum['count_total'] x = random.random() y = random.random() inside = False d = math.hypot(x,y) if d < 1: inside = True count_inside += 1 count += 1 pi = 4.0 * count_inside / count cur.execute('INSERT INTO pi_log (count_inside, count_total) VALUES (%s,%s)', (count_inside, count)) db.commit() except mdb.Error as e: db.rollback() self.log.error("database error doing pi stuff") self.log.exception(e) return self.irc.reply(event, "database error while estimating pi: {0:s}".format(str(e))) finally: cur.close() return self.irc.reply(event, "({0:.10f}, {1:.10f}) is {2}within the unit circle. "\ "pi is {5:.10f}. (i:{3:d} p:{4:d})".format(x, y, "" if inside else "not ", count_inside, count, pi)) # vi:tabstop=4:expandtab:autoindent # kate: indent-mode python;indent-width 4;replace-tabs on;