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 ;; Maintainer: Colin Walters <walters@debian.org>
8 ;; This file is part of GNU Emacs.
10 ;; GNU Emacs is distributed in the hope that it will be useful,
11 ;; but WITHOUT ANY WARRANTY. No author or distributor
12 ;; accepts responsibility to anyone for the consequences of using it
13 ;; or for whether it serves any particular purpose or works at all,
14 ;; unless he says so in writing. Refer to the GNU Emacs General Public
15 ;; License for full details.
17 ;; Everyone is granted permission to copy, modify and redistribute
18 ;; GNU Emacs, but only under the conditions described in the
19 ;; GNU Emacs General Public License. A copy of this license is
20 ;; supposed to have been given to you along with GNU Emacs so you
21 ;; can know your rights and responsibilities. It should be in a
22 ;; file named COPYING. Among other things, the copyright notice
23 ;; and this notice must be preserved on all copies.
29 ;; This file is autoloaded from calc-ext.el.
34 (defun calc-Need-calc-units () nil
)
38 ;;; Units table last updated 9-Jan-91 by Ulrich Mueller (ulm@vsnhd1.cern.ch)
39 ;;; with some additions by Przemek Klosowski (przemek@rrdstrad.nist.gov)
41 (defvar math-standard-units
44 ( in
"2.54 cm" "Inch" )
47 ( mi
"5280 ft" "Mile" )
48 ( au
"1.495979e11 m" "Astronomical Unit" )
49 ( lyr
"9460536207068016 m" "Light Year" )
50 ( pc
"206264.80625 au" "Parsec" )
51 ( nmi
"1852 m" "Nautical Mile" )
52 ( fath
"6 ft" "Fathom" )
54 ( mil
"in/1000" "Mil" )
55 ( point
"in/72" "Point (1/72 inch)" )
56 ( tpt
"in/72.27" "Point (TeX conventions)" )
57 ( Ang
"1e-10 m" "Angstrom" )
58 ( mfi
"mi+ft+in" "Miles + feet + inches" )
61 ( hect
"10000 m^2" "*Hectare" )
62 ( acre
"mi^2 / 640" "Acre" )
63 ( b
"1e-28 m^2" "Barn" )
66 ( l
"1e-3 m^3" "*Liter" )
67 ( L
"1e-3 m^3" "Liter" )
68 ( gal
"4 qt" "US Gallon" )
71 ( cup
"8 ozfl" "Cup" )
72 ( ozfl
"2 tbsp" "Fluid Ounce" )
73 ( floz
"2 tbsp" "Fluid Ounce" )
74 ( tbsp
"3 tsp" "Tablespoon" )
75 ( tsp
"4.92892159375 ml" "Teaspoon" )
76 ( vol
"tsp+tbsp+ozfl+cup+pt+qt+gal" "Gallons + ... + teaspoons" )
77 ( galC
"4.54609 l" "Canadian Gallon" )
78 ( galUK
"4.546092 l" "UK Gallon" )
83 ( min
"60 s" "Minute" )
84 ( hr
"60 min" "Hour" )
87 ( hms
"wk+day+hr+min+s" "Hours, minutes, seconds" )
88 ( yr
"365.25 day" "Year" )
92 ( mph
"mi/hr" "*Miles per hour" )
93 ( kph
"km/hr" "Kilometers per hour" )
94 ( knot
"nmi/hr" "Knot" )
95 ( c
"2.99792458e8 m/s" "Speed of light" )
98 ( ga
"9.80665 m/s^2" "*\"g\" acceleration" )
102 ( lb
"16 oz" "Pound (mass)" )
103 ( oz
"28.349523125 g" "Ounce (mass)" )
104 ( ton
"2000 lb" "Ton" )
105 ( tpo
"ton+lb+oz" "Tons + pounds + ounces (mass)" )
106 ( t
"1000 kg" "Metric ton" )
107 ( tonUK
"1016.0469088 kg" "UK ton" )
108 ( lbt
"12 ozt" "Troy pound" )
109 ( ozt
"31.103475 g" "Troy ounce" )
110 ( ct
".2 g" "Carat" )
111 ( amu
"1.6605402e-24 g" "Unified atomic mass" )
114 ( N
"m kg/s^2" "*Newton" )
115 ( dyn
"1e-5 N" "Dyne" )
116 ( gf
"ga g" "Gram (force)" )
117 ( lbf
"4.44822161526 N" "Pound (force)" )
118 ( kip
"1000 lbf" "Kilopound (force)" )
119 ( pdl
"0.138255 N" "Poundal" )
123 ( erg
"1e-7 J" "Erg" )
124 ( cal
"4.1868 J" "International Table Calorie" )
125 ( Btu
"1055.05585262 J" "International Table Btu" )
126 ( eV
"ech V" "Electron volt" )
127 ( ev
"eV" "Electron volt" )
128 ( therm
"105506000 J" "EEC therm" )
129 ( invcm
"h c/cm" "Energy in inverse centimeters" )
130 ( Kayser
"invcm" "Kayser (inverse centimeter energy)" )
131 ( men
"100/invcm" "Inverse energy in meters" )
132 ( Hzen
"h Hz" "Energy in Hertz")
133 ( Ken
"k K" "Energy in Kelvins")
134 ;; ( invcm "eV / 8065.47835185" "Energy in inverse centimeters" )
135 ;; ( Hzen "eV / 2.41796958004e14" "Energy in Hertz")
136 ;; ( Ken "eV / 11604.7967327" "Energy in Kelvins")
140 ( hp
"745.7 W" "Horsepower" )
143 ( K nil
"*Degree Kelvin" K
)
144 ( dK
"K" "Degree Kelvin" K
)
145 ( degK
"K" "Degree Kelvin" K
)
146 ( dC
"K" "Degree Celsius" C
)
147 ( degC
"K" "Degree Celsius" C
)
148 ( dF
"(5/9) K" "Degree Fahrenheit" F
)
149 ( degF
"(5/9) K" "Degree Fahrenheit" F
)
152 ( Pa
"N/m^2" "*Pascal" )
153 ( bar
"1e5 Pa" "Bar" )
154 ( atm
"101325 Pa" "Standard atmosphere" )
155 ( torr
"atm/760" "Torr" )
156 ( mHg
"1000 torr" "Meter of mercury" )
157 ( inHg
"25.4 mmHg" "Inch of mercury" )
158 ( inH2O
"248.84 Pa" "Inch of water" )
159 ( psi
"6894.75729317 Pa" "Pound per square inch" )
162 ( P
"0.1 Pa s" "*Poise" )
163 ( St
"1e-4 m^2/s" "Stokes" )
167 ( C
"A s" "Coulomb" )
168 ( Fdy
"ech Nav" "Faraday" )
169 ( e
"1.60217733e-19 C" "Elementary charge" )
170 ( ech
"1.60217733e-19 C" "Elementary charge" )
174 ( S
"A/V" "Siemens" )
177 ( T
"Wb/m^2" "Tesla" )
178 ( G
"1e-4 T" "Gauss" )
181 ;; Luminous intensity
182 ( cd nil
"*Candela" )
183 ( sb
"1e4 cd/m^2" "Stilb" )
184 ( lm
"cd sr" "Lumen" )
185 ( lx
"lm/m^2" "Lux" )
186 ( ph
"1e4 lx" "Phot" )
187 ( fc
"10.76 lx" "Footcandle" )
188 ( lam
"1e4 lm/m^2" "Lambert" )
189 ( flam
"1.07639104e-3 lam" "Footlambert" )
192 ( Bq
"1/s" "*Becquerel" )
193 ( Ci
"3.7e10 Bq" "Curie" )
195 ( Sv
"Gy" "Sievert" )
196 ( R
"2.58e-4 C/kg" "Roentgen" )
197 ( rd
".01 Gy" "Rad" )
200 ;; Amount of substance
204 ( rad nil
"*Radian" )
205 ( circ
"2 pi rad" "Full circle" )
206 ( rev
"circ" "Full revolution" )
207 ( deg
"circ/360" "Degree" )
208 ( arcmin
"deg/60" "Arc minute" )
209 ( arcsec
"arcmin/60" "Arc second" )
210 ( grad
"circ/400" "Grade" )
211 ( rpm
"rev/min" "Revolutions per minute" )
214 ( sr nil
"*Steradian" )
216 ;; Other physical quantities (Physics Letters B239, 1 (1990))
217 ( h
"6.6260755e-34 J s" "*Planck's constant" )
218 ( hbar
"h / 2 pi" "Planck's constant" )
219 ( mu0
"4 pi 1e-7 H/m" "Permeability of vacuum" )
220 ( Grav
"6.67259e-11 N m^2/kg^2" "Gravitational constant" )
221 ( Nav
"6.0221367e23 / mol" "Avagadro's constant" )
222 ( me
"0.51099906 MeV/c^2" "Electron rest mass" )
223 ( mp
"1.007276470 amu" "Proton rest mass" )
224 ( mn
"1.008664904 amu" "Neutron rest mass" )
225 ( mu
"0.113428913 amu" "Muon rest mass" )
226 ( Ryd
"1.0973731571e5 invcm" "Rydberg's constant" )
227 ( k
"1.3806513e-23 J/K" "Boltzmann's constant" )
228 ( fsc
"1 / 137.0359895" "Fine structure constant" )
229 ( muB
"5.78838263e-11 MeV/T" "Bohr magneton" )
230 ( muN
"3.15245166e-14 MeV/T" "Nuclear magneton" )
231 ( mue
"1.001159652193 muB" "Electron magnetic moment" )
232 ( mup
"2.792847386 muN" "Proton magnetic moment" )
233 ( R0
"Nav k" "Molar gas constant" )
234 ( V0
"22.413992 L/mol" "Standard volume of ideal gas" )))
237 (defvar math-additional-units nil
238 "*Additional units table for user-defined units.
239 Must be formatted like math-standard-units.
240 If this is changed, be sure to set math-units-table to nil to ensure
241 that the combined units table will be rebuilt.")
243 (defvar math-unit-prefixes
244 '( ( ?E
(float 1 18) "Exa" )
245 ( ?P
(float 1 15) "Peta" )
246 ( ?T
(float 1 12) "Tera" )
247 ( ?G
(float 1 9) "Giga" )
248 ( ?M
(float 1 6) "Mega" )
249 ( ?k
(float 1 3) "Kilo" )
250 ( ?K
(float 1 3) "Kilo" )
251 ( ?h
(float 1 2) "Hecto" )
252 ( ?H
(float 1 2) "Hecto" )
253 ( ?D
(float 1 1) "Deka" )
254 ( 0 (float 1 0) nil
)
255 ( ?d
(float 1 -
1) "Deci" )
256 ( ?c
(float 1 -
2) "Centi" )
257 ( ?m
(float 1 -
3) "Milli" )
258 ( ?u
(float 1 -
6) "Micro" )
259 ( ?n
(float 1 -
9) "Nano" )
260 ( ?p
(float 1 -
12) "Pico" )
261 ( ?f
(float 1 -
15) "Femto" )
262 ( ?a
(float 1 -
18) "Atto" )))
264 (defvar math-standard-units-systems
266 ( si
( ( g
'(* (var kg var-kg
) (float 1 -
3)) ) ) )
267 ( mks
( ( g
'(* (var kg var-kg
) (float 1 -
3)) ) ) )
268 ( cgs
( ( m
'(* (var cm var-cm
) 100 ) ) ) )))
270 (defvar math-units-table nil
271 "Internal units table derived from math-defined-units.
272 Entries are (SYMBOL EXPR DOC-STRING TEMP-TYPE BASE-UNITS).")
274 (defvar math-units-table-buffer-valid nil
)
278 (defun calc-base-units ()
281 (let ((calc-autorange-units nil
))
282 (calc-enter-result 1 "bsun" (math-simplify-units
283 (math-to-standard-units (calc-top-n 1)
286 (defun calc-quick-units ()
289 (let* ((num (- last-command-char ?
0))
290 (pos (if (= num
0) 10 num
))
291 (units (calc-var-value 'var-Units
))
292 (expr (calc-top-n 1)))
293 (unless (and (>= num
0) (<= num
9))
294 (errunless "Bad unit number"))
295 (unless (math-vectorp units
)
296 (errunless "No \"quick units\" are defined"))
297 (unless (< pos
(length units
))
298 (errunless "Unit number %d not defined" pos
))
299 (if (math-units-in-expr-p expr nil
)
300 (calc-enter-result 1 (format "cun%d" num
)
301 (math-convert-units expr
(nth pos units
)))
302 (calc-enter-result 1 (format "*un%d" num
)
304 (math-mul expr
(nth pos units
))))))))
306 (defun calc-convert-units (&optional old-units new-units
)
309 (let ((expr (calc-top-n 1))
312 (unless (math-units-in-expr-p expr t
)
313 (let ((uold (or old-units
315 (setq uoldname
(read-string "Old units: "))
316 (if (equal uoldname
"")
320 (if (string-match "\\` */" uoldname
)
321 (setq uoldname
(concat "1" uoldname
)))
322 (math-read-expr uoldname
))))))
323 (when (eq (car-safe uold
) 'error
)
324 (error "Bad format in units expression: %s" (nth 1 uold
)))
325 (setq expr
(math-mul expr uold
))))
327 (setq new-units
(read-string (if uoldname
328 (concat "Old units: "
332 (when (string-match "\\` */" new-units
)
333 (setq new-units
(concat "1" new-units
)))
334 (setq units
(math-read-expr new-units
))
335 (when (eq (car-safe units
) 'error
)
336 (error "Bad format in units expression: %s" (nth 2 units
)))
337 (let ((unew (math-units-in-expr-p units t
))
338 (std (and (eq (car-safe units
) 'var
)
339 (assq (nth 1 units
) math-standard-units-systems
))))
341 (calc-enter-result 1 "cvun" (math-simplify-units
342 (math-to-standard-units expr
345 (error "No units specified"))
346 (calc-enter-result 1 "cvun"
349 (and uoldname
(not (equal uoldname
"1"))))))))))
351 (defun calc-autorange-units (arg)
354 (calc-change-mode 'calc-autorange-units arg nil t
)
355 (message (if calc-autorange-units
356 "Adjusting target unit prefix automatically"
357 "Using target units exactly"))))
359 (defun calc-convert-temperature (&optional old-units new-units
)
362 (let ((expr (calc-top-n 1))
366 (setq uold
(or old-units
367 (let ((units (math-single-units-in-expr-p expr
)))
370 (list 'var
(car units
)
371 (intern (concat "var-"
374 (error "Not a pure temperature expression"))
376 (setq uoldname
(read-string
377 "Old temperature units: ")))))))
378 (when (eq (car-safe uold
) 'error
)
379 (error "Bad format in units expression: %s" (nth 2 uold
)))
380 (or (math-units-in-expr-p expr nil
)
381 (setq expr
(math-mul expr uold
)))
382 (setq unew
(or new-units
384 (read-string (if uoldname
385 (concat "Old temperature units: "
388 "New temperature units: ")))))
389 (when (eq (car-safe unew
) 'error
)
390 (error "Bad format in units expression: %s" (nth 2 unew
)))
391 (calc-enter-result 1 "cvtm" (math-simplify-units
392 (math-convert-temperature expr uold unew
395 (defun calc-remove-units ()
398 (calc-enter-result 1 "rmun" (math-simplify-units
399 (math-remove-units (calc-top-n 1))))))
401 (defun calc-extract-units ()
404 (calc-enter-result 1 "rmun" (math-simplify-units
405 (math-extract-units (calc-top-n 1))))))
407 (defun calc-explain-units ()
410 (let ((num-units nil
)
412 (calc-explain-units-rec (calc-top-n 1) 1)
413 (and den-units
(string-match "^[^(].* .*[^)]$" den-units
)
414 (setq den-units
(concat "(" den-units
")")))
417 (message "%s per %s" num-units den-units
)
418 (message "%s" num-units
))
420 (message "1 per %s" den-units
)
421 (message "No units in expression"))))))
423 (defun calc-explain-units-rec (expr pow
)
424 (let ((u (math-check-unit-name expr
))
426 (if (and u
(not (math-zerop pow
)))
427 (let ((name (or (nth 2 u
) (symbol-name (car u
)))))
428 (if (eq (aref name
0) ?\
*)
429 (setq name
(substring name
1)))
430 (if (string-match "[^a-zA-Z0-9']" name
)
431 (if (string-match "^[a-zA-Z0-9' ()]*$" name
)
432 (while (setq pos
(string-match "[ ()]" name
))
433 (setq name
(concat (substring name
0 pos
)
434 (if (eq (aref name pos
) 32) "-" "")
435 (substring name
(1+ pos
)))))
436 (setq name
(concat "(" name
")"))))
437 (or (eq (nth 1 expr
) (car u
))
438 (setq name
(concat (nth 2 (assq (aref (symbol-name
441 (if (and (string-match "[^a-zA-Z0-9']" name
)
442 (not (memq (car u
) '(mHg gf
))))
445 (cond ((or (math-equal-int pow
1)
446 (math-equal-int pow -
1)))
447 ((or (math-equal-int pow
2)
448 (math-equal-int pow -
2))
449 (if (equal (nth 4 u
) '((m .
1)))
450 (setq name
(concat "Square-" name
))
451 (setq name
(concat name
"-squared"))))
452 ((or (math-equal-int pow
3)
453 (math-equal-int pow -
3))
454 (if (equal (nth 4 u
) '((m .
1)))
455 (setq name
(concat "Cubic-" name
))
456 (setq name
(concat name
"-cubed"))))
458 (setq name
(concat name
"^"
459 (math-format-number (math-abs pow
))))))
461 (setq num-units
(if num-units
462 (concat num-units
" " name
)
464 (setq den-units
(if den-units
465 (concat den-units
" " name
)
467 (cond ((eq (car-safe expr
) '*)
468 (calc-explain-units-rec (nth 1 expr
) pow
)
469 (calc-explain-units-rec (nth 2 expr
) pow
))
470 ((eq (car-safe expr
) '/)
471 (calc-explain-units-rec (nth 1 expr
) pow
)
472 (calc-explain-units-rec (nth 2 expr
) (- pow
)))
473 ((memq (car-safe expr
) '(neg + -
))
474 (calc-explain-units-rec (nth 1 expr
) pow
))
475 ((and (eq (car-safe expr
) '^
)
476 (math-realp (nth 2 expr
)))
477 (calc-explain-units-rec (nth 1 expr
)
478 (math-mul pow
(nth 2 expr
))))))))
480 (defun calc-simplify-units ()
483 (calc-with-default-simplification
484 (calc-enter-result 1 "smun" (math-simplify-units (calc-top-n 1))))))
486 (defun calc-view-units-table (n)
488 (and n
(setq math-units-table-buffer-valid nil
))
489 (let ((win (get-buffer-window "*Units Table*")))
492 math-units-table-buffer-valid
)
494 (bury-buffer (window-buffer win
))
495 (let ((curwin (selected-window)))
497 (switch-to-buffer nil
)
498 (select-window curwin
)))
499 (math-build-units-table-buffer nil
))))
501 (defun calc-enter-units-table (n)
503 (and n
(setq math-units-table-buffer-valid nil
))
504 (math-build-units-table-buffer t
)
505 (message (substitute-command-keys "Type \\[calc] to return to the Calculator")))
507 (defun calc-define-unit (uname desc
)
508 (interactive "SDefine unit name: \nsDescription: ")
510 (let ((form (calc-top-n 1))
511 (unit (assq uname math-additional-units
)))
513 (setq math-additional-units
514 (cons (setq unit
(list uname nil nil
))
515 math-additional-units
)
516 math-units-table nil
))
517 (setcar (cdr unit
) (and (not (and (eq (car-safe form
) 'var
)
518 (eq (nth 1 form
) uname
)))
519 (not (math-equal-int form
1))
520 (math-format-flat-expr form
0)))
521 (setcar (cdr (cdr unit
)) (and (not (equal desc
""))
523 (calc-invalidate-units-table))
525 (defun calc-undefine-unit (uname)
526 (interactive "SUndefine unit name: ")
528 (let ((unit (assq uname math-additional-units
)))
530 (if (assq uname math-standard-units
)
531 (error "\"%s\" is a predefined unit name" uname
)
532 (error "Unit name \"%s\" not found" uname
)))
533 (setq math-additional-units
(delq unit math-additional-units
)
534 math-units-table nil
)))
535 (calc-invalidate-units-table))
537 (defun calc-invalidate-units-table ()
538 (setq math-units-table nil
)
539 (let ((buf (get-buffer "*Units Table*")))
544 (goto-char (point-min))
545 (if (looking-at "Calculator Units Table")
546 (let ((buffer-read-only nil
))
547 (insert "(Obsolete) "))))))))
549 (defun calc-get-unit-definition (uname)
550 (interactive "SGet definition for unit: ")
552 (math-build-units-table)
553 (let ((unit (assq uname math-units-table
)))
555 (error "Unit name \"%s\" not found" uname
))
556 (let ((msg (nth 2 unit
)))
558 (if (string-match "^\\*" msg
)
559 (setq msg
(substring msg
1)))
560 (setq msg
(symbol-name uname
)))
563 (calc-enter-result 0 "ugdf" (nth 1 unit
))
564 (message "Derived unit: %s" msg
))
565 (calc-enter-result 0 "ugdf" (list 'var uname
568 (symbol-name uname
)))))
569 (message "Base unit: %s" msg
))))))
571 (defun calc-permanent-units ()
575 (set-buffer (find-file-noselect (substitute-in-file-name
576 calc-settings-file
)))
577 (goto-char (point-min))
578 (if (and (search-forward ";;; Custom units stored by Calc" nil t
)
582 (search-forward "\n;;; End of custom units" nil t
)))
586 (delete-region pos
(point)))
587 (goto-char (point-max))
590 (insert ";;; Custom units stored by Calc on " (current-time-string) "\n")
591 (if math-additional-units
593 (insert "(setq math-additional-units '(\n")
594 (let ((list math-additional-units
))
596 (insert " (" (symbol-name (car (car list
))) " "
597 (if (nth 1 (car list
))
598 (if (stringp (nth 1 (car list
)))
599 (prin1-to-string (nth 1 (car list
)))
600 (prin1-to-string (math-format-flat-expr
601 (nth 1 (car list
)) 0)))
604 (prin1-to-string (nth 2 (car list
)))
606 (setq list
(cdr list
))))
608 (insert ";;; (no custom units defined)\n"))
609 (insert ";;; End of custom units\n")
614 (defun math-build-units-table ()
616 (let* ((combined-units (append math-additional-units
617 math-standard-units
))
618 (unit-list (mapcar 'car combined-units
))
620 (message "Building units table...")
621 (setq math-units-table-buffer-valid nil
)
622 (setq tab
(mapcar (function
626 (if (stringp (nth 1 x
))
627 (let ((exp (math-read-plain-expr
629 (if (eq (car-safe exp
) 'error
)
630 (error "Format error in definition of %s in units table: %s"
637 (list (cons (car x
) 1))))))
639 (let ((math-units-table tab
))
640 (mapcar 'math-find-base-units tab
))
641 (message "Building units table...done")
642 (setq math-units-table tab
))))
644 (defun math-find-base-units (entry)
645 (if (eq (nth 4 entry
) 'boom
)
646 (error "Circular definition involving unit %s" (car entry
)))
649 (setcar (nthcdr 4 entry
) 'boom
)
650 (math-find-base-units-rec (nth 1 entry
) 1)
652 (error "Dimensionless definition for unit %s" (car entry
)))
653 (while (eq (cdr (car base
)) 0)
654 (setq base
(cdr base
)))
657 (if (eq (cdr (car (cdr b
))) 0)
658 (setcdr b
(cdr (cdr b
)))
660 (setq base
(sort base
'math-compare-unit-names
))
661 (setcar (nthcdr 4 entry
) base
)
664 (defun math-compare-unit-names (a b
)
665 (memq (car b
) (cdr (memq (car a
) unit-list
))))
667 (defun math-find-base-units-rec (expr pow
)
668 (let ((u (math-check-unit-name expr
)))
670 (let ((ulist (math-find-base-units u
)))
672 (let ((p (* (cdr (car ulist
)) pow
))
673 (old (assq (car (car ulist
)) base
)))
675 (setcdr old
(+ (cdr old
) p
))
676 (setq base
(cons (cons (car (car ulist
)) p
) base
))))
677 (setq ulist
(cdr ulist
)))))
678 ((math-scalarp expr
))
679 ((and (eq (car expr
) '^
)
680 (integerp (nth 2 expr
)))
681 (math-find-base-units-rec (nth 1 expr
) (* pow
(nth 2 expr
))))
683 (math-find-base-units-rec (nth 1 expr
) pow
)
684 (math-find-base-units-rec (nth 2 expr
) pow
))
686 (math-find-base-units-rec (nth 1 expr
) pow
)
687 (math-find-base-units-rec (nth 2 expr
) (- pow
)))
688 ((eq (car expr
) 'neg
)
689 (math-find-base-units-rec (nth 1 expr
) pow
))
691 (math-find-base-units-rec (nth 1 expr
) pow
))
692 ((eq (car expr
) 'var
)
693 (or (eq (nth 1 expr
) 'pi
)
694 (error "Unknown name %s in defining expression for unit %s"
695 (nth 1 expr
) (car entry
))))
696 (t (error "Malformed defining expression for unit %s" (car entry
))))))
699 (defun math-units-in-expr-p (expr sub-exprs
)
701 (if (eq (car expr
) 'var
)
702 (math-check-unit-name expr
)
704 (memq (car expr
) '(* / ^
)))
705 (or (math-units-in-expr-p (nth 1 expr
) sub-exprs
)
706 (math-units-in-expr-p (nth 2 expr
) sub-exprs
))))))
708 (defun math-only-units-in-expr-p (expr)
710 (if (eq (car expr
) 'var
)
711 (math-check-unit-name expr
)
712 (if (memq (car expr
) '(* /))
713 (and (math-only-units-in-expr-p (nth 1 expr
))
714 (math-only-units-in-expr-p (nth 2 expr
)))
715 (and (eq (car expr
) '^
)
716 (and (math-only-units-in-expr-p (nth 1 expr
))
717 (math-realp (nth 2 expr
))))))))
719 (defun math-single-units-in-expr-p (expr)
720 (cond ((math-scalarp expr
) nil
)
721 ((eq (car expr
) 'var
)
722 (math-check-unit-name expr
))
724 (let ((u1 (math-single-units-in-expr-p (nth 1 expr
)))
725 (u2 (math-single-units-in-expr-p (nth 2 expr
))))
726 (or (and u1 u2
'wrong
)
730 (if (math-units-in-expr-p (nth 2 expr
) nil
)
732 (math-single-units-in-expr-p (nth 1 expr
))))
735 (defun math-check-unit-name (v)
736 (and (eq (car-safe v
) 'var
)
737 (or (assq (nth 1 v
) (or math-units-table
(math-build-units-table)))
738 (let ((name (symbol-name (nth 1 v
))))
739 (and (> (length name
) 1)
740 (assq (aref name
0) math-unit-prefixes
)
741 (or (assq (intern (substring name
1)) math-units-table
)
742 (and (eq (aref name
0) ?M
)
744 (eq (aref name
1) ?e
)
745 (eq (aref name
2) ?g
)
746 (assq (intern (substring name
3))
747 math-units-table
))))))))
750 (defun math-to-standard-units (expr which-standard
)
751 (math-to-standard-rec expr
))
753 (defun math-to-standard-rec (expr)
754 (if (eq (car-safe expr
) 'var
)
755 (let ((u (math-check-unit-name expr
))
760 (setq expr
(math-to-standard-rec (nth 1 u
)))
761 (let ((st (assq (car u
) which-standard
)))
763 (setq expr
(nth 1 st
))
764 (setq expr
(list 'var
(car u
)
765 (intern (concat "var-"
771 (nth 1 (assq (aref (symbol-name base
) 0)
778 (if (Math-primp expr
)
781 (mapcar 'math-to-standard-rec
(cdr expr
))))))
783 (defun math-apply-units (expr units ulist
&optional pure
)
787 (setq expr
(math-simplify-units expr
))
788 (or (math-numberp expr
)
789 (error "Incompatible units"))
791 (setq value
(math-div expr
(nth 1 (car ulist
)))
792 value
(math-floor (let ((calc-internal-prec
793 (1- calc-internal-prec
)))
794 (math-normalize value
)))
795 new
(math-add new
(math-mul value
(car (car ulist
))))
796 expr
(math-sub expr
(math-mul value
(nth 1 (car ulist
))))
798 (math-add new
(math-mul (math-div expr
(nth 1 (car ulist
)))
800 (math-simplify-units (if pure
802 (list '* expr units
)))))
804 (defvar math-decompose-units-cache nil
)
805 (defun math-decompose-units (units)
806 (let ((u (math-check-unit-name units
)))
807 (and u
(eq (car-safe (nth 1 u
)) '+)
808 (setq units
(nth 1 u
))))
809 (setq units
(calcFunc-expand units
))
810 (and (eq (car-safe units
) '+)
811 (let ((entry (list units calc-internal-prec calc-prefer-frac
)))
812 (or (equal entry
(car math-decompose-units-cache
))
816 (while (eq (car-safe utemp
) '+)
817 (setq ulist
(cons (math-decompose-unit-part (nth 2 utemp
))
819 utemp
(nth 1 utemp
)))
820 (setq ulist
(cons (math-decompose-unit-part utemp
) ulist
)
822 (while (setq utemp
(cdr utemp
))
823 (unless (equal (nth 2 (car utemp
)) (nth 2 (car ulist
)))
824 (error "Inconsistent units in sum")))
825 (setq math-decompose-units-cache
830 (not (Math-lessp (nth 1 x
)
832 (cdr math-decompose-units-cache
))))
834 (defun math-decompose-unit-part (unit)
836 (math-is-multiple (math-simplify-units (math-to-standard-units
840 (defun math-find-compatible-unit (expr unit
)
841 (let ((u (math-check-unit-name unit
)))
843 (math-find-compatible-unit-rec expr
1))))
845 (defun math-find-compatible-unit-rec (expr pow
)
846 (cond ((eq (car-safe expr
) '*)
847 (or (math-find-compatible-unit-rec (nth 1 expr
) pow
)
848 (math-find-compatible-unit-rec (nth 2 expr
) pow
)))
849 ((eq (car-safe expr
) '/)
850 (or (math-find-compatible-unit-rec (nth 1 expr
) pow
)
851 (math-find-compatible-unit-rec (nth 2 expr
) (- pow
))))
852 ((and (eq (car-safe expr
) '^
)
853 (integerp (nth 2 expr
)))
854 (math-find-compatible-unit-rec (nth 1 expr
) (* pow
(nth 2 expr
))))
856 (let ((u2 (math-check-unit-name expr
)))
857 (if (equal (nth 4 u
) (nth 4 u2
))
860 (defun math-convert-units (expr new-units
&optional pure
)
861 (math-with-extra-prec 2
862 (let ((compat (and (not pure
) (math-find-compatible-unit expr new-units
)))
864 (math-combining-units nil
))
867 (math-mul (math-mul (math-simplify-units
868 (math-div expr
(math-pow (car compat
)
870 (math-pow new-units
(cdr compat
)))
872 (math-to-standard-units
873 (math-pow (math-div (car compat
) new-units
)
876 (when (setq unit-list
(math-decompose-units new-units
))
877 (setq new-units
(nth 2 (car unit-list
))))
878 (when (eq (car-safe expr
) '+)
879 (setq expr
(math-simplify-units expr
)))
880 (if (math-units-in-expr-p expr t
)
881 (math-convert-units-rec expr
)
882 (math-apply-units (math-to-standard-units
883 (list '/ expr new-units
) nil
)
884 new-units unit-list pure
))))))
886 (defun math-convert-units-rec (expr)
887 (if (math-units-in-expr-p expr nil
)
888 (math-apply-units (math-to-standard-units (list '/ expr new-units
) nil
)
889 new-units unit-list pure
)
890 (if (Math-primp expr
)
893 (mapcar 'math-convert-units-rec
(cdr expr
))))))
895 (defun math-convert-temperature (expr old new
&optional pure
)
896 (let* ((units (math-single-units-in-expr-p expr
))
899 (equal (nth 1 old
) (car units
)))
900 (math-check-unit-name old
)
901 (error "Inconsistent temperature units"))
903 (unew (math-check-unit-name new
)))
904 (unless (and (consp unew
) (nth 3 unew
))
905 (error "Not a valid temperature unit"))
906 (unless (and (consp uold
) (nth 3 uold
))
907 (error "Not a pure temperature expression"))
908 (let ((v (car uold
)))
909 (setq expr
(list '/ expr
(list 'var v
910 (intern (concat "var-"
911 (symbol-name v
)))))))
912 (or (eq (nth 3 uold
) (nth 3 unew
))
913 (cond ((eq (nth 3 uold
) 'K
)
914 (setq expr
(list '- expr
'(float 27315 -
2)))
915 (if (eq (nth 3 unew
) 'F
)
916 (setq expr
(list '+ (list '* expr
'(frac 9 5)) 32))))
917 ((eq (nth 3 uold
) 'C
)
918 (if (eq (nth 3 unew
) 'F
)
919 (setq expr
(list '+ (list '* expr
'(frac 9 5)) 32))
920 (setq expr
(list '+ expr
'(float 27315 -
2)))))
922 (setq expr
(list '* (list '- expr
32) '(frac 5 9)))
923 (if (eq (nth 3 unew
) 'K
)
924 (setq expr
(list '+ expr
'(float 27315 -
2)))))))
927 (list '* expr new
))))
931 (defun math-simplify-units (a)
932 (let ((math-simplifying-units t
)
933 (calc-matrix-mode 'scalar
))
935 (defalias 'calcFunc-usimplify
'math-simplify-units
)
937 (math-defsimplify (+ -
)
938 (and math-simplifying-units
939 (math-units-in-expr-p (nth 1 expr
) nil
)
940 (let* ((units (math-extract-units (nth 1 expr
)))
941 (ratio (math-simplify (math-to-standard-units
942 (list '/ (nth 2 expr
) units
) nil
))))
943 (if (math-units-in-expr-p ratio nil
)
945 (calc-record-why "*Inconsistent units" expr
)
947 (list '* (math-add (math-remove-units (nth 1 expr
))
948 (if (eq (car expr
) '-
) (math-neg ratio
) ratio
))
952 (math-simplify-units-prod))
954 (defun math-simplify-units-prod ()
955 (and math-simplifying-units
957 (Math-realp (nth 1 expr
))
958 (let* ((num (math-float (nth 1 expr
)))
959 (xpon (calcFunc-xpon num
))
960 (unitp (cdr (cdr expr
)))
962 (pow (if (eq (car expr
) '*) 1 -
1))
964 (and (eq (car-safe unit
) '*)
965 (setq unitp
(cdr unit
)
967 (and (eq (car-safe unit
) '^
)
968 (integerp (nth 2 unit
))
969 (setq pow
(* pow
(nth 2 unit
))
972 (and (setq u
(math-check-unit-name unit
))
975 (>= xpon
(if (eq (car u
) 'm
) 1 3)))
978 (reverse math-unit-prefixes
)
982 (or (eq (car u
) (nth 1 unit
))
987 math-unit-prefixes
))))))
988 (setq xpon
(+ xpon uxpon
))
990 (or (memq (car (car p
)) '(?d ?D ?h ?H
))
991 (and (eq (car (car p
)) ?c
)
992 (not (eq (car u
) 'm
)))
993 (< xpon
(setq pxpon
(* (nth 2 (nth 1 (car p
)))
996 (setq pname
(math-build-var-name
997 (if (eq (car (car p
)) 0)
999 (concat (char-to-string
1003 (and (/= (car (car p
)) 0)
1005 math-units-table
)))))
1009 (or (not (eq p pref
))
1010 (< xpon
(+ pxpon
(* (math-abs pow
) 3))))
1013 (let ((calc-prefer-frac nil
))
1014 (calcFunc-scf (nth 1 expr
)
1016 (setcar unitp pname
)
1020 (and math-simplifying-units
1021 (let ((np (cdr expr
))
1022 (try-cancel-units 0)
1024 (setq n
(if (eq (car-safe (nth 2 expr
)) '*)
1027 (if (math-realp (car n
))
1029 (setcar (cdr expr
) (math-mul (nth 1 expr
)
1030 (let ((calc-prefer-frac nil
))
1031 (math-div 1 (car n
)))))
1033 (while (eq (car-safe (setq n
(car np
))) '*)
1034 (math-simplify-units-divisor (cdr n
) (cdr (cdr expr
)))
1035 (setq np
(cdr (cdr n
))))
1036 (math-simplify-units-divisor np
(cdr (cdr expr
)))
1037 (if (eq try-cancel-units
0)
1038 (let* ((math-simplifying-units nil
)
1039 (base (math-simplify (math-to-standard-units expr nil
))))
1040 (if (Math-numberp base
)
1042 (if (eq (car-safe expr
) '/)
1043 (math-simplify-units-prod))
1046 (defun math-simplify-units-divisor (np dp
)
1049 (while (eq (car-safe (setq d
(car dp
))) '*)
1050 (when (setq temp
(math-simplify-units-quotient n
(nth 1 d
)))
1051 (setcar np
(setq n temp
))
1053 (setq dp
(cdr (cdr d
))))
1054 (when (setq temp
(math-simplify-units-quotient n d
))
1055 (setcar np
(setq n temp
))
1058 ;; Simplify, e.g., "in / cm" to "2.54" in a units expression.
1059 (defun math-simplify-units-quotient (n d
)
1062 (when (and (eq (car-safe n
) '^
)
1063 (integerp (nth 2 n
)))
1064 (setq pow1
(nth 2 n
) n
(nth 1 n
)))
1065 (when (and (eq (car-safe d
) '^
)
1066 (integerp (nth 2 d
)))
1067 (setq pow2
(nth 2 d
) d
(nth 1 d
)))
1068 (let ((un (math-check-unit-name n
))
1069 (ud (math-check-unit-name d
)))
1071 (if (and (equal (nth 4 un
) (nth 4 ud
))
1073 (math-to-standard-units (list '/ n d
) nil
)
1080 (and (eq (car (car un
)) (car (car ud1
)))
1081 (setq try-cancel-units
1083 (- (* (cdr (car un
)) pow1
)
1084 (* (cdr (car ud
)) pow2
)))))
1085 (setq ud1
(cdr ud1
)))
1090 (and math-simplifying-units
1091 (math-realp (nth 2 expr
))
1092 (if (memq (car-safe (nth 1 expr
)) '(* /))
1093 (list (car (nth 1 expr
))
1094 (list '^
(nth 1 (nth 1 expr
)) (nth 2 expr
))
1095 (list '^
(nth 2 (nth 1 expr
)) (nth 2 expr
)))
1096 (math-simplify-units-pow (nth 1 expr
) (nth 2 expr
)))))
1098 (math-defsimplify calcFunc-sqrt
1099 (and math-simplifying-units
1100 (if (memq (car-safe (nth 1 expr
)) '(* /))
1101 (list (car (nth 1 expr
))
1102 (list 'calcFunc-sqrt
(nth 1 (nth 1 expr
)))
1103 (list 'calcFunc-sqrt
(nth 2 (nth 1 expr
))))
1104 (math-simplify-units-pow (nth 1 expr
) '(frac 1 2)))))
1106 (math-defsimplify (calcFunc-floor
1116 (and math-simplifying-units
1118 (if (math-only-units-in-expr-p (nth 1 expr
))
1120 (if (and (memq (car-safe (nth 1 expr
)) '(* /))
1121 (or (math-only-units-in-expr-p
1122 (nth 1 (nth 1 expr
)))
1123 (math-only-units-in-expr-p
1124 (nth 2 (nth 1 expr
)))))
1125 (list (car (nth 1 expr
))
1127 (cons (nth 1 (nth 1 expr
))
1130 (cons (nth 2 (nth 1 expr
))
1131 (cdr (cdr expr
)))))))))
1133 (defun math-simplify-units-pow (a pow
)
1134 (if (and (eq (car-safe a
) '^
)
1135 (math-check-unit-name (nth 1 a
))
1136 (math-realp (nth 2 a
)))
1137 (list '^
(nth 1 a
) (math-mul pow
(nth 2 a
)))
1138 (let* ((u (math-check-unit-name a
))
1139 (pf (math-to-simple-fraction pow
))
1140 (d (and (eq (car-safe pf
) 'frac
) (nth 2 pf
))))
1142 (math-units-are-multiple u d
)
1143 (list '^
(math-to-standard-units a nil
) pow
)))))
1146 (defun math-units-are-multiple (u n
)
1148 (while (and u
(= (%
(cdr (car u
)) n
) 0))
1152 (math-defsimplify calcFunc-sin
1153 (and math-simplifying-units
1154 (math-units-in-expr-p (nth 1 expr
) nil
)
1155 (let ((rad (math-simplify-units
1157 (math-to-standard-units (nth 1 expr
) nil
))))
1158 (calc-angle-mode 'rad
))
1159 (and (eq (car-safe rad
) '*)
1160 (math-realp (nth 1 rad
))
1161 (eq (car-safe (nth 2 rad
)) 'var
)
1162 (eq (nth 1 (nth 2 rad
)) 'rad
)
1163 (list 'calcFunc-sin
(nth 1 rad
))))))
1165 (math-defsimplify calcFunc-cos
1166 (and math-simplifying-units
1167 (math-units-in-expr-p (nth 1 expr
) nil
)
1168 (let ((rad (math-simplify-units
1170 (math-to-standard-units (nth 1 expr
) nil
))))
1171 (calc-angle-mode 'rad
))
1172 (and (eq (car-safe rad
) '*)
1173 (math-realp (nth 1 rad
))
1174 (eq (car-safe (nth 2 rad
)) 'var
)
1175 (eq (nth 1 (nth 2 rad
)) 'rad
)
1176 (list 'calcFunc-cos
(nth 1 rad
))))))
1178 (math-defsimplify calcFunc-tan
1179 (and math-simplifying-units
1180 (math-units-in-expr-p (nth 1 expr
) nil
)
1181 (let ((rad (math-simplify-units
1183 (math-to-standard-units (nth 1 expr
) nil
))))
1184 (calc-angle-mode 'rad
))
1185 (and (eq (car-safe rad
) '*)
1186 (math-realp (nth 1 rad
))
1187 (eq (car-safe (nth 2 rad
)) 'var
)
1188 (eq (nth 1 (nth 2 rad
)) 'rad
)
1189 (list 'calcFunc-tan
(nth 1 rad
))))))
1192 (defun math-remove-units (expr)
1193 (if (math-check-unit-name expr
)
1195 (if (Math-primp expr
)
1198 (mapcar 'math-remove-units
(cdr expr
))))))
1200 (defun math-extract-units (expr)
1201 (if (memq (car-safe expr
) '(* /))
1203 (mapcar 'math-extract-units
(cdr expr
)))
1204 (if (math-check-unit-name expr
) expr
1)))
1206 (defun math-build-units-table-buffer (enter-buffer)
1207 (if (not (and math-units-table math-units-table-buffer-valid
1208 (get-buffer "*Units Table*")))
1209 (let ((buf (get-buffer-create "*Units Table*"))
1210 (uptr (math-build-units-table))
1211 (calc-language (if (eq calc-language
'big
) nil calc-language
))
1212 (calc-float-format '(float 0))
1213 (calc-group-digits nil
)
1214 (calc-number-radix 10)
1215 (calc-point-char ".")
1219 (message "Formatting units table...")
1221 (setq buffer-read-only nil
)
1223 (insert "Calculator Units Table:\n\n")
1224 (insert "Unit Type Definition Description\n\n")
1228 (when (eq (car u
) 'm
)
1230 (setq shadowed
(and std
(assq (car u
) math-additional-units
)))
1233 (eq (aref name
0) ?\
*))
1234 (unless (eq uptr math-units-table
)
1236 (setq name
(substring name
1)))
1238 (and shadowed
(insert "("))
1239 (insert (symbol-name (car u
)))
1240 (and shadowed
(insert ")"))
1244 (insert (symbol-name (nth 3 u
))))
1250 (and shadowed
(insert "("))
1252 (insert (math-format-value (nth 1 u
) 80))
1253 (insert (symbol-name (car u
))))
1254 (and shadowed
(insert ")"))
1260 (insert " (redefined above)")
1262 (insert " (base unit)")))
1264 (setq uptr
(cdr uptr
)))
1265 (insert "\n\nUnit Prefix Table:\n\n")
1266 (setq uptr math-unit-prefixes
)
1269 (insert " " (char-to-string (car u
)))
1270 (if (equal (nth 1 u
) (nth 1 (nth 1 uptr
)))
1271 (insert " " (char-to-string (car (car (setq uptr
(cdr uptr
)))))
1274 (insert "10^" (int-to-string (nth 2 (nth 1 u
))))
1276 (insert " " (nth 2 u
) "\n")
1277 (while (eq (car (car (setq uptr
(cdr uptr
)))) 0)))
1279 (setq buffer-read-only t
)
1280 (message "Formatting units table...done"))
1281 (setq math-units-table-buffer-valid t
)
1282 (let ((oldbuf (current-buffer)))
1284 (goto-char (point-min))
1285 (set-buffer oldbuf
))
1288 (display-buffer buf
)))
1290 (pop-to-buffer (get-buffer "*Units Table*"))
1291 (display-buffer (get-buffer "*Units Table*")))))
1293 ;;; calc-units.el ends here