]> git.somenet.org - irc/bugbot.git/blob - BotModules/Converter.bm
some old base
[irc/bugbot.git] / BotModules / Converter.bm
1 # -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*-
2 ################################
3 # Converter Module             #
4 ################################
5 # Originally by GluffiS <gluffis@mean.net>
6
7 package BotModules::Converter;
8 use vars qw(@ISA);
9 @ISA = qw(BotModules);
10 1;
11
12 # XXX support the suffixes "to <n> sf" or "to <n> dp"
13 # XXX support speed, volume, twips
14 # XXX support light year, parsec, furlong; fm, pm, µm, Mm, Gm, Tm, Pm
15 # XXX support 1x10^1 notation as well as the already-supported 1e1 notation
16
17 sub Help {
18     my $self = shift;
19     my ($event) = @_;
20     return {
21         '' => 'A generic converter. Currently supports converting between positive integers in binary, octal, decimal and hexidecimal forms, and converting temperatures, lengths, times and masses.',
22         'syntax' => 'To convert a number, simply give the number with units or appropriate prefixes, for example to convert from hexadecimal: \'0x2F\'',
23         'integers' => 'Decimal: Simply give the number.  Hexadecimal: Prefix with 0x.  Octal: Prefix with 0.  Binary: Prefix with 0b.',
24         'temperature' => 'Kelvin: Suffix with K.  Celsius: Suffix with C.  Fahrenheit: Suffix with F.',
25         'length' => 'Imperial: in, ft, yd, mi.  Metric: A, nm, mm, cm, m, km.', # XXX should also support light year, parsec, furlong; fm, pm, µm, Mm, Gm, Tm, Pm
26         'time' => 'ISO time units: year, month, week, day, hour, minute, second.  Exotic time units: millifortnight.',
27         'mass' => 'Imperial: lbs, oz, stone.  Metric: kg, g.',
28         # XXX should support speed, volume, twips
29   };
30 }
31
32
33 sub Told {
34     my $self = shift;
35     my ($event, $message) = @_;
36
37     # integers
38     if ($message =~ m/^\s*([1-9][0-9]*|0)\s*\??\s*$/osi) {
39         $self->convertDecimal($event, $1);
40     } elsif ($message =~ m/^\s*0x([a-f0-9]+)\s*\??\s*$/osi) {
41         $self->convertHex($event, $1);
42     } elsif ($message =~ m/^\s*0([0-9]+)\s*\??\s*$/osi) {
43         $self->convertOctal($event, $1);
44     } elsif ($message =~ m/^\s*0b([0-9]+)\s*\??\s*$/osi) {
45         $self->convertBinary($event, $1);
46
47     # temperatures
48     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:kelvin|K)\s*\??\s*$/osi) {
49         $self->convertKelvin($event, $1);
50     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:deg(?:rees?)|[\`°])?\s*(?:cel[sc]ius|centigrade|c)\s*\??\s*$/osi) {
51         $self->convertCelsius($event, $1);
52     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:deg(?:rees?)|[\`°])?\s*(?:fahrenheit|f)\s*\??\s*$/osi) {
53         $self->convertFahrenheit($event, $1);
54
55     # imperial lengths
56     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:ins?|inch(?:es)?)\s*\??\s*$/osi) {
57         $self->convertInches($event, $1);
58     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:ft|feet|foot)\s*\??\s*$/osi) {
59         $self->convertFeet($event, $1);
60     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:yds?|yards?)\s*\??\s*$/osi) {
61         $self->convertYards($event, $1);
62     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:mi|miles?)\s*\??\s*$/osi) {
63         $self->convertMiles($event, $1);
64
65     # metric lengths
66     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:Å|a|angstroms?)\s*\??\s*$/osi) {
67         $self->convertAngstroms($event, $1);
68     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:nms?|nanometers?|nanometres?)\s*\??\s*$/osi) {
69         $self->convertNanometers($event, $1);
70     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:mms?|millimeters?|millimetres?)\s*\??\s*$/osi) {
71         $self->convertMillimeters($event, $1);
72     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:cms?|centimeters?|centimetres?)\s*\??\s*$/osi) {
73         $self->convertCentimeters($event, $1);
74     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:m|meters?|metres?)\s*\??\s*$/osi) {
75         $self->convertMeters($event, $1);
76     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:kms?|kilometers?|kilometres?|klic?ks?)\s*\??\s*$/osi) {
77         $self->convertKilometers($event, $1);
78         
79     # times
80     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:years|year|yr)\s*\??\s*$/osi) {
81         $self->convertYears($event, $1);
82     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:months|month|mo)\s*\??\s*$/osi) {
83         $self->convertMonths($event, $1);
84     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:weeks|week|wk)\s*\??\s*$/osi) {
85         $self->convertWeeks($event, $1);
86     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:fortnights|fortnight|mf)\s*\??\s*$/osi) {
87         $self->convertMillifortnights($event, $1);
88     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:days|day|d)\s*\??\s*$/osi) {
89         $self->convertDays($event, $1);
90     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:hours|hour|hr|h)\s*\??\s*$/osi) {  
91         $self->convertHours($event, $1);
92     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:minutes|minute|min)\s*\??\s*$/osi) {
93        $self->convertMinutes($event, $1);
94     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:seconds|second|sec|s)\s*\??\s*$/osi) {
95        $self->convertSeconds($event, $1);
96
97     # masses
98     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:grams|gram|g)\s*\??\s*$/osi) {
99         $self->convertGrams($event, $1);
100     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:kilograms|kilogram|kilos|kilo|kg)\s*\??\s*$/osi) {
101         $self->convertKilograms($event, $1);
102     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:pounds|pound|lbs)\s*\??\s*$/osi) {
103         $self->convertPounds($event, $1);
104     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:ounces|ounce|oz)\s*\??\s*$/osi) {
105         $self->convertOunces($event, $1);
106     } elsif ($message =~ m/^\s*(-?(?:[0-9]+\.?|[0-9]+\.[0-9]+|\.[0-9]+)(?:e-?[0-9]+)?)\s*(?:stones|stone)\s*\??\s*$/osi) {
107         $self->convertStones($event, $1);
108     
109     # oh well
110     } else {
111         return $self->SUPER::Told(@_);
112     }
113     return 0;
114 }
115
116
117 # Integers
118
119 sub convertDecimal {
120     my $self = shift;
121     my($event, $decimal) = @_;
122     my $hex = sprintf('%X', $decimal);
123     my $octal = sprintf('%o', $decimal);
124     my $binary = sprintf('%b', $decimal);
125     $self->say($event, "$event->{'from'}: $decimal = 0x$hex, 0$octal, 0b$binary");
126 }
127
128 sub convertHex {
129     my $self = shift;
130     my($event, $hex) = @_;
131     my $decimal = hex($hex);
132     my $hex = sprintf('%X', $decimal); # normalise
133     my $octal = sprintf('%o', $decimal);
134     my $binary = sprintf('%b', $decimal);
135     $self->say($event, "$event->{'from'}: 0x$hex = $decimal, 0$octal, 0b$binary");
136 }
137
138 sub convertOctal {
139     my $self = shift;
140     my($event, $octal) = @_;
141     my $decimal = oct("0$octal");
142     my $hex = sprintf('%X', $decimal);
143     my $binary = sprintf('%b', $decimal);
144     $self->say($event, "$event->{'from'}: 0$octal = $decimal, 0x$hex, 0b$binary");
145 }
146
147 sub convertBinary {
148     my $self = shift;
149     my($event, $binary) = @_;
150     my $decimal = oct("0b$binary");
151     my $hex = sprintf('%X', $decimal);
152     my $octal = sprintf('%o', $decimal);
153     $self->say($event, "$event->{'from'}: 0b$binary = $decimal, 0x$hex, 0$octal");
154 }
155
156
157 # Temperature
158
159 sub convertKelvin {
160     my $self = shift;
161     my($event, $kelvin) = @_;
162     my $celsius = round(1, $kelvin - 273.14);
163     my $fahrenheit = round(1, ($kelvin - 273.14) * 9 / 5 + 32);
164     my $kelvin = round(1, $kelvin); # normalise
165     my $prognosis = diagnoseTemperature($kelvin, $celsius, $fahrenheit);
166     $self->say($event, "$event->{'from'}: ${kelvin}K = $celsius°C, $fahrenheit°F, $prognosis");
167 }
168
169 sub convertCelsius {
170     my $self = shift;
171     my($event, $celsius) = @_;
172     my $kelvin = round(1, $celsius + 273.14);
173     my $fahrenheit = round(1, $celsius * 9 / 5 + 32);
174     my $celsius = round(1, $celsius); # normalise
175     my $prognosis = diagnoseTemperature($kelvin, $celsius, $fahrenheit);
176     $self->say($event, "$event->{'from'}: $celsius°C = ${kelvin}K, $fahrenheit°F, $prognosis");
177 }
178
179 sub convertFahrenheit {
180     my $self = shift;
181     my($event, $fahrenheit) = @_;
182     my $celsius = round(1, ($fahrenheit - 32) * 5 / 9);
183     my $kelvin = round(1, ($fahrenheit - 32) * 5 / 9 + 273.14);
184     my $fahrenheit = round(1, $fahrenheit); # normalise
185     my $prognosis = diagnoseTemperature($kelvin, $celsius, $fahrenheit);
186     $self->say($event, "$event->{'from'}: $fahrenheit°F = ${kelvin}K, $celsius°C, $prognosis");
187 }
188
189 sub diagnoseTemperature($$$) {
190     my($kelvin, $celsius, $fahrenheit) = @_;
191     return
192       $kelvin < 0 ? 'an impossible temperature' :
193         $kelvin == 0 ? 'absolute zero' :
194           $fahrenheit < 0 ? 'extremely cold' :
195             $celsius < 0 ? 'freezing cold' :
196               $celsius == 0 ? 'freezing point of water' :
197                 $celsius < 18 ? 'cold' :
198                   $celsius == 20 ? 'standard room temperature' :
199                     $celsius < 25 ? 'warm' :
200                       $celsius < 35 ? 'hot' :
201                         $celsius <= 37 ? 'body temperature' :
202                           $celsius < 65 ? 'very hot' :
203                             $celsius < 95 ? 'scorching hot' :
204                               $celsius == 100 ? 'boiling point of water' :
205                                 $celsius < 105 ? 'boiling hot' :
206                                   'ridiculously hot';
207 }
208
209
210 # Imperial Lengths
211
212 sub convertInches {
213     my $self = shift;
214     my($event, $inches) = @_;
215     # imperial
216     # (inches)
217     my $feet = sigfig(3, $inches / 12.0);
218     my $yards = sigfig(3, $inches / 36.0);
219     my $miles = sigfig(3, $inches / 63360.0);
220     # metric
221     my $kilometers = sigfig(3, $inches * 0.0000254);
222     my $meters = sigfig(3, $inches * 0.0254);
223     my $centimeters = sigfig(3, $inches * 2.54);
224     my $millimeters = sigfig(3, $inches * 25.4);
225     my $nanometers = sigfig(3, $inches * 25400000.0);
226     my $angstroms = sigfig(3, $inches * 254000000.0);
227     # normalise
228     my $inches = sigfig(3, $inches);
229     $self->say($event, "$event->{'from'}: ${inches}in = ${feet}ft, ${yards}yd, ${miles}mi; ${kilometers}Km, ${meters}m, ${centimeters}cm, ${millimeters}mm, ${nanometers}nm, ${angstroms}Å (to 3sf)");
230 }
231
232 sub convertFeet {
233     my $self = shift;
234     my($event, $feet) = @_;
235     my $inches = $feet * 12.0;
236     # imperial
237     # (inches)
238     my $feet = sigfig(3, $inches / 12.0);
239     my $yards = sigfig(3, $inches / 36.0);
240     my $miles = sigfig(3, $inches / 63360.0);
241     # metric
242     my $kilometers = sigfig(3, $inches * 0.0000254);
243     my $meters = sigfig(3, $inches * 0.0254);
244     my $centimeters = sigfig(3, $inches * 2.54);
245     my $millimeters = sigfig(3, $inches * 25.4);
246     my $nanometers = sigfig(3, $inches * 25400000.0);
247     my $angstroms = sigfig(3, $inches * 254000000.0);
248     # normalise
249     my $inches = sigfig(3, $inches);
250     $self->say($event, "$event->{'from'}: ${feet}ft = ${inches}in, ${yards}yd, ${miles}mi, ${kilometers}Km, ${meters}m, ${centimeters}cm, ${millimeters}mm, ${nanometers}nm, ${angstroms}Å (to 3sf)");
251 }
252
253 sub convertYards {
254     my $self = shift;
255     my($event, $yards) = @_;
256     my $inches = $yards * 36.0;
257     # imperial
258     # (inches)
259     my $feet = sigfig(3, $inches / 12.0);
260     my $yards = sigfig(3, $inches / 36.0);
261     my $miles = sigfig(3, $inches / 63360.0);
262     # metric
263     my $kilometers = sigfig(3, $inches * 0.0000254);
264     my $meters = sigfig(3, $inches * 0.0254);
265     my $centimeters = sigfig(3, $inches * 2.54);
266     my $millimeters = sigfig(3, $inches * 25.4);
267     my $nanometers = sigfig(3, $inches * 25400000.0);
268     my $angstroms = sigfig(3, $inches * 254000000.0);
269     # normalise
270     my $inches = sigfig(3, $inches);
271     $self->say($event, "$event->{'from'}: ${yards}yd = ${inches}in, ${feet}ft, ${miles}mi, ${kilometers}Km, ${meters}m, ${centimeters}cm, ${millimeters}mm, ${nanometers}nm, ${angstroms}Å (to 3sf)");
272 }
273
274 sub convertMiles {
275     my $self = shift;
276     my($event, $miles) = @_;
277     my $inches = $miles * 190080.0;
278     # imperial
279     # (inches)
280     my $feet = sigfig(3, $inches / 12.0);
281     my $yards = sigfig(3, $inches / 36.0);
282     my $miles = sigfig(3, $inches / 63360.0);
283     # metric
284     my $kilometers = sigfig(3, $inches * 0.0000254);
285     my $meters = sigfig(3, $inches * 0.0254);
286     my $centimeters = sigfig(3, $inches * 2.54);
287     my $millimeters = sigfig(3, $inches * 25.4);
288     my $nanometers = sigfig(3, $inches * 25400000.0);
289     my $angstroms = sigfig(3, $inches * 254000000.0);
290     # normalise
291     my $inches = sigfig(3, $inches);
292     $self->say($event, "$event->{'from'}: ${miles}mi = ${inches}in, ${feet}ft, ${yards}yd, ${kilometers}Km, ${meters}m, ${centimeters}cm, ${millimeters}mm, ${nanometers}nm, ${angstroms}Å (to 3sf)");
293 }
294
295
296 # Metric Lengths
297
298 sub convertAngstroms {
299     my $self = shift;
300     my($event, $input) = @_;
301     # get the number
302     my $accurateMeters = $input / 10000000000.0;
303     $self->debug("Accurate KiloMeters: ".$accurateMeters/1000);
304     # imperial
305     my $inches = sigfig(3, $accurateMeters / (0.0254 * 1.0));
306     my $feet = sigfig(3, $accurateMeters / (0.0254 * 12.0));
307     my $yards = sigfig(3, $accurateMeters / (0.0254 * 36.0));
308     my $miles = sigfig(3, $accurateMeters / (0.0254 * 63360.0));
309     # metric
310     my $kilometers = sigfig(3, $accurateMeters / 1000.0);
311     my $meters = sigfig(3, $accurateMeters);
312     my $centimeters = sigfig(3, $accurateMeters * 100.0);
313     my $millimeters = sigfig(3, $accurateMeters * 1000.0);
314     my $nanometers = sigfig(3, $accurateMeters * 1000000000.0);
315     my $angstroms = sigfig(3, $accurateMeters * 10000000000.0);
316     $self->say($event, "$event->{'from'}: ${angstroms}Å = ${inches}in, ${feet}ft, ${yards}yd, ${miles}mi; ${kilometers}Km, ${meters}m, ${centimeters}cm, ${millimeters}mm, ${nanometers}nm (to 3sf)");
317 }
318
319 sub convertNanometers {
320     my $self = shift;
321     my($event, $input) = @_;
322     # get the number
323     my $accurateMeters = $input / 1000000000.0;
324     # imperial
325     my $inches = sigfig(3, $accurateMeters / (0.0254 * 1.0));
326     my $feet = sigfig(3, $accurateMeters / (0.0254 * 12.0));
327     my $yards = sigfig(3, $accurateMeters / (0.0254 * 36.0));
328     my $miles = sigfig(3, $accurateMeters / (0.0254 * 63360.0));
329     # metric
330     my $kilometers = sigfig(3, $accurateMeters / 1000.0);
331     my $meters = sigfig(3, $accurateMeters);
332     my $centimeters = sigfig(3, $accurateMeters * 100.0);
333     my $millimeters = sigfig(3, $accurateMeters * 1000.0);
334     my $nanometers = sigfig(3, $accurateMeters * 1000000000.0);
335     my $angstroms = sigfig(3, $accurateMeters * 10000000000.0);
336     $self->say($event, "$event->{'from'}: ${nanometers}nm = ${inches}in, ${feet}ft, ${yards}yd, ${miles}mi; ${kilometers}Km, ${meters}m, ${centimeters}cm, ${millimeters}mm, ${angstroms}Å (to 3sf)");
337 }
338
339 sub convertMillimeters {
340     my $self = shift;
341     my($event, $input) = @_;
342     # get the number
343     my $accurateMeters = $input / 1000.0;
344     # imperial
345     my $inches = sigfig(3, $accurateMeters / (0.0254 * 1.0));
346     my $feet = sigfig(3, $accurateMeters / (0.0254 * 12.0));
347     my $yards = sigfig(3, $accurateMeters / (0.0254 * 36.0));
348     my $miles = sigfig(3, $accurateMeters / (0.0254 * 63360.0));
349     # metric
350     my $kilometers = sigfig(3, $accurateMeters / 1000.0);
351     my $meters = sigfig(3, $accurateMeters);
352     my $centimeters = sigfig(3, $accurateMeters * 100.0);
353     my $millimeters = sigfig(3, $accurateMeters * 1000.0);
354     my $nanometers = sigfig(3, $accurateMeters * 1000000000.0);
355     my $angstroms = sigfig(3, $accurateMeters * 10000000000.0);
356     $self->say($event, "$event->{'from'}: ${millimeters}mm = ${inches}in, ${feet}ft, ${yards}yd, ${miles}mi; ${kilometers}Km, ${meters}m, ${centimeters}cm, ${nanometers}nm, ${angstroms}Å (to 3sf)");
357 }
358
359 sub convertCentimeters {
360     my $self = shift;
361     my($event, $input) = @_;
362     # get the number
363     my $accurateMeters = $input / 100.0;
364     # imperial
365     my $inches = sigfig(3, $accurateMeters / (0.0254 * 1.0));
366     my $feet = sigfig(3, $accurateMeters / (0.0254 * 12.0));
367     my $yards = sigfig(3, $accurateMeters / (0.0254 * 36.0));
368     my $miles = sigfig(3, $accurateMeters / (0.0254 * 63360.0));
369     # metric
370     my $kilometers = sigfig(3, $accurateMeters / 1000.0);
371     my $meters = sigfig(3, $accurateMeters);
372     my $centimeters = sigfig(3, $accurateMeters * 100.0);
373     my $millimeters = sigfig(3, $accurateMeters * 1000.0);
374     my $nanometers = sigfig(3, $accurateMeters * 1000000000.0);
375     my $angstroms = sigfig(3, $accurateMeters * 10000000000.0);
376     $self->say($event, "$event->{'from'}: ${centimeters}cm = ${inches}in, ${feet}ft, ${yards}yd, ${miles}mi; ${kilometers}Km, ${meters}m, ${millimeters}mm, ${nanometers}nm, ${angstroms}Å (to 3sf)");
377 }
378
379 sub convertMeters {
380     my $self = shift;
381     my($event, $input) = @_;
382     # get the number
383     my $accurateMeters = $input * 1.0;
384     # imperial
385     my $inches = sigfig(3, $accurateMeters / (0.0254 * 1.0));
386     my $feet = sigfig(3, $accurateMeters / (0.0254 * 12.0));
387     my $yards = sigfig(3, $accurateMeters / (0.0254 * 36.0));
388     my $miles = sigfig(3, $accurateMeters / (0.0254 * 63360.0));
389     # metric
390     my $kilometers = sigfig(3, $accurateMeters / 1000.0);
391     my $meters = sigfig(3, $accurateMeters);
392     my $centimeters = sigfig(3, $accurateMeters * 100.0);
393     my $millimeters = sigfig(3, $accurateMeters * 1000.0);
394     my $nanometers = sigfig(3, $accurateMeters * 1000000000.0);
395     my $angstroms = sigfig(3, $accurateMeters * 10000000000.0);
396     $self->say($event, "$event->{'from'}: ${meters}m = ${inches}in, ${feet}ft, ${yards}yd, ${miles}mi; ${kilometers}Km, ${centimeters}cm, ${millimeters}mm, ${nanometers}nm, ${angstroms}Å (to 3sf)");
397 }
398
399 sub convertKilometers {
400     my $self = shift;
401     my($event, $input) = @_;
402     # get the number
403     my $accurateMeters = $input * 1000.0;
404     # imperial
405     my $inches = sigfig(3, $accurateMeters / (0.0254 * 1.0));
406     my $feet = sigfig(3, $accurateMeters / (0.0254 * 12.0));
407     my $yards = sigfig(3, $accurateMeters / (0.0254 * 36.0));
408     my $miles = sigfig(3, $accurateMeters / (0.0254 * 63360.0));
409     # metric
410     my $kilometers = sigfig(3, $accurateMeters / 1000.0);
411     my $meters = sigfig(3, $accurateMeters);
412     my $centimeters = sigfig(3, $accurateMeters * 100.0);
413     my $millimeters = sigfig(3, $accurateMeters * 1000.0);
414     my $nanometers = sigfig(3, $accurateMeters * 1000000000.0);
415     my $angstroms = sigfig(3, $accurateMeters * 10000000000.0);
416     $self->say($event, "$event->{'from'}: ${kilometers}km = ${inches}in, ${feet}ft, ${yards}yd, ${miles}mi; ${meters}m, ${centimeters}cm, ${millimeters}mm, ${nanometers}nm, ${angstroms}Å (to 3sf)");
417 }
418
419
420 # Time
421
422 sub convertYears {
423     my $self = shift;
424     my($event, $input) = @_;
425     my $accurateSeconds = $input * 60.0 * 60.0 * 24.0 * 365.25;
426     my $years = sigfig(3, $input);
427     my $months = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * (365.25 / 12)));
428     my $weeks = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * (365.25 / (365.25 / 7.0))));
429     my $millifortnights = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * (365.25 / (365.25 / 7.0)) * 2.0 / 1000.0));
430     my $days = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0));
431     my $hours = sigfig(3, $accurateSeconds / (60.0 * 60.0));
432     my $minutes = sigfig(3, $accurateSeconds / 60.0);
433     my $seconds = sigfig(3, $accurateSeconds);
434     $self->say($event, "$event->{'from'}: ${years}yr = ${months}mo, ${weeks}wk, ${days}d, ${hours}hr, ${minutes}min, ${seconds}s, ${millifortnights}mf");
435 }
436      
437 sub convertMonths {
438     my $self = shift;
439     my($event, $input) = @_;
440     my $accurateSeconds = $input * 60.0 * 60.0 * 24.0 * (365.25 / 12);
441     my $years = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * 365.25));
442     my $months = sigfig(3, $input);
443     my $weeks = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * (365.25 / (365.25 / 7.0))));
444     my $millifortnights = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * (365.25 / (365.25 / 7.0)) * 2.0 / 1000.0));
445     my $days = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0));
446     my $hours = sigfig(3, $accurateSeconds / (60.0 * 60.0));
447     my $minutes = sigfig(3, $accurateSeconds / 60.0);
448     my $seconds = sigfig(3, $accurateSeconds);
449     $self->say($event, "$event->{'from'}: ${months}mo = ${years}yr, ${weeks}wk, ${days}d, ${hours}hr, ${minutes}min, ${seconds}s, ${millifortnights}mf");
450 }
451
452 sub convertWeeks {
453     my $self = shift;
454     my($event, $input) = @_;
455     my $accurateSeconds = $input * 60.0 * 60.0 * 24.0 * 7.0; 
456     my $years = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * 365.25));
457     my $months = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * (365.25 / 12)));
458     my $weeks = sigfig(3, $input);
459     my $millifortnights = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * (365.25 / (365.25 / 7.0)) * 2.0 / 1000.0));
460     my $days = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0));
461     my $hours = sigfig(3, $accurateSeconds / (60.0 * 60.0));
462     my $minutes = sigfig(3, $accurateSeconds / 60.0);
463     my $seconds = sigfig(3, $accurateSeconds);
464     $self->say($event, "$event->{'from'}: ${weeks}wk = ${years}yr, ${months}mo, ${days}d, ${hours}hr, ${minutes}min, ${seconds}s, ${millifortnights}mf");
465 }
466
467 sub convertMillifortnights {
468     my $self = shift;
469     my($event, $input) = @_;
470     my $accurateSeconds = $input * 60.0 * 60.0 * 24.0 * 7.0 * 2.0 / 1000.0;
471     my $years = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * 365.25));
472     my $months = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * (365.25 / 12)));
473     my $weeks = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * (365.25 / (365.25 / 7.0))));
474     my $millifortnights = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * (365.25 / (365.25 / 7.0)) * 2.0 / 1000.0));
475     my $days = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0));
476     my $hours = sigfig(3, $accurateSeconds / (60.0 * 60.0));
477     my $minutes = sigfig(3, $accurateSeconds / 60.0);
478     my $seconds = sigfig(3, $accurateSeconds);
479     $self->say($event, "$event->{'from'}: ${millifortnights}mf = ${years}yr, ${months}mo, ${weeks}wk, ${days}d, ${hours}hr, ${minutes}min, ${seconds}s");
480 }
481
482 sub convertDays {
483     my $self = shift;
484     my($event, $input) = @_;
485     my $accurateSeconds = $input * 60.0 * 60.0 * 24.0;
486     my $years = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * 365.25));
487     my $months = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * (365.25 / 12)));
488     my $weeks = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * (365.25 / (365.25 / 7.0))));
489     my $millifortnights = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * (365.25 / (365.25 / 7.0)) * 2.0 / 1000.0));
490     my $days = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0));
491     my $hours = sigfig(3, $accurateSeconds / (60.0 * 60.0));
492     my $minutes = sigfig(3, $accurateSeconds / 60.0);
493     my $seconds = sigfig(3, $accurateSeconds);
494     $self->say($event, "$event->{'from'}: ${days}d = ${years}yr, ${months}mo, ${weeks}wk, ${hours}hr, ${minutes}min, ${seconds}s, ${millifortnights}mf");
495 }
496
497 sub convertHours {
498     my $self = shift;
499     my($event, $input) = @_;
500     my $accurateSeconds = $input * 60.0 * 60.0;
501     my $years = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * 365.25));
502     my $months = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * (365.25 / 12)));
503     my $weeks = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * (365.25 / (365.25 / 7.0))));
504     my $millifortnights = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * (365.25 / (365.25 / 7.0)) * 2.0 / 1000.0));
505     my $days = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0));
506     my $hours = sigfig(3, $accurateSeconds / (60.0 * 60.0));
507     my $minutes = sigfig(3, $accurateSeconds / 60.0);
508     my $seconds = sigfig(3, $accurateSeconds);
509     $self->say($event, "$event->{'from'}: ${hours}hr = ${years}yr, ${months}mo, ${weeks}wk, ${days}d, ${minutes}min, ${seconds}s, ${millifortnights}mf");
510 }
511
512 sub convertMinutes {
513     my $self = shift;
514     my($event, $input) = @_;
515     my $accurateSeconds = $input * 60.0;
516     my $years = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * 365.25));
517     my $months = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * (365.25 / 12)));
518     my $weeks = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * (365.25 / (365.25 / 7.0))));
519     my $millifortnights = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * (365.25 / (365.25 / 7.0)) * 2.0 / 1000.0));
520     my $days = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0));
521     my $hours = sigfig(3, $accurateSeconds / (60.0 * 60.0));
522     my $minutes = sigfig(3, $accurateSeconds / 60.0);
523     my $seconds = sigfig(3, $accurateSeconds);
524     $self->say($event, "$event->{'from'}: ${minutes}min = ${years}yr, ${months}mo, ${weeks}wk, ${days}d, ${hours}hr, ${seconds}s, ${millifortnights}mf");
525 }
526
527 sub convertSeconds {
528     my $self = shift;
529     my($event, $input) = @_;
530     my $accurateSeconds = $input;
531     my $years = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * 365.25));
532     my $months = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * (365.25 / 12)));
533     my $weeks = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * (365.25 / (365.25 / 7.0))));
534     my $millifortnights = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0 * (365.25 / (365.25 / 7.0)) * 2.0 / 1000.0));
535     my $days = sigfig(3, $accurateSeconds / (60.0 * 60.0 * 24.0));
536     my $hours = sigfig(3, $accurateSeconds / (60.0 * 60.0));
537     my $minutes = sigfig(3, $accurateSeconds / 60.0);
538     my $seconds = sigfig(3, $accurateSeconds);
539     $self->say($event, "$event->{'from'}: ${seconds}s = ${years}yr, ${months}mo, ${weeks}wk, ${days}d, ${hours}hr, ${minutes}min, ${millifortnights}mf");
540 }
541      
542
543 # Mass
544
545 sub convertGrams {
546     my $self = shift;
547     my($event, $input) = @_;
548     my $accurateGrams = $input;
549     my $grams = sigfig(3, $accurateGrams);
550     my $kgs = sigfig(3, $accurateGrams / 1000.0);
551     my $ounces = sigfig(3, $accurateGrams * 0.03527);
552     my $pounds = sigfig(3, $accurateGrams * 0.002205);
553     my $stones = sigfig(3, $accurateGrams * 0.00016);
554     $self->say($event, "$event->{'from'}: ${grams}g = ${kgs}kg, ${ounces}oz, ${pounds}lbs, ${stones}stone");
555 }
556
557 sub convertKilograms {
558     my $self = shift;
559     my($event, $input) = @_;
560     my $accurateGrams = $input * 1000.0;
561     my $grams = sigfig(3, $accurateGrams);
562     my $kgs = sigfig(3, $input);
563     my $ounces = sigfig(3, $accurateGrams * 0.03527);
564     my $pounds = sigfig(3, $accurateGrams * 0.002205);
565     my $stones = sigfig(3, $accurateGrams * 0.00016);
566     $self->say($event, "$event->{'from'}: ${kgs}kg = ${grams}g, ${ounces}oz, ${pounds}lbs, ${stones}stone");
567 }
568      
569 sub convertPounds {
570     my $self = shift;
571     my($event, $input) = @_;
572     my $accurateGrams = $input * 453.6;
573     my $grams = sigfig(3, $accurateGrams);
574     my $kgs = sigfig(3, $accurateGrams / 1000.0);
575     my $ounces = sigfig(3, $accurateGrams * 0.03527);
576     my $pounds = sigfig(3, $input);
577     my $stones = sigfig(3, $accurateGrams * 0.00016);
578     $self->say($event, "$event->{'from'}: ${pounds}lbs = ${grams}g, ${kgs}kg, ${ounces}oz, ${stones}stone");
579 }
580      
581 sub convertOunces {
582     my $self = shift;
583     my($event, $input) = @_;
584     my $accurateGrams = $input * 28.35;
585     my $grams = sigfig(3, $accurateGrams);
586     my $kgs = sigfig(3, $accurateGrams / 1000.0);
587     my $ounces = sigfig(3, $input);
588     my $pounds = sigfig(3, $accurateGrams * 0.002205);
589     my $stones = sigfig(3, $accurateGrams * 0.00016);
590     $self->say($event, "$event->{'from'}: ${ounces}oz = ${grams}g, ${kgs}kg, ${pounds}lbs, ${stones}stone");
591 }
592      
593 sub convertStones {
594     my $self = shift;
595     my($event, $input) = @_;
596     my $accurateGrams = $input * 6350.3;
597     my $grams = sigfig(3, $accurateGrams);
598     my $kgs = sigfig(3, $accurateGrams / 1000.0);
599     my $ounces = sigfig(3, $accurateGrams * 0.03527);
600     my $pounds = sigfig(3, $accurateGrams * 0.002205);
601     my $stones = sigfig(3, $accurateGrams * 0.00016);
602     $self->say($event, "$event->{'from'}: ${stones}stone = ${grams}g, ${kgs}kg, ${ounces}oz, ${pounds}lbs");
603 }
604
605      
606 # Utility Functions
607
608 sub round($$) {
609     return sprintf("%.*f", @_);
610 }
611
612 sub sigfig($$) {
613     my($sf, $float) = @_;
614     my $length = length(int($float));
615     if ($length == $sf) {
616         $float = int($float);
617     } elsif ($length > $sf) {
618         my $factor = (10 ** ($length - $sf));
619         $float = int($float / $factor) * $factor;
620     } else {
621         my $factor = 0;
622         while (length(int($float * 10 ** $factor)) < $sf) {
623             $factor++;
624         }
625         $float = int($float * 10 ** $factor) / (10 ** $factor);
626     }
627     $float = sprintf("%g", $float);
628     $float =~ s/e(?:\+|(-))0*/x10^$1/os;
629     return $float;
630 }