factoids plugin: mark factoids as changed after factfile import
[rbot] / lib / rbot / plugins / math.rb
1 class MathPlugin < Plugin
2   @@digits = {
3      "first" => "1",
4      "second" => "2",
5      "third" => "3",
6      "fourth" => "4",
7      "fifth" => "5",
8      "sixth" => "6",
9      "seventh" => "7",
10      "eighth" => "8",
11      "ninth" => "9",
12      "tenth" => "10",
13      "one" => "1",
14      "two" => "2",
15      "three" => "3",
16      "four" => "4",
17      "five" => "5",
18      "six" => "6",
19      "seven" => "7",
20      "eight" => "8",
21      "nine" => "9",
22      "ten" => "10"
23   };
24
25   def help(plugin, topic="")
26     "math <expression>, evaluate mathematical expression"
27   end
28   def privmsg(m)
29     unless(m.params)
30       m.reply "incorrect usage: " + help(m.plugin)
31       return
32     end
33
34     expr = m.params.dup
35     @@digits.each {|k,v|
36       expr.gsub!(/\b#{k}\b/, v)
37     }
38
39     while expr =~ /(exp ([\w\d]+))/
40       exp = $1
41       val = Math.exp($2).to_s
42       expr.gsub!(/#{Regexp.escape exp}/, "+#{val}")
43     end
44     
45     while expr =~ /^\s*(dec2hex\s*(\d+))\s*\?*/
46       exp = $1
47       val = sprintf("%x", $2)
48       expr.gsub!(/#{Regexp.escape exp}/, "+#{val}")
49     end
50
51     expr.gsub(/\be\b/, Math.exp(1).to_s)
52
53     while expr =~ /(log\s*((\d+\.?\d*)|\d*\.?\d+))\s*/
54       exp = $1
55       res = $2
56       
57       if res == 0
58         val = "Infinity"
59       else
60         val = Math.log(res).to_s
61       end
62       
63       expr.gsub!(/#{Regexp.escape exp}/, "+#{val}")
64     end
65
66     while expr =~ /(bin2dec ([01]+))/
67       exp = $1
68       val = join('', unpack('B*', pack('N', $2)))
69       val.gsub!(/^0+/, "")
70       expr.gsub!(/#{Regexp.escape exp}/, "+#{val}")
71     end
72
73     expr.gsub!(/ to the power of /, " ** ")
74     expr.gsub!(/ to the /, " ** ")
75     expr.gsub!(/\btimes\b/, "*")
76     expr.gsub!(/\bdiv(ided by)? /, "/ ")
77     expr.gsub!(/\bover /, "/ ")
78     expr.gsub!(/\bsquared/, "**2 ")
79     expr.gsub!(/\bcubed/, "**3 ")
80     expr.gsub!(/\bto\s+(\d+)(r?st|nd|rd|th)?( power)?/, "**\1 ")
81     expr.gsub!(/\bpercent of/, "*0.01*")
82     expr.gsub!(/\bpercent/, "*0.01")
83     expr.gsub!(/\% of\b/, "*0.01*")
84     expr.gsub!(/\%/, "*0.01")
85     expr.gsub!(/\bsquare root of (\d+)/, "\1 ** 0.5 ")
86     expr.gsub!(/\bcubed? root of (\d+)/, "\1 **(1.0/3.0) ")
87     expr.gsub!(/ of /, " * ")
88     expr.gsub!(/(bit(-| )?)?xor(\'?e?d( with))?/, "^")
89     expr.gsub!(/(bit(-| )?)?or(\'?e?d( with))?/, "|")
90     expr.gsub!(/bit(-| )?and(\'?e?d( with))?/, "& ")
91     expr.gsub!(/(plus|and)/, "+")
92
93     if (expr =~ /^\s*[-\d*+\s()\/^\.\|\&\*\!]+\s*$/ &&
94        expr !~ /^\s*\(?\d+\.?\d*\)?\s*$/ &&
95        expr !~ /^\s*$/ &&
96        expr !~ /^\s*[( )]+\s*$/)
97
98        begin
99          debug "evaluating expression \"#{expr}\""
100          answer = eval(expr)
101          if answer =~ /^[-+\de\.]+$/
102            answer = sprintf("%1.12f", answer)
103            answer.gsub!(/\.?0+$/, "")
104            answer.gsub!(/(\.\d+)000\d+/, '\1')
105            if (answer.length > 30)
106              answer = "a number with >30 digits..."
107            end
108          end
109          m.reply answer
110        rescue Exception => e
111          puts "couldn't evaluate expression \"#{m.params}\": #{e}"
112          m.reply "illegal expression \"#{m.params}\""
113          return
114        end
115     else
116       m.reply "illegal expression \"#{m.params}\""
117       return
118     end
119   end
120 end
121 plugin = MathPlugin.new
122 plugin.register("math")