1 ;;; calc-units.el --- unit conversion functions for Calc
3 ;; Copyright (C) 1990, 1991, 1992, 1993, 2001 Free Software Foundation, Inc.
5 ;; Author: David Gillespie <daveg@synaptics.com>
6 ;; Maintainers: D. Goel <deego@gnufans.org>
7 ;; Colin Walters <walters@debian.org>
9 ;; This file is part of GNU Emacs.
11 ;; GNU Emacs is distributed in the hope that it will be useful,
12 ;; but WITHOUT ANY WARRANTY. No author or distributor
13 ;; accepts responsibility to anyone for the consequences of using it
14 ;; or for whether it serves any particular purpose or works at all,
15 ;; unless he says so in writing. Refer to the GNU Emacs General Public
16 ;; License for full details.
18 ;; Everyone is granted permission to copy, modify and redistribute
19 ;; GNU Emacs, but only under the conditions described in the
20 ;; GNU Emacs General Public License. A copy of this license is
21 ;; supposed to have been given to you along with GNU Emacs so you
22 ;; can know your rights and responsibilities. It should be in a
23 ;; file named COPYING. Among other things, the copyright notice
24 ;; and this notice must be preserved on all copies.
30 ;; This file is autoloaded from calc-ext.el.
35 (defun calc-Need-calc-units () nil
)
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
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 ;;; - http://physics.nist.gov/cuu/Constants/index.html
48 (defvar math-standard-units
51 ( in
"2.54 cm" "Inch" )
54 ( mi
"5280 ft" "Mile" )
55 ( au
"149597870691 m" "Astronomical Unit" ) ;; NASA JPL (http://neo.jpl.nasa.gov/glossary/au.html)
56 ( lyr
"9460536207068016 m" "Light Year" )
57 ( pc
"206264.80625 au" "Parsec" )
58 ( nmi
"1852 m" "Nautical Mile" )
59 ( fath
"6 ft" "Fathom" )
61 ( mil
"in/1000" "Mil" )
62 ( point
"in/72" "Point (1/72 inch)" )
63 ( tpt
"in/72.27" "Point (TeX conventions)" )
64 ( Ang
"1e-10 m" "Angstrom" )
65 ( mfi
"mi+ft+in" "Miles + feet + inches" )
68 ( hect
"10000 m^2" "*Hectare" )
69 ( acre
"mi^2 / 640" "Acre" )
70 ( b
"1e-28 m^2" "Barn" )
73 ( l
"1e-3 m^3" "*Liter" )
74 ( L
"1e-3 m^3" "Liter" )
75 ( gal
"4 qt" "US Gallon" )
78 ( cup
"8 ozfl" "Cup" )
79 ( ozfl
"2 tbsp" "Fluid Ounce" )
80 ( floz
"2 tbsp" "Fluid Ounce" )
81 ( tbsp
"3 tsp" "Tablespoon" )
82 ( tsp
"4.92892159375 ml" "Teaspoon" )
83 ( vol
"tsp+tbsp+ozfl+cup+pt+qt+gal" "Gallons + ... + teaspoons" )
84 ( galC
"4.54609 l" "Canadian Gallon" )
85 ( galUK
"4.546092 l" "UK Gallon" )
90 ( min
"60 s" "Minute" )
91 ( hr
"60 min" "Hour" )
94 ( hms
"wk+day+hr+min+s" "Hours, minutes, seconds" )
95 ( yr
"365.25 day" "Year" )
99 ( mph
"mi/hr" "*Miles per hour" )
100 ( kph
"km/hr" "Kilometers per hour" )
101 ( knot
"nmi/hr" "Knot" )
102 ( c
"2.99792458e8 m/s" "Speed of light" )
105 ( ga
"9.80665 m/s^2" "*\"g\" acceleration" )
109 ( lb
"16 oz" "Pound (mass)" )
110 ( oz
"28.349523125 g" "Ounce (mass)" )
111 ( ton
"2000 lb" "Ton" )
112 ( tpo
"ton+lb+oz" "Tons + pounds + ounces (mass)" )
113 ( t
"1000 kg" "Metric ton" )
114 ( tonUK
"1016.0469088 kg" "UK ton" )
115 ( lbt
"12 ozt" "Troy pound" )
116 ( ozt
"31.103475 g" "Troy ounce" )
117 ( ct
".2 g" "Carat" )
118 ( amu
"1.66053873e-27 kg" "Unified atomic mass" ) ;; CODATA 1998
121 ( N
"m kg/s^2" "*Newton" )
122 ( dyn
"1e-5 N" "Dyne" )
123 ( gf
"ga g" "Gram (force)" )
124 ( lbf
"4.44822161526 N" "Pound (force)" )
125 ( kip
"1000 lbf" "Kilopound (force)" )
126 ( pdl
"0.138255 N" "Poundal" )
130 ( erg
"1e-7 J" "Erg" )
131 ( cal
"4.1868 J" "International Table Calorie" )
132 ( Btu
"1055.05585262 J" "International Table Btu" )
133 ( eV
"ech V" "Electron volt" )
134 ( ev
"eV" "Electron volt" )
135 ( therm
"105506000 J" "EEC therm" )
136 ( invcm
"h c/cm" "Energy in inverse centimeters" )
137 ( Kayser
"invcm" "Kayser (inverse centimeter energy)" )
138 ( men
"100/invcm" "Inverse energy in meters" )
139 ( Hzen
"h Hz" "Energy in Hertz")
140 ( Ken
"k K" "Energy in Kelvins")
141 ( Wh
"W h" "Watt hour")
142 ( Ws
"W s" "Watt second")
146 ( hp
"745.7 W" "Horsepower" )
149 ( K nil
"*Degree Kelvin" K
)
150 ( dK
"K" "Degree Kelvin" K
)
151 ( degK
"K" "Degree Kelvin" K
)
152 ( dC
"K" "Degree Celsius" C
)
153 ( degC
"K" "Degree Celsius" C
)
154 ( dF
"(5/9) K" "Degree Fahrenheit" F
)
155 ( degF
"(5/9) K" "Degree Fahrenheit" F
)
158 ( Pa
"N/m^2" "*Pascal" )
159 ( bar
"1e5 Pa" "Bar" )
160 ( atm
"101325 Pa" "Standard atmosphere" )
161 ( torr
" 1.333224e2 Pa" "Torr" ) ;; NIST (http://physics.nist.gov/Pubs/SP811/appenB9.html)
162 ( mHg
"1000 torr" "Meter of mercury" )
163 ( inHg
"25.4 mmHg" "Inch of mercury" )
164 ( inH2O
"2.490889e2 Pa" "Inch of water" ) ;; NIST (http://physics.nist.gov/Pubs/SP811/appenB9.html)
165 ( psi
"6894.75729317 Pa" "Pound per square inch" )
168 ( P
"0.1 Pa s" "*Poise" )
169 ( St
"1e-4 m^2/s" "Stokes" )
173 ( C
"A s" "Coulomb" )
174 ( Fdy
"ech Nav" "Faraday" )
175 ( e
"1.602176462e-19 C" "Elementary charge" ) ;; CODATA 1998
176 ( ech
"1.602176462e-19 C" "Elementary charge" ) ;; CODATA 1998
180 ( S
"A/V" "Siemens" )
183 ( T
"Wb/m^2" "Tesla" )
184 ( G
"1e-4 T" "Gauss" )
187 ;; Luminous intensity
188 ( cd nil
"*Candela" )
189 ( sb
"1e4 cd/m^2" "Stilb" )
190 ( lm
"cd sr" "Lumen" )
191 ( lx
"lm/m^2" "Lux" )
192 ( ph
"1e4 lx" "Phot" )
193 ( fc
"10.76391 lx" "Footcandle" ) ;; NIST (http://physics.nist.gov/Pubs/SP811/appenB9.html)
194 ( lam
"1e4 lm/m^2" "Lambert" )
195 ( flam
"3.426259 cd/m^2" "Footlambert" ) ;; NIST (http://physics.nist.gov/Pubs/SP811/appenB9.html)
198 ( Bq
"1/s" "*Becquerel" )
199 ( Ci
"3.7e10 Bq" "Curie" )
201 ( Sv
"Gy" "Sievert" )
202 ( R
"2.58e-4 C/kg" "Roentgen" )
203 ( rd
".01 Gy" "Rad" )
206 ;; Amount of substance
210 ( rad nil
"*Radian" )
211 ( circ
"2 pi rad" "Full circle" )
212 ( rev
"circ" "Full revolution" )
213 ( deg
"circ/360" "Degree" )
214 ( arcmin
"deg/60" "Arc minute" )
215 ( arcsec
"arcmin/60" "Arc second" )
216 ( grad
"circ/400" "Grade" )
217 ( rpm
"rev/min" "Revolutions per minute" )
220 ( sr nil
"*Steradian" )
222 ;; Other physical quantities (CODATA 1998)
223 ( h
"6.62606876e-34 J s" "*Planck's constant" )
224 ( hbar
"h / 2 pi" "Planck's constant" )
225 ( mu0
"4 pi 1e-7 H/m" "Permeability of vacuum" )
226 ( Grav
"6.673e-11 m^3/kg^1/s^2" "Gravitational constant" )
227 ( Nav
"6.02214199e23 / mol" "Avagadro's constant" )
228 ( me
"9.10938188e-31 kg" "Electron rest mass" )
229 ( mp
"1.67262158e-27 kg" "Proton rest mass" )
230 ( mn
"1.67492716e-27 kg" "Neutron rest mass" )
231 ( mu
"1.88353109e-28 kg" "Muon rest mass" )
232 ( Ryd
"10973731.568549 /m" "Rydberg's constant" )
233 ( k
"1.3806503e-23 J/K" "Boltzmann's constant" )
234 ( fsc
"7.297352533e-3" "Fine structure constant" )
235 ( muB
"927.400899e-26 J/T" "Bohr magneton" )
236 ( muN
"5.05078317e-27 J/T" "Nuclear magneton" )
237 ( mue
"-928.476362e-26 J/T" "Electron magnetic moment" )
238 ( mup
"1.410606633e-26 J/T" "Proton magnetic moment" )
239 ( R0
"8.314472 J/mol/K" "Molar gas constant" )
240 ( V0
"22.710981e-3 m^3/mol" "Standard volume of ideal gas" )))
243 (defvar math-additional-units nil
244 "*Additional units table for user-defined units.
245 Must be formatted like math-standard-units.
246 If this is changed, be sure to set math-units-table to nil to ensure
247 that the combined units table will be rebuilt.")
249 (defvar math-unit-prefixes
250 '( ( ?E
(float 1 18) "Exa" )
251 ( ?P
(float 1 15) "Peta" )
252 ( ?T
(float 1 12) "Tera" )
253 ( ?G
(float 1 9) "Giga" )
254 ( ?M
(float 1 6) "Mega" )
255 ( ?k
(float 1 3) "Kilo" )
256 ( ?K
(float 1 3) "Kilo" )
257 ( ?h
(float 1 2) "Hecto" )
258 ( ?H
(float 1 2) "Hecto" )
259 ( ?D
(float 1 1) "Deka" )
260 ( 0 (float 1 0) nil
)
261 ( ?d
(float 1 -
1) "Deci" )
262 ( ?c
(float 1 -
2) "Centi" )
263 ( ?m
(float 1 -
3) "Milli" )
264 ( ?u
(float 1 -
6) "Micro" )
265 ( ?n
(float 1 -
9) "Nano" )
266 ( ?p
(float 1 -
12) "Pico" )
267 ( ?f
(float 1 -
15) "Femto" )
268 ( ?a
(float 1 -
18) "Atto" )))
270 (defvar math-standard-units-systems
272 ( si
( ( g
'(* (var kg var-kg
) (float 1 -
3)) ) ) )
273 ( mks
( ( g
'(* (var kg var-kg
) (float 1 -
3)) ) ) )
274 ( cgs
( ( m
'(* (var cm var-cm
) 100 ) ) ) )))
276 (defvar math-units-table nil
277 "Internal units table derived from math-defined-units.
278 Entries are (SYMBOL EXPR DOC-STRING TEMP-TYPE BASE-UNITS).")
280 (defvar math-units-table-buffer-valid nil
)
284 (defun calc-base-units ()
287 (let ((calc-autorange-units nil
))
288 (calc-enter-result 1 "bsun" (math-simplify-units
289 (math-to-standard-units (calc-top-n 1)
292 (defun calc-quick-units ()
295 (let* ((num (- last-command-char ?
0))
296 (pos (if (= num
0) 10 num
))
297 (units (calc-var-value 'var-Units
))
298 (expr (calc-top-n 1)))
299 (unless (and (>= num
0) (<= num
9))
300 (errunless "Bad unit number"))
301 (unless (math-vectorp units
)
302 (errunless "No \"quick units\" are defined"))
303 (unless (< pos
(length units
))
304 (errunless "Unit number %d not defined" pos
))
305 (if (math-units-in-expr-p expr nil
)
306 (calc-enter-result 1 (format "cun%d" num
)
307 (math-convert-units expr
(nth pos units
)))
308 (calc-enter-result 1 (format "*un%d" num
)
310 (math-mul expr
(nth pos units
))))))))
312 (defun calc-convert-units (&optional old-units new-units
)
315 (let ((expr (calc-top-n 1))
318 (unless (math-units-in-expr-p expr t
)
319 (let ((uold (or old-units
321 (setq uoldname
(read-string "Old units: "))
322 (if (equal uoldname
"")
326 (if (string-match "\\` */" uoldname
)
327 (setq uoldname
(concat "1" uoldname
)))
328 (math-read-expr uoldname
))))))
329 (when (eq (car-safe uold
) 'error
)
330 (error "Bad format in units expression: %s" (nth 1 uold
)))
331 (setq expr
(math-mul expr uold
))))
333 (setq new-units
(read-string (if uoldname
334 (concat "Old units: "
338 (when (string-match "\\` */" new-units
)
339 (setq new-units
(concat "1" new-units
)))
340 (setq units
(math-read-expr new-units
))
341 (when (eq (car-safe units
) 'error
)
342 (error "Bad format in units expression: %s" (nth 2 units
)))
343 (let ((unew (math-units-in-expr-p units t
))
344 (std (and (eq (car-safe units
) 'var
)
345 (assq (nth 1 units
) math-standard-units-systems
))))
347 (calc-enter-result 1 "cvun" (math-simplify-units
348 (math-to-standard-units expr
351 (error "No units specified"))
352 (calc-enter-result 1 "cvun"
355 (and uoldname
(not (equal uoldname
"1"))))))))))
357 (defun calc-autorange-units (arg)
360 (calc-change-mode 'calc-autorange-units arg nil t
)
361 (message (if calc-autorange-units
362 "Adjusting target unit prefix automatically"
363 "Using target units exactly"))))
365 (defun calc-convert-temperature (&optional old-units new-units
)
368 (let ((expr (calc-top-n 1))
372 (setq uold
(or old-units
373 (let ((units (math-single-units-in-expr-p expr
)))
376 (list 'var
(car units
)
377 (intern (concat "var-"
380 (error "Not a pure temperature expression"))
382 (setq uoldname
(read-string
383 "Old temperature units: ")))))))
384 (when (eq (car-safe uold
) 'error
)
385 (error "Bad format in units expression: %s" (nth 2 uold
)))
386 (or (math-units-in-expr-p expr nil
)
387 (setq expr
(math-mul expr uold
)))
388 (setq unew
(or new-units
390 (read-string (if uoldname
391 (concat "Old temperature units: "
394 "New temperature units: ")))))
395 (when (eq (car-safe unew
) 'error
)
396 (error "Bad format in units expression: %s" (nth 2 unew
)))
397 (calc-enter-result 1 "cvtm" (math-simplify-units
398 (math-convert-temperature expr uold unew
401 (defun calc-remove-units ()
404 (calc-enter-result 1 "rmun" (math-simplify-units
405 (math-remove-units (calc-top-n 1))))))
407 (defun calc-extract-units ()
410 (calc-enter-result 1 "rmun" (math-simplify-units
411 (math-extract-units (calc-top-n 1))))))
413 (defun calc-explain-units ()
416 (let ((num-units nil
)
418 (calc-explain-units-rec (calc-top-n 1) 1)
419 (and den-units
(string-match "^[^(].* .*[^)]$" den-units
)
420 (setq den-units
(concat "(" den-units
")")))
423 (message "%s per %s" num-units den-units
)
424 (message "%s" num-units
))
426 (message "1 per %s" den-units
)
427 (message "No units in expression"))))))
429 (defun calc-explain-units-rec (expr pow
)
430 (let ((u (math-check-unit-name expr
))
432 (if (and u
(not (math-zerop pow
)))
433 (let ((name (or (nth 2 u
) (symbol-name (car u
)))))
434 (if (eq (aref name
0) ?\
*)
435 (setq name
(substring name
1)))
436 (if (string-match "[^a-zA-Z0-9']" name
)
437 (if (string-match "^[a-zA-Z0-9' ()]*$" name
)
438 (while (setq pos
(string-match "[ ()]" name
))
439 (setq name
(concat (substring name
0 pos
)
440 (if (eq (aref name pos
) 32) "-" "")
441 (substring name
(1+ pos
)))))
442 (setq name
(concat "(" name
")"))))
443 (or (eq (nth 1 expr
) (car u
))
444 (setq name
(concat (nth 2 (assq (aref (symbol-name
447 (if (and (string-match "[^a-zA-Z0-9']" name
)
448 (not (memq (car u
) '(mHg gf
))))
451 (cond ((or (math-equal-int pow
1)
452 (math-equal-int pow -
1)))
453 ((or (math-equal-int pow
2)
454 (math-equal-int pow -
2))
455 (if (equal (nth 4 u
) '((m .
1)))
456 (setq name
(concat "Square-" name
))
457 (setq name
(concat name
"-squared"))))
458 ((or (math-equal-int pow
3)
459 (math-equal-int pow -
3))
460 (if (equal (nth 4 u
) '((m .
1)))
461 (setq name
(concat "Cubic-" name
))
462 (setq name
(concat name
"-cubed"))))
464 (setq name
(concat name
"^"
465 (math-format-number (math-abs pow
))))))
467 (setq num-units
(if num-units
468 (concat num-units
" " name
)
470 (setq den-units
(if den-units
471 (concat den-units
" " name
)
473 (cond ((eq (car-safe expr
) '*)
474 (calc-explain-units-rec (nth 1 expr
) pow
)
475 (calc-explain-units-rec (nth 2 expr
) pow
))
476 ((eq (car-safe expr
) '/)
477 (calc-explain-units-rec (nth 1 expr
) pow
)
478 (calc-explain-units-rec (nth 2 expr
) (- pow
)))
479 ((memq (car-safe expr
) '(neg + -
))
480 (calc-explain-units-rec (nth 1 expr
) pow
))
481 ((and (eq (car-safe expr
) '^
)
482 (math-realp (nth 2 expr
)))
483 (calc-explain-units-rec (nth 1 expr
)
484 (math-mul pow
(nth 2 expr
))))))))
486 (defun calc-simplify-units ()
489 (calc-with-default-simplification
490 (calc-enter-result 1 "smun" (math-simplify-units (calc-top-n 1))))))
492 (defun calc-view-units-table (n)
494 (and n
(setq math-units-table-buffer-valid nil
))
495 (let ((win (get-buffer-window "*Units Table*")))
498 math-units-table-buffer-valid
)
500 (bury-buffer (window-buffer win
))
501 (let ((curwin (selected-window)))
503 (switch-to-buffer nil
)
504 (select-window curwin
)))
505 (math-build-units-table-buffer nil
))))
507 (defun calc-enter-units-table (n)
509 (and n
(setq math-units-table-buffer-valid nil
))
510 (math-build-units-table-buffer t
)
511 (message (substitute-command-keys "Type \\[calc] to return to the Calculator")))
513 (defun calc-define-unit (uname desc
)
514 (interactive "SDefine unit name: \nsDescription: ")
516 (let ((form (calc-top-n 1))
517 (unit (assq uname math-additional-units
)))
519 (setq math-additional-units
520 (cons (setq unit
(list uname nil nil
))
521 math-additional-units
)
522 math-units-table nil
))
523 (setcar (cdr unit
) (and (not (and (eq (car-safe form
) 'var
)
524 (eq (nth 1 form
) uname
)))
525 (not (math-equal-int form
1))
526 (math-format-flat-expr form
0)))
527 (setcar (cdr (cdr unit
)) (and (not (equal desc
""))
529 (calc-invalidate-units-table))
531 (defun calc-undefine-unit (uname)
532 (interactive "SUndefine unit name: ")
534 (let ((unit (assq uname math-additional-units
)))
536 (if (assq uname math-standard-units
)
537 (error "\"%s\" is a predefined unit name" uname
)
538 (error "Unit name \"%s\" not found" uname
)))
539 (setq math-additional-units
(delq unit math-additional-units
)
540 math-units-table nil
)))
541 (calc-invalidate-units-table))
543 (defun calc-invalidate-units-table ()
544 (setq math-units-table nil
)
545 (let ((buf (get-buffer "*Units Table*")))
550 (goto-char (point-min))
551 (if (looking-at "Calculator Units Table")
552 (let ((buffer-read-only nil
))
553 (insert "(Obsolete) "))))))))
555 (defun calc-get-unit-definition (uname)
556 (interactive "SGet definition for unit: ")
558 (math-build-units-table)
559 (let ((unit (assq uname math-units-table
)))
561 (error "Unit name \"%s\" not found" uname
))
562 (let ((msg (nth 2 unit
)))
564 (if (string-match "^\\*" msg
)
565 (setq msg
(substring msg
1)))
566 (setq msg
(symbol-name uname
)))
569 (calc-enter-result 0 "ugdf" (nth 1 unit
))
570 (message "Derived unit: %s" msg
))
571 (calc-enter-result 0 "ugdf" (list 'var uname
574 (symbol-name uname
)))))
575 (message "Base unit: %s" msg
))))))
577 (defun calc-permanent-units ()
581 (set-buffer (find-file-noselect (substitute-in-file-name
582 calc-settings-file
)))
583 (goto-char (point-min))
584 (if (and (search-forward ";;; Custom units stored by Calc" nil t
)
588 (search-forward "\n;;; End of custom units" nil t
)))
592 (delete-region pos
(point)))
593 (goto-char (point-max))
596 (insert ";;; Custom units stored by Calc on " (current-time-string) "\n")
597 (if math-additional-units
599 (insert "(setq math-additional-units '(\n")
600 (let ((list math-additional-units
))
602 (insert " (" (symbol-name (car (car list
))) " "
603 (if (nth 1 (car list
))
604 (if (stringp (nth 1 (car list
)))
605 (prin1-to-string (nth 1 (car list
)))
606 (prin1-to-string (math-format-flat-expr
607 (nth 1 (car list
)) 0)))
610 (prin1-to-string (nth 2 (car list
)))
612 (setq list
(cdr list
))))
614 (insert ";;; (no custom units defined)\n"))
615 (insert ";;; End of custom units\n")
620 (defun math-build-units-table ()
622 (let* ((combined-units (append math-additional-units
623 math-standard-units
))
624 (unit-list (mapcar 'car combined-units
))
626 (message "Building units table...")
627 (setq math-units-table-buffer-valid nil
)
628 (setq tab
(mapcar (function
632 (if (stringp (nth 1 x
))
633 (let ((exp (math-read-plain-expr
635 (if (eq (car-safe exp
) 'error
)
636 (error "Format error in definition of %s in units table: %s"
643 (list (cons (car x
) 1))))))
645 (let ((math-units-table tab
))
646 (mapcar 'math-find-base-units tab
))
647 (message "Building units table...done")
648 (setq math-units-table tab
))))
650 (defun math-find-base-units (entry)
651 (if (eq (nth 4 entry
) 'boom
)
652 (error "Circular definition involving unit %s" (car entry
)))
655 (setcar (nthcdr 4 entry
) 'boom
)
656 (math-find-base-units-rec (nth 1 entry
) 1)
658 (error "Dimensionless definition for unit %s" (car entry
)))
659 (while (eq (cdr (car base
)) 0)
660 (setq base
(cdr base
)))
663 (if (eq (cdr (car (cdr b
))) 0)
664 (setcdr b
(cdr (cdr b
)))
666 (setq base
(sort base
'math-compare-unit-names
))
667 (setcar (nthcdr 4 entry
) base
)
670 (defun math-compare-unit-names (a b
)
671 (memq (car b
) (cdr (memq (car a
) unit-list
))))
673 (defun math-find-base-units-rec (expr pow
)
674 (let ((u (math-check-unit-name expr
)))
676 (let ((ulist (math-find-base-units u
)))
678 (let ((p (* (cdr (car ulist
)) pow
))
679 (old (assq (car (car ulist
)) base
)))
681 (setcdr old
(+ (cdr old
) p
))
682 (setq base
(cons (cons (car (car ulist
)) p
) base
))))
683 (setq ulist
(cdr ulist
)))))
684 ((math-scalarp expr
))
685 ((and (eq (car expr
) '^
)
686 (integerp (nth 2 expr
)))
687 (math-find-base-units-rec (nth 1 expr
) (* pow
(nth 2 expr
))))
689 (math-find-base-units-rec (nth 1 expr
) pow
)
690 (math-find-base-units-rec (nth 2 expr
) pow
))
692 (math-find-base-units-rec (nth 1 expr
) pow
)
693 (math-find-base-units-rec (nth 2 expr
) (- pow
)))
694 ((eq (car expr
) 'neg
)
695 (math-find-base-units-rec (nth 1 expr
) pow
))
697 (math-find-base-units-rec (nth 1 expr
) pow
))
698 ((eq (car expr
) 'var
)
699 (or (eq (nth 1 expr
) 'pi
)
700 (error "Unknown name %s in defining expression for unit %s"
701 (nth 1 expr
) (car entry
))))
702 (t (error "Malformed defining expression for unit %s" (car entry
))))))
705 (defun math-units-in-expr-p (expr sub-exprs
)
707 (if (eq (car expr
) 'var
)
708 (math-check-unit-name expr
)
710 (memq (car expr
) '(* / ^
)))
711 (or (math-units-in-expr-p (nth 1 expr
) sub-exprs
)
712 (math-units-in-expr-p (nth 2 expr
) sub-exprs
))))))
714 (defun math-only-units-in-expr-p (expr)
716 (if (eq (car expr
) 'var
)
717 (math-check-unit-name expr
)
718 (if (memq (car expr
) '(* /))
719 (and (math-only-units-in-expr-p (nth 1 expr
))
720 (math-only-units-in-expr-p (nth 2 expr
)))
721 (and (eq (car expr
) '^
)
722 (and (math-only-units-in-expr-p (nth 1 expr
))
723 (math-realp (nth 2 expr
))))))))
725 (defun math-single-units-in-expr-p (expr)
726 (cond ((math-scalarp expr
) nil
)
727 ((eq (car expr
) 'var
)
728 (math-check-unit-name expr
))
730 (let ((u1 (math-single-units-in-expr-p (nth 1 expr
)))
731 (u2 (math-single-units-in-expr-p (nth 2 expr
))))
732 (or (and u1 u2
'wrong
)
736 (if (math-units-in-expr-p (nth 2 expr
) nil
)
738 (math-single-units-in-expr-p (nth 1 expr
))))
741 (defun math-check-unit-name (v)
742 (and (eq (car-safe v
) 'var
)
743 (or (assq (nth 1 v
) (or math-units-table
(math-build-units-table)))
744 (let ((name (symbol-name (nth 1 v
))))
745 (and (> (length name
) 1)
746 (assq (aref name
0) math-unit-prefixes
)
747 (or (assq (intern (substring name
1)) math-units-table
)
748 (and (eq (aref name
0) ?M
)
750 (eq (aref name
1) ?e
)
751 (eq (aref name
2) ?g
)
752 (assq (intern (substring name
3))
753 math-units-table
))))))))
756 (defun math-to-standard-units (expr which-standard
)
757 (math-to-standard-rec expr
))
759 (defun math-to-standard-rec (expr)
760 (if (eq (car-safe expr
) 'var
)
761 (let ((u (math-check-unit-name expr
))
766 (setq expr
(math-to-standard-rec (nth 1 u
)))
767 (let ((st (assq (car u
) which-standard
)))
769 (setq expr
(nth 1 st
))
770 (setq expr
(list 'var
(car u
)
771 (intern (concat "var-"
777 (nth 1 (assq (aref (symbol-name base
) 0)
784 (if (Math-primp expr
)
787 (mapcar 'math-to-standard-rec
(cdr expr
))))))
789 (defun math-apply-units (expr units ulist
&optional pure
)
793 (setq expr
(math-simplify-units expr
))
794 (or (math-numberp expr
)
795 (error "Incompatible units"))
797 (setq value
(math-div expr
(nth 1 (car ulist
)))
798 value
(math-floor (let ((calc-internal-prec
799 (1- calc-internal-prec
)))
800 (math-normalize value
)))
801 new
(math-add new
(math-mul value
(car (car ulist
))))
802 expr
(math-sub expr
(math-mul value
(nth 1 (car ulist
))))
804 (math-add new
(math-mul (math-div expr
(nth 1 (car ulist
)))
806 (math-simplify-units (if pure
808 (list '* expr units
)))))
810 (defvar math-decompose-units-cache nil
)
811 (defun math-decompose-units (units)
812 (let ((u (math-check-unit-name units
)))
813 (and u
(eq (car-safe (nth 1 u
)) '+)
814 (setq units
(nth 1 u
))))
815 (setq units
(calcFunc-expand units
))
816 (and (eq (car-safe units
) '+)
817 (let ((entry (list units calc-internal-prec calc-prefer-frac
)))
818 (or (equal entry
(car math-decompose-units-cache
))
822 (while (eq (car-safe utemp
) '+)
823 (setq ulist
(cons (math-decompose-unit-part (nth 2 utemp
))
825 utemp
(nth 1 utemp
)))
826 (setq ulist
(cons (math-decompose-unit-part utemp
) ulist
)
828 (while (setq utemp
(cdr utemp
))
829 (unless (equal (nth 2 (car utemp
)) (nth 2 (car ulist
)))
830 (error "Inconsistent units in sum")))
831 (setq math-decompose-units-cache
836 (not (Math-lessp (nth 1 x
)
838 (cdr math-decompose-units-cache
))))
840 (defun math-decompose-unit-part (unit)
842 (math-is-multiple (math-simplify-units (math-to-standard-units
846 (defun math-find-compatible-unit (expr unit
)
847 (let ((u (math-check-unit-name unit
)))
849 (math-find-compatible-unit-rec expr
1))))
851 (defun math-find-compatible-unit-rec (expr pow
)
852 (cond ((eq (car-safe expr
) '*)
853 (or (math-find-compatible-unit-rec (nth 1 expr
) pow
)
854 (math-find-compatible-unit-rec (nth 2 expr
) pow
)))
855 ((eq (car-safe expr
) '/)
856 (or (math-find-compatible-unit-rec (nth 1 expr
) pow
)
857 (math-find-compatible-unit-rec (nth 2 expr
) (- pow
))))
858 ((and (eq (car-safe expr
) '^
)
859 (integerp (nth 2 expr
)))
860 (math-find-compatible-unit-rec (nth 1 expr
) (* pow
(nth 2 expr
))))
862 (let ((u2 (math-check-unit-name expr
)))
863 (if (equal (nth 4 u
) (nth 4 u2
))
866 (defun math-convert-units (expr new-units
&optional pure
)
867 (math-with-extra-prec 2
868 (let ((compat (and (not pure
) (math-find-compatible-unit expr new-units
)))
870 (math-combining-units nil
))
873 (math-mul (math-mul (math-simplify-units
874 (math-div expr
(math-pow (car compat
)
876 (math-pow new-units
(cdr compat
)))
878 (math-to-standard-units
879 (math-pow (math-div (car compat
) new-units
)
882 (when (setq unit-list
(math-decompose-units new-units
))
883 (setq new-units
(nth 2 (car unit-list
))))
884 (when (eq (car-safe expr
) '+)
885 (setq expr
(math-simplify-units expr
)))
886 (if (math-units-in-expr-p expr t
)
887 (math-convert-units-rec expr
)
888 (math-apply-units (math-to-standard-units
889 (list '/ expr new-units
) nil
)
890 new-units unit-list pure
))))))
892 (defun math-convert-units-rec (expr)
893 (if (math-units-in-expr-p expr nil
)
894 (math-apply-units (math-to-standard-units (list '/ expr new-units
) nil
)
895 new-units unit-list pure
)
896 (if (Math-primp expr
)
899 (mapcar 'math-convert-units-rec
(cdr expr
))))))
901 (defun math-convert-temperature (expr old new
&optional pure
)
902 (let* ((units (math-single-units-in-expr-p expr
))
905 (equal (nth 1 old
) (car units
)))
906 (math-check-unit-name old
)
907 (error "Inconsistent temperature units"))
909 (unew (math-check-unit-name new
)))
910 (unless (and (consp unew
) (nth 3 unew
))
911 (error "Not a valid temperature unit"))
912 (unless (and (consp uold
) (nth 3 uold
))
913 (error "Not a pure temperature expression"))
914 (let ((v (car uold
)))
915 (setq expr
(list '/ expr
(list 'var v
916 (intern (concat "var-"
917 (symbol-name v
)))))))
918 (or (eq (nth 3 uold
) (nth 3 unew
))
919 (cond ((eq (nth 3 uold
) 'K
)
920 (setq expr
(list '- expr
'(float 27315 -
2)))
921 (if (eq (nth 3 unew
) 'F
)
922 (setq expr
(list '+ (list '* expr
'(frac 9 5)) 32))))
923 ((eq (nth 3 uold
) 'C
)
924 (if (eq (nth 3 unew
) 'F
)
925 (setq expr
(list '+ (list '* expr
'(frac 9 5)) 32))
926 (setq expr
(list '+ expr
'(float 27315 -
2)))))
928 (setq expr
(list '* (list '- expr
32) '(frac 5 9)))
929 (if (eq (nth 3 unew
) 'K
)
930 (setq expr
(list '+ expr
'(float 27315 -
2)))))))
933 (list '* expr new
))))
937 (defun math-simplify-units (a)
938 (let ((math-simplifying-units t
)
939 (calc-matrix-mode 'scalar
))
941 (defalias 'calcFunc-usimplify
'math-simplify-units
)
943 (math-defsimplify (+ -
)
944 (and math-simplifying-units
945 (math-units-in-expr-p (nth 1 expr
) nil
)
946 (let* ((units (math-extract-units (nth 1 expr
)))
947 (ratio (math-simplify (math-to-standard-units
948 (list '/ (nth 2 expr
) units
) nil
))))
949 (if (math-units-in-expr-p ratio nil
)
951 (calc-record-why "*Inconsistent units" expr
)
953 (list '* (math-add (math-remove-units (nth 1 expr
))
954 (if (eq (car expr
) '-
) (math-neg ratio
) ratio
))
958 (math-simplify-units-prod))
960 (defun math-simplify-units-prod ()
961 (and math-simplifying-units
963 (Math-realp (nth 1 expr
))
964 (let* ((num (math-float (nth 1 expr
)))
965 (xpon (calcFunc-xpon num
))
966 (unitp (cdr (cdr expr
)))
968 (pow (if (eq (car expr
) '*) 1 -
1))
970 (and (eq (car-safe unit
) '*)
971 (setq unitp
(cdr unit
)
973 (and (eq (car-safe unit
) '^
)
974 (integerp (nth 2 unit
))
975 (setq pow
(* pow
(nth 2 unit
))
978 (and (setq u
(math-check-unit-name unit
))
981 (>= xpon
(if (eq (car u
) 'm
) 1 3)))
984 (reverse math-unit-prefixes
)
988 (or (eq (car u
) (nth 1 unit
))
993 math-unit-prefixes
))))))
994 (setq xpon
(+ xpon uxpon
))
996 (or (memq (car (car p
)) '(?d ?D ?h ?H
))
997 (and (eq (car (car p
)) ?c
)
998 (not (eq (car u
) 'm
)))
999 (< xpon
(setq pxpon
(* (nth 2 (nth 1 (car p
)))
1002 (setq pname
(math-build-var-name
1003 (if (eq (car (car p
)) 0)
1005 (concat (char-to-string
1009 (and (/= (car (car p
)) 0)
1011 math-units-table
)))))
1015 (or (not (eq p pref
))
1016 (< xpon
(+ pxpon
(* (math-abs pow
) 3))))
1019 (let ((calc-prefer-frac nil
))
1020 (calcFunc-scf (nth 1 expr
)
1022 (setcar unitp pname
)
1026 (and math-simplifying-units
1027 (let ((np (cdr expr
))
1028 (try-cancel-units 0)
1030 (setq n
(if (eq (car-safe (nth 2 expr
)) '*)
1033 (if (math-realp (car n
))
1035 (setcar (cdr expr
) (math-mul (nth 1 expr
)
1036 (let ((calc-prefer-frac nil
))
1037 (math-div 1 (car n
)))))
1039 (while (eq (car-safe (setq n
(car np
))) '*)
1040 (math-simplify-units-divisor (cdr n
) (cdr (cdr expr
)))
1041 (setq np
(cdr (cdr n
))))
1042 (math-simplify-units-divisor np
(cdr (cdr expr
)))
1043 (if (eq try-cancel-units
0)
1044 (let* ((math-simplifying-units nil
)
1045 (base (math-simplify (math-to-standard-units expr nil
))))
1046 (if (Math-numberp base
)
1048 (if (eq (car-safe expr
) '/)
1049 (math-simplify-units-prod))
1052 (defun math-simplify-units-divisor (np dp
)
1055 (while (eq (car-safe (setq d
(car dp
))) '*)
1056 (when (setq temp
(math-simplify-units-quotient n
(nth 1 d
)))
1057 (setcar np
(setq n temp
))
1059 (setq dp
(cdr (cdr d
))))
1060 (when (setq temp
(math-simplify-units-quotient n d
))
1061 (setcar np
(setq n temp
))
1064 ;; Simplify, e.g., "in / cm" to "2.54" in a units expression.
1065 (defun math-simplify-units-quotient (n d
)
1068 (when (and (eq (car-safe n
) '^
)
1069 (integerp (nth 2 n
)))
1070 (setq pow1
(nth 2 n
) n
(nth 1 n
)))
1071 (when (and (eq (car-safe d
) '^
)
1072 (integerp (nth 2 d
)))
1073 (setq pow2
(nth 2 d
) d
(nth 1 d
)))
1074 (let ((un (math-check-unit-name n
))
1075 (ud (math-check-unit-name d
)))
1077 (if (and (equal (nth 4 un
) (nth 4 ud
))
1079 (math-to-standard-units (list '/ n d
) nil
)
1086 (and (eq (car (car un
)) (car (car ud1
)))
1087 (setq try-cancel-units
1089 (- (* (cdr (car un
)) pow1
)
1090 (* (cdr (car ud
)) pow2
)))))
1091 (setq ud1
(cdr ud1
)))
1096 (and math-simplifying-units
1097 (math-realp (nth 2 expr
))
1098 (if (memq (car-safe (nth 1 expr
)) '(* /))
1099 (list (car (nth 1 expr
))
1100 (list '^
(nth 1 (nth 1 expr
)) (nth 2 expr
))
1101 (list '^
(nth 2 (nth 1 expr
)) (nth 2 expr
)))
1102 (math-simplify-units-pow (nth 1 expr
) (nth 2 expr
)))))
1104 (math-defsimplify calcFunc-sqrt
1105 (and math-simplifying-units
1106 (if (memq (car-safe (nth 1 expr
)) '(* /))
1107 (list (car (nth 1 expr
))
1108 (list 'calcFunc-sqrt
(nth 1 (nth 1 expr
)))
1109 (list 'calcFunc-sqrt
(nth 2 (nth 1 expr
))))
1110 (math-simplify-units-pow (nth 1 expr
) '(frac 1 2)))))
1112 (math-defsimplify (calcFunc-floor
1122 (and math-simplifying-units
1124 (if (math-only-units-in-expr-p (nth 1 expr
))
1126 (if (and (memq (car-safe (nth 1 expr
)) '(* /))
1127 (or (math-only-units-in-expr-p
1128 (nth 1 (nth 1 expr
)))
1129 (math-only-units-in-expr-p
1130 (nth 2 (nth 1 expr
)))))
1131 (list (car (nth 1 expr
))
1133 (cons (nth 1 (nth 1 expr
))
1136 (cons (nth 2 (nth 1 expr
))
1137 (cdr (cdr expr
)))))))))
1139 (defun math-simplify-units-pow (a pow
)
1140 (if (and (eq (car-safe a
) '^
)
1141 (math-check-unit-name (nth 1 a
))
1142 (math-realp (nth 2 a
)))
1143 (list '^
(nth 1 a
) (math-mul pow
(nth 2 a
)))
1144 (let* ((u (math-check-unit-name a
))
1145 (pf (math-to-simple-fraction pow
))
1146 (d (and (eq (car-safe pf
) 'frac
) (nth 2 pf
))))
1148 (math-units-are-multiple u d
)
1149 (list '^
(math-to-standard-units a nil
) pow
)))))
1152 (defun math-units-are-multiple (u n
)
1154 (while (and u
(= (%
(cdr (car u
)) n
) 0))
1158 (math-defsimplify calcFunc-sin
1159 (and math-simplifying-units
1160 (math-units-in-expr-p (nth 1 expr
) nil
)
1161 (let ((rad (math-simplify-units
1163 (math-to-standard-units (nth 1 expr
) nil
))))
1164 (calc-angle-mode 'rad
))
1165 (and (eq (car-safe rad
) '*)
1166 (math-realp (nth 1 rad
))
1167 (eq (car-safe (nth 2 rad
)) 'var
)
1168 (eq (nth 1 (nth 2 rad
)) 'rad
)
1169 (list 'calcFunc-sin
(nth 1 rad
))))))
1171 (math-defsimplify calcFunc-cos
1172 (and math-simplifying-units
1173 (math-units-in-expr-p (nth 1 expr
) nil
)
1174 (let ((rad (math-simplify-units
1176 (math-to-standard-units (nth 1 expr
) nil
))))
1177 (calc-angle-mode 'rad
))
1178 (and (eq (car-safe rad
) '*)
1179 (math-realp (nth 1 rad
))
1180 (eq (car-safe (nth 2 rad
)) 'var
)
1181 (eq (nth 1 (nth 2 rad
)) 'rad
)
1182 (list 'calcFunc-cos
(nth 1 rad
))))))
1184 (math-defsimplify calcFunc-tan
1185 (and math-simplifying-units
1186 (math-units-in-expr-p (nth 1 expr
) nil
)
1187 (let ((rad (math-simplify-units
1189 (math-to-standard-units (nth 1 expr
) nil
))))
1190 (calc-angle-mode 'rad
))
1191 (and (eq (car-safe rad
) '*)
1192 (math-realp (nth 1 rad
))
1193 (eq (car-safe (nth 2 rad
)) 'var
)
1194 (eq (nth 1 (nth 2 rad
)) 'rad
)
1195 (list 'calcFunc-tan
(nth 1 rad
))))))
1198 (defun math-remove-units (expr)
1199 (if (math-check-unit-name expr
)
1201 (if (Math-primp expr
)
1204 (mapcar 'math-remove-units
(cdr expr
))))))
1206 (defun math-extract-units (expr)
1207 (if (memq (car-safe expr
) '(* /))
1209 (mapcar 'math-extract-units
(cdr expr
)))
1210 (if (math-check-unit-name expr
) expr
1)))
1212 (defun math-build-units-table-buffer (enter-buffer)
1213 (if (not (and math-units-table math-units-table-buffer-valid
1214 (get-buffer "*Units Table*")))
1215 (let ((buf (get-buffer-create "*Units Table*"))
1216 (uptr (math-build-units-table))
1217 (calc-language (if (eq calc-language
'big
) nil calc-language
))
1218 (calc-float-format '(float 0))
1219 (calc-group-digits nil
)
1220 (calc-number-radix 10)
1221 (calc-point-char ".")
1225 (message "Formatting units table...")
1227 (setq buffer-read-only nil
)
1229 (insert "Calculator Units Table:\n\n")
1230 (insert "Unit Type Definition Description\n\n")
1234 (when (eq (car u
) 'm
)
1236 (setq shadowed
(and std
(assq (car u
) math-additional-units
)))
1239 (eq (aref name
0) ?\
*))
1240 (unless (eq uptr math-units-table
)
1242 (setq name
(substring name
1)))
1244 (and shadowed
(insert "("))
1245 (insert (symbol-name (car u
)))
1246 (and shadowed
(insert ")"))
1250 (insert (symbol-name (nth 3 u
))))
1256 (and shadowed
(insert "("))
1258 (insert (math-format-value (nth 1 u
) 80))
1259 (insert (symbol-name (car u
))))
1260 (and shadowed
(insert ")"))
1266 (insert " (redefined above)")
1268 (insert " (base unit)")))
1270 (setq uptr
(cdr uptr
)))
1271 (insert "\n\nUnit Prefix Table:\n\n")
1272 (setq uptr math-unit-prefixes
)
1275 (insert " " (char-to-string (car u
)))
1276 (if (equal (nth 1 u
) (nth 1 (nth 1 uptr
)))
1277 (insert " " (char-to-string (car (car (setq uptr
(cdr uptr
)))))
1280 (insert "10^" (int-to-string (nth 2 (nth 1 u
))))
1282 (insert " " (nth 2 u
) "\n")
1283 (while (eq (car (car (setq uptr
(cdr uptr
)))) 0)))
1285 (setq buffer-read-only t
)
1286 (message "Formatting units table...done"))
1287 (setq math-units-table-buffer-valid t
)
1288 (let ((oldbuf (current-buffer)))
1290 (goto-char (point-min))
1291 (set-buffer oldbuf
))
1294 (display-buffer buf
)))
1296 (pop-to-buffer (get-buffer "*Units Table*"))
1297 (display-buffer (get-buffer "*Units Table*")))))
1300 ;; coding: iso-latin-1
1303 ;;; calc-units.el ends here