1 ;;; calc-units.el --- unit conversion functions for Calc
3 ;; Copyright (C) 1990-1993, 2001-2015 Free Software Foundation, Inc.
5 ;; Author: David Gillespie <daveg@synaptics.com>
6 ;; Maintainer: Jay Belanger <jay.p.belanger@gmail.com>
8 ;; This file is part of GNU Emacs.
10 ;; GNU Emacs is free software: you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation, either version 3 of the License, or
13 ;; (at your option) any later version.
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
27 ;; This file is autoloaded from calc-ext.el.
36 ;;; Units table last updated 9-Jan-91 by Ulrich Mueller (ulm@vsnhd1.cern.ch)
37 ;;; with some additions by Przemek Klosowski (przemek@rrdstrad.nist.gov)
38 ;;; Updated April 2002 by Jochen Küpper
40 ;;; Updated August 2007, using
41 ;;; CODATA (http://physics.nist.gov/cuu/Constants/index.html)
42 ;;; NIST (http://physics.nist.gov/Pubs/SP811/appenB9.html)
43 ;;; ESUWM (Encyclopaedia of Scientific Units, Weights and
44 ;;; Measures, by François Cardarelli)
45 ;;; All conversions are exact unless otherwise noted.
47 (defvar math-standard-units
50 ( in
"254*10^(-2) cm" "Inch" nil
54 ( mi
"5280 ft" "Mile" )
55 ( au
"149597870691. m" "Astronomical Unit" nil
57 ;; (approx) NASA JPL (http://neo.jpl.nasa.gov/glossary/au.html)
58 ( lyr
"c yr" "Light Year" )
59 ( pc
"3.0856775854*10^16 m" "Parsec (**)" nil
60 "3.0856775854 10^16 m (*)") ;; (approx) ESUWM
61 ( nmi
"1852 m" "Nautical Mile" )
62 ( fath
"6 ft" "Fathom" )
63 ( fur
"660 ft" "Furlong")
64 ( mu
"1 um" "Micron" )
65 ( mil
"(1/1000) in" "Mil" )
66 ( point
"(1/72) in" "Point (PostScript convention)" )
67 ( Ang
"10^(-10) m" "Angstrom" )
68 ( mfi
"mi+ft+in" "Miles + feet + inches" )
70 ( texpt
"(100/7227) in" "Point (TeX convention) (**)" )
71 ( texpc
"12 texpt" "Pica (TeX convention) (**)" )
72 ( texbp
"point" "Big point (TeX convention) (**)" )
73 ( texdd
"(1238/1157) texpt" "Didot point (TeX convention) (**)" )
74 ( texcc
"12 texdd" "Cicero (TeX convention) (**)" )
75 ( texsp
"(1/65536) texpt" "Scaled TeX point (TeX convention) (**)" )
78 ( hect
"10000 m^2" "*Hectare" )
80 ( acre
"(1/640) mi^2" "Acre" )
81 ( b
"10^(-28) m^2" "Barn" )
84 ( L
"10^(-3) m^3" "*Liter" )
86 ( gal
"4 qt" "US Gallon" )
88 ( pt
"2 cup" "Pint (**)" )
89 ( cup
"8 ozfl" "Cup" )
90 ( ozfl
"2 tbsp" "Fluid Ounce" )
91 ( floz
"2 tbsp" "Fluid Ounce" )
92 ( tbsp
"3 tsp" "Tablespoon" )
93 ;; ESUWM defines a US gallon as 231 in^3.
94 ;; That gives the following exact value for tsp.
95 ( tsp
"492892159375*10^(-11) ml" "Teaspoon" nil
97 ( vol
"tsp+tbsp+ozfl+cup+pt+qt+gal" "Gallons + ... + teaspoons" nil
98 "tsp+tbsp+ozfl+cup+pt+qt+gal")
99 ( galC
"galUK" "Canadian Gallon" )
100 ( galUK
"454609*10^(-5) L" "UK Gallon" nil
106 ( min
"60 s" "Minute" )
107 ( hr
"60 min" "Hour" )
108 ( day
"24 hr" "Day" )
109 ( wk
"7 day" "Week" )
110 ( hms
"wk+day+hr+min+s" "Hours, minutes, seconds" )
111 ( yr
"36525*10^(-2) day" "Year (Julian)" nil
116 ( mph
"mi/hr" "*Miles per hour" )
117 ( kph
"km/hr" "Kilometers per hour" )
118 ( knot
"nmi/hr" "Knot" )
119 ( c
"299792458 m/s" "Speed of light" ) ;;; CODATA
122 ( ga
"980665*10^(-5) m/s^2" "*\"g\" acceleration" nil
123 "9.80665 m / s^2") ;; CODATA
127 ( lb
"16 oz" "Pound (mass)" )
128 ( oz
"28349523125*10^(-9) g" "Ounce (mass)" nil
129 "28.349523125 g") ;; ESUWM
130 ( ton
"2000 lb" "Ton" )
131 ( tpo
"ton+lb+oz" "Tons + pounds + ounces (mass)" )
132 ( t
"1000 kg" "Metric ton" )
133 ( tonUK
"10160469088*10^(-7) kg" "UK ton" nil
134 "1016.0469088 kg") ;; ESUWM
135 ( lbt
"12 ozt" "Troy pound" )
136 ( ozt
"311034768*10^(-7) g" "Troy ounce" nil
137 "31.10347680 g") ;; ESUWM, 1/12 exact value for lbt
138 ( ct
"(2/10) g" "Carat" nil
140 ( u
"1.660538782*10^(-27) kg" "Unified atomic mass" nil
141 "1.660538782 10^-27 kg (*)");;(approx) CODATA
144 ( N
"m kg/s^2" "*Newton" )
145 ( dyn
"10^(-5) N" "Dyne" )
146 ( gf
"ga g" "Gram (force)" )
147 ( lbf
"ga lb" "Pound (force)" )
148 ( kip
"1000 lbf" "Kilopound (force)" )
149 ( pdl
"138254954376*10^(-12) N" "Poundal" nil
150 "0.138254954376 N") ;; ESUWM
154 ( erg
"10^(-7) J" "Erg" )
155 ( cal
"41868*10^(-4) J" "International Table Calorie" nil
157 ( calth
"4184*10^(-3) J" "Thermochemical Calorie" nil
159 ( Cal
"1000 cal" "Large Calorie")
160 ( Btu
"105505585262*10^(-8) J" "International Table Btu" nil
161 "1055.05585262 J") ;; ESUWM
162 ( eV
"ech V" "Electron volt" )
163 ( ev
"eV" "Electron volt" )
164 ( therm
"105506000 J" "EEC therm" )
165 ( invcm
"h c/cm" "Energy in inverse centimeters" )
166 ( Kayser
"invcm" "Kayser (inverse centimeter energy)" )
167 ( men
"100/invcm" "Inverse energy in meters" )
168 ( Hzen
"h Hz" "Energy in Hertz")
169 ( Ken
"k K" "Energy in Kelvins")
170 ( Wh
"W hr" "Watt hour")
171 ( Ws
"W s" "Watt second")
175 ( hp
"550 ft lbf/s" "Horsepower") ;;ESUWM
176 ( hpm
"75 m kgf/s" "Metric Horsepower") ;;ESUWM
179 ( K nil
"*Degree Kelvin" K
)
180 ( dK
"K" "Degree Kelvin" K
)
181 ( degK
"K" "Degree Kelvin" K
)
182 ( dC
"K" "Degree Celsius" C
)
183 ( degC
"K" "Degree Celsius" C
)
184 ( dF
"(5/9) K" "Degree Fahrenheit" F
)
185 ( degF
"(5/9) K" "Degree Fahrenheit" F
)
188 ( Pa
"N/m^2" "*Pascal" )
189 ( bar
"10^5 Pa" "Bar" )
190 ( atm
"101325 Pa" "Standard atmosphere" ) ;; CODATA
191 ( Torr
"(1/760) atm" "Torr")
192 ( mHg
"1000 Torr" "Meter of mercury" )
193 ( inHg
"254*10^(-1) mmHg" "Inch of mercury" nil
195 ( inH2O
"2.490889*10^2 Pa" "Inch of water" nil
196 "2.490889 10^2 Pa (*)") ;;(approx) NIST
197 ( psi
"lbf/in^2" "Pounds per square inch" )
200 ( P
"(1/10) Pa s" "*Poise" )
201 ( St
"10^(-4) m^2/s" "Stokes" )
205 ( C
"A s" "Coulomb" )
206 ( Fdy
"ech Nav" "Faraday" )
207 ( e
"ech" "Elementary charge" )
208 ( ech
"1.602176487*10^(-19) C" "Elementary charge" nil
209 "1.602176487 10^-19 C (*)") ;;(approx) CODATA
214 ( S
"A/V" "Siemens" )
217 ( T
"Wb/m^2" "Tesla" )
218 ( Gs
"10^(-4) T" "Gauss" )
221 ;; Luminous intensity
222 ( cd nil
"*Candela" )
223 ( sb
"10000 cd/m^2" "Stilb" )
224 ( lm
"cd sr" "Lumen" )
225 ( lx
"lm/m^2" "Lux" )
226 ( ph
"10000 lx" "Phot" )
227 ( fc
"lm/ft^2" "Footcandle") ;; ESUWM
228 ( lam
"10000 lm/m^2" "Lambert" )
229 ( flam
"(1/pi) cd/ft^2" "Footlambert") ;; ESUWM
232 ( Bq
"1/s" "*Becquerel" )
233 ( Ci
"37*10^9 Bq" "Curie" ) ;; ESUWM
235 ( Sv
"Gy" "Sievert" )
236 ( R
"258*10^(-6) C/kg" "Roentgen" ) ;; NIST
237 ( rd
"(1/100) Gy" "Rad" )
240 ;; Amount of substance
244 ( rad nil
"*Radian" )
245 ( circ
"2 pi rad" "Full circle" )
246 ( rev
"circ" "Full revolution" )
247 ( deg
"circ/360" "Degree" )
248 ( arcmin
"deg/60" "Arc minute" )
249 ( arcsec
"arcmin/60" "Arc second" )
250 ( grad
"circ/400" "Grade" )
251 ( rpm
"rev/min" "Revolutions per minute" )
254 ( sr nil
"*Steradian" )
256 ;; Other physical quantities
257 ;; The values are from CODATA, and are approximate.
258 ( h
"6.62606896*10^(-34) J s" "*Planck's constant" nil
259 "6.62606896 10^-34 J s (*)")
260 ( hbar
"h / (2 pi)" "Planck's constant" ) ;; Exact
261 ( mu0
"4 pi 10^(-7) H/m" "Permeability of vacuum") ;; Exact
262 ( μ
0 "mu0" "Permeability of vacuum") ;; Exact
263 ( eps0
"1 / (mu0 c^2)" "Permittivity of vacuum" )
264 ( ε
0 "eps0" "Permittivity of vacuum" )
265 ( G
"6.67428*10^(-11) m^3/(kg s^2)" "Gravitational constant" nil
266 "6.67428 10^-11 m^3/(kg s^2) (*)")
267 ( Nav
"6.02214179*10^(23) / mol" "Avogadro's constant" nil
268 "6.02214179 10^23 / mol (*)")
269 ( me
"9.10938215*10^(-31) kg" "Electron rest mass" nil
270 "9.10938215 10^-31 kg (*)")
271 ( mp
"1.672621637*10^(-27) kg" "Proton rest mass" nil
272 "1.672621637 10^-27 kg (*)")
273 ( mn
"1.674927211*10^(-27) kg" "Neutron rest mass" nil
274 "1.674927211 10^-27 kg (*)")
275 ( mmu
"1.88353130*10^(-28) kg" "Muon rest mass" nil
276 "1.88353130 10^-28 kg (*)")
277 ( mμ
"mmu" "Muon rest mass" nil
278 "1.88353130 10^-28 kg (*)")
279 ( Ryd
"10973731.568527 /m" "Rydberg's constant" nil
280 "10973731.568527 /m (*)")
281 ( k
"1.3806504*10^(-23) J/K" "Boltzmann's constant" nil
282 "1.3806504 10^-23 J/K (*)")
283 ( alpha
"7.2973525376*10^(-3)" "Fine structure constant" nil
284 "7.2973525376 10^-3 (*)")
285 ( α
"alpha" "Fine structure constant" nil
286 "7.2973525376 10^-3 (*)")
287 ( muB
"927.400915*10^(-26) J/T" "Bohr magneton" nil
288 "927.400915 10^-26 J/T (*)")
289 ( muN
"5.05078324*10^(-27) J/T" "Nuclear magneton" nil
290 "5.05078324 10^-27 J/T (*)")
291 ( mue
"-928.476377*10^(-26) J/T" "Electron magnetic moment" nil
292 "-928.476377 10^-26 J/T (*)")
293 ( mup
"1.410606662*10^(-26) J/T" "Proton magnetic moment" nil
294 "1.410606662 10^-26 J/T (*)")
295 ( R0
"8.314472 J/(mol K)" "Molar gas constant" nil
296 "8.314472 J/(mol K) (*)")
297 ( V0
"22.710981*10^(-3) m^3/mol" "Standard volume of ideal gas" nil
298 "22.710981 10^-3 m^3/mol (*)")
301 ( dB
"(ln(10)/20) Np" "decibel")))
304 (defvar math-additional-units nil
305 "Additional units table for user-defined units.
306 Must be formatted like `math-standard-units'.
307 If you change this, be sure to set `math-units-table' to nil to ensure
308 that the combined units table will be rebuilt.")
310 (defvar math-unit-prefixes
311 '( ( ?Y
(^
10 24) "Yotta" )
312 ( ?Z
(^
10 21) "Zetta" )
313 ( ?E
(^
10 18) "Exa" )
314 ( ?P
(^
10 15) "Peta" )
315 ( ?T
(^
10 12) "Tera" )
316 ( ?G
(^
10 9) "Giga" )
317 ( ?M
(^
10 6) "Mega" )
318 ( ?k
(^
10 3) "Kilo" )
319 ( ?K
(^
10 3) "Kilo" )
320 ( ?h
(^
10 2) "Hecto" )
321 ( ?H
(^
10 2) "Hecto" )
322 ( ?D
(^
10 1) "Deka" )
324 ( ?d
(^
10 -
1) "Deci" )
325 ( ?c
(^
10 -
2) "Centi" )
326 ( ?m
(^
10 -
3) "Milli" )
327 ( ?u
(^
10 -
6) "Micro" )
328 ( ?μ
(^
10 -
6) "Micro" )
329 ( ?n
(^
10 -
9) "Nano" )
330 ( ?p
(^
10 -
12) "Pico" )
331 ( ?f
(^
10 -
15) "Femto" )
332 ( ?a
(^
10 -
18) "Atto" )
333 ( ?z
(^
10 -
21) "zepto" )
334 ( ?y
(^
10 -
24) "yocto" )))
336 (defvar math-standard-units-systems
338 ( si
( ( g
'(/ (var kg var-kg
) 1000) ) ) )
339 ( mks
( ( g
'(/ (var kg var-kg
) 1000) ) ) )
340 ( cgs
( ( m
'(* (var cm var-cm
) 100 ) ) ) )))
342 (defvar math-units-table nil
343 "Internal units table.
344 Derived from `math-standard-units' and `math-additional-units'.
345 Entries are (SYMBOL EXPR DOC-STRING TEMP-TYPE BASE-UNITS).")
347 (defvar math-units-table-buffer-valid nil
)
351 (defun calc-base-units ()
354 (let ((calc-autorange-units nil
))
355 (calc-enter-result 1 "bsun" (math-simplify-units
356 (math-to-standard-units (calc-top-n 1)
359 (defvar calc-ensure-consistent-units
)
361 (defun calc-quick-units ()
364 (let* ((num (- last-command-event ?
0))
365 (pos (if (= num
0) 10 num
))
366 (units (calc-var-value 'var-Units
))
367 (expr (calc-top-n 1)))
368 (unless (and (>= num
0) (<= num
9))
369 (error "Bad unit number"))
370 (unless (math-vectorp units
)
371 (error "No \"quick units\" are defined"))
372 (unless (< pos
(length units
))
373 (error "Unit number %d not defined" pos
))
374 (if (math-units-in-expr-p expr nil
)
376 (if calc-ensure-consistent-units
377 (math-check-unit-consistency expr
(nth pos units
)))
378 (calc-enter-result 1 (format "cun%d" num
)
379 (math-convert-units expr
(nth pos units
))))
380 (calc-enter-result 1 (format "*un%d" num
)
382 (math-mul expr
(nth pos units
))))))))
384 (defun math-get-standard-units (expr)
385 "Return the standard units in EXPR."
388 (math-to-standard-units expr nil
))))
390 (defun math-get-units (expr)
391 "Return the units in EXPR."
393 (math-extract-units expr
)))
395 (defun math-make-unit-string (expr)
396 "Return EXPR in string form.
397 If EXPR is nil, return nil."
399 (let ((cexpr (math-compose-expr expr
0)))
400 (replace-regexp-in-string
404 (math-composition-to-string cexpr
))))))
406 (defvar math-default-units-table
407 (make-hash-table :test
'equal
)
408 "A table storing previously converted units.")
410 (defun math-get-default-units (expr)
411 "Get default units to use when converting the units in EXPR."
412 (let* ((units (math-get-units expr
))
413 (standard-units (math-get-standard-units expr
))
414 (default-units (gethash
416 math-default-units-table
)))
417 (if (equal units
(car default-units
))
418 (math-make-unit-string (cadr default-units
))
419 (math-make-unit-string (car default-units
)))))
421 (defun math-put-default-units (expr &optional comp std
)
422 "Put the units in EXPR in the default units table.
423 If COMP or STD is non-nil, put that in the units table instead."
424 (let* ((new-units (or comp std
(math-get-units expr
)))
425 (standard-units (math-get-standard-units
427 (comp (math-simplify-units expr
))
430 (default-units (gethash standard-units math-default-units-table
)))
431 (unless (eq standard-units
1)
434 (puthash standard-units
(list new-units
) math-default-units-table
))
435 ((not (equal new-units
(car default-units
)))
436 (puthash standard-units
437 (list new-units
(car default-units
))
438 math-default-units-table
))))))
440 (defvar calc-allow-units-as-numbers t
)
442 (defun calc-convert-units (&optional old-units new-units
)
445 (let ((expr (calc-top-n 1))
452 (if (or (not (math-units-in-expr-p expr t
))
453 (setq unitscancel
(and
454 (if (get 'calc-allow-units-as-numbers
'saved-value
)
455 (car (get 'calc-allow-units-as-numbers
'saved-value
))
456 calc-allow-units-as-numbers
)
457 (eq (math-get-standard-units expr
) 1))))
458 (let ((uold (or old-units
463 "(The expression is unitless when simplified) Old Units: ")
464 (read-string "Old units: ")))
465 (if (equal uoldname
"")
467 (setq nouold unitscancel
)
470 (if (string-match "\\` */" uoldname
)
471 (setq uoldname
(concat "1" uoldname
)))
472 (math-read-expr uoldname
))))))
473 (unless (math-units-in-expr-p uold t
)
474 (error "No units specified"))
475 (when (eq (car-safe uold
) 'error
)
476 (error "Bad format in units expression: %s" (nth 1 uold
)))
477 (setq expr
(math-mul expr uold
))))
478 (setq defunits
(math-get-default-units expr
))
482 (if (and uoldname
(not nouold
))
483 (concat "Old units: "
494 (string= new-units
"")
496 (setq new-units defunits
)))
497 (when (string-match "\\` */" new-units
)
498 (setq new-units
(concat "1" new-units
)))
499 (setq units
(math-read-expr new-units
))
500 (when (eq (car-safe units
) 'error
)
501 (error "Bad format in units expression: %s" (nth 2 units
)))
502 (if calc-ensure-consistent-units
503 (math-check-unit-consistency expr units
))
504 (let ((unew (math-units-in-expr-p units t
))
505 (std (and (eq (car-safe units
) 'var
)
506 (assq (nth 1 units
) math-standard-units-systems
)))
507 (comp (eq (car-safe units
) '+)))
508 (unless (or unew std
)
509 (error "No units specified"))
510 (let* ((noold (and uoldname
(not (equal uoldname
"1"))))
513 (math-simplify-units (math-to-standard-units expr
(nth 1 std
)))
514 (math-convert-units expr units noold
))))
516 (math-put-default-units (if noold units res
) (if comp units
)))
517 (calc-enter-result 1 "cvun" res
))))))
519 (defun calc-convert-exact-units ()
522 (let* ((expr (calc-top-n 1)))
523 (unless (math-units-in-expr-p expr t
)
524 (error "No units in expression."))
525 (let* ((old-units (math-extract-units expr
))
526 (defunits (math-get-default-units expr
))
529 (read-string (concat "New units"
537 (string= new-units
"")
539 (setq new-units defunits
))
540 (setq units
(math-read-expr new-units
))
541 (when (eq (car-safe units
) 'error
)
542 (error "Bad format in units expression: %s" (nth 2 units
)))
543 (math-check-unit-consistency old-units units
)
545 (list '* (math-mul (math-remove-units expr
)
547 (math-to-standard-units (list '/ old-units units
) nil
)))
549 (calc-enter-result 1 "cvxu" res
))))))
551 (defun calc-autorange-units (arg)
554 (calc-change-mode 'calc-autorange-units arg nil t
)
555 (message (if calc-autorange-units
556 "Adjusting target unit prefix automatically"
557 "Using target units exactly"))))
559 (defun calc-convert-temperature (&optional old-units new-units
)
562 (let ((expr (calc-top-n 1))
567 (setq uold
(or old-units
568 (let ((units (math-single-units-in-expr-p expr
)))
571 (list 'var
(car units
)
572 (intern (concat "var-"
575 (error "Not a pure temperature expression"))
577 (setq uoldname
(read-string
578 "Old temperature units: ")))))))
579 (when (eq (car-safe uold
) 'error
)
580 (error "Bad format in units expression: %s" (nth 2 uold
)))
581 (or (math-units-in-expr-p expr nil
)
582 (setq expr
(math-mul expr uold
)))
583 (setq defunits
(math-get-default-units expr
))
584 (setq unew
(or new-units
588 (concat "Old temperature units: "
591 "New temperature units")
597 (setq unew
(math-read-expr (if (string= unew
"") defunits unew
)))
598 (when (eq (car-safe unew
) 'error
)
599 (error "Bad format in units expression: %s" (nth 2 unew
)))
600 (math-put-default-units unew
)
601 (let ((ntemp (calc-normalize
603 (math-convert-temperature expr uold unew
605 (if (Math-zerop ntemp
)
606 (setq ntemp
(list '* ntemp unew
)))
607 (let ((calc-simplify-mode 'none
))
608 (calc-enter-result 1 "cvtm" ntemp
))))))
610 (defun calc-remove-units ()
613 (calc-enter-result 1 "rmun" (math-simplify-units
614 (math-remove-units (calc-top-n 1))))))
616 (defun calc-extract-units ()
619 (calc-enter-result 1 "exun" (math-simplify-units
620 (math-extract-units (calc-top-n 1))))))
622 ;; The variables calc-num-units and calc-den-units are local to
623 ;; calc-explain-units, but are used by calc-explain-units-rec,
624 ;; which is called by calc-explain-units.
625 (defvar calc-num-units
)
626 (defvar calc-den-units
)
628 (defun calc-explain-units ()
631 (let ((calc-num-units nil
)
632 (calc-den-units nil
))
633 (calc-explain-units-rec (calc-top-n 1) 1)
634 (and calc-den-units
(string-match "^[^(].* .*[^)]$" calc-den-units
)
635 (setq calc-den-units
(concat "(" calc-den-units
")")))
638 (message "%s per %s" calc-num-units calc-den-units
)
639 (message "%s" calc-num-units
))
641 (message "1 per %s" calc-den-units
)
642 (message "No units in expression"))))))
644 (defun calc-explain-units-rec (expr pow
)
645 (let ((u (math-check-unit-name expr
))
647 (if (and u
(not (math-zerop pow
)))
648 (let ((name (or (nth 2 u
) (symbol-name (car u
)))))
649 (if (eq (aref name
0) ?\
*)
650 (setq name
(substring name
1)))
651 (if (string-match "[^a-zA-Zα-ωΑ-Ω0-9']" name
)
652 (if (string-match "^[a-zA-Zα-ωΑ-Ω0-9' ()]*$" name
)
653 (while (setq pos
(string-match "[ ()]" name
))
654 (setq name
(concat (substring name
0 pos
)
655 (if (eq (aref name pos
) 32) "-" "")
656 (substring name
(1+ pos
)))))
657 (setq name
(concat "(" name
")"))))
658 (or (eq (nth 1 expr
) (car u
))
659 (setq name
(concat (nth 2 (assq (aref (symbol-name
662 (if (and (string-match "[^a-zA-Zα-ωΑ-Ω0-9']" name
)
663 (not (memq (car u
) '(mHg gf
))))
666 (cond ((or (math-equal-int pow
1)
667 (math-equal-int pow -
1)))
668 ((or (math-equal-int pow
2)
669 (math-equal-int pow -
2))
670 (if (equal (nth 4 u
) '((m .
1)))
671 (setq name
(concat "Square-" name
))
672 (setq name
(concat name
"-squared"))))
673 ((or (math-equal-int pow
3)
674 (math-equal-int pow -
3))
675 (if (equal (nth 4 u
) '((m .
1)))
676 (setq name
(concat "Cubic-" name
))
677 (setq name
(concat name
"-cubed"))))
679 (setq name
(concat name
"^"
680 (math-format-number (math-abs pow
))))))
682 (setq calc-num-units
(if calc-num-units
683 (concat calc-num-units
" " name
)
685 (setq calc-den-units
(if calc-den-units
686 (concat calc-den-units
" " name
)
688 (cond ((eq (car-safe expr
) '*)
689 (calc-explain-units-rec (nth 1 expr
) pow
)
690 (calc-explain-units-rec (nth 2 expr
) pow
))
691 ((eq (car-safe expr
) '/)
692 (calc-explain-units-rec (nth 1 expr
) pow
)
693 (calc-explain-units-rec (nth 2 expr
) (- pow
)))
694 ((memq (car-safe expr
) '(neg + -
))
695 (calc-explain-units-rec (nth 1 expr
) pow
))
696 ((and (eq (car-safe expr
) '^
)
697 (math-realp (nth 2 expr
)))
698 (calc-explain-units-rec (nth 1 expr
)
699 (math-mul pow
(nth 2 expr
))))))))
701 (defun calc-simplify-units ()
704 (calc-with-default-simplification
705 (calc-enter-result 1 "smun" (math-simplify-units (calc-top-n 1))))))
707 (defun calc-view-units-table (n)
709 (and n
(setq math-units-table-buffer-valid nil
))
710 (let ((win (get-buffer-window "*Units Table*")))
713 math-units-table-buffer-valid
)
715 (bury-buffer (window-buffer win
))
716 (let ((curwin (selected-window)))
718 (switch-to-buffer nil
)
719 (select-window curwin
)))
720 (math-build-units-table-buffer nil
))))
722 (defun calc-enter-units-table (n)
724 (and n
(setq math-units-table-buffer-valid nil
))
725 (math-build-units-table-buffer t
)
726 (message "%s" (substitute-command-keys "Type \\[calc] to return to the Calculator")))
728 (defun calc-define-unit (uname desc
&optional disp
)
729 (interactive "SDefine unit name: \nsDescription: \nP")
730 (if disp
(setq disp
(read-string "Display definition: ")))
732 (let ((form (calc-top-n 1))
733 (unit (assq uname math-additional-units
)))
735 (setq math-additional-units
736 (cons (setq unit
(list uname nil nil nil nil
))
737 math-additional-units
)
738 math-units-table nil
))
739 (setcar (cdr unit
) (and (not (and (eq (car-safe form
) 'var
)
740 (eq (nth 1 form
) uname
)))
741 (not (math-equal-int form
1))
742 (math-format-flat-expr form
0)))
743 (setcar (cdr (cdr unit
)) (and (not (equal desc
""))
746 (setcar (cdr (cdr (cdr (cdr unit
)))) disp
))))
747 (calc-invalidate-units-table))
749 (defun calc-undefine-unit (uname)
750 (interactive "SUndefine unit name: ")
752 (let ((unit (assq uname math-additional-units
)))
754 (if (assq uname math-standard-units
)
755 (error "\"%s\" is a predefined unit name" uname
)
756 (error "Unit name \"%s\" not found" uname
)))
757 (setq math-additional-units
(delq unit math-additional-units
)
758 math-units-table nil
)))
759 (calc-invalidate-units-table))
761 (defun calc-invalidate-units-table ()
762 (setq math-units-table nil
)
763 (let ((buf (get-buffer "*Units Table*")))
765 (with-current-buffer buf
767 (goto-char (point-min))
768 (if (looking-at "Calculator Units Table")
769 (let ((inhibit-read-only t
))
770 (insert "(Obsolete) "))))))))
772 (defun calc-get-unit-definition (uname)
773 (interactive "SGet definition for unit: ")
775 (math-build-units-table)
776 (let ((unit (assq uname math-units-table
)))
778 (error "Unit name \"%s\" not found" uname
))
779 (let ((msg (nth 2 unit
)))
781 (if (string-match "^\\*" msg
)
782 (setq msg
(substring msg
1)))
783 (setq msg
(symbol-name uname
)))
786 (calc-enter-result 0 "ugdf" (nth 1 unit
))
787 (message "Derived unit: %s" msg
))
788 (calc-enter-result 0 "ugdf" (list 'var uname
791 (symbol-name uname
)))))
792 (message "Base unit: %s" msg
))))))
794 (defun calc-permanent-units ()
798 (set-buffer (find-file-noselect (substitute-in-file-name
799 calc-settings-file
)))
800 (goto-char (point-min))
801 (if (and (search-forward ";;; Custom units stored by Calc" nil t
)
805 (search-forward "\n;;; End of custom units" nil t
)))
809 (delete-region pos
(point)))
810 (goto-char (point-max))
813 (insert ";;; Custom units stored by Calc on " (current-time-string) "\n")
814 (if math-additional-units
816 (insert "(setq math-additional-units '(\n")
817 (let ((list math-additional-units
))
819 (insert " (" (symbol-name (car (car list
))) " "
820 (if (nth 1 (car list
))
821 (if (stringp (nth 1 (car list
)))
822 (prin1-to-string (nth 1 (car list
)))
823 (prin1-to-string (math-format-flat-expr
824 (nth 1 (car list
)) 0)))
827 (prin1-to-string (nth 2 (car list
)))
829 (setq list
(cdr list
))))
831 (insert ";;; (no custom units defined)\n"))
832 (insert ";;; End of custom units\n")
836 ;; The variable math-cu-unit-list is local to math-build-units-table,
837 ;; but is used by math-compare-unit-names, which is called (indirectly)
838 ;; by math-build-units-table.
839 ;; math-cu-unit-list is also local to math-convert-units, but is used
840 ;; by math-convert-units-rec, which is called by math-convert-units.
841 (defvar math-cu-unit-list
)
843 (defun math-build-units-table ()
845 (let* ((combined-units (append math-additional-units
846 math-standard-units
))
847 (math-cu-unit-list (mapcar 'car combined-units
))
849 (message "Building units table...")
850 (setq math-units-table-buffer-valid nil
)
851 (setq tab
(mapcar (function
855 (if (stringp (nth 1 x
))
856 (let ((exp (math-read-plain-expr
858 (if (eq (car-safe exp
) 'error
)
859 (error "Format error in definition of %s in units table: %s"
866 (list (cons (car x
) 1)))
869 (let ((math-units-table tab
))
870 (mapc 'math-find-base-units tab
))
871 (message "Building units table...done")
872 (setq math-units-table tab
))))
874 ;; The variables math-fbu-base and math-fbu-entry are local to
875 ;; math-find-base-units, but are used by math-find-base-units-rec,
876 ;; which is called by math-find-base-units.
877 (defvar math-fbu-base
)
878 (defvar math-fbu-entry
)
880 (defun math-find-base-units (math-fbu-entry)
881 (if (eq (nth 4 math-fbu-entry
) 'boom
)
882 (error "Circular definition involving unit %s" (car math-fbu-entry
)))
883 (or (nth 4 math-fbu-entry
)
885 (setcar (nthcdr 4 math-fbu-entry
) 'boom
)
886 (math-find-base-units-rec (nth 1 math-fbu-entry
) 1)
888 (error "Dimensionless definition for unit %s" (car math-fbu-entry
)))
889 (while (eq (cdr (car math-fbu-base
)) 0)
890 (setq math-fbu-base
(cdr math-fbu-base
)))
891 (let ((b math-fbu-base
))
893 (if (eq (cdr (car (cdr b
))) 0)
894 (setcdr b
(cdr (cdr b
)))
896 (setq math-fbu-base
(sort math-fbu-base
'math-compare-unit-names
))
897 (setcar (nthcdr 4 math-fbu-entry
) math-fbu-base
)
900 (defun math-compare-unit-names (a b
)
901 (memq (car b
) (cdr (memq (car a
) math-cu-unit-list
))))
903 (defun math-find-base-units-rec (expr pow
)
904 (let ((u (math-check-unit-name expr
)))
906 (let ((ulist (math-find-base-units u
)))
908 (let ((p (* (cdr (car ulist
)) pow
))
909 (old (assq (car (car ulist
)) math-fbu-base
)))
911 (setcdr old
(+ (cdr old
) p
))
913 (cons (cons (car (car ulist
)) p
) math-fbu-base
))))
914 (setq ulist
(cdr ulist
)))))
915 ((math-scalarp expr
))
916 ((and (eq (car expr
) '^
)
917 (integerp (nth 2 expr
)))
918 (math-find-base-units-rec (nth 1 expr
) (* pow
(nth 2 expr
))))
920 (math-find-base-units-rec (nth 1 expr
) pow
)
921 (math-find-base-units-rec (nth 2 expr
) pow
))
923 (math-find-base-units-rec (nth 1 expr
) pow
)
924 (math-find-base-units-rec (nth 2 expr
) (- pow
)))
925 ((eq (car expr
) 'neg
)
926 (math-find-base-units-rec (nth 1 expr
) pow
))
928 (math-find-base-units-rec (nth 1 expr
) pow
))
929 ((eq (car expr
) 'var
)
930 (or (eq (nth 1 expr
) 'pi
)
931 (error "Unknown name %s in defining expression for unit %s"
932 (nth 1 expr
) (car math-fbu-entry
))))
933 ((equal expr
'(calcFunc-ln 10)))
934 (t (error "Malformed defining expression for unit %s" (car math-fbu-entry
))))))
937 (defun math-units-in-expr-p (expr sub-exprs
)
939 (if (eq (car expr
) 'var
)
940 (math-check-unit-name expr
)
941 (if (eq (car expr
) 'neg
)
942 (math-units-in-expr-p (nth 1 expr
) sub-exprs
)
944 (memq (car expr
) '(* / ^
)))
945 (or (math-units-in-expr-p (nth 1 expr
) sub-exprs
)
946 (math-units-in-expr-p (nth 2 expr
) sub-exprs
)))))))
948 (defun math-only-units-in-expr-p (expr)
950 (if (eq (car expr
) 'var
)
951 (math-check-unit-name expr
)
952 (if (memq (car expr
) '(* /))
953 (and (math-only-units-in-expr-p (nth 1 expr
))
954 (math-only-units-in-expr-p (nth 2 expr
)))
955 (and (eq (car expr
) '^
)
956 (and (math-only-units-in-expr-p (nth 1 expr
))
957 (math-realp (nth 2 expr
))))))))
959 (defun math-single-units-in-expr-p (expr)
960 (cond ((math-scalarp expr
) nil
)
961 ((eq (car expr
) 'var
)
962 (math-check-unit-name expr
))
963 ((eq (car expr
) 'neg
)
964 (math-single-units-in-expr-p (nth 1 expr
)))
966 (let ((u1 (math-single-units-in-expr-p (nth 1 expr
)))
967 (u2 (math-single-units-in-expr-p (nth 2 expr
))))
968 (or (and u1 u2
'wrong
)
972 (if (math-units-in-expr-p (nth 2 expr
) nil
)
974 (math-single-units-in-expr-p (nth 1 expr
))))
977 (defun math-consistent-units-p (expr newunits
)
978 "Non-nil if EXPR and NEWUNITS have consistent units."
980 (and (eq (car-safe newunits
) 'var
)
981 (assq (nth 1 newunits
) math-standard-units-systems
))
982 (math-numberp (math-get-units (math-to-standard-units (list '/ expr newunits
) nil
)))))
984 (defun math-check-unit-consistency (expr units
)
985 "Give an error if EXPR and UNITS do not have consistent units."
986 (unless (math-consistent-units-p expr units
)
987 (error "New units (%s) are inconsistent with current units (%s)"
988 (math-format-value units
)
989 (math-format-value (math-get-units expr
)))))
991 (defun math-check-unit-name (v)
992 (and (eq (car-safe v
) 'var
)
993 (or (assq (nth 1 v
) (or math-units-table
(math-build-units-table)))
994 (let ((name (symbol-name (nth 1 v
))))
995 (and (> (length name
) 1)
996 (assq (aref name
0) math-unit-prefixes
)
997 (or (assq (intern (substring name
1)) math-units-table
)
998 (and (eq (aref name
0) ?M
)
1000 (eq (aref name
1) ?e
)
1001 (eq (aref name
2) ?g
)
1002 (assq (intern (substring name
3))
1003 math-units-table
))))))))
1005 ;; The variable math-which-standard is local to math-to-standard-units,
1006 ;; but is used by math-to-standard-rec, which is called by
1007 ;; math-to-standard-units.
1008 (defvar math-which-standard
)
1010 (defun math-to-standard-units (expr math-which-standard
)
1011 (math-to-standard-rec expr
))
1013 (defun math-to-standard-rec (expr)
1014 (if (eq (car-safe expr
) 'var
)
1015 (let ((u (math-check-unit-name expr
))
1016 (base (nth 1 expr
)))
1020 (setq expr
(math-to-standard-rec (nth 1 u
)))
1021 (let ((st (assq (car u
) math-which-standard
)))
1023 (setq expr
(nth 1 st
))
1024 (setq expr
(list 'var
(car u
)
1025 (intern (concat "var-"
1031 (nth 1 (assq (aref (symbol-name base
) 0)
1032 math-unit-prefixes
))
1040 (and (eq (car-safe expr
) 'calcFunc-subscr
)
1041 (eq (car-safe (nth 1 expr
)) 'var
)))
1044 (mapcar 'math-to-standard-rec
(cdr expr
))))))
1046 (defun math-apply-units (expr units ulist
&optional pure
)
1047 (setq expr
(math-simplify-units expr
))
1051 (or (math-numberp expr
)
1052 (error "Incompatible units"))
1054 (setq value
(math-div expr
(nth 1 (car ulist
)))
1055 value
(math-floor (let ((calc-internal-prec
1056 (1- calc-internal-prec
)))
1057 (math-normalize value
)))
1058 new
(math-add new
(math-mul value
(car (car ulist
))))
1059 expr
(math-sub expr
(math-mul value
(nth 1 (car ulist
))))
1061 (math-add new
(math-mul (math-div expr
(nth 1 (car ulist
)))
1062 (car (car ulist
)))))
1065 (math-simplify-units (list '* expr units
)))))
1067 (defvar math-decompose-units-cache nil
)
1068 (defun math-decompose-units (units)
1069 (let ((u (math-check-unit-name units
)))
1070 (and u
(eq (car-safe (nth 1 u
)) '+)
1071 (setq units
(nth 1 u
))))
1072 (setq units
(calcFunc-expand units
))
1073 (and (eq (car-safe units
) '+)
1074 (let ((entry (list units calc-internal-prec calc-prefer-frac
)))
1075 (or (equal entry
(car math-decompose-units-cache
))
1079 (while (eq (car-safe utemp
) '+)
1080 (setq ulist
(cons (math-decompose-unit-part (nth 2 utemp
))
1082 utemp
(nth 1 utemp
)))
1083 (setq ulist
(cons (math-decompose-unit-part utemp
) ulist
)
1085 (while (setq utemp
(cdr utemp
))
1086 (unless (equal (nth 2 (car utemp
)) (nth 2 (car ulist
)))
1087 (error "Inconsistent units in sum")))
1088 (setq math-decompose-units-cache
1093 (not (Math-lessp (nth 1 x
)
1095 (cdr math-decompose-units-cache
))))
1097 (defun math-decompose-unit-part (unit)
1099 (math-is-multiple (math-simplify-units (math-to-standard-units
1103 ;; The variable math-fcu-u is local to math-find-compatible-unit,
1104 ;; but is used by math-find-compatible-rec which is called by
1105 ;; math-find-compatible-unit.
1108 (defun math-find-compatible-unit (expr unit
)
1109 (let ((math-fcu-u (math-check-unit-name unit
)))
1111 (math-find-compatible-unit-rec expr
1))))
1113 (defun math-find-compatible-unit-rec (expr pow
)
1114 (cond ((eq (car-safe expr
) '*)
1115 (or (math-find-compatible-unit-rec (nth 1 expr
) pow
)
1116 (math-find-compatible-unit-rec (nth 2 expr
) pow
)))
1117 ((eq (car-safe expr
) '/)
1118 (or (math-find-compatible-unit-rec (nth 1 expr
) pow
)
1119 (math-find-compatible-unit-rec (nth 2 expr
) (- pow
))))
1120 ((eq (car-safe expr
) 'neg
)
1121 (math-find-compatible-unit-rec (nth 1 expr
) pow
))
1122 ((and (eq (car-safe expr
) '^
)
1123 (integerp (nth 2 expr
)))
1124 (math-find-compatible-unit-rec (nth 1 expr
) (* pow
(nth 2 expr
))))
1126 (let ((u2 (math-check-unit-name expr
)))
1127 (if (equal (nth 4 math-fcu-u
) (nth 4 u2
))
1128 (cons expr pow
))))))
1130 ;; The variables math-cu-new-units and math-cu-pure are local to
1131 ;; math-convert-units, but are used by math-convert-units-rec,
1132 ;; which is called by math-convert-units.
1133 (defvar math-cu-new-units
)
1134 (defvar math-cu-pure
)
1136 (defun math-convert-units (expr math-cu-new-units
&optional math-cu-pure
)
1137 (if (eq (car-safe math-cu-new-units
) 'var
)
1138 (let ((unew (assq (nth 1 math-cu-new-units
)
1139 (math-build-units-table))))
1140 (if (eq (car-safe (nth 1 unew
)) '+)
1141 (setq math-cu-new-units
(nth 1 unew
)))))
1142 (math-with-extra-prec 2
1143 (let ((compat (and (not math-cu-pure
)
1144 (math-find-compatible-unit expr math-cu-new-units
)))
1145 (math-cu-unit-list nil
)
1146 (math-combining-units nil
))
1148 (math-simplify-units
1149 (math-mul (math-mul (math-simplify-units
1150 (math-div expr
(math-pow (car compat
)
1152 (math-pow math-cu-new-units
(cdr compat
)))
1153 (math-simplify-units
1154 (math-to-standard-units
1155 (math-pow (math-div (car compat
) math-cu-new-units
)
1158 (when (setq math-cu-unit-list
(math-decompose-units math-cu-new-units
))
1159 (setq math-cu-new-units
(nth 2 (car math-cu-unit-list
))))
1160 (when (eq (car-safe expr
) '+)
1161 (setq expr
(math-simplify-units expr
)))
1162 (if (math-units-in-expr-p expr t
)
1163 (math-convert-units-rec expr
)
1164 (math-apply-units (math-to-standard-units
1165 (list '/ expr math-cu-new-units
) nil
)
1166 math-cu-new-units math-cu-unit-list math-cu-pure
))))))
1168 (defun math-convert-units-rec (expr)
1169 (if (math-units-in-expr-p expr nil
)
1170 (math-apply-units (math-to-standard-units
1171 (list '/ expr math-cu-new-units
) nil
)
1172 math-cu-new-units math-cu-unit-list math-cu-pure
)
1173 (if (Math-primp expr
)
1176 (mapcar 'math-convert-units-rec
(cdr expr
))))))
1178 (defun math-convert-temperature (expr old new
&optional pure
)
1179 (let* ((units (math-single-units-in-expr-p expr
))
1181 (if (or (null units
)
1182 (equal (nth 1 old
) (car units
)))
1183 (math-check-unit-name old
)
1184 (error "Inconsistent temperature units"))
1186 (unew (math-check-unit-name new
)))
1187 (unless (and (consp unew
) (nth 3 unew
))
1188 (error "Not a valid temperature unit"))
1189 (unless (and (consp uold
) (nth 3 uold
))
1190 (error "Not a pure temperature expression"))
1191 (let ((v (car uold
)))
1192 (setq expr
(list '/ expr
(list 'var v
1193 (intern (concat "var-"
1194 (symbol-name v
)))))))
1195 (or (eq (nth 3 uold
) (nth 3 unew
))
1196 (cond ((eq (nth 3 uold
) 'K
)
1197 (setq expr
(list '- expr
'(/ 27315 100)))
1198 (if (eq (nth 3 unew
) 'F
)
1199 (setq expr
(list '+ (list '* expr
'(/ 9 5)) 32))))
1200 ((eq (nth 3 uold
) 'C
)
1201 (if (eq (nth 3 unew
) 'F
)
1202 (setq expr
(list '+ (list '* expr
'(/ 9 5)) 32))
1203 (setq expr
(list '+ expr
'(/ 27315 100)))))
1205 (setq expr
(list '* (list '- expr
32) '(/ 5 9)))
1206 (if (eq (nth 3 unew
) 'K
)
1207 (setq expr
(list '+ expr
'(/ 27315 100)))))))
1210 (list '* expr new
))))
1214 (defun math-simplify-units (a)
1215 (let ((math-simplifying-units t
)
1216 (calc-matrix-mode 'scalar
))
1218 (defalias 'calcFunc-usimplify
'math-simplify-units
)
1220 ;; The function created by math-defsimplify uses the variable
1221 ;; math-simplify-expr, and so is used by functions in math-defsimplify
1222 (defvar math-simplify-expr
)
1224 (math-defsimplify (+ -
)
1225 (and math-simplifying-units
1226 (math-units-in-expr-p (nth 1 math-simplify-expr
) nil
)
1227 (let* ((units (math-extract-units (nth 1 math-simplify-expr
)))
1228 (ratio (math-simplify (math-to-standard-units
1229 (list '/ (nth 2 math-simplify-expr
) units
) nil
))))
1230 (if (math-units-in-expr-p ratio nil
)
1232 (calc-record-why "*Inconsistent units" math-simplify-expr
)
1234 (list '* (math-add (math-remove-units (nth 1 math-simplify-expr
))
1235 (if (eq (car math-simplify-expr
) '-
)
1236 (math-neg ratio
) ratio
))
1240 (math-simplify-units-prod))
1242 (defun math-simplify-units-prod ()
1243 (and math-simplifying-units
1244 calc-autorange-units
1245 (Math-realp (nth 1 math-simplify-expr
))
1246 (let* ((num (math-float (nth 1 math-simplify-expr
)))
1247 (xpon (calcFunc-xpon num
))
1248 (unitp (cdr (cdr math-simplify-expr
)))
1250 (pow (if (eq (car math-simplify-expr
) '*) 1 -
1))
1252 (and (eq (car-safe unit
) '*)
1253 (setq unitp
(cdr unit
)
1255 (and (eq (car-safe unit
) '^
)
1256 (integerp (nth 2 unit
))
1257 (setq pow
(* pow
(nth 2 unit
))
1260 (and (setq u
(math-check-unit-name unit
))
1263 (>= xpon
(if (eq (car u
) 'm
) 1 3)))
1266 (reverse math-unit-prefixes
)
1267 math-unit-prefixes
))
1270 (or (eq (car u
) (nth 1 unit
))
1275 math-unit-prefixes
))))))
1276 (setq xpon
(+ xpon uxpon
))
1278 (or (memq (car (car p
)) '(?d ?D ?h ?H
))
1279 (and (eq (car (car p
)) ?c
)
1280 (not (eq (car u
) 'm
)))
1281 (< xpon
(setq pxpon
(* (nth 2 (nth 1 (car p
)))
1284 (setq pname
(math-build-var-name
1285 (if (eq (car (car p
)) 0)
1287 (concat (char-to-string
1291 (and (/= (car (car p
)) 0)
1293 math-units-table
)))))
1297 (or (not (eq p pref
))
1298 (< xpon
(+ pxpon
(* (math-abs pow
) 3))))
1300 (setcar (cdr math-simplify-expr
)
1301 (let ((calc-prefer-frac nil
))
1302 (calcFunc-scf (nth 1 math-simplify-expr
)
1304 (setcar unitp pname
)
1305 math-simplify-expr
)))))))
1307 (defvar math-try-cancel-units
)
1310 (and math-simplifying-units
1311 (let ((np (cdr math-simplify-expr
))
1312 (math-try-cancel-units 0)
1314 (setq n
(if (eq (car-safe (nth 2 math-simplify-expr
)) '*)
1315 (cdr (nth 2 math-simplify-expr
))
1316 (nthcdr 2 math-simplify-expr
)))
1317 (if (math-realp (car n
))
1319 (setcar (cdr math-simplify-expr
) (math-mul (nth 1 math-simplify-expr
)
1320 (let ((calc-prefer-frac nil
))
1321 (math-div 1 (car n
)))))
1323 (while (eq (car-safe (setq n
(car np
))) '*)
1324 (math-simplify-units-divisor (cdr n
) (cdr (cdr math-simplify-expr
)))
1325 (setq np
(cdr (cdr n
))))
1326 (math-simplify-units-divisor np
(cdr (cdr math-simplify-expr
)))
1327 (if (eq math-try-cancel-units
0)
1328 (let* ((math-simplifying-units nil
)
1329 (base (math-simplify
1330 (math-to-standard-units math-simplify-expr nil
))))
1331 (if (Math-numberp base
)
1332 (setq math-simplify-expr base
))))
1333 (if (eq (car-safe math-simplify-expr
) '/)
1334 (math-simplify-units-prod))
1335 math-simplify-expr
)))
1337 (defun math-simplify-units-divisor (np dp
)
1340 (while (eq (car-safe (setq d
(car dp
))) '*)
1341 (when (setq temp
(math-simplify-units-quotient n
(nth 1 d
)))
1342 (setcar np
(setq n temp
))
1344 (setq dp
(cdr (cdr d
))))
1345 (when (setq temp
(math-simplify-units-quotient n d
))
1346 (setcar np
(setq n temp
))
1349 ;; Simplify, e.g., "in / cm" to "2.54" in a units expression.
1350 (defun math-simplify-units-quotient (n d
)
1353 (when (and (eq (car-safe n
) '^
)
1354 (integerp (nth 2 n
)))
1355 (setq pow1
(nth 2 n
) n
(nth 1 n
)))
1356 (when (and (eq (car-safe d
) '^
)
1357 (integerp (nth 2 d
)))
1358 (setq pow2
(nth 2 d
) d
(nth 1 d
)))
1359 (let ((un (math-check-unit-name n
))
1360 (ud (math-check-unit-name d
)))
1362 (if (and (equal (nth 4 un
) (nth 4 ud
))
1365 (math-to-standard-units (list '/ n d
) nil
)
1366 (list '^
(math-to-standard-units (list '/ n d
) nil
) pow1
))
1373 (and (eq (car (car un
)) (car (car ud1
)))
1374 (setq math-try-cancel-units
1375 (+ math-try-cancel-units
1376 (- (* (cdr (car un
)) pow1
)
1377 (* (cdr (car ud
)) pow2
)))))
1378 (setq ud1
(cdr ud1
)))
1383 (and math-simplifying-units
1384 (math-realp (nth 2 math-simplify-expr
))
1385 (if (memq (car-safe (nth 1 math-simplify-expr
)) '(* /))
1386 (list (car (nth 1 math-simplify-expr
))
1387 (list '^
(nth 1 (nth 1 math-simplify-expr
))
1388 (nth 2 math-simplify-expr
))
1389 (list '^
(nth 2 (nth 1 math-simplify-expr
))
1390 (nth 2 math-simplify-expr
)))
1391 (math-simplify-units-pow (nth 1 math-simplify-expr
)
1392 (nth 2 math-simplify-expr
)))))
1394 (math-defsimplify calcFunc-sqrt
1395 (and math-simplifying-units
1396 (if (memq (car-safe (nth 1 math-simplify-expr
)) '(* /))
1397 (list (car (nth 1 math-simplify-expr
))
1398 (list 'calcFunc-sqrt
(nth 1 (nth 1 math-simplify-expr
)))
1399 (list 'calcFunc-sqrt
(nth 2 (nth 1 math-simplify-expr
))))
1400 (math-simplify-units-pow (nth 1 math-simplify-expr
) '(frac 1 2)))))
1402 (math-defsimplify (calcFunc-floor
1412 (and math-simplifying-units
1413 (= (length math-simplify-expr
) 2)
1414 (if (math-only-units-in-expr-p (nth 1 math-simplify-expr
))
1415 (nth 1 math-simplify-expr
)
1416 (if (and (memq (car-safe (nth 1 math-simplify-expr
)) '(* /))
1417 (or (math-only-units-in-expr-p
1418 (nth 1 (nth 1 math-simplify-expr
)))
1419 (math-only-units-in-expr-p
1420 (nth 2 (nth 1 math-simplify-expr
)))))
1421 (list (car (nth 1 math-simplify-expr
))
1422 (cons (car math-simplify-expr
)
1423 (cons (nth 1 (nth 1 math-simplify-expr
))
1424 (cdr (cdr math-simplify-expr
))))
1425 (cons (car math-simplify-expr
)
1426 (cons (nth 2 (nth 1 math-simplify-expr
))
1427 (cdr (cdr math-simplify-expr
)))))))))
1429 (defun math-simplify-units-pow (a pow
)
1430 (if (and (eq (car-safe a
) '^
)
1431 (math-check-unit-name (nth 1 a
))
1432 (math-realp (nth 2 a
)))
1433 (list '^
(nth 1 a
) (math-mul pow
(nth 2 a
)))
1434 (let* ((u (math-check-unit-name a
))
1435 (pf (math-to-simple-fraction pow
))
1436 (d (and (eq (car-safe pf
) 'frac
) (nth 2 pf
))))
1438 (math-units-are-multiple u d
)
1439 (list '^
(math-to-standard-units a nil
) pow
)))))
1442 (defun math-units-are-multiple (u n
)
1444 (while (and u
(= (%
(cdr (car u
)) n
) 0))
1448 (math-defsimplify calcFunc-sin
1449 (and math-simplifying-units
1450 (math-units-in-expr-p (nth 1 math-simplify-expr
) nil
)
1451 (let ((rad (math-simplify-units
1453 (math-to-standard-units (nth 1 math-simplify-expr
) nil
))))
1454 (calc-angle-mode 'rad
))
1455 (and (eq (car-safe rad
) '*)
1456 (math-realp (nth 1 rad
))
1457 (eq (car-safe (nth 2 rad
)) 'var
)
1458 (eq (nth 1 (nth 2 rad
)) 'rad
)
1459 (list 'calcFunc-sin
(nth 1 rad
))))))
1461 (math-defsimplify calcFunc-cos
1462 (and math-simplifying-units
1463 (math-units-in-expr-p (nth 1 math-simplify-expr
) nil
)
1464 (let ((rad (math-simplify-units
1466 (math-to-standard-units (nth 1 math-simplify-expr
) nil
))))
1467 (calc-angle-mode 'rad
))
1468 (and (eq (car-safe rad
) '*)
1469 (math-realp (nth 1 rad
))
1470 (eq (car-safe (nth 2 rad
)) 'var
)
1471 (eq (nth 1 (nth 2 rad
)) 'rad
)
1472 (list 'calcFunc-cos
(nth 1 rad
))))))
1474 (math-defsimplify calcFunc-tan
1475 (and math-simplifying-units
1476 (math-units-in-expr-p (nth 1 math-simplify-expr
) nil
)
1477 (let ((rad (math-simplify-units
1479 (math-to-standard-units (nth 1 math-simplify-expr
) nil
))))
1480 (calc-angle-mode 'rad
))
1481 (and (eq (car-safe rad
) '*)
1482 (math-realp (nth 1 rad
))
1483 (eq (car-safe (nth 2 rad
)) 'var
)
1484 (eq (nth 1 (nth 2 rad
)) 'rad
)
1485 (list 'calcFunc-tan
(nth 1 rad
))))))
1487 (math-defsimplify calcFunc-sec
1488 (and math-simplifying-units
1489 (math-units-in-expr-p (nth 1 math-simplify-expr
) nil
)
1490 (let ((rad (math-simplify-units
1492 (math-to-standard-units (nth 1 math-simplify-expr
) nil
))))
1493 (calc-angle-mode 'rad
))
1494 (and (eq (car-safe rad
) '*)
1495 (math-realp (nth 1 rad
))
1496 (eq (car-safe (nth 2 rad
)) 'var
)
1497 (eq (nth 1 (nth 2 rad
)) 'rad
)
1498 (list 'calcFunc-sec
(nth 1 rad
))))))
1500 (math-defsimplify calcFunc-csc
1501 (and math-simplifying-units
1502 (math-units-in-expr-p (nth 1 math-simplify-expr
) nil
)
1503 (let ((rad (math-simplify-units
1505 (math-to-standard-units (nth 1 math-simplify-expr
) nil
))))
1506 (calc-angle-mode 'rad
))
1507 (and (eq (car-safe rad
) '*)
1508 (math-realp (nth 1 rad
))
1509 (eq (car-safe (nth 2 rad
)) 'var
)
1510 (eq (nth 1 (nth 2 rad
)) 'rad
)
1511 (list 'calcFunc-csc
(nth 1 rad
))))))
1513 (math-defsimplify calcFunc-cot
1514 (and math-simplifying-units
1515 (math-units-in-expr-p (nth 1 math-simplify-expr
) nil
)
1516 (let ((rad (math-simplify-units
1518 (math-to-standard-units (nth 1 math-simplify-expr
) nil
))))
1519 (calc-angle-mode 'rad
))
1520 (and (eq (car-safe rad
) '*)
1521 (math-realp (nth 1 rad
))
1522 (eq (car-safe (nth 2 rad
)) 'var
)
1523 (eq (nth 1 (nth 2 rad
)) 'rad
)
1524 (list 'calcFunc-cot
(nth 1 rad
))))))
1527 (defun math-remove-units (expr)
1528 (if (math-check-unit-name expr
)
1530 (if (Math-primp expr
)
1533 (mapcar 'math-remove-units
(cdr expr
))))))
1535 (defun math-extract-units (expr)
1537 ((memq (car-safe expr
) '(* /))
1539 (mapcar 'math-extract-units
(cdr expr
))))
1540 ((eq (car-safe expr
) 'neg
)
1541 (math-extract-units (nth 1 expr
)))
1542 ((eq (car-safe expr
) '^
)
1543 (list '^
(math-extract-units (nth 1 expr
)) (nth 2 expr
)))
1544 ((math-check-unit-name expr
) expr
)
1547 (defun math-build-units-table-buffer (enter-buffer)
1548 (if (not (and math-units-table math-units-table-buffer-valid
1549 (get-buffer "*Units Table*")))
1550 (let ((buf (get-buffer-create "*Units Table*"))
1551 (uptr (math-build-units-table))
1552 (calc-language (if (eq calc-language
'big
) nil calc-language
))
1553 (calc-float-format '(float 0))
1554 (calc-group-digits nil
)
1555 (calc-number-radix 10)
1556 (calc-twos-complement-mode nil
)
1557 (calc-point-char ".")
1561 (message "Formatting units table...")
1563 (let ((inhibit-read-only t
))
1565 (insert "Calculator Units Table:\n\n")
1566 (insert "(All definitions are exact unless marked with an asterisk (*).)\n\n")
1567 (insert "Unit Type Definition Description\n\n")
1571 (when (eq (car u
) 'm
)
1573 (setq shadowed
(and std
(assq (car u
) math-additional-units
)))
1576 (eq (aref name
0) ?\
*))
1577 (unless (eq uptr math-units-table
)
1579 (setq name
(substring name
1)))
1581 (and shadowed
(insert "("))
1582 (insert (symbol-name (car u
)))
1583 (and shadowed
(insert ")"))
1587 (insert (symbol-name (nth 3 u
))))
1593 (and shadowed
(insert "("))
1597 (insert (math-format-value (nth 1 u
) 80))
1598 (insert (symbol-name (car u
)))))
1599 (and shadowed
(insert ")"))
1605 (insert " (redefined above)")
1607 (insert " (base unit)")))
1609 (setq uptr
(cdr uptr
)))
1610 (insert "\n\nUnit Prefix Table:\n\n")
1611 (setq uptr math-unit-prefixes
)
1614 (insert " " (char-to-string (car u
)))
1615 (if (equal (nth 1 u
) (nth 1 (nth 1 uptr
)))
1616 (insert " " (char-to-string (car (car (setq uptr
(cdr uptr
)))))
1619 (insert "10^" (int-to-string (nth 2 (nth 1 u
))))
1621 (insert " " (nth 2 u
) "\n")
1622 (while (eq (car (car (setq uptr
(cdr uptr
)))) 0)))
1627 "(**) When in TeX or LaTeX display mode, the TeX specific unit\n"
1628 "names will not use the ‘tex’ prefix; the unit name for a\n"
1629 "TeX point will be ‘pt’ instead of ‘texpt’, for example.\n"
1630 "To avoid conflicts, the unit names for pint and parsec will\n"
1631 "be ‘pint’ and ‘parsec’ instead of ‘pt’ and ‘pc’."))))
1633 (message "Formatting units table...done"))
1634 (setq math-units-table-buffer-valid t
)
1635 (let ((oldbuf (current-buffer)))
1637 (goto-char (point-min))
1638 (set-buffer oldbuf
))
1641 (display-buffer buf
)))
1643 (pop-to-buffer (get-buffer "*Units Table*"))
1644 (display-buffer (get-buffer "*Units Table*")))))
1646 ;;; Logarithmic units functions
1648 (defvar math-logunits
'((var dB var-dB
)
1651 (defun math-conditional-apply (fn &rest args
)
1652 "Evaluate f(args) unless in symbolic mode.
1653 In symbolic mode, return the list (fn args)."
1654 (if calc-symbolic-mode
1658 (defun math-conditional-pow (a b
)
1659 "Evaluate a^b unless in symbolic mode.
1660 In symbolic mode, return the list (^ a b)."
1661 (if calc-symbolic-mode
1665 (defun math-extract-logunits (expr)
1666 (if (memq (car-safe expr
) '(* /))
1668 (mapcar 'math-extract-logunits
(cdr expr
)))
1669 (if (memq (car-safe expr
) '(^
))
1670 (list '^
(math-extract-logunits (nth 1 expr
)) (nth 2 expr
))
1671 (if (member expr math-logunits
) expr
1))))
1673 (defun math-logunits-add (a b neg power
)
1674 (let ((aunit (math-simplify (math-extract-logunits a
))))
1675 (if (not (eq (car-safe aunit
) 'var
))
1676 (calc-record-why "*Improper logarithmic unit" aunit
)
1677 (let* ((units (math-extract-units a
))
1678 (acoeff (math-simplify (math-remove-units a
)))
1679 (bcoeff (math-simplify (math-to-standard-units
1680 (list '/ b units
) nil
))))
1681 (if (math-units-in-expr-p bcoeff nil
)
1682 (calc-record-why "*Inconsistent units" nil
)
1684 (or (math-lessp acoeff bcoeff
)
1685 (math-equal acoeff bcoeff
)))
1686 (calc-record-why "*Improper coefficients" nil
)
1688 (if (equal aunit
'(var dB var-dB
))
1689 (let ((coef (if power
10 20)))
1691 (math-conditional-apply 'calcFunc-log10
1694 (math-conditional-pow 10 (math-div acoeff coef
))
1695 (math-conditional-pow 10 (math-div bcoeff coef
)))
1697 (math-conditional-pow 10 (math-div acoeff coef
))
1698 (math-conditional-pow 10 (math-div bcoeff coef
)))))))
1699 (let ((coef (if power
2 1)))
1701 (math-conditional-apply 'calcFunc-ln
1704 (math-conditional-apply 'calcFunc-exp
(math-mul coef acoeff
))
1705 (math-conditional-apply 'calcFunc-exp
(math-mul coef bcoeff
)))
1707 (math-conditional-apply 'calcFunc-exp
(math-mul coef acoeff
))
1708 (math-conditional-apply 'calcFunc-exp
(math-mul coef bcoeff
)))))
1712 (defun calcFunc-lufadd (a b
)
1713 (math-logunits-add a b nil nil
))
1715 (defun calcFunc-lupadd (a b
)
1716 (math-logunits-add a b nil t
))
1718 (defun calcFunc-lufsub (a b
)
1719 (math-logunits-add a b t nil
))
1721 (defun calcFunc-lupsub (a b
)
1722 (math-logunits-add a b t t
))
1724 (defun calc-lu-plus (arg)
1727 (if (calc-is-inverse)
1728 (if (calc-is-hyperbolic)
1729 (calc-binary-op "lu-" 'calcFunc-lufsub arg
)
1730 (calc-binary-op "lu-" 'calcFunc-lupsub arg
))
1731 (if (calc-is-hyperbolic)
1732 (calc-binary-op "lu+" 'calcFunc-lufadd arg
)
1733 (calc-binary-op "lu+" 'calcFunc-lupadd arg
)))))
1735 (defun calc-lu-minus (arg)
1738 (if (calc-is-inverse)
1739 (if (calc-is-hyperbolic)
1740 (calc-binary-op "lu+" 'calcFunc-lufadd arg
)
1741 (calc-binary-op "lu+" 'calcFunc-lupadd arg
))
1742 (if (calc-is-hyperbolic)
1743 (calc-binary-op "lu-" 'calcFunc-lufsub arg
)
1744 (calc-binary-op "lu-" 'calcFunc-lupsub arg
)))))
1746 (defun math-logunits-mul (a b power
)
1747 (let (logunit coef units number
)
1750 (setq logunit
(math-simplify (math-extract-logunits a
)))
1751 (eq (car-safe logunit
) 'var
)
1752 (eq (math-simplify (math-extract-units b
)) 1))
1753 (setq coef
(math-simplify (math-remove-units a
))
1754 units
(math-extract-units a
)
1757 (setq logunit
(math-simplify (math-extract-logunits b
)))
1758 (eq (car-safe logunit
) 'var
)
1759 (eq (math-simplify (math-extract-units a
)) 1))
1760 (setq coef
(math-simplify (math-remove-units b
))
1761 units
(math-extract-units b
)
1763 (t (setq logunit nil
)))
1766 ((equal logunit
'(var dB var-dB
))
1771 (math-mul (if power
10 20)
1772 (math-conditional-apply 'calcFunc-log10 number
)))
1779 (math-div (math-conditional-apply 'calcFunc-ln number
) (if power
2 1)))
1781 (calc-record-why "*Improper units" nil
))))
1783 (defun math-logunits-divide (a b power
)
1784 (let ((logunit (math-simplify (math-extract-logunits a
))))
1785 (if (not (eq (car-safe logunit
) 'var
))
1786 (calc-record-why "*Improper logarithmic unit" logunit
)
1787 (if (math-units-in-expr-p b nil
)
1788 (calc-record-why "*Improper units quantity" b
)
1789 (let* ((units (math-extract-units a
))
1790 (coef (math-simplify (math-remove-units a
))))
1792 ((equal logunit
'(var dB var-dB
))
1797 (math-mul (if power
10 20)
1798 (math-conditional-apply 'calcFunc-log10 b
)))
1805 (math-div (math-conditional-apply 'calcFunc-ln b
) (if power
2 1)))
1808 (defun calcFunc-lufmul (a b
)
1809 (math-logunits-mul a b nil
))
1811 (defun calcFunc-lupmul (a b
)
1812 (math-logunits-mul a b t
))
1814 (defun calc-lu-times (arg)
1817 (if (calc-is-inverse)
1818 (if (calc-is-hyperbolic)
1819 (calc-binary-op "lu/" 'calcFunc-lufdiv arg
)
1820 (calc-binary-op "lu/" 'calcFunc-lupdiv arg
))
1821 (if (calc-is-hyperbolic)
1822 (calc-binary-op "lu*" 'calcFunc-lufmul arg
)
1823 (calc-binary-op "lu*" 'calcFunc-lupmul arg
)))))
1825 (defun calcFunc-lufdiv (a b
)
1826 (math-logunits-divide a b nil
))
1828 (defun calcFunc-lupdiv (a b
)
1829 (math-logunits-divide a b t
))
1831 (defun calc-lu-divide (arg)
1834 (if (calc-is-inverse)
1835 (if (calc-is-hyperbolic)
1836 (calc-binary-op "lu*" 'calcFunc-lufmul arg
)
1837 (calc-binary-op "lu*" 'calcFunc-lupmul arg
))
1838 (if (calc-is-hyperbolic)
1839 (calc-binary-op "lu/" 'calcFunc-lufdiv arg
)
1840 (calc-binary-op "lu/" 'calcFunc-lupdiv arg
)))))
1842 (defun math-logunits-quant (val ref power
)
1843 (let* ((units (math-simplify (math-extract-units val
)))
1844 (lunit (math-simplify (math-extract-logunits units
))))
1845 (if (not (eq (car-safe lunit
) 'var
))
1846 (calc-record-why "*Improper logarithmic unit" lunit
)
1847 (let ((runits (math-simplify (math-div units lunit
)))
1848 (coeff (math-simplify (math-div val units
))))
1850 (if (equal lunit
'(var dB var-dB
))
1853 (math-conditional-pow
1860 (math-conditional-apply 'calcFunc-exp
1866 (defvar calc-lu-field-reference
)
1867 (defvar calc-lu-power-reference
)
1869 (defun calcFunc-lufquant (val &optional ref
)
1871 (setq ref
(math-read-expr calc-lu-field-reference
)))
1872 (math-logunits-quant val ref nil
))
1874 (defun calcFunc-lupquant (val &optional ref
)
1876 (setq ref
(math-read-expr calc-lu-power-reference
)))
1877 (math-logunits-quant val ref t
))
1879 (defun calc-lu-quant (arg)
1882 (if (calc-is-hyperbolic)
1883 (if (calc-is-option)
1884 (calc-binary-op "lupq" 'calcFunc-lufquant arg
)
1885 (calc-unary-op "lupq" 'calcFunc-lufquant arg
))
1886 (if (calc-is-option)
1887 (calc-binary-op "lufq" 'calcFunc-lupquant arg
)
1888 (calc-unary-op "lufq" 'calcFunc-lupquant arg
)))))
1890 (defun math-logunits-level (val ref db power
)
1891 "Compute the value of VAL in decibels or nepers."
1892 (let* ((ratio (math-simplify-units (math-div val ref
)))
1893 (ratiou (math-simplify-units (math-remove-units ratio
)))
1894 (units (math-simplify (math-extract-units ratio
))))
1898 (math-mul (if power
10 20)
1899 (math-conditional-apply 'calcFunc-log10 ratiou
))
1902 (math-div (math-conditional-apply 'calcFunc-ln ratiou
) (if power
2 1))
1906 (defun calcFunc-dbfield (val &optional ref
)
1908 (setq ref
(math-read-expr calc-lu-field-reference
)))
1909 (math-logunits-level val ref t nil
))
1911 (defun calcFunc-dbpower (val &optional ref
)
1913 (setq ref
(math-read-expr calc-lu-power-reference
)))
1914 (math-logunits-level val ref t t
))
1916 (defun calcFunc-npfield (val &optional ref
)
1918 (setq ref
(math-read-expr calc-lu-field-reference
)))
1919 (math-logunits-level val ref nil nil
))
1921 (defun calcFunc-nppower (val &optional ref
)
1923 (setq ref
(math-read-expr calc-lu-power-reference
)))
1924 (math-logunits-level val ref nil t
))
1926 (defun calc-db (arg)
1929 (if (calc-is-hyperbolic)
1930 (if (calc-is-option)
1931 (calc-binary-op "ludb" 'calcFunc-dbfield arg
)
1932 (calc-unary-op "ludb" 'calcFunc-dbfield arg
))
1933 (if (calc-is-option)
1934 (calc-binary-op "ludb" 'calcFunc-dbpower arg
)
1935 (calc-unary-op "ludb" 'calcFunc-dbpower arg
)))))
1937 (defun calc-np (arg)
1940 (if (calc-is-hyperbolic)
1941 (if (calc-is-option)
1942 (calc-binary-op "lunp" 'calcFunc-npfield arg
)
1943 (calc-unary-op "lunp" 'calcFunc-npfield arg
))
1944 (if (calc-is-option)
1945 (calc-binary-op "lunp" 'calcFunc-nppower arg
)
1946 (calc-unary-op "lunp" 'calcFunc-nppower arg
)))))
1951 (defvar calc-note-threshold
)
1953 (defun math-midi-round (num)
1954 "Round NUM to an integer N if NUM is within calc-note-threshold cents of N."
1955 (let* ((n (math-round num
))
1958 (if (< (math-compare diff
1959 (math-div (math-read-expr calc-note-threshold
) 100)) 0)
1963 (defconst math-notes
1964 '(((var C var-C
) .
0)
1965 ((var Csharp var-Csharp
) .
1)
1966 ; ((var C♯ var-C♯) . 1)
1967 ((var Dflat var-Dflat
) .
1)
1968 ; ((var D♭ var-D♭) . 1)
1970 ((var Dsharp var-Dsharp
) .
3)
1971 ; ((var D♯ var-D♯) . 3)
1974 ((var Fsharp var-Fsharp
) .
6)
1975 ; ((var F♯ var-F♯) . 6)
1976 ((var Gflat var-Gflat
) .
6)
1977 ; ((var G♭ var-G♭) . 6)
1979 ((var Gsharp var-Gsharp
) .
8)
1980 ; ((var G♯ var-G♯) . 8)
1982 ((var Asharp var-Asharp
) .
10)
1983 ; ((var A♯ var-A♯) . 10)
1984 ((var Bflat var-Bflat
) .
10)
1985 ; ((var B♭ var-B♭) . 10)
1986 ((var B var-B
) .
11))
1987 "An alist of notes with their number of semitones above C.")
1989 (defun math-freqp (freq)
1990 "Non-nil if FREQ is a positive number times the unit Hz.
1991 If non-nil, return the coefficient of Hz."
1992 (let ((freqcoef (math-simplify-units
1993 (math-div freq
'(var Hz var-Hz
)))))
1994 (if (Math-posp freqcoef
) freqcoef
)))
1996 (defun math-midip (num)
1997 "Non-nil if NUM is a possible MIDI note number.
1998 If non-nil, return NUM."
1999 (if (Math-numberp num
) num
))
2001 (defun math-spnp (spn)
2002 "Non-nil if NUM is a scientific pitch note (note + cents).
2003 If non-nil, return a list consisting of the note and the cents coefficient."
2004 (let (note cents rnote rcents
)
2005 (if (eq (car-safe spn
) '+)
2006 (setq note
(nth 1 spn
)
2011 ((and ;; NOTE is a note, CENTS is nil or cents.
2012 (eq (car-safe note
) 'calcFunc-subscr
)
2013 (assoc (nth 1 note
) math-notes
)
2014 (integerp (nth 2 note
))
2018 (Math-numberp (setq rcents
2020 (math-div cents
'(var cents var-cents
)))))))
2021 (list rnote rcents
))
2022 ((and ;; CENTS is a note, NOTE is cents.
2023 (eq (car-safe cents
) 'calcFunc-subscr
)
2024 (assoc (nth 1 cents
) math-notes
)
2025 (integerp (nth 2 cents
))
2029 (Math-numberp (setq rcents
2031 (math-div note
'(var cents var-cents
)))))))
2032 (list rnote rcents
)))))
2034 (defun math-freq-to-midi (freq)
2035 "Return the midi note number corresponding to FREQ Hz."
2036 (let ((midi (math-add
2043 (math-midi-round midi
)))
2045 (defun math-spn-to-midi (spn)
2046 "Return the MIDI number corresponding to SPN."
2047 (let* ((note (cdr (assoc (nth 1 (car spn
)) math-notes
)))
2048 (octave (math-add (nth 2 (car spn
)) 1))
2051 (math-mul 12 octave
)
2054 (math-add midi
(math-div cents
100))
2057 (defun math-midi-to-spn (midi)
2058 "Return the scientific pitch notation corresponding to midi number MIDI."
2060 (if (math-integerp midi
)
2063 (setq midin
(math-floor midi
)
2064 cents
(math-mul 100 (math-sub midi midin
))))
2065 (let* ((nr ;; This should be (math-idivmod midin 12), but with
2066 ;; better behavior for negative midin.
2067 (if (Math-negp midin
)
2068 (let ((dm (math-idivmod (math-neg midin
) 12)))
2070 (cons (math-neg (car dm
)) 0)
2072 (math-sub (math-neg (car dm
)) 1)
2073 (math-sub 12 (cdr dm
)))))
2074 (math-idivmod midin
12)))
2075 (n (math-sub (car nr
) 1))
2076 (note (car (rassoc (cdr nr
) math-notes
))))
2078 (list '+ (list 'calcFunc-subscr note n
)
2079 (list '* cents
'(var cents var-cents
)))
2080 (list 'calcFunc-subscr note n
)))))
2082 (defun math-freq-to-spn (freq)
2083 "Return the scientific pitch notation corresponding to FREQ Hz."
2084 (math-with-extra-prec 3
2085 (math-midi-to-spn (math-freq-to-midi freq
))))
2087 (defun math-midi-to-freq (midi)
2088 "Return the frequency of the note with midi number MIDI."
2101 (defun math-spn-to-freq (spn)
2102 "Return the frequency of the note with scientific pitch notation SPN."
2103 (math-midi-to-freq (math-spn-to-midi spn
)))
2105 (defun calcFunc-spn (expr)
2106 "Return EXPR written as scientific pitch notation + cents."
2107 ;; Get the coefficient of Hz
2110 ((setq note
(math-freqp expr
))
2111 (math-freq-to-spn note
))
2112 ((setq note
(math-midip expr
))
2113 (math-midi-to-spn note
))
2117 (math-reject-arg expr
"*Improper expression")))))
2119 (defun calcFunc-midi (expr)
2120 "Return EXPR written as a MIDI number."
2123 ((setq note
(math-freqp expr
))
2124 (math-freq-to-midi note
))
2125 ((setq note
(math-spnp expr
))
2126 (math-spn-to-midi note
))
2130 (math-reject-arg expr
"*Improper expression")))))
2132 (defun calcFunc-freq (expr)
2133 "Return the frequency corresponding to EXPR."
2136 ((setq note
(math-midip expr
))
2137 (math-midi-to-freq note
))
2138 ((setq note
(math-spnp expr
))
2139 (math-spn-to-freq note
))
2143 (math-reject-arg expr
"*Improper expression")))))
2145 (defun calc-freq (arg)
2146 "Return the frequency corresponding to the expression on the stack."
2149 (calc-unary-op "freq" 'calcFunc-freq arg
)))
2151 (defun calc-midi (arg)
2152 "Return the MIDI number corresponding to the expression on the stack."
2155 (calc-unary-op "midi" 'calcFunc-midi arg
)))
2157 (defun calc-spn (arg)
2158 "Return the scientific pitch notation corresponding to the expression on the stack."
2161 (calc-unary-op "spn" 'calcFunc-spn arg
)))
2164 (provide 'calc-units
)
2170 ;;; calc-units.el ends here