1;;; calc-units.el --- unit conversion functions for Calc
2
3;; Copyright (C) 1990, 1991, 1992, 1993, 2001, 2002, 2003, 2004,
4;;   2005, 2006, 2007 Free Software Foundation, Inc.
5
6;; Author: David Gillespie <daveg@synaptics.com>
7;; Maintainer: Jay Belanger <jay.p.belanger@gmail.com>
8
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation; either version 2, or (at your option)
14;; any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
22;; along with GNU Emacs; see the file COPYING.  If not, write to the
23;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24;; Boston, MA 02110-1301, USA.
25
26;;; Commentary:
27
28;;; Code:
29
30;; This file is autoloaded from calc-ext.el.
31
32(require 'calc-ext)
33(require 'calc-macs)
34(eval-when-compile
35  (require 'calc-alg))
36
37;;; Units operations.
38
39;;; Units table last updated 9-Jan-91 by Ulrich Mueller (ulm@vsnhd1.cern.ch)
40;;; with some additions by Przemek Klosowski (przemek@rrdstrad.nist.gov)
41;;; Updated April 2002 by Jochen K�pper
42
43;;; for CODATA 1998 see one of
44;;; - Journal of Physical and Chemical Reference Data, 28(6), 1713-1852, 1999.
45;;; - Reviews of Modern Physics, 72(2), 351-495, 2000.
46;;; for CODATA 2005 see
47;;; - http://physics.nist.gov/cuu/Constants/index.html
48
49(defvar math-standard-units
50  '( ;; Length
51    ( m       nil                    "*Meter" )
52    ( in      "2.54 cm"           "Inch" )
53    ( ft      "12 in"                "Foot" )
54    ( yd      "3 ft"                 "Yard" )
55    ( mi      "5280 ft"              "Mile" )
56    ( au      "149597870691 m"       "Astronomical Unit" ) ;; NASA JPL (http://neo.jpl.nasa.gov/glossary/au.html)
57    ( lyr     "9460536207068016 m"   "Light Year" )
58    ( pc      "206264.80625 au"      "Parsec" )
59    ( nmi     "1852 m"               "Nautical Mile" )
60    ( fath    "6 ft"                 "Fathom" )
61    ( mu      "1 um"                 "Micron" )
62    ( mil     "in/1000"              "Mil" )
63    ( point   "in/72"                "Point (1/72 inch)" )
64    ( Ang     "1e-10 m"              "Angstrom" )
65    ( mfi     "mi+ft+in"             "Miles + feet + inches" )
66    ;; TeX lengths
67    ( texpt   "in/72.27"             "Point (TeX conventions)" )
68    ( texpc   "12 texpt"             "Pica" )
69    ( texbp   "point"                "Big point (TeX conventions)" )
70    ( texdd   "1238/1157 texpt"      "Didot point" )
71    ( texcc   "12 texdd"             "Cicero" )
72    ( texsp   "1/66536 texpt"        "Scaled TeX point" )
73
74    ;; Area
75    ( hect    "10000 m^2"            "*Hectare" )
76    ( a       "100 m^2"              "Are")
77    ( acre    "mi^2 / 640"           "Acre" )
78    ( b       "1e-28 m^2"            "Barn" )
79
80    ;; Volume
81    ( L       "1e-3 m^3"             "*Liter" )
82    ( l       "L"                    "Liter" )
83    ( gal     "4 qt"                 "US Gallon" )
84    ( qt      "2 pt"                 "Quart" )
85    ( pt      "2 cup"                "Pint" )
86    ( cup     "8 ozfl"               "Cup" )
87    ( ozfl    "2 tbsp"               "Fluid Ounce" )
88    ( floz    "2 tbsp"               "Fluid Ounce" )
89    ( tbsp    "3 tsp"                "Tablespoon" )
90    ( tsp     "4.92892159375 ml"     "Teaspoon" )
91    ( vol     "tsp+tbsp+ozfl+cup+pt+qt+gal" "Gallons + ... + teaspoons" )
92    ( galC    "4.54609 L"            "Canadian Gallon" )
93    ( galUK   "4.546092 L"           "UK Gallon" )
94
95    ;; Time
96    ( s       nil                    "*Second" )
97    ( sec     "s"                    "Second" )
98    ( min     "60 s"                 "Minute" )
99    ( hr      "60 min"               "Hour" )
100    ( day     "24 hr"                "Day" )
101    ( wk      "7 day"                "Week" )
102    ( hms     "wk+day+hr+min+s"      "Hours, minutes, seconds" )
103    ( yr      "365.25 day"           "Year" )
104    ( Hz      "1/s"                  "Hertz" )
105
106    ;; Speed
107    ( mph     "mi/hr"                "*Miles per hour" )
108    ( kph     "km/hr"                "Kilometers per hour" )
109    ( knot    "nmi/hr"               "Knot" )
110    ( c       "299792458 m/s"        "Speed of light" ) ;;; CODATA 2005
111
112    ;; Acceleration
113    ( ga      "9.80665 m/s^2"        "*\"g\" acceleration" ) ;; CODATA 2005
114
115    ;; Mass
116    ( g       nil                    "*Gram" )
117    ( lb      "16 oz"                "Pound (mass)" )
118    ( oz      "28.349523125 g"       "Ounce (mass)" )
119    ( ton     "2000 lb"              "Ton" )
120    ( tpo     "ton+lb+oz"            "Tons + pounds + ounces (mass)" )
121    ( t       "1000 kg"              "Metric ton" )
122    ( tonUK   "1016.0469088 kg"      "UK ton" )
123    ( lbt     "12 ozt"               "Troy pound" )
124    ( ozt     "31.103475 g"          "Troy ounce" )
125    ( ct      ".2 g"                 "Carat" )
126    ( u       "1.66053886e-27 kg"    "Unified atomic mass" ) ;; CODATA 2005
127
128    ;; Force
129    ( N       "m kg/s^2"             "*Newton" )
130    ( dyn     "1e-5 N"               "Dyne" )
131    ( gf      "ga g"                 "Gram (force)" )
132    ( lbf     "4.44822161526 N"      "Pound (force)" )
133    ( kip     "1000 lbf"             "Kilopound (force)" )
134    ( pdl     "0.138255 N"           "Poundal" )
135
136    ;; Energy
137    ( J       "N m"                  "*Joule" )
138    ( erg     "1e-7 J"               "Erg" )
139    ( cal     "4.1868 J"             "International Table Calorie" )
140    ( Btu     "1055.05585262 J"      "International Table Btu" )
141    ( eV      "ech V"                "Electron volt" )
142    ( ev      "eV"                   "Electron volt" )
143    ( therm   "105506000 J"          "EEC therm" )
144    ( invcm   "h c/cm"               "Energy in inverse centimeters" )
145    ( Kayser  "invcm"                "Kayser (inverse centimeter energy)" )
146    ( men     "100/invcm"            "Inverse energy in meters" )
147    ( Hzen    "h Hz"                 "Energy in Hertz")
148    ( Ken     "k K"                  "Energy in Kelvins")
149    ( Wh      "W hr"                 "Watt hour")
150    ( Ws      "W s"                  "Watt second")
151
152    ;; Power
153    ( W       "J/s"                  "*Watt" )
154    ( hp      "745.7 W"              "Horsepower" )
155
156    ;; Temperature
157    ( K       nil                    "*Degree Kelvin"     K )
158    ( dK      "K"                    "Degree Kelvin"      K )
159    ( degK    "K"                    "Degree Kelvin"      K )
160    ( dC      "K"                    "Degree Celsius"     C )
161    ( degC    "K"                    "Degree Celsius"     C )
162    ( dF      "(5/9) K"              "Degree Fahrenheit"  F )
163    ( degF    "(5/9) K"              "Degree Fahrenheit"  F )
164
165    ;; Pressure
166    ( Pa      "N/m^2"                "*Pascal" )
167    ( bar     "1e5 Pa"               "Bar" )
168    ( atm     "101325 Pa"            "Standard atmosphere" ) ;; CODATA 2005
169    ( Torr    " 1.333224e2 Pa"       "Torr" ) ;; NIST (http://physics.nist.gov/Pubs/SP811/appenB9.html)
170    ( mHg     "1000 Torr"            "Meter of mercury" )
171    ( inHg    "25.4 mmHg"            "Inch of mercury" )
172    ( inH2O   "2.490889e2 Pa"        "Inch of water" ) ;; NIST (http://physics.nist.gov/Pubs/SP811/appenB9.html)
173    ( psi     "6894.75729317 Pa"     "Pound per square inch" )
174
175    ;; Viscosity
176    ( P       "0.1 Pa s"              "*Poise" )
177    ( St      "1e-4 m^2/s"            "Stokes" )
178
179    ;; Electromagnetism
180    ( A       nil                     "*Ampere" )
181    ( C       "A s"                   "Coulomb" )
182    ( Fdy     "ech Nav"               "Faraday" )
183    ( e       "1.60217653e-19 C"      "Elementary charge" ) ;; CODATA 2005
184    ( ech     "1.60217653e-19 C"      "Elementary charge" ) ;; CODATA 2005
185    ( V       "W/A"                   "Volt" )
186    ( ohm     "V/A"                   "Ohm" )
187    ( mho     "A/V"                   "Mho" )
188    ( S       "A/V"                   "Siemens" )
189    ( F       "C/V"                   "Farad" )
190    ( H       "Wb/A"                  "Henry" )
191    ( T       "Wb/m^2"                "Tesla" )
192    ( Gs      "1e-4 T"                "Gauss" )
193    ( Wb      "V s"                   "Weber" )
194
195    ;; Luminous intensity
196    ( cd      nil                     "*Candela" )
197    ( sb      "1e4 cd/m^2"            "Stilb" )
198    ( lm      "cd sr"                 "Lumen" )
199    ( lx      "lm/m^2"                "Lux" )
200    ( ph      "1e4 lx"                "Phot" )
201    ( fc      "10.76391 lx"           "Footcandle" ) ;; NIST (http://physics.nist.gov/Pubs/SP811/appenB9.html)
202    ( lam     "1e4 lm/m^2"            "Lambert" )
203    ( flam    "3.426259 cd/m^2"       "Footlambert" ) ;; NIST (http://physics.nist.gov/Pubs/SP811/appenB9.html)
204
205    ;; Radioactivity
206    ( Bq      "1/s"                    "*Becquerel" )
207    ( Ci      "3.7e10 Bq"              "Curie" )
208    ( Gy      "J/kg"                   "Gray" )
209    ( Sv      "Gy"                     "Sievert" )
210    ( R       "2.58e-4 C/kg"           "Roentgen" )
211    ( rd      ".01 Gy"                 "Rad" )
212    ( rem     "rd"                     "Rem" )
213
214    ;; Amount of substance
215    ( mol     nil                      "*Mole" )
216
217    ;; Plane angle
218    ( rad     nil                      "*Radian" )
219    ( circ    "2 pi rad"               "Full circle" )
220    ( rev     "circ"                   "Full revolution" )
221    ( deg     "circ/360"               "Degree" )
222    ( arcmin  "deg/60"                 "Arc minute" )
223    ( arcsec  "arcmin/60"              "Arc second" )
224    ( grad    "circ/400"               "Grade" )
225    ( rpm     "rev/min"                "Revolutions per minute" )
226
227    ;; Solid angle
228    ( sr      nil                      "*Steradian" )
229
230    ;; Other physical quantities
231    ( h       "6.6260693e-34 J s"     "*Planck's constant" ) ;; CODATA 2005
232    ( hbar    "h / 2 pi"               "Planck's constant" )
233    ( mu0     "4 pi 1e-7 H/m"          "Permeability of vacuum" )
234    ( G       "6.6742e-11 m^3/kg^1/s^2" "Gravitational constant" ) ;; CODATA 2005
235    ( Nav     "6.02214115e23 / mol"    "Avagadro's constant" ) ;; CODATA 2005
236    ( me      "9.1093826e-31 kg"       "Electron rest mass" ) ;; CODATA 2005
237    ( mp      "1.67262171e-27 kg"      "Proton rest mass" ) ;; CODATA 2005
238    ( mn      "1.67492728e-27 kg"      "Neutron rest mass" ) ;; CODATA 2005
239    ( mmu     "1.88353140e-28 kg"      "Muon rest mass" ) ;; CODATA 2005
240    ( Ryd     "10973731.568525 /m"     "Rydberg's constant" ) ;; CODATA 2005
241    ( k       "1.3806505e-23 J/K"      "Boltzmann's constant" ) ;; CODATA 2005
242    ( alpha   "7.297352568e-3"         "Fine structure constant" ) ;; CODATA 2005
243    ( muB     "927.400949e-26 J/T"     "Bohr magneton" ) ;; CODATA 2005
244    ( muN     "5.05078343e-27 J/T"     "Nuclear magneton" ) ;; CODATA 2005
245    ( mue     "-928.476412e-26 J/T"    "Electron magnetic moment" ) ;; CODATA 2005
246    ( mup     "1.41060671e-26 J/T"     "Proton magnetic moment" ) ;; CODATA 2005
247    ( R0      "8.314472 J/mol/K"       "Molar gas constant" ) ;; CODATA 2005
248    ( V0      "22.710981e-3 m^3/mol"   "Standard volume of ideal gas" )))
249
250
251(defvar math-additional-units nil
252  "*Additional units table for user-defined units.
253Must be formatted like math-standard-units.
254If this is changed, be sure to set math-units-table to nil to ensure
255that the combined units table will be rebuilt.")
256
257(defvar math-unit-prefixes
258  '( ( ?Y  (float 1 24)  "Yotta"  )
259     ( ?Z  (float 1 21)  "Zetta"  )
260     ( ?E  (float 1 18)  "Exa"    )
261     ( ?P  (float 1 15)  "Peta"   )
262     ( ?T  (float 1 12)  "Tera"	  )
263     ( ?G  (float 1 9)   "Giga"	  )
264     ( ?M  (float 1 6)   "Mega"	  )
265     ( ?k  (float 1 3)   "Kilo"	  )
266     ( ?K  (float 1 3)   "Kilo"	  )
267     ( ?h  (float 1 2)   "Hecto"  )
268     ( ?H  (float 1 2)   "Hecto"  )
269     ( ?D  (float 1 1)   "Deka"	  )
270     ( 0   (float 1 0)   nil      )
271     ( ?d  (float 1 -1)  "Deci"	  )
272     ( ?c  (float 1 -2)  "Centi"  )
273     ( ?m  (float 1 -3)  "Milli"  )
274     ( ?u  (float 1 -6)  "Micro"  )
275     ( ?n  (float 1 -9)  "Nano"	  )
276     ( ?p  (float 1 -12) "Pico"	  )
277     ( ?f  (float 1 -15) "Femto"  )
278     ( ?a  (float 1 -18) "Atto"   )
279     ( ?z  (float 1 -21) "zepto"  )
280     ( ?y  (float 1 -24) "yocto"  )))
281
282(defvar math-standard-units-systems
283  '( ( base  nil )
284     ( si    ( ( g   '(* (var kg var-kg) (float 1 -3)) ) ) )
285     ( mks   ( ( g   '(* (var kg var-kg) (float 1 -3)) ) ) )
286     ( cgs   ( ( m   '(* (var cm var-cm) 100         ) ) ) )))
287
288(defvar math-units-table nil
289  "Internal units table derived from math-defined-units.
290Entries are (SYMBOL EXPR DOC-STRING TEMP-TYPE BASE-UNITS).")
291
292(defvar math-units-table-buffer-valid nil)
293
294;;; Units commands.
295
296(defun calc-base-units ()
297  (interactive)
298  (calc-slow-wrapper
299   (let ((calc-autorange-units nil))
300     (calc-enter-result 1 "bsun" (math-simplify-units
301				  (math-to-standard-units (calc-top-n 1)
302							  nil))))))
303
304(defun calc-quick-units ()
305  (interactive)
306  (calc-slow-wrapper
307   (let* ((num (- last-command-char ?0))
308	  (pos (if (= num 0) 10 num))
309	  (units (calc-var-value 'var-Units))
310	  (expr (calc-top-n 1)))
311     (unless (and (>= num 0) (<= num 9))
312       (error "Bad unit number"))
313     (unless (math-vectorp units)
314       (error "No \"quick units\" are defined"))
315     (unless (< pos (length units))
316       (error "Unit number %d not defined" pos))
317     (if (math-units-in-expr-p expr nil)
318	 (calc-enter-result 1 (format "cun%d" num)
319			    (math-convert-units expr (nth pos units)))
320       (calc-enter-result 1 (format "*un%d" num)
321			  (math-simplify-units
322			   (math-mul expr (nth pos units))))))))
323
324(defun calc-convert-units (&optional old-units new-units)
325  (interactive)
326  (calc-slow-wrapper
327   (let ((expr (calc-top-n 1))
328	 (uoldname nil)
329	 unew
330         units)
331     (unless (math-units-in-expr-p expr t)
332       (let ((uold (or old-units
333		       (progn
334			 (setq uoldname (read-string "Old units: "))
335			 (if (equal uoldname "")
336			     (progn
337			       (setq uoldname "1")
338			       1)
339			   (if (string-match "\\` */" uoldname)
340			       (setq uoldname (concat "1" uoldname)))
341			   (math-read-expr uoldname))))))
342	 (when (eq (car-safe uold) 'error)
343	   (error "Bad format in units expression: %s" (nth 1 uold)))
344	 (setq expr (math-mul expr uold))))
345     (unless new-units
346       (setq new-units (read-string (if uoldname
347					(concat "Old units: "
348						uoldname
349						", new units: ")
350				      "New units: "))))
351     (when (string-match "\\` */" new-units)
352       (setq new-units (concat "1" new-units)))
353     (setq units (math-read-expr new-units))
354     (when (eq (car-safe units) 'error)
355       (error "Bad format in units expression: %s" (nth 2 units)))
356     (let ((unew (math-units-in-expr-p units t))
357	   (std (and (eq (car-safe units) 'var)
358		     (assq (nth 1 units) math-standard-units-systems))))
359       (if std
360	   (calc-enter-result 1 "cvun" (math-simplify-units
361					(math-to-standard-units expr
362								(nth 1 std))))
363	 (unless unew
364	   (error "No units specified"))
365	 (calc-enter-result 1 "cvun"
366			    (math-convert-units
367			     expr units
368			     (and uoldname (not (equal uoldname "1"))))))))))
369
370(defun calc-autorange-units (arg)
371  (interactive "P")
372  (calc-wrapper
373   (calc-change-mode 'calc-autorange-units arg nil t)
374   (message (if calc-autorange-units
375		"Adjusting target unit prefix automatically"
376	      "Using target units exactly"))))
377
378(defun calc-convert-temperature (&optional old-units new-units)
379  (interactive)
380  (calc-slow-wrapper
381   (let ((expr (calc-top-n 1))
382	 (uold nil)
383	 (uoldname nil)
384	 unew)
385     (setq uold (or old-units
386		    (let ((units (math-single-units-in-expr-p expr)))
387		      (if units
388			  (if (consp units)
389			      (list 'var (car units)
390				    (intern (concat "var-"
391						    (symbol-name
392						     (car units)))))
393			    (error "Not a pure temperature expression"))
394			(math-read-expr
395			 (setq uoldname (read-string
396					 "Old temperature units: ")))))))
397     (when (eq (car-safe uold) 'error)
398       (error "Bad format in units expression: %s" (nth 2 uold)))
399     (or (math-units-in-expr-p expr nil)
400	 (setq expr (math-mul expr uold)))
401     (setq unew (or new-units
402		    (math-read-expr
403		     (read-string (if uoldname
404				      (concat "Old temperature units: "
405					      uoldname
406					      ", new units: ")
407				    "New temperature units: ")))))
408     (when (eq (car-safe unew) 'error)
409       (error "Bad format in units expression: %s" (nth 2 unew)))
410     (calc-enter-result 1 "cvtm" (math-simplify-units
411				  (math-convert-temperature expr uold unew
412							    uoldname))))))
413
414(defun calc-remove-units ()
415  (interactive)
416  (calc-slow-wrapper
417   (calc-enter-result 1 "rmun" (math-simplify-units
418				(math-remove-units (calc-top-n 1))))))
419
420(defun calc-extract-units ()
421  (interactive)
422  (calc-slow-wrapper
423   (calc-enter-result 1 "rmun" (math-simplify-units
424				(math-extract-units (calc-top-n 1))))))
425
426;; The variables calc-num-units and calc-den-units are local to
427;; calc-explain-units, but are used by calc-explain-units-rec,
428;; which is called by calc-explain-units.
429(defvar calc-num-units)
430(defvar calc-den-units)
431
432(defun calc-explain-units ()
433  (interactive)
434  (calc-wrapper
435   (let ((calc-num-units nil)
436	 (calc-den-units nil))
437     (calc-explain-units-rec (calc-top-n 1) 1)
438     (and calc-den-units (string-match "^[^(].* .*[^)]$" calc-den-units)
439	  (setq calc-den-units (concat "(" calc-den-units ")")))
440     (if calc-num-units
441	 (if calc-den-units
442	     (message "%s per %s" calc-num-units calc-den-units)
443	   (message "%s" calc-num-units))
444       (if calc-den-units
445	   (message "1 per %s" calc-den-units)
446	 (message "No units in expression"))))))
447
448(defun calc-explain-units-rec (expr pow)
449  (let ((u (math-check-unit-name expr))
450	pos)
451    (if (and u (not (math-zerop pow)))
452	(let ((name (or (nth 2 u) (symbol-name (car u)))))
453	  (if (eq (aref name 0) ?\*)
454	      (setq name (substring name 1)))
455	  (if (string-match "[^a-zA-Z0-9']" name)
456	      (if (string-match "^[a-zA-Z0-9' ()]*$" name)
457		  (while (setq pos (string-match "[ ()]" name))
458		    (setq name (concat (substring name 0 pos)
459				       (if (eq (aref name pos) 32) "-" "")
460				       (substring name (1+ pos)))))
461		(setq name (concat "(" name ")"))))
462	  (or (eq (nth 1 expr) (car u))
463	      (setq name (concat (nth 2 (assq (aref (symbol-name
464						     (nth 1 expr)) 0)
465					      math-unit-prefixes))
466				 (if (and (string-match "[^a-zA-Z0-9']" name)
467					  (not (memq (car u) '(mHg gf))))
468				     (concat "-" name)
469				   (downcase name)))))
470	  (cond ((or (math-equal-int pow 1)
471		     (math-equal-int pow -1)))
472		((or (math-equal-int pow 2)
473		     (math-equal-int pow -2))
474		 (if (equal (nth 4 u) '((m . 1)))
475		     (setq name (concat "Square-" name))
476		   (setq name (concat name "-squared"))))
477		((or (math-equal-int pow 3)
478		     (math-equal-int pow -3))
479		 (if (equal (nth 4 u) '((m . 1)))
480		     (setq name (concat "Cubic-" name))
481		   (setq name (concat name "-cubed"))))
482		(t
483		 (setq name (concat name "^"
484				    (math-format-number (math-abs pow))))))
485	  (if (math-posp pow)
486	      (setq calc-num-units (if calc-num-units
487				  (concat calc-num-units " " name)
488				name))
489	    (setq calc-den-units (if calc-den-units
490				(concat calc-den-units " " name)
491			      name))))
492      (cond ((eq (car-safe expr) '*)
493	     (calc-explain-units-rec (nth 1 expr) pow)
494	     (calc-explain-units-rec (nth 2 expr) pow))
495	    ((eq (car-safe expr) '/)
496	     (calc-explain-units-rec (nth 1 expr) pow)
497	     (calc-explain-units-rec (nth 2 expr) (- pow)))
498	    ((memq (car-safe expr) '(neg + -))
499	     (calc-explain-units-rec (nth 1 expr) pow))
500	    ((and (eq (car-safe expr) '^)
501		  (math-realp (nth 2 expr)))
502	     (calc-explain-units-rec (nth 1 expr)
503				     (math-mul pow (nth 2 expr))))))))
504
505(defun calc-simplify-units ()
506  (interactive)
507  (calc-slow-wrapper
508   (calc-with-default-simplification
509    (calc-enter-result 1 "smun" (math-simplify-units (calc-top-n 1))))))
510
511(defun calc-view-units-table (n)
512  (interactive "P")
513  (and n (setq math-units-table-buffer-valid nil))
514  (let ((win (get-buffer-window "*Units Table*")))
515    (if (and win
516	     math-units-table
517	     math-units-table-buffer-valid)
518	(progn
519	  (bury-buffer (window-buffer win))
520	  (let ((curwin (selected-window)))
521	    (select-window win)
522	    (switch-to-buffer nil)
523	    (select-window curwin)))
524      (math-build-units-table-buffer nil))))
525
526(defun calc-enter-units-table (n)
527  (interactive "P")
528  (and n (setq math-units-table-buffer-valid nil))
529  (math-build-units-table-buffer t)
530  (message "%s" (substitute-command-keys "Type \\[calc] to return to the Calculator")))
531
532(defun calc-define-unit (uname desc)
533  (interactive "SDefine unit name: \nsDescription: ")
534  (calc-wrapper
535   (let ((form (calc-top-n 1))
536	 (unit (assq uname math-additional-units)))
537     (or unit
538	 (setq math-additional-units
539	       (cons (setq unit (list uname nil nil))
540		     math-additional-units)
541	       math-units-table nil))
542     (setcar (cdr unit) (and (not (and (eq (car-safe form) 'var)
543				       (eq (nth 1 form) uname)))
544			     (not (math-equal-int form 1))
545			     (math-format-flat-expr form 0)))
546     (setcar (cdr (cdr unit)) (and (not (equal desc ""))
547				   desc))))
548  (calc-invalidate-units-table))
549
550(defun calc-undefine-unit (uname)
551  (interactive "SUndefine unit name: ")
552  (calc-wrapper
553   (let ((unit (assq uname math-additional-units)))
554     (or unit
555	 (if (assq uname math-standard-units)
556	     (error "\"%s\" is a predefined unit name" uname)
557	   (error "Unit name \"%s\" not found" uname)))
558     (setq math-additional-units (delq unit math-additional-units)
559	   math-units-table nil)))
560  (calc-invalidate-units-table))
561
562(defun calc-invalidate-units-table ()
563  (setq math-units-table nil)
564  (let ((buf (get-buffer "*Units Table*")))
565    (and buf
566	 (save-excursion
567	   (set-buffer buf)
568	   (save-excursion
569	     (goto-char (point-min))
570	     (if (looking-at "Calculator Units Table")
571		 (let ((inhibit-read-only t))
572		   (insert "(Obsolete) "))))))))
573
574(defun calc-get-unit-definition (uname)
575  (interactive "SGet definition for unit: ")
576  (calc-wrapper
577   (math-build-units-table)
578   (let ((unit (assq uname math-units-table)))
579     (or unit
580	 (error "Unit name \"%s\" not found" uname))
581     (let ((msg (nth 2 unit)))
582       (if (stringp msg)
583	   (if (string-match "^\\*" msg)
584	       (setq msg (substring msg 1)))
585	 (setq msg (symbol-name uname)))
586       (if (nth 1 unit)
587	   (progn
588	     (calc-enter-result 0 "ugdf" (nth 1 unit))
589	     (message "Derived unit: %s" msg))
590	 (calc-enter-result 0 "ugdf" (list 'var uname
591					   (intern
592					    (concat "var-"
593						    (symbol-name uname)))))
594	 (message "Base unit: %s" msg))))))
595
596(defun calc-permanent-units ()
597  (interactive)
598  (calc-wrapper
599   (let (pos)
600     (set-buffer (find-file-noselect (substitute-in-file-name
601				      calc-settings-file)))
602     (goto-char (point-min))
603     (if (and (search-forward ";;; Custom units stored by Calc" nil t)
604	      (progn
605		(beginning-of-line)
606		(setq pos (point))
607		(search-forward "\n;;; End of custom units" nil t)))
608	 (progn
609	   (beginning-of-line)
610	   (forward-line 1)
611	   (delete-region pos (point)))
612       (goto-char (point-max))
613       (insert "\n\n")
614       (forward-char -1))
615     (insert ";;; Custom units stored by Calc on " (current-time-string) "\n")
616     (if math-additional-units
617	 (progn
618	   (insert "(setq math-additional-units '(\n")
619	   (let ((list math-additional-units))
620	     (while list
621	       (insert "  (" (symbol-name (car (car list))) " "
622		       (if (nth 1 (car list))
623			   (if (stringp (nth 1 (car list)))
624			       (prin1-to-string (nth 1 (car list)))
625			     (prin1-to-string (math-format-flat-expr
626					       (nth 1 (car list)) 0)))
627			 "nil")
628		       " "
629		       (prin1-to-string (nth 2 (car list)))
630		       ")\n")
631	       (setq list (cdr list))))
632	   (insert "))\n"))
633       (insert ";;; (no custom units defined)\n"))
634     (insert ";;; End of custom units\n")
635     (save-buffer))))
636
637
638;; The variable math-cu-unit-list is local to math-build-units-table,
639;; but is used by math-compare-unit-names, which is called (indirectly)
640;; by math-build-units-table.
641;; math-cu-unit-list is also local to math-convert-units, but is used
642;; by math-convert-units-rec, which is called by math-convert-units.
643(defvar math-cu-unit-list)
644
645(defun math-build-units-table ()
646  (or math-units-table
647      (let* ((combined-units (append math-additional-units
648				     math-standard-units))
649	     (math-cu-unit-list (mapcar 'car combined-units))
650	     tab)
651	(message "Building units table...")
652	(setq math-units-table-buffer-valid nil)
653	(setq tab (mapcar (function
654			   (lambda (x)
655			     (list (car x)
656				   (and (nth 1 x)
657					(if (stringp (nth 1 x))
658					    (let ((exp (math-read-plain-expr
659							(nth 1 x))))
660					      (if (eq (car-safe exp) 'error)
661						  (error "Format error in definition of %s in units table: %s"
662							 (car x) (nth 2 exp))
663						exp))
664					  (nth 1 x)))
665				   (nth 2 x)
666				   (nth 3 x)
667				   (and (not (nth 1 x))
668					(list (cons (car x) 1))))))
669			  combined-units))
670	(let ((math-units-table tab))
671	  (mapcar 'math-find-base-units tab))
672	(message "Building units table...done")
673	(setq math-units-table tab))))
674
675;; The variables math-fbu-base and math-fbu-entry are local to
676;; math-find-base-units, but are used by math-find-base-units-rec,
677;; which is called by math-find-base-units.
678(defvar math-fbu-base)
679(defvar math-fbu-entry)
680
681(defun math-find-base-units (math-fbu-entry)
682  (if (eq (nth 4 math-fbu-entry) 'boom)
683      (error "Circular definition involving unit %s" (car math-fbu-entry)))
684  (or (nth 4 math-fbu-entry)
685      (let (math-fbu-base)
686	(setcar (nthcdr 4 math-fbu-entry) 'boom)
687	(math-find-base-units-rec (nth 1 math-fbu-entry) 1)
688	'(or math-fbu-base
689	    (error "Dimensionless definition for unit %s" (car math-fbu-entry)))
690	(while (eq (cdr (car math-fbu-base)) 0)
691	  (setq math-fbu-base (cdr math-fbu-base)))
692	(let ((b math-fbu-base))
693	  (while (cdr b)
694	    (if (eq (cdr (car (cdr b))) 0)
695		(setcdr b (cdr (cdr b)))
696	      (setq b (cdr b)))))
697	(setq math-fbu-base (sort math-fbu-base 'math-compare-unit-names))
698	(setcar (nthcdr 4 math-fbu-entry) math-fbu-base)
699	math-fbu-base)))
700
701(defun math-compare-unit-names (a b)
702  (memq (car b) (cdr (memq (car a) math-cu-unit-list))))
703
704(defun math-find-base-units-rec (expr pow)
705  (let ((u (math-check-unit-name expr)))
706    (cond (u
707	   (let ((ulist (math-find-base-units u)))
708	     (while ulist
709	       (let ((p (* (cdr (car ulist)) pow))
710		     (old (assq (car (car ulist)) math-fbu-base)))
711		 (if old
712		     (setcdr old (+ (cdr old) p))
713		   (setq math-fbu-base
714                         (cons (cons (car (car ulist)) p) math-fbu-base))))
715	       (setq ulist (cdr ulist)))))
716	  ((math-scalarp expr))
717	  ((and (eq (car expr) '^)
718		(integerp (nth 2 expr)))
719	   (math-find-base-units-rec (nth 1 expr) (* pow (nth 2 expr))))
720	  ((eq (car expr) '*)
721	   (math-find-base-units-rec (nth 1 expr) pow)
722	   (math-find-base-units-rec (nth 2 expr) pow))
723	  ((eq (car expr) '/)
724	   (math-find-base-units-rec (nth 1 expr) pow)
725	   (math-find-base-units-rec (nth 2 expr) (- pow)))
726	  ((eq (car expr) 'neg)
727	   (math-find-base-units-rec (nth 1 expr) pow))
728	  ((eq (car expr) '+)
729	   (math-find-base-units-rec (nth 1 expr) pow))
730	  ((eq (car expr) 'var)
731	   (or (eq (nth 1 expr) 'pi)
732	       (error "Unknown name %s in defining expression for unit %s"
733		      (nth 1 expr) (car math-fbu-entry))))
734	  (t (error "Malformed defining expression for unit %s" (car math-fbu-entry))))))
735
736
737(defun math-units-in-expr-p (expr sub-exprs)
738  (and (consp expr)
739       (if (eq (car expr) 'var)
740	   (math-check-unit-name expr)
741	 (and (or sub-exprs
742		  (memq (car expr) '(* / ^)))
743	      (or (math-units-in-expr-p (nth 1 expr) sub-exprs)
744		  (math-units-in-expr-p (nth 2 expr) sub-exprs))))))
745
746(defun math-only-units-in-expr-p (expr)
747  (and (consp expr)
748       (if (eq (car expr) 'var)
749	   (math-check-unit-name expr)
750	 (if (memq (car expr) '(* /))
751	     (and (math-only-units-in-expr-p (nth 1 expr))
752		  (math-only-units-in-expr-p (nth 2 expr)))
753	   (and (eq (car expr) '^)
754		(and (math-only-units-in-expr-p (nth 1 expr))
755		     (math-realp (nth 2 expr))))))))
756
757(defun math-single-units-in-expr-p (expr)
758  (cond ((math-scalarp expr) nil)
759	((eq (car expr) 'var)
760	 (math-check-unit-name expr))
761	((eq (car expr) '*)
762	 (let ((u1 (math-single-units-in-expr-p (nth 1 expr)))
763	       (u2 (math-single-units-in-expr-p (nth 2 expr))))
764	   (or (and u1 u2 'wrong)
765	       u1
766	       u2)))
767	((eq (car expr) '/)
768	 (if (math-units-in-expr-p (nth 2 expr) nil)
769	     'wrong
770	   (math-single-units-in-expr-p (nth 1 expr))))
771	(t 'wrong)))
772
773(defun math-check-unit-name (v)
774  (and (eq (car-safe v) 'var)
775       (or (assq (nth 1 v) (or math-units-table (math-build-units-table)))
776	   (let ((name (symbol-name (nth 1 v))))
777	     (and (> (length name) 1)
778		  (assq (aref name 0) math-unit-prefixes)
779		  (or (assq (intern (substring name 1)) math-units-table)
780		      (and (eq (aref name 0) ?M)
781			   (> (length name) 3)
782			   (eq (aref name 1) ?e)
783			   (eq (aref name 2) ?g)
784			   (assq (intern (substring name 3))
785				 math-units-table))))))))
786
787;; The variable math-which-standard is local to math-to-standard-units,
788;; but is used by math-to-standard-rec, which is called by
789;; math-to-standard-units.
790(defvar math-which-standard)
791
792(defun math-to-standard-units (expr math-which-standard)
793  (math-to-standard-rec expr))
794
795(defun math-to-standard-rec (expr)
796  (if (eq (car-safe expr) 'var)
797      (let ((u (math-check-unit-name expr))
798	    (base (nth 1 expr)))
799	(if u
800	    (progn
801	      (if (nth 1 u)
802		  (setq expr (math-to-standard-rec (nth 1 u)))
803		(let ((st (assq (car u) math-which-standard)))
804		  (if st
805		      (setq expr (nth 1 st))
806		    (setq expr (list 'var (car u)
807				     (intern (concat "var-"
808						     (symbol-name
809						      (car u)))))))))
810	      (or (null u)
811		  (eq base (car u))
812		  (setq expr (list '*
813				   (nth 1 (assq (aref (symbol-name base) 0)
814						math-unit-prefixes))
815				   expr)))
816	      expr)
817	  (if (eq base 'pi)
818	      (math-pi)
819	    expr)))
820    (if (Math-primp expr)
821	expr
822      (cons (car expr)
823	    (mapcar 'math-to-standard-rec (cdr expr))))))
824
825(defun math-apply-units (expr units ulist &optional pure)
826  (setq expr (math-simplify-units expr))
827  (if ulist
828      (let ((new 0)
829	    value)
830	(or (math-numberp expr)
831	    (error "Incompatible units"))
832	(while (cdr ulist)
833	  (setq value (math-div expr (nth 1 (car ulist)))
834		value (math-floor (let ((calc-internal-prec
835					 (1- calc-internal-prec)))
836				    (math-normalize value)))
837		new (math-add new (math-mul value (car (car ulist))))
838		expr (math-sub expr (math-mul value (nth 1 (car ulist))))
839		ulist (cdr ulist)))
840	(math-add new (math-mul (math-div expr (nth 1 (car ulist)))
841				(car (car ulist)))))
842    (if pure
843        expr
844      (math-simplify-units (list '* expr units)))))
845
846(defvar math-decompose-units-cache nil)
847(defun math-decompose-units (units)
848  (let ((u (math-check-unit-name units)))
849    (and u (eq (car-safe (nth 1 u)) '+)
850	 (setq units (nth 1 u))))
851  (setq units (calcFunc-expand units))
852  (and (eq (car-safe units) '+)
853       (let ((entry (list units calc-internal-prec calc-prefer-frac)))
854	 (or (equal entry (car math-decompose-units-cache))
855	     (let ((ulist nil)
856		   (utemp units)
857		   qty unit)
858	       (while (eq (car-safe utemp) '+)
859		 (setq ulist (cons (math-decompose-unit-part (nth 2 utemp))
860				   ulist)
861		       utemp (nth 1 utemp)))
862	       (setq ulist (cons (math-decompose-unit-part utemp) ulist)
863		     utemp ulist)
864	       (while (setq utemp (cdr utemp))
865		 (unless (equal (nth 2 (car utemp)) (nth 2 (car ulist)))
866		   (error "Inconsistent units in sum")))
867	       (setq math-decompose-units-cache
868		     (cons entry
869			   (sort ulist
870				 (function
871				  (lambda (x y)
872				    (not (Math-lessp (nth 1 x)
873						     (nth 1 y))))))))))
874	 (cdr math-decompose-units-cache))))
875
876(defun math-decompose-unit-part (unit)
877  (cons unit
878	(math-is-multiple (math-simplify-units (math-to-standard-units
879						unit nil))
880			  t)))
881
882;; The variable math-fcu-u is local to math-find-compatible-unit,
883;; but is used by math-find-compatible-rec which is called by
884;; math-find-compatible-unit.
885(defvar math-fcu-u)
886
887(defun math-find-compatible-unit (expr unit)
888  (let ((math-fcu-u (math-check-unit-name unit)))
889    (if math-fcu-u
890	(math-find-compatible-unit-rec expr 1))))
891
892(defun math-find-compatible-unit-rec (expr pow)
893  (cond ((eq (car-safe expr) '*)
894	 (or (math-find-compatible-unit-rec (nth 1 expr) pow)
895	     (math-find-compatible-unit-rec (nth 2 expr) pow)))
896	((eq (car-safe expr) '/)
897	 (or (math-find-compatible-unit-rec (nth 1 expr) pow)
898	     (math-find-compatible-unit-rec (nth 2 expr) (- pow))))
899	((and (eq (car-safe expr) '^)
900	      (integerp (nth 2 expr)))
901	 (math-find-compatible-unit-rec (nth 1 expr) (* pow (nth 2 expr))))
902	(t
903	 (let ((u2 (math-check-unit-name expr)))
904	   (if (equal (nth 4 math-fcu-u) (nth 4 u2))
905	       (cons expr pow))))))
906
907;; The variables math-cu-new-units and math-cu-pure are local to
908;; math-convert-units, but are used by math-convert-units-rec,
909;; which is called by math-convert-units.
910(defvar math-cu-new-units)
911(defvar math-cu-pure)
912
913(defun math-convert-units (expr math-cu-new-units &optional math-cu-pure)
914  (if (eq (car-safe math-cu-new-units) 'var)
915      (let ((unew (assq (nth 1 math-cu-new-units)
916                        (math-build-units-table))))
917        (if (eq (car-safe (nth 1 unew)) '+)
918            (setq math-cu-new-units (nth 1 unew)))))
919  (math-with-extra-prec 2
920    (let ((compat (and (not math-cu-pure)
921                       (math-find-compatible-unit expr math-cu-new-units)))
922	  (math-cu-unit-list nil)
923	  (math-combining-units nil))
924      (if compat
925	  (math-simplify-units
926	   (math-mul (math-mul (math-simplify-units
927				(math-div expr (math-pow (car compat)
928							 (cdr compat))))
929			       (math-pow math-cu-new-units (cdr compat)))
930		     (math-simplify-units
931		      (math-to-standard-units
932		       (math-pow (math-div (car compat) math-cu-new-units)
933				 (cdr compat))
934		       nil))))
935	(when (setq math-cu-unit-list (math-decompose-units math-cu-new-units))
936	  (setq math-cu-new-units (nth 2 (car math-cu-unit-list))))
937	(when (eq (car-safe expr) '+)
938	  (setq expr (math-simplify-units expr)))
939	(if (math-units-in-expr-p expr t)
940	    (math-convert-units-rec expr)
941	  (math-apply-units (math-to-standard-units
942			     (list '/ expr math-cu-new-units) nil)
943			    math-cu-new-units math-cu-unit-list math-cu-pure))))))
944
945(defun math-convert-units-rec (expr)
946  (if (math-units-in-expr-p expr nil)
947      (math-apply-units (math-to-standard-units
948                         (list '/ expr math-cu-new-units) nil)
949			math-cu-new-units math-cu-unit-list math-cu-pure)
950    (if (Math-primp expr)
951	expr
952      (cons (car expr)
953	    (mapcar 'math-convert-units-rec (cdr expr))))))
954
955(defun math-convert-temperature (expr old new &optional pure)
956  (let* ((units (math-single-units-in-expr-p expr))
957	 (uold (if old
958		   (if (or (null units)
959			   (equal (nth 1 old) (car units)))
960		       (math-check-unit-name old)
961		     (error "Inconsistent temperature units"))
962		 units))
963	 (unew (math-check-unit-name new)))
964    (unless (and (consp unew) (nth 3 unew))
965      (error "Not a valid temperature unit"))
966    (unless (and (consp uold) (nth 3 uold))
967      (error "Not a pure temperature expression"))
968    (let ((v (car uold)))
969      (setq expr (list '/ expr (list 'var v
970				     (intern (concat "var-"
971						     (symbol-name v)))))))
972    (or (eq (nth 3 uold) (nth 3 unew))
973	(cond ((eq (nth 3 uold) 'K)
974	       (setq expr (list '- expr '(float 27315 -2)))
975	       (if (eq (nth 3 unew) 'F)
976		   (setq expr (list '+ (list '* expr '(frac 9 5)) 32))))
977	      ((eq (nth 3 uold) 'C)
978	       (if (eq (nth 3 unew) 'F)
979		   (setq expr (list '+ (list '* expr '(frac 9 5)) 32))
980		 (setq expr (list '+ expr '(float 27315 -2)))))
981	      (t
982	       (setq expr (list '* (list '- expr 32) '(frac 5 9)))
983	       (if (eq (nth 3 unew) 'K)
984		   (setq expr (list '+ expr '(float 27315 -2)))))))
985    (if pure
986	expr
987      (list '* expr new))))
988
989
990
991(defun math-simplify-units (a)
992  (let ((math-simplifying-units t)
993	(calc-matrix-mode 'scalar))
994    (math-simplify a)))
995(defalias 'calcFunc-usimplify 'math-simplify-units)
996
997;; The function created by math-defsimplify uses the variable
998;; math-simplify-expr, and so is used by functions in math-defsimplify
999(defvar math-simplify-expr)
1000
1001(math-defsimplify (+ -)
1002  (and math-simplifying-units
1003       (math-units-in-expr-p (nth 1 math-simplify-expr) nil)
1004       (let* ((units (math-extract-units (nth 1 math-simplify-expr)))
1005	      (ratio (math-simplify (math-to-standard-units
1006				     (list '/ (nth 2 math-simplify-expr) units) nil))))
1007	 (if (math-units-in-expr-p ratio nil)
1008	     (progn
1009	       (calc-record-why "*Inconsistent units" math-simplify-expr)
1010	       math-simplify-expr)
1011	   (list '* (math-add (math-remove-units (nth 1 math-simplify-expr))
1012			      (if (eq (car math-simplify-expr) '-)
1013                                  (math-neg ratio) ratio))
1014		 units)))))
1015
1016(math-defsimplify *
1017  (math-simplify-units-prod))
1018
1019(defun math-simplify-units-prod ()
1020  (and math-simplifying-units
1021       calc-autorange-units
1022       (Math-realp (nth 1 math-simplify-expr))
1023       (let* ((num (math-float (nth 1 math-simplify-expr)))
1024	      (xpon (calcFunc-xpon num))
1025	      (unitp (cdr (cdr math-simplify-expr)))
1026	      (unit (car unitp))
1027	      (pow (if (eq (car math-simplify-expr) '*) 1 -1))
1028	      u)
1029	 (and (eq (car-safe unit) '*)
1030	      (setq unitp (cdr unit)
1031		    unit (car unitp)))
1032	 (and (eq (car-safe unit) '^)
1033	      (integerp (nth 2 unit))
1034	      (setq pow (* pow (nth 2 unit))
1035		    unitp (cdr unit)
1036		    unit (car unitp)))
1037	 (and (setq u (math-check-unit-name unit))
1038	      (integerp xpon)
1039	      (or (< xpon 0)
1040		  (>= xpon (if (eq (car u) 'm) 1 3)))
1041	      (let* ((uxpon 0)
1042		     (pref (if (< pow 0)
1043			       (reverse math-unit-prefixes)
1044			     math-unit-prefixes))
1045		     (p pref)
1046		     pxpon pname)
1047		(or (eq (car u) (nth 1 unit))
1048		    (setq uxpon (* pow
1049				   (nth 2 (nth 1 (assq
1050						  (aref (symbol-name
1051							 (nth 1 unit)) 0)
1052						  math-unit-prefixes))))))
1053		(setq xpon (+ xpon uxpon))
1054		(while (and p
1055			    (or (memq (car (car p)) '(?d ?D ?h ?H))
1056				(and (eq (car (car p)) ?c)
1057				     (not (eq (car u) 'm)))
1058				(< xpon (setq pxpon (* (nth 2 (nth 1 (car p)))
1059						       pow)))
1060				(progn
1061				  (setq pname (math-build-var-name
1062					       (if (eq (car (car p)) 0)
1063						   (car u)
1064						 (concat (char-to-string
1065							  (car (car p)))
1066							 (symbol-name
1067							  (car u))))))
1068				  (and (/= (car (car p)) 0)
1069				       (assq (nth 1 pname)
1070					     math-units-table)))))
1071		  (setq p (cdr p)))
1072		(and p
1073		     (/= pxpon uxpon)
1074		     (or (not (eq p pref))
1075			 (< xpon (+ pxpon (* (math-abs pow) 3))))
1076		     (progn
1077		       (setcar (cdr math-simplify-expr)
1078			       (let ((calc-prefer-frac nil))
1079				 (calcFunc-scf (nth 1 math-simplify-expr)
1080					       (- uxpon pxpon))))
1081		       (setcar unitp pname)
1082		       math-simplify-expr)))))))
1083
1084(defvar math-try-cancel-units)
1085
1086(math-defsimplify /
1087  (and math-simplifying-units
1088       (let ((np (cdr math-simplify-expr))
1089	     (math-try-cancel-units 0)
1090	     n nn)
1091	 (setq n (if (eq (car-safe (nth 2 math-simplify-expr)) '*)
1092		     (cdr (nth 2 math-simplify-expr))
1093		   (nthcdr 2 math-simplify-expr)))
1094	 (if (math-realp (car n))
1095	     (progn
1096	       (setcar (cdr math-simplify-expr) (math-mul (nth 1 math-simplify-expr)
1097					    (let ((calc-prefer-frac nil))
1098					      (math-div 1 (car n)))))
1099	       (setcar n 1)))
1100	 (while (eq (car-safe (setq n (car np))) '*)
1101	   (math-simplify-units-divisor (cdr n) (cdr (cdr math-simplify-expr)))
1102	   (setq np (cdr (cdr n))))
1103	 (math-simplify-units-divisor np (cdr (cdr math-simplify-expr)))
1104	 (if (eq math-try-cancel-units 0)
1105	     (let* ((math-simplifying-units nil)
1106		    (base (math-simplify
1107                           (math-to-standard-units math-simplify-expr nil))))
1108	       (if (Math-numberp base)
1109		   (setq math-simplify-expr base))))
1110	 (if (eq (car-safe math-simplify-expr) '/)
1111	     (math-simplify-units-prod))
1112	 math-simplify-expr)))
1113
1114(defun math-simplify-units-divisor (np dp)
1115  (let ((n (car np))
1116	d dd temp)
1117    (while (eq (car-safe (setq d (car dp))) '*)
1118      (when (setq temp (math-simplify-units-quotient n (nth 1 d)))
1119	(setcar np (setq n temp))
1120	(setcar (cdr d) 1))
1121      (setq dp (cdr (cdr d))))
1122    (when (setq temp (math-simplify-units-quotient n d))
1123      (setcar np (setq n temp))
1124      (setcar dp 1))))
1125
1126;; Simplify, e.g., "in / cm" to "2.54" in a units expression.
1127(defun math-simplify-units-quotient (n d)
1128  (let ((pow1 1)
1129	(pow2 1))
1130    (when (and (eq (car-safe n) '^)
1131	       (integerp (nth 2 n)))
1132      (setq pow1 (nth 2 n) n (nth 1 n)))
1133    (when (and (eq (car-safe d) '^)
1134	       (integerp (nth 2 d)))
1135      (setq pow2 (nth 2 d) d (nth 1 d)))
1136    (let ((un (math-check-unit-name n))
1137	  (ud (math-check-unit-name d)))
1138      (and un ud
1139	   (if (and (equal (nth 4 un) (nth 4 ud))
1140		    (eq pow1 pow2))
1141	       (math-to-standard-units (list '/ n d) nil)
1142	     (let (ud1)
1143	       (setq un (nth 4 un)
1144		     ud (nth 4 ud))
1145	       (while un
1146		 (setq ud1 ud)
1147		 (while ud1
1148		   (and (eq (car (car un)) (car (car ud1)))
1149			(setq math-try-cancel-units
1150			      (+ math-try-cancel-units
1151				 (- (* (cdr (car un)) pow1)
1152				    (* (cdr (car ud)) pow2)))))
1153		   (setq ud1 (cdr ud1)))
1154		 (setq un (cdr un)))
1155	       nil))))))
1156
1157(math-defsimplify ^
1158  (and math-simplifying-units
1159       (math-realp (nth 2 math-simplify-expr))
1160       (if (memq (car-safe (nth 1 math-simplify-expr)) '(* /))
1161	   (list (car (nth 1 math-simplify-expr))
1162		 (list '^ (nth 1 (nth 1 math-simplify-expr))
1163                       (nth 2 math-simplify-expr))
1164		 (list '^ (nth 2 (nth 1 math-simplify-expr))
1165                       (nth 2 math-simplify-expr)))
1166	 (math-simplify-units-pow (nth 1 math-simplify-expr)
1167                                  (nth 2 math-simplify-expr)))))
1168
1169(math-defsimplify calcFunc-sqrt
1170  (and math-simplifying-units
1171       (if (memq (car-safe (nth 1 math-simplify-expr)) '(* /))
1172	   (list (car (nth 1 math-simplify-expr))
1173		 (list 'calcFunc-sqrt (nth 1 (nth 1 math-simplify-expr)))
1174		 (list 'calcFunc-sqrt (nth 2 (nth 1 math-simplify-expr))))
1175	 (math-simplify-units-pow (nth 1 math-simplify-expr) '(frac 1 2)))))
1176
1177(math-defsimplify (calcFunc-floor
1178		   calcFunc-ceil
1179		   calcFunc-round
1180		   calcFunc-rounde
1181		   calcFunc-roundu
1182		   calcFunc-trunc
1183		   calcFunc-float
1184		   calcFunc-frac
1185		   calcFunc-abs
1186		   calcFunc-clean)
1187  (and math-simplifying-units
1188       (= (length math-simplify-expr) 2)
1189       (if (math-only-units-in-expr-p (nth 1 math-simplify-expr))
1190	   (nth 1 math-simplify-expr)
1191	 (if (and (memq (car-safe (nth 1 math-simplify-expr)) '(* /))
1192		  (or (math-only-units-in-expr-p
1193		       (nth 1 (nth 1 math-simplify-expr)))
1194		      (math-only-units-in-expr-p
1195		       (nth 2 (nth 1 math-simplify-expr)))))
1196	     (list (car (nth 1 math-simplify-expr))
1197		   (cons (car math-simplify-expr)
1198			 (cons (nth 1 (nth 1 math-simplify-expr))
1199			       (cdr (cdr math-simplify-expr))))
1200		   (cons (car math-simplify-expr)
1201			 (cons (nth 2 (nth 1 math-simplify-expr))
1202			       (cdr (cdr math-simplify-expr)))))))))
1203
1204(defun math-simplify-units-pow (a pow)
1205  (if (and (eq (car-safe a) '^)
1206	   (math-check-unit-name (nth 1 a))
1207	   (math-realp (nth 2 a)))
1208      (list '^ (nth 1 a) (math-mul pow (nth 2 a)))
1209    (let* ((u (math-check-unit-name a))
1210	   (pf (math-to-simple-fraction pow))
1211	   (d (and (eq (car-safe pf) 'frac) (nth 2 pf))))
1212      (and u d
1213	   (math-units-are-multiple u d)
1214	   (list '^ (math-to-standard-units a nil) pow)))))
1215
1216
1217(defun math-units-are-multiple (u n)
1218  (setq u (nth 4 u))
1219  (while (and u (= (% (cdr (car u)) n) 0))
1220    (setq u (cdr u)))
1221  (null u))
1222
1223(math-defsimplify calcFunc-sin
1224  (and math-simplifying-units
1225       (math-units-in-expr-p (nth 1 math-simplify-expr) nil)
1226       (let ((rad (math-simplify-units
1227		   (math-evaluate-expr
1228		    (math-to-standard-units (nth 1 math-simplify-expr) nil))))
1229	     (calc-angle-mode 'rad))
1230	 (and (eq (car-safe rad) '*)
1231	      (math-realp (nth 1 rad))
1232	      (eq (car-safe (nth 2 rad)) 'var)
1233	      (eq (nth 1 (nth 2 rad)) 'rad)
1234	      (list 'calcFunc-sin (nth 1 rad))))))
1235
1236(math-defsimplify calcFunc-cos
1237  (and math-simplifying-units
1238       (math-units-in-expr-p (nth 1 math-simplify-expr) nil)
1239       (let ((rad (math-simplify-units
1240		   (math-evaluate-expr
1241		    (math-to-standard-units (nth 1 math-simplify-expr) nil))))
1242	     (calc-angle-mode 'rad))
1243	 (and (eq (car-safe rad) '*)
1244	      (math-realp (nth 1 rad))
1245	      (eq (car-safe (nth 2 rad)) 'var)
1246	      (eq (nth 1 (nth 2 rad)) 'rad)
1247	      (list 'calcFunc-cos (nth 1 rad))))))
1248
1249(math-defsimplify calcFunc-tan
1250  (and math-simplifying-units
1251       (math-units-in-expr-p (nth 1 math-simplify-expr) nil)
1252       (let ((rad (math-simplify-units
1253		   (math-evaluate-expr
1254		    (math-to-standard-units (nth 1 math-simplify-expr) nil))))
1255	     (calc-angle-mode 'rad))
1256	 (and (eq (car-safe rad) '*)
1257	      (math-realp (nth 1 rad))
1258	      (eq (car-safe (nth 2 rad)) 'var)
1259	      (eq (nth 1 (nth 2 rad)) 'rad)
1260	      (list 'calcFunc-tan (nth 1 rad))))))
1261
1262(math-defsimplify calcFunc-sec
1263  (and math-simplifying-units
1264       (math-units-in-expr-p (nth 1 math-simplify-expr) nil)
1265       (let ((rad (math-simplify-units
1266		   (math-evaluate-expr
1267		    (math-to-standard-units (nth 1 math-simplify-expr) nil))))
1268	     (calc-angle-mode 'rad))
1269	 (and (eq (car-safe rad) '*)
1270	      (math-realp (nth 1 rad))
1271	      (eq (car-safe (nth 2 rad)) 'var)
1272	      (eq (nth 1 (nth 2 rad)) 'rad)
1273	      (list 'calcFunc-sec (nth 1 rad))))))
1274
1275(math-defsimplify calcFunc-csc
1276  (and math-simplifying-units
1277       (math-units-in-expr-p (nth 1 math-simplify-expr) nil)
1278       (let ((rad (math-simplify-units
1279		   (math-evaluate-expr
1280		    (math-to-standard-units (nth 1 math-simplify-expr) nil))))
1281	     (calc-angle-mode 'rad))
1282	 (and (eq (car-safe rad) '*)
1283	      (math-realp (nth 1 rad))
1284	      (eq (car-safe (nth 2 rad)) 'var)
1285	      (eq (nth 1 (nth 2 rad)) 'rad)
1286	      (list 'calcFunc-csc (nth 1 rad))))))
1287
1288(math-defsimplify calcFunc-cot
1289  (and math-simplifying-units
1290       (math-units-in-expr-p (nth 1 math-simplify-expr) nil)
1291       (let ((rad (math-simplify-units
1292		   (math-evaluate-expr
1293		    (math-to-standard-units (nth 1 math-simplify-expr) nil))))
1294	     (calc-angle-mode 'rad))
1295	 (and (eq (car-safe rad) '*)
1296	      (math-realp (nth 1 rad))
1297	      (eq (car-safe (nth 2 rad)) 'var)
1298	      (eq (nth 1 (nth 2 rad)) 'rad)
1299	      (list 'calcFunc-cot (nth 1 rad))))))
1300
1301
1302(defun math-remove-units (expr)
1303  (if (math-check-unit-name expr)
1304      1
1305    (if (Math-primp expr)
1306	expr
1307      (cons (car expr)
1308	    (mapcar 'math-remove-units (cdr expr))))))
1309
1310(defun math-extract-units (expr)
1311  (if (memq (car-safe expr) '(* /))
1312      (cons (car expr)
1313	    (mapcar 'math-extract-units (cdr expr)))
1314    (if (math-check-unit-name expr) expr 1)))
1315
1316(defun math-build-units-table-buffer (enter-buffer)
1317  (if (not (and math-units-table math-units-table-buffer-valid
1318		(get-buffer "*Units Table*")))
1319      (let ((buf (get-buffer-create "*Units Table*"))
1320	    (uptr (math-build-units-table))
1321	    (calc-language (if (eq calc-language 'big) nil calc-language))
1322	    (calc-float-format '(float 0))
1323	    (calc-group-digits nil)
1324	    (calc-number-radix 10)
1325	    (calc-point-char ".")
1326	    (std nil)
1327	    u name shadowed)
1328	(save-excursion
1329	  (message "Formatting units table...")
1330	  (set-buffer buf)
1331          (let ((inhibit-read-only t))
1332            (erase-buffer)
1333            (insert "Calculator Units Table:\n\n")
1334            (insert "Unit    Type  Definition                  Description\n\n")
1335            (while uptr
1336              (setq u (car uptr)
1337                    name (nth 2 u))
1338              (when (eq (car u) 'm)
1339                (setq std t))
1340              (setq shadowed (and std (assq (car u) math-additional-units)))
1341              (when (and name
1342                         (> (length name) 1)
1343                         (eq (aref name 0) ?\*))
1344                (unless (eq uptr math-units-table)
1345                  (insert "\n"))
1346                (setq name (substring name 1)))
1347              (insert " ")
1348              (and shadowed (insert "("))
1349              (insert (symbol-name (car u)))
1350              (and shadowed (insert ")"))
1351              (if (nth 3 u)
1352                  (progn
1353                    (indent-to 10)
1354                    (insert (symbol-name (nth 3 u))))
1355                (or std
1356                    (progn
1357                      (indent-to 10)
1358                      (insert "U"))))
1359              (indent-to 14)
1360              (and shadowed (insert "("))
1361              (if (nth 1 u)
1362                  (insert (math-format-value (nth 1 u) 80))
1363                (insert (symbol-name (car u))))
1364              (and shadowed (insert ")"))
1365              (indent-to 41)
1366              (insert " ")
1367              (when name
1368                (insert name))
1369              (if shadowed
1370                  (insert " (redefined above)")
1371                (unless (nth 1 u)
1372                  (insert " (base unit)")))
1373              (insert "\n")
1374              (setq uptr (cdr uptr)))
1375            (insert "\n\nUnit Prefix Table:\n\n")
1376            (setq uptr math-unit-prefixes)
1377            (while uptr
1378              (setq u (car uptr))
1379              (insert " " (char-to-string (car u)))
1380              (if (equal (nth 1 u) (nth 1 (nth 1 uptr)))
1381                  (insert " " (char-to-string (car (car (setq uptr (cdr uptr)))))
1382                          "   ")
1383                (insert "     "))
1384              (insert "10^" (int-to-string (nth 2 (nth 1 u))))
1385              (indent-to 15)
1386              (insert "   " (nth 2 u) "\n")
1387              (while (eq (car (car (setq uptr (cdr uptr)))) 0)))
1388            (insert "\n"))
1389	  (view-mode)
1390	  (message "Formatting units table...done"))
1391	(setq math-units-table-buffer-valid t)
1392	(let ((oldbuf (current-buffer)))
1393	  (set-buffer buf)
1394	  (goto-char (point-min))
1395	  (set-buffer oldbuf))
1396	(if enter-buffer
1397	    (pop-to-buffer buf)
1398	  (display-buffer buf)))
1399    (if enter-buffer
1400	(pop-to-buffer (get-buffer "*Units Table*"))
1401      (display-buffer (get-buffer "*Units Table*")))))
1402
1403(provide 'calc-units)
1404
1405;; Local Variables:
1406;; coding: iso-latin-1
1407;; End:
1408
1409;;; arch-tag: e993314f-3adc-4191-be61-4ef8874881c4
1410;;; calc-units.el ends here
1411