Merge from emacs-24; up to 2012-12-13T09:45:54Z!lekktu@gmail.com
[bpt/emacs.git] / lisp / calc / calc-units.el
CommitLineData
8cd8ee52
CW
1;;; calc-units.el --- unit conversion functions for Calc
2
ab422c4d 3;; Copyright (C) 1990-1993, 2001-2013 Free Software Foundation, Inc.
8cd8ee52
CW
4
5;; Author: David Gillespie <daveg@synaptics.com>
e8fff8ed 6;; Maintainer: Jay Belanger <jay.p.belanger@gmail.com>
136211a9
EZ
7
8;; This file is part of GNU Emacs.
9
662c9c64 10;; GNU Emacs is free software: you can redistribute it and/or modify
7c671b23 11;; it under the terms of the GNU General Public License as published by
662c9c64
GM
12;; the Free Software Foundation, either version 3 of the License, or
13;; (at your option) any later version.
7c671b23 14
136211a9 15;; GNU Emacs is distributed in the hope that it will be useful,
7c671b23
GM
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.
19
20;; You should have received a copy of the GNU General Public License
662c9c64 21;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
136211a9 22
8cd8ee52 23;;; Commentary:
136211a9 24
8cd8ee52 25;;; Code:
136211a9
EZ
26
27;; This file is autoloaded from calc-ext.el.
136211a9 28
7d02e8cd 29(require 'calc-ext)
136211a9 30(require 'calc-macs)
c7388379
JB
31(eval-when-compile
32 (require 'calc-alg))
136211a9 33
8cd8ee52
CW
34;;; Units operations.
35
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)
ae6bc504 38;;; Updated April 2002 by Jochen Küpper
be59410c 39
a21f3259
JB
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
ae6bc504 44;;; Measures, by François Cardarelli)
a21f3259 45;;; All conversions are exact unless otherwise noted.
8cd8ee52
CW
46
47(defvar math-standard-units
48 '( ;; Length
cf39c182 49 ( m nil "*Meter" )
60afd99b 50 ( in "254*10^(-2) cm" "Inch" nil
19bdc4d8
JB
51 "2.54 cm")
52 ( ft "12 in" "Foot")
cf39c182
JB
53 ( yd "3 ft" "Yard" )
54 ( mi "5280 ft" "Mile" )
60afd99b 55 ( au "149597870691. m" "Astronomical Unit" nil
19bdc4d8 56 "149597870691 m (*)")
a21f3259
JB
57 ;; (approx) NASA JPL (http://neo.jpl.nasa.gov/glossary/au.html)
58 ( lyr "c yr" "Light Year" )
1265829e 59 ( pc "3.0856775854*10^16 m" "Parsec (**)" nil
ac318df0 60 "3.0856775854 10^16 m (*)") ;; (approx) ESUWM
cf39c182
JB
61 ( nmi "1852 m" "Nautical Mile" )
62 ( fath "6 ft" "Fathom" )
ac318df0 63 ( fur "660 ft" "Furlong")
cf39c182 64 ( mu "1 um" "Micron" )
ac318df0 65 ( mil "(1/1000) in" "Mil" )
1265829e 66 ( point "(1/72) in" "Point (PostScript convention)" )
a21f3259 67 ( Ang "10^(-10) m" "Angstrom" )
cf39c182
JB
68 ( mfi "mi+ft+in" "Miles + feet + inches" )
69 ;; TeX lengths
1265829e
JB
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) (**)" )
a1506d29 76
be59410c 77 ;; Area
cf39c182
JB
78 ( hect "10000 m^2" "*Hectare" )
79 ( a "100 m^2" "Are")
ac318df0 80 ( acre "(1/640) mi^2" "Acre" )
a21f3259 81 ( b "10^(-28) m^2" "Barn" )
a1506d29 82
be59410c 83 ;; Volume
a21f3259 84 ( L "10^(-3) m^3" "*Liter" )
cf39c182
JB
85 ( l "L" "Liter" )
86 ( gal "4 qt" "US Gallon" )
87 ( qt "2 pt" "Quart" )
1265829e 88 ( pt "2 cup" "Pint (**)" )
cf39c182
JB
89 ( cup "8 ozfl" "Cup" )
90 ( ozfl "2 tbsp" "Fluid Ounce" )
91 ( floz "2 tbsp" "Fluid Ounce" )
92 ( tbsp "3 tsp" "Tablespoon" )
7026903c
JB
93 ;; ESUWM defines a US gallon as 231 in^3.
94 ;; That gives the following exact value for tsp.
60afd99b 95 ( tsp "492892159375*10^(-11) ml" "Teaspoon" nil
19bdc4d8 96 "4.92892159375 ml")
ac318df0
JB
97 ( vol "tsp+tbsp+ozfl+cup+pt+qt+gal" "Gallons + ... + teaspoons" nil
98 "tsp+tbsp+ozfl+cup+pt+qt+gal")
a21f3259 99 ( galC "galUK" "Canadian Gallon" )
60afd99b 100 ( galUK "454609*10^(-5) L" "UK Gallon" nil
19bdc4d8 101 "4.54609 L") ;; NIST
a1506d29 102
be59410c 103 ;; Time
cf39c182
JB
104 ( s nil "*Second" )
105 ( sec "s" "Second" )
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" )
ac318df0
JB
111 ( yr "36525*10^(-2) day" "Year (Julian)" nil
112 "365.25 day")
cf39c182 113 ( Hz "1/s" "Hertz" )
be59410c
CW
114
115 ;; Speed
cf39c182
JB
116 ( mph "mi/hr" "*Miles per hour" )
117 ( kph "km/hr" "Kilometers per hour" )
118 ( knot "nmi/hr" "Knot" )
a21f3259 119 ( c "299792458 m/s" "Speed of light" ) ;;; CODATA
a1506d29 120
be59410c 121 ;; Acceleration
60afd99b 122 ( ga "980665*10^(-5) m/s^2" "*\"g\" acceleration" nil
19bdc4d8 123 "9.80665 m / s^2") ;; CODATA
be59410c
CW
124
125 ;; Mass
cf39c182
JB
126 ( g nil "*Gram" )
127 ( lb "16 oz" "Pound (mass)" )
60afd99b 128 ( oz "28349523125*10^(-9) g" "Ounce (mass)" nil
19bdc4d8 129 "28.349523125 g") ;; ESUWM
cf39c182
JB
130 ( ton "2000 lb" "Ton" )
131 ( tpo "ton+lb+oz" "Tons + pounds + ounces (mass)" )
132 ( t "1000 kg" "Metric ton" )
60afd99b 133 ( tonUK "10160469088*10^(-7) kg" "UK ton" nil
19bdc4d8 134 "1016.0469088 kg") ;; ESUWM
cf39c182 135 ( lbt "12 ozt" "Troy pound" )
60afd99b 136 ( ozt "311034768*10^(-7) g" "Troy ounce" nil
ac318df0 137 "31.10347680 g") ;; ESUWM, 1/12 exact value for lbt
60afd99b 138 ( ct "(2/10) g" "Carat" nil
19bdc4d8 139 "0.2 g") ;; ESUWM
60afd99b 140 ( u "1.660538782*10^(-27) kg" "Unified atomic mass" nil
ac318df0 141 "1.660538782 10^-27 kg (*)");;(approx) CODATA
be59410c
CW
142
143 ;; Force
cf39c182 144 ( N "m kg/s^2" "*Newton" )
a21f3259 145 ( dyn "10^(-5) N" "Dyne" )
cf39c182 146 ( gf "ga g" "Gram (force)" )
a21f3259 147 ( lbf "ga lb" "Pound (force)" )
cf39c182 148 ( kip "1000 lbf" "Kilopound (force)" )
60afd99b 149 ( pdl "138254954376*10^(-12) N" "Poundal" nil
19bdc4d8 150 "0.138254954376 N") ;; ESUWM
be59410c
CW
151
152 ;; Energy
cf39c182 153 ( J "N m" "*Joule" )
a21f3259 154 ( erg "10^(-7) J" "Erg" )
60afd99b 155 ( cal "41868*10^(-4) J" "International Table Calorie" nil
ac318df0 156 "4.1868 J") ;; NIST
60afd99b 157 ( calth "4184*10^(-3) J" "Thermochemical Calorie" nil
ac318df0
JB
158 "4.184 J") ;; NIST
159 ( Cal "1000 cal" "Large Calorie")
60afd99b 160 ( Btu "105505585262*10^(-8) J" "International Table Btu" nil
19bdc4d8 161 "1055.05585262 J") ;; ESUWM
cf39c182
JB
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")
02bf5ab9 170 ( Wh "W hr" "Watt hour")
cf39c182 171 ( Ws "W s" "Watt second")
be59410c
CW
172
173 ;; Power
cf39c182 174 ( W "J/s" "*Watt" )
ac318df0
JB
175 ( hp "550 ft lbf/s" "Horsepower") ;;ESUWM
176 ( hpm "75 m kgf/s" "Metric Horsepower") ;;ESUWM
be59410c
CW
177
178 ;; Temperature
cf39c182
JB
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 )
be59410c
CW
186
187 ;; Pressure
cf39c182 188 ( Pa "N/m^2" "*Pascal" )
a21f3259
JB
189 ( bar "10^5 Pa" "Bar" )
190 ( atm "101325 Pa" "Standard atmosphere" ) ;; CODATA
ac318df0 191 ( Torr "(1/760) atm" "Torr")
cf39c182 192 ( mHg "1000 Torr" "Meter of mercury" )
60afd99b 193 ( inHg "254*10^(-1) mmHg" "Inch of mercury" nil
19bdc4d8 194 "25.4 mmHg")
60afd99b 195 ( inH2O "2.490889*10^2 Pa" "Inch of water" nil
ac318df0 196 "2.490889 10^2 Pa (*)") ;;(approx) NIST
a21f3259 197 ( psi "lbf/in^2" "Pounds per square inch" )
be59410c
CW
198
199 ;; Viscosity
a21f3259
JB
200 ( P "(1/10) Pa s" "*Poise" )
201 ( St "10^(-4) m^2/s" "Stokes" )
be59410c
CW
202
203 ;; Electromagnetism
cf39c182
JB
204 ( A nil "*Ampere" )
205 ( C "A s" "Coulomb" )
206 ( Fdy "ech Nav" "Faraday" )
a21f3259 207 ( e "ech" "Elementary charge" )
60afd99b 208 ( ech "1.602176487*10^(-19) C" "Elementary charge" nil
ac318df0 209 "1.602176487 10^-19 C (*)") ;;(approx) CODATA
cf39c182
JB
210 ( V "W/A" "Volt" )
211 ( ohm "V/A" "Ohm" )
ae6bc504 212 ( Ω "ohm" "Ohm" )
cf39c182
JB
213 ( mho "A/V" "Mho" )
214 ( S "A/V" "Siemens" )
215 ( F "C/V" "Farad" )
216 ( H "Wb/A" "Henry" )
217 ( T "Wb/m^2" "Tesla" )
a21f3259 218 ( Gs "10^(-4) T" "Gauss" )
cf39c182 219 ( Wb "V s" "Weber" )
be59410c
CW
220
221 ;; Luminous intensity
cf39c182 222 ( cd nil "*Candela" )
a21f3259 223 ( sb "10000 cd/m^2" "Stilb" )
cf39c182
JB
224 ( lm "cd sr" "Lumen" )
225 ( lx "lm/m^2" "Lux" )
a21f3259 226 ( ph "10000 lx" "Phot" )
ac318df0 227 ( fc "lm/ft^2" "Footcandle") ;; ESUWM
a21f3259 228 ( lam "10000 lm/m^2" "Lambert" )
ac318df0 229 ( flam "(1/pi) cd/ft^2" "Footlambert") ;; ESUWM
be59410c
CW
230
231 ;; Radioactivity
cf39c182 232 ( Bq "1/s" "*Becquerel" )
a21f3259 233 ( Ci "37*10^9 Bq" "Curie" ) ;; ESUWM
cf39c182
JB
234 ( Gy "J/kg" "Gray" )
235 ( Sv "Gy" "Sievert" )
a21f3259
JB
236 ( R "258*10^(-6) C/kg" "Roentgen" ) ;; NIST
237 ( rd "(1/100) Gy" "Rad" )
cf39c182 238 ( rem "rd" "Rem" )
be59410c
CW
239
240 ;; Amount of substance
cf39c182 241 ( mol nil "*Mole" )
be59410c
CW
242
243 ;; Plane angle
cf39c182
JB
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" )
be59410c
CW
252
253 ;; Solid angle
cf39c182 254 ( sr nil "*Steradian" )
be59410c 255
cf39c182 256 ;; Other physical quantities
a21f3259 257 ;; The values are from CODATA, and are approximate.
60afd99b 258 ( h "6.62606896*10^(-34) J s" "*Planck's constant" nil
ac318df0
JB
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
ae6bc504 262 ( μ0 "mu0" "Permeability of vacuum") ;; Exact
4929aa69 263 ( eps0 "1 / (mu0 c^2)" "Permittivity of vacuum" )
ae6bc504 264 ( ε0 "eps0" "Permittivity of vacuum" )
60afd99b 265 ( G "6.67428*10^(-11) m^3/(kg s^2)" "Gravitational constant" nil
9b3e5667 266 "6.67428 10^-11 m^3/(kg s^2) (*)")
60afd99b 267 ( Nav "6.02214179*10^(23) / mol" "Avogadro's constant" nil
ac318df0 268 "6.02214179 10^23 / mol (*)")
60afd99b 269 ( me "9.10938215*10^(-31) kg" "Electron rest mass" nil
ac318df0 270 "9.10938215 10^-31 kg (*)")
60afd99b 271 ( mp "1.672621637*10^(-27) kg" "Proton rest mass" nil
ac318df0 272 "1.672621637 10^-27 kg (*)")
60afd99b 273 ( mn "1.674927211*10^(-27) kg" "Neutron rest mass" nil
ac318df0 274 "1.674927211 10^-27 kg (*)")
60afd99b 275 ( mmu "1.88353130*10^(-28) kg" "Muon rest mass" nil
ac318df0 276 "1.88353130 10^-28 kg (*)")
ae6bc504
JB
277 ( mμ "mmu" "Muon rest mass" nil
278 "1.88353130 10^-28 kg (*)")
60afd99b 279 ( Ryd "10973731.568527 /m" "Rydberg's constant" nil
19bdc4d8 280 "10973731.568527 /m (*)")
60afd99b 281 ( k "1.3806504*10^(-23) J/K" "Boltzmann's constant" nil
ac318df0 282 "1.3806504 10^-23 J/K (*)")
60afd99b 283 ( alpha "7.2973525376*10^(-3)" "Fine structure constant" nil
ac318df0 284 "7.2973525376 10^-3 (*)")
ae6bc504
JB
285 ( α "alpha" "Fine structure constant" nil
286 "7.2973525376 10^-3 (*)")
60afd99b 287 ( muB "927.400915*10^(-26) J/T" "Bohr magneton" nil
ac318df0 288 "927.400915 10^-26 J/T (*)")
60afd99b 289 ( muN "5.05078324*10^(-27) J/T" "Nuclear magneton" nil
ac318df0 290 "5.05078324 10^-27 J/T (*)")
60afd99b 291 ( mue "-928.476377*10^(-26) J/T" "Electron magnetic moment" nil
ac318df0 292 "-928.476377 10^-26 J/T (*)")
60afd99b 293 ( mup "1.410606662*10^(-26) J/T" "Proton magnetic moment" nil
ac318df0 294 "1.410606662 10^-26 J/T (*)")
60afd99b 295 ( R0 "8.314472 J/(mol K)" "Molar gas constant" nil
19bdc4d8 296 "8.314472 J/(mol K) (*)")
60afd99b 297 ( V0 "22.710981*10^(-3) m^3/mol" "Standard volume of ideal gas" nil
603823f5
JB
298 "22.710981 10^-3 m^3/mol (*)")
299 ;; Logarithmic units
300 ( Np nil "*Neper")
301 ( dB "(ln(10)/20) Np" "decibel")))
8cd8ee52
CW
302
303
304(defvar math-additional-units nil
fb7ada5f 305 "Additional units table for user-defined units.
60afd99b 306Must be formatted like `math-standard-units'.
11220394 307If you change this, be sure to set `math-units-table' to nil to ensure
8cd8ee52
CW
308that the combined units table will be rebuilt.")
309
310(defvar math-unit-prefixes
a21f3259
JB
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" )
323 ( 0 (^ 10 0) nil )
324 ( ?d (^ 10 -1) "Deci" )
325 ( ?c (^ 10 -2) "Centi" )
326 ( ?m (^ 10 -3) "Milli" )
327 ( ?u (^ 10 -6) "Micro" )
be19ef0b 328 ( ?μ (^ 10 -6) "Micro" )
a21f3259
JB
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" )))
8cd8ee52
CW
335
336(defvar math-standard-units-systems
337 '( ( base nil )
a21f3259
JB
338 ( si ( ( g '(/ (var kg var-kg) 1000) ) ) )
339 ( mks ( ( g '(/ (var kg var-kg) 1000) ) ) )
340 ( cgs ( ( m '(* (var cm var-cm) 100 ) ) ) )))
8cd8ee52
CW
341
342(defvar math-units-table nil
60afd99b
JB
343 "Internal units table.
344Derived from `math-standard-units' and `math-additional-units'.
8cd8ee52
CW
345Entries are (SYMBOL EXPR DOC-STRING TEMP-TYPE BASE-UNITS).")
346
347(defvar math-units-table-buffer-valid nil)
136211a9
EZ
348
349;;; Units commands.
350
351(defun calc-base-units ()
352 (interactive)
353 (calc-slow-wrapper
354 (let ((calc-autorange-units nil))
355 (calc-enter-result 1 "bsun" (math-simplify-units
356 (math-to-standard-units (calc-top-n 1)
c9aef719 357 nil))))))
136211a9 358
cd3466e6
JB
359(defvar calc-ensure-consistent-units)
360
136211a9
EZ
361(defun calc-quick-units ()
362 (interactive)
363 (calc-slow-wrapper
e93c003e 364 (let* ((num (- last-command-event ?0))
136211a9
EZ
365 (pos (if (= num 0) 10 num))
366 (units (calc-var-value 'var-Units))
367 (expr (calc-top-n 1)))
8cd8ee52 368 (unless (and (>= num 0) (<= num 9))
91af11b2 369 (error "Bad unit number"))
8cd8ee52 370 (unless (math-vectorp units)
91af11b2 371 (error "No \"quick units\" are defined"))
8cd8ee52 372 (unless (< pos (length units))
91af11b2 373 (error "Unit number %d not defined" pos))
136211a9 374 (if (math-units-in-expr-p expr nil)
d14b0029
JB
375 (progn
376 (if calc-ensure-consistent-units
6b38973b 377 (math-check-unit-consistency expr (nth pos units)))
d14b0029
JB
378 (calc-enter-result 1 (format "cun%d" num)
379 (math-convert-units expr (nth pos units))))
136211a9
EZ
380 (calc-enter-result 1 (format "*un%d" num)
381 (math-simplify-units
c9aef719 382 (math-mul expr (nth pos units))))))))
136211a9 383
5360ea16
JB
384(defun math-get-standard-units (expr)
385 "Return the standard units in EXPR."
386 (math-simplify-units
387 (math-extract-units
388 (math-to-standard-units expr nil))))
389
390(defun math-get-units (expr)
391 "Return the units in EXPR."
392 (math-simplify-units
393 (math-extract-units expr)))
394
395(defun math-make-unit-string (expr)
396 "Return EXPR in string form.
397If EXPR is nil, return nil."
398 (if expr
399 (let ((cexpr (math-compose-expr expr 0)))
09040c8d 400 (replace-regexp-in-string
a21f3259
JB
401 " / " "/"
402 (if (stringp cexpr)
403 cexpr
404 (math-composition-to-string cexpr))))))
5360ea16 405
09040c8d 406(defvar math-default-units-table
7c3fb76f 407 (make-hash-table :test 'equal)
5360ea16
JB
408 "A table storing previously converted units.")
409
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))
09040c8d 414 (default-units (gethash
5360ea16
JB
415 standard-units
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)))))
420
0fd09128
JB
421(defun math-put-default-units (expr &optional comp std)
422 "Put the units in EXPR in the default units table.
423If 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
426 (cond
427 (comp (math-simplify-units expr))
428 (std expr)
429 (t new-units))))
430 (default-units (gethash standard-units math-default-units-table)))
431 (unless (eq standard-units 1)
432 (cond
433 ((not default-units)
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))))))
5360ea16 439
136211a9
EZ
440(defun calc-convert-units (&optional old-units new-units)
441 (interactive)
442 (calc-slow-wrapper
443 (let ((expr (calc-top-n 1))
444 (uoldname nil)
7c3fb76f 445 (unitscancel nil)
27971241 446 unew
5360ea16
JB
447 units
448 defunits)
7c3fb76f
JB
449 (if (or (not (math-units-in-expr-p expr t))
450 (setq unitscancel (eq (math-get-standard-units expr) 1)))
8cd8ee52
CW
451 (let ((uold (or old-units
452 (progn
7c3fb76f
JB
453 (setq uoldname
454 (if unitscancel
455 (read-string
456 "(The expression is unitless when simplified) Old Units: ")
457 (read-string "Old units: ")))
8cd8ee52
CW
458 (if (equal uoldname "")
459 (progn
460 (setq uoldname "1")
461 1)
462 (if (string-match "\\` */" uoldname)
463 (setq uoldname (concat "1" uoldname)))
464 (math-read-expr uoldname))))))
465 (when (eq (car-safe uold) 'error)
466 (error "Bad format in units expression: %s" (nth 1 uold)))
467 (setq expr (math-mul expr uold))))
0fd09128 468 (setq defunits (math-get-default-units expr))
7c3fb76f
JB
469 (unless new-units
470 (setq new-units
471 (read-string (concat
472 (if uoldname
473 (concat "Old units: "
474 uoldname
475 ", new units")
476 "New units")
477 (if defunits
478 (concat
479 " (default "
480 defunits
481 "): ")
482 ": "))))
483 (if (and
484 (string= new-units "")
485 defunits)
486 (setq new-units defunits)))
487 (when (string-match "\\` */" new-units)
488 (setq new-units (concat "1" new-units)))
489 (setq units (math-read-expr new-units))
490 (when (eq (car-safe units) 'error)
491 (error "Bad format in units expression: %s" (nth 2 units)))
492 (if calc-ensure-consistent-units
493 (math-check-unit-consistency expr units))
494 (let ((unew (math-units-in-expr-p units t))
495 (std (and (eq (car-safe units) 'var)
496 (assq (nth 1 units) math-standard-units-systems)))
497 (comp (eq (car-safe units) '+)))
498 (unless (or unew std)
499 (error "No units specified"))
500 (let ((res
501 (if std
502 (math-simplify-units (math-to-standard-units expr (nth 1 std)))
503 (math-convert-units expr units (and uoldname (not (equal uoldname "1")))))))
504 (math-put-default-units res (if comp units))
505 (calc-enter-result 1 "cvun" res))))))
136211a9
EZ
506
507(defun calc-autorange-units (arg)
508 (interactive "P")
509 (calc-wrapper
510 (calc-change-mode 'calc-autorange-units arg nil t)
511 (message (if calc-autorange-units
8cd8ee52
CW
512 "Adjusting target unit prefix automatically"
513 "Using target units exactly"))))
136211a9
EZ
514
515(defun calc-convert-temperature (&optional old-units new-units)
516 (interactive)
517 (calc-slow-wrapper
518 (let ((expr (calc-top-n 1))
519 (uold nil)
520 (uoldname nil)
5360ea16
JB
521 unew
522 defunits)
136211a9
EZ
523 (setq uold (or old-units
524 (let ((units (math-single-units-in-expr-p expr)))
525 (if units
526 (if (consp units)
527 (list 'var (car units)
528 (intern (concat "var-"
529 (symbol-name
530 (car units)))))
531 (error "Not a pure temperature expression"))
532 (math-read-expr
533 (setq uoldname (read-string
534 "Old temperature units: ")))))))
8cd8ee52
CW
535 (when (eq (car-safe uold) 'error)
536 (error "Bad format in units expression: %s" (nth 2 uold)))
136211a9
EZ
537 (or (math-units-in-expr-p expr nil)
538 (setq expr (math-mul expr uold)))
5360ea16 539 (setq defunits (math-get-default-units expr))
136211a9 540 (setq unew (or new-units
fa786329
JB
541 (read-string
542 (concat
543 (if uoldname
544 (concat "Old temperature units: "
545 uoldname
546 ", new units")
547 "New temperature units")
548 (if defunits
549 (concat " (default "
550 defunits
551 "): ")
552 ": ")))))
553 (setq unew (math-read-expr (if (string= unew "") defunits unew)))
8cd8ee52
CW
554 (when (eq (car-safe unew) 'error)
555 (error "Bad format in units expression: %s" (nth 2 unew)))
5360ea16 556 (math-put-default-units unew)
a34f800f
JB
557 (let ((ntemp (calc-normalize
558 (math-simplify-units
559 (math-convert-temperature expr uold unew
560 uoldname)))))
561 (if (Math-zerop ntemp)
562 (setq ntemp (list '* ntemp unew)))
563 (let ((calc-simplify-mode 'none))
564 (calc-enter-result 1 "cvtm" ntemp))))))
136211a9
EZ
565
566(defun calc-remove-units ()
567 (interactive)
568 (calc-slow-wrapper
569 (calc-enter-result 1 "rmun" (math-simplify-units
c9aef719 570 (math-remove-units (calc-top-n 1))))))
136211a9
EZ
571
572(defun calc-extract-units ()
573 (interactive)
574 (calc-slow-wrapper
d14b0029 575 (calc-enter-result 1 "exun" (math-simplify-units
c9aef719 576 (math-extract-units (calc-top-n 1))))))
136211a9 577
09040c8d 578;; The variables calc-num-units and calc-den-units are local to
27971241
JB
579;; calc-explain-units, but are used by calc-explain-units-rec,
580;; which is called by calc-explain-units.
581(defvar calc-num-units)
582(defvar calc-den-units)
583
136211a9
EZ
584(defun calc-explain-units ()
585 (interactive)
586 (calc-wrapper
27971241
JB
587 (let ((calc-num-units nil)
588 (calc-den-units nil))
136211a9 589 (calc-explain-units-rec (calc-top-n 1) 1)
27971241
JB
590 (and calc-den-units (string-match "^[^(].* .*[^)]$" calc-den-units)
591 (setq calc-den-units (concat "(" calc-den-units ")")))
592 (if calc-num-units
593 (if calc-den-units
594 (message "%s per %s" calc-num-units calc-den-units)
595 (message "%s" calc-num-units))
596 (if calc-den-units
597 (message "1 per %s" calc-den-units)
c9aef719 598 (message "No units in expression"))))))
136211a9
EZ
599
600(defun calc-explain-units-rec (expr pow)
601 (let ((u (math-check-unit-name expr))
602 pos)
603 (if (and u (not (math-zerop pow)))
604 (let ((name (or (nth 2 u) (symbol-name (car u)))))
605 (if (eq (aref name 0) ?\*)
606 (setq name (substring name 1)))
ae6bc504
JB
607 (if (string-match "[^a-zA-Zα-ωΑ-Ω0-9']" name)
608 (if (string-match "^[a-zA-Zα-ωΑ-Ω0-9' ()]*$" name)
136211a9
EZ
609 (while (setq pos (string-match "[ ()]" name))
610 (setq name (concat (substring name 0 pos)
611 (if (eq (aref name pos) 32) "-" "")
612 (substring name (1+ pos)))))
613 (setq name (concat "(" name ")"))))
614 (or (eq (nth 1 expr) (car u))
615 (setq name (concat (nth 2 (assq (aref (symbol-name
616 (nth 1 expr)) 0)
617 math-unit-prefixes))
ae6bc504 618 (if (and (string-match "[^a-zA-Zα-ωΑ-Ω0-9']" name)
136211a9
EZ
619 (not (memq (car u) '(mHg gf))))
620 (concat "-" name)
621 (downcase name)))))
622 (cond ((or (math-equal-int pow 1)
623 (math-equal-int pow -1)))
624 ((or (math-equal-int pow 2)
625 (math-equal-int pow -2))
626 (if (equal (nth 4 u) '((m . 1)))
627 (setq name (concat "Square-" name))
628 (setq name (concat name "-squared"))))
629 ((or (math-equal-int pow 3)
630 (math-equal-int pow -3))
631 (if (equal (nth 4 u) '((m . 1)))
632 (setq name (concat "Cubic-" name))
633 (setq name (concat name "-cubed"))))
634 (t
635 (setq name (concat name "^"
636 (math-format-number (math-abs pow))))))
637 (if (math-posp pow)
27971241
JB
638 (setq calc-num-units (if calc-num-units
639 (concat calc-num-units " " name)
136211a9 640 name))
27971241
JB
641 (setq calc-den-units (if calc-den-units
642 (concat calc-den-units " " name)
136211a9
EZ
643 name))))
644 (cond ((eq (car-safe expr) '*)
645 (calc-explain-units-rec (nth 1 expr) pow)
646 (calc-explain-units-rec (nth 2 expr) pow))
647 ((eq (car-safe expr) '/)
648 (calc-explain-units-rec (nth 1 expr) pow)
649 (calc-explain-units-rec (nth 2 expr) (- pow)))
650 ((memq (car-safe expr) '(neg + -))
651 (calc-explain-units-rec (nth 1 expr) pow))
652 ((and (eq (car-safe expr) '^)
653 (math-realp (nth 2 expr)))
654 (calc-explain-units-rec (nth 1 expr)
c9aef719 655 (math-mul pow (nth 2 expr))))))))
136211a9
EZ
656
657(defun calc-simplify-units ()
658 (interactive)
659 (calc-slow-wrapper
660 (calc-with-default-simplification
c9aef719 661 (calc-enter-result 1 "smun" (math-simplify-units (calc-top-n 1))))))
136211a9
EZ
662
663(defun calc-view-units-table (n)
664 (interactive "P")
665 (and n (setq math-units-table-buffer-valid nil))
666 (let ((win (get-buffer-window "*Units Table*")))
667 (if (and win
668 math-units-table
669 math-units-table-buffer-valid)
670 (progn
671 (bury-buffer (window-buffer win))
672 (let ((curwin (selected-window)))
673 (select-window win)
674 (switch-to-buffer nil)
675 (select-window curwin)))
c9aef719 676 (math-build-units-table-buffer nil))))
136211a9
EZ
677
678(defun calc-enter-units-table (n)
679 (interactive "P")
680 (and n (setq math-units-table-buffer-valid nil))
681 (math-build-units-table-buffer t)
5c08522d 682 (message "%s" (substitute-command-keys "Type \\[calc] to return to the Calculator")))
136211a9 683
19bdc4d8
JB
684(defun calc-define-unit (uname desc &optional disp)
685 (interactive "SDefine unit name: \nsDescription: \nP")
686 (if disp (setq disp (read-string "Display definition: ")))
136211a9
EZ
687 (calc-wrapper
688 (let ((form (calc-top-n 1))
689 (unit (assq uname math-additional-units)))
690 (or unit
691 (setq math-additional-units
19bdc4d8 692 (cons (setq unit (list uname nil nil nil nil))
136211a9
EZ
693 math-additional-units)
694 math-units-table nil))
695 (setcar (cdr unit) (and (not (and (eq (car-safe form) 'var)
696 (eq (nth 1 form) uname)))
697 (not (math-equal-int form 1))
698 (math-format-flat-expr form 0)))
699 (setcar (cdr (cdr unit)) (and (not (equal desc ""))
19bdc4d8
JB
700 desc))
701 (if disp
702 (setcar (cdr (cdr (cdr (cdr unit)))) disp))))
c9aef719 703 (calc-invalidate-units-table))
136211a9
EZ
704
705(defun calc-undefine-unit (uname)
706 (interactive "SUndefine unit name: ")
707 (calc-wrapper
708 (let ((unit (assq uname math-additional-units)))
709 (or unit
710 (if (assq uname math-standard-units)
711 (error "\"%s\" is a predefined unit name" uname)
712 (error "Unit name \"%s\" not found" uname)))
713 (setq math-additional-units (delq unit math-additional-units)
714 math-units-table nil)))
c9aef719 715 (calc-invalidate-units-table))
136211a9
EZ
716
717(defun calc-invalidate-units-table ()
718 (setq math-units-table nil)
719 (let ((buf (get-buffer "*Units Table*")))
720 (and buf
6df9b6d7 721 (with-current-buffer buf
136211a9
EZ
722 (save-excursion
723 (goto-char (point-min))
724 (if (looking-at "Calculator Units Table")
ed8060d9 725 (let ((inhibit-read-only t))
c9aef719 726 (insert "(Obsolete) "))))))))
136211a9
EZ
727
728(defun calc-get-unit-definition (uname)
729 (interactive "SGet definition for unit: ")
730 (calc-wrapper
731 (math-build-units-table)
732 (let ((unit (assq uname math-units-table)))
733 (or unit
734 (error "Unit name \"%s\" not found" uname))
735 (let ((msg (nth 2 unit)))
736 (if (stringp msg)
737 (if (string-match "^\\*" msg)
738 (setq msg (substring msg 1)))
739 (setq msg (symbol-name uname)))
740 (if (nth 1 unit)
741 (progn
742 (calc-enter-result 0 "ugdf" (nth 1 unit))
743 (message "Derived unit: %s" msg))
744 (calc-enter-result 0 "ugdf" (list 'var uname
745 (intern
746 (concat "var-"
747 (symbol-name uname)))))
c9aef719 748 (message "Base unit: %s" msg))))))
136211a9
EZ
749
750(defun calc-permanent-units ()
751 (interactive)
752 (calc-wrapper
753 (let (pos)
754 (set-buffer (find-file-noselect (substitute-in-file-name
755 calc-settings-file)))
756 (goto-char (point-min))
757 (if (and (search-forward ";;; Custom units stored by Calc" nil t)
758 (progn
759 (beginning-of-line)
760 (setq pos (point))
761 (search-forward "\n;;; End of custom units" nil t)))
762 (progn
763 (beginning-of-line)
764 (forward-line 1)
765 (delete-region pos (point)))
766 (goto-char (point-max))
767 (insert "\n\n")
768 (forward-char -1))
769 (insert ";;; Custom units stored by Calc on " (current-time-string) "\n")
770 (if math-additional-units
771 (progn
772 (insert "(setq math-additional-units '(\n")
773 (let ((list math-additional-units))
774 (while list
775 (insert " (" (symbol-name (car (car list))) " "
776 (if (nth 1 (car list))
777 (if (stringp (nth 1 (car list)))
778 (prin1-to-string (nth 1 (car list)))
779 (prin1-to-string (math-format-flat-expr
780 (nth 1 (car list)) 0)))
781 "nil")
782 " "
783 (prin1-to-string (nth 2 (car list)))
784 ")\n")
785 (setq list (cdr list))))
786 (insert "))\n"))
787 (insert ";;; (no custom units defined)\n"))
788 (insert ";;; End of custom units\n")
c9aef719 789 (save-buffer))))
136211a9
EZ
790
791
27971241
JB
792;; The variable math-cu-unit-list is local to math-build-units-table,
793;; but is used by math-compare-unit-names, which is called (indirectly)
794;; by math-build-units-table.
795;; math-cu-unit-list is also local to math-convert-units, but is used
796;; by math-convert-units-rec, which is called by math-convert-units.
797(defvar math-cu-unit-list)
136211a9 798
136211a9
EZ
799(defun math-build-units-table ()
800 (or math-units-table
801 (let* ((combined-units (append math-additional-units
802 math-standard-units))
27971241 803 (math-cu-unit-list (mapcar 'car combined-units))
136211a9
EZ
804 tab)
805 (message "Building units table...")
806 (setq math-units-table-buffer-valid nil)
807 (setq tab (mapcar (function
808 (lambda (x)
809 (list (car x)
810 (and (nth 1 x)
811 (if (stringp (nth 1 x))
812 (let ((exp (math-read-plain-expr
813 (nth 1 x))))
814 (if (eq (car-safe exp) 'error)
815 (error "Format error in definition of %s in units table: %s"
816 (car x) (nth 2 exp))
817 exp))
818 (nth 1 x)))
819 (nth 2 x)
820 (nth 3 x)
821 (and (not (nth 1 x))
19bdc4d8
JB
822 (list (cons (car x) 1)))
823 (nth 4 x))))
136211a9
EZ
824 combined-units))
825 (let ((math-units-table tab))
09040c8d 826 (mapc 'math-find-base-units tab))
136211a9 827 (message "Building units table...done")
c9aef719 828 (setq math-units-table tab))))
136211a9 829
27971241
JB
830;; The variables math-fbu-base and math-fbu-entry are local to
831;; math-find-base-units, but are used by math-find-base-units-rec,
832;; which is called by math-find-base-units.
833(defvar math-fbu-base)
834(defvar math-fbu-entry)
835
3effaa28
JB
836(defun math-find-base-units (math-fbu-entry)
837 (if (eq (nth 4 math-fbu-entry) 'boom)
838 (error "Circular definition involving unit %s" (car math-fbu-entry)))
839 (or (nth 4 math-fbu-entry)
840 (let (math-fbu-base)
841 (setcar (nthcdr 4 math-fbu-entry) 'boom)
842 (math-find-base-units-rec (nth 1 math-fbu-entry) 1)
843 '(or math-fbu-base
844 (error "Dimensionless definition for unit %s" (car math-fbu-entry)))
845 (while (eq (cdr (car math-fbu-base)) 0)
846 (setq math-fbu-base (cdr math-fbu-base)))
847 (let ((b math-fbu-base))
136211a9
EZ
848 (while (cdr b)
849 (if (eq (cdr (car (cdr b))) 0)
850 (setcdr b (cdr (cdr b)))
851 (setq b (cdr b)))))
3effaa28
JB
852 (setq math-fbu-base (sort math-fbu-base 'math-compare-unit-names))
853 (setcar (nthcdr 4 math-fbu-entry) math-fbu-base)
854 math-fbu-base)))
136211a9
EZ
855
856(defun math-compare-unit-names (a b)
27971241 857 (memq (car b) (cdr (memq (car a) math-cu-unit-list))))
136211a9
EZ
858
859(defun math-find-base-units-rec (expr pow)
860 (let ((u (math-check-unit-name expr)))
861 (cond (u
862 (let ((ulist (math-find-base-units u)))
863 (while ulist
864 (let ((p (* (cdr (car ulist)) pow))
3effaa28 865 (old (assq (car (car ulist)) math-fbu-base)))
136211a9
EZ
866 (if old
867 (setcdr old (+ (cdr old) p))
09040c8d 868 (setq math-fbu-base
3effaa28 869 (cons (cons (car (car ulist)) p) math-fbu-base))))
136211a9
EZ
870 (setq ulist (cdr ulist)))))
871 ((math-scalarp expr))
872 ((and (eq (car expr) '^)
873 (integerp (nth 2 expr)))
874 (math-find-base-units-rec (nth 1 expr) (* pow (nth 2 expr))))
875 ((eq (car expr) '*)
876 (math-find-base-units-rec (nth 1 expr) pow)
877 (math-find-base-units-rec (nth 2 expr) pow))
878 ((eq (car expr) '/)
879 (math-find-base-units-rec (nth 1 expr) pow)
880 (math-find-base-units-rec (nth 2 expr) (- pow)))
881 ((eq (car expr) 'neg)
882 (math-find-base-units-rec (nth 1 expr) pow))
883 ((eq (car expr) '+)
884 (math-find-base-units-rec (nth 1 expr) pow))
885 ((eq (car expr) 'var)
886 (or (eq (nth 1 expr) 'pi)
887 (error "Unknown name %s in defining expression for unit %s"
3effaa28 888 (nth 1 expr) (car math-fbu-entry))))
603823f5 889 ((equal expr '(calcFunc-ln 10)))
3effaa28 890 (t (error "Malformed defining expression for unit %s" (car math-fbu-entry))))))
136211a9
EZ
891
892
893(defun math-units-in-expr-p (expr sub-exprs)
894 (and (consp expr)
895 (if (eq (car expr) 'var)
896 (math-check-unit-name expr)
897 (and (or sub-exprs
898 (memq (car expr) '(* / ^)))
899 (or (math-units-in-expr-p (nth 1 expr) sub-exprs)
c9aef719 900 (math-units-in-expr-p (nth 2 expr) sub-exprs))))))
136211a9
EZ
901
902(defun math-only-units-in-expr-p (expr)
903 (and (consp expr)
904 (if (eq (car expr) 'var)
905 (math-check-unit-name expr)
906 (if (memq (car expr) '(* /))
907 (and (math-only-units-in-expr-p (nth 1 expr))
908 (math-only-units-in-expr-p (nth 2 expr)))
909 (and (eq (car expr) '^)
910 (and (math-only-units-in-expr-p (nth 1 expr))
c9aef719 911 (math-realp (nth 2 expr))))))))
136211a9
EZ
912
913(defun math-single-units-in-expr-p (expr)
914 (cond ((math-scalarp expr) nil)
915 ((eq (car expr) 'var)
916 (math-check-unit-name expr))
917 ((eq (car expr) '*)
918 (let ((u1 (math-single-units-in-expr-p (nth 1 expr)))
919 (u2 (math-single-units-in-expr-p (nth 2 expr))))
920 (or (and u1 u2 'wrong)
921 u1
922 u2)))
923 ((eq (car expr) '/)
924 (if (math-units-in-expr-p (nth 2 expr) nil)
925 'wrong
926 (math-single-units-in-expr-p (nth 1 expr))))
c9aef719 927 (t 'wrong)))
136211a9 928
76bc2ec7
JB
929(defun math-consistent-units-p (expr newunits)
930 "Non-nil if EXPR and NEWUNITS have consistent units."
931 (or
932 (and (eq (car-safe newunits) 'var)
933 (assq (nth 1 newunits) math-standard-units-systems))
934 (math-numberp (math-get-units (list '/ expr newunits)))))
d14b0029
JB
935
936(defun math-check-unit-consistency (expr units)
937 "Give an error if EXPR and UNITS do not have consistent units."
938 (unless (math-consistent-units-p expr units)
939 (error "New units (%s) are inconsistent with current units (%s)"
940 (math-format-value units)
941 (math-format-value (math-get-units expr)))))
942
136211a9
EZ
943(defun math-check-unit-name (v)
944 (and (eq (car-safe v) 'var)
945 (or (assq (nth 1 v) (or math-units-table (math-build-units-table)))
946 (let ((name (symbol-name (nth 1 v))))
947 (and (> (length name) 1)
948 (assq (aref name 0) math-unit-prefixes)
949 (or (assq (intern (substring name 1)) math-units-table)
950 (and (eq (aref name 0) ?M)
951 (> (length name) 3)
952 (eq (aref name 1) ?e)
953 (eq (aref name 2) ?g)
954 (assq (intern (substring name 3))
c9aef719 955 math-units-table))))))))
136211a9 956
27971241
JB
957;; The variable math-which-standard is local to math-to-standard-units,
958;; but is used by math-to-standard-rec, which is called by
959;; math-to-standard-units.
960(defvar math-which-standard)
136211a9 961
27971241 962(defun math-to-standard-units (expr math-which-standard)
c9aef719 963 (math-to-standard-rec expr))
136211a9
EZ
964
965(defun math-to-standard-rec (expr)
966 (if (eq (car-safe expr) 'var)
967 (let ((u (math-check-unit-name expr))
968 (base (nth 1 expr)))
969 (if u
970 (progn
971 (if (nth 1 u)
972 (setq expr (math-to-standard-rec (nth 1 u)))
27971241 973 (let ((st (assq (car u) math-which-standard)))
136211a9
EZ
974 (if st
975 (setq expr (nth 1 st))
976 (setq expr (list 'var (car u)
977 (intern (concat "var-"
978 (symbol-name
979 (car u)))))))))
980 (or (null u)
981 (eq base (car u))
982 (setq expr (list '*
983 (nth 1 (assq (aref (symbol-name base) 0)
984 math-unit-prefixes))
985 expr)))
986 expr)
987 (if (eq base 'pi)
988 (math-pi)
989 expr)))
d6ffd3f8
JB
990 (if (or
991 (Math-primp expr)
992 (and (eq (car-safe expr) 'calcFunc-subscr)
993 (eq (car-safe (nth 1 expr)) 'var)))
136211a9
EZ
994 expr
995 (cons (car expr)
c9aef719 996 (mapcar 'math-to-standard-rec (cdr expr))))))
136211a9
EZ
997
998(defun math-apply-units (expr units ulist &optional pure)
2e2b4fbe 999 (setq expr (math-simplify-units expr))
136211a9
EZ
1000 (if ulist
1001 (let ((new 0)
1002 value)
136211a9
EZ
1003 (or (math-numberp expr)
1004 (error "Incompatible units"))
1005 (while (cdr ulist)
1006 (setq value (math-div expr (nth 1 (car ulist)))
1007 value (math-floor (let ((calc-internal-prec
1008 (1- calc-internal-prec)))
1009 (math-normalize value)))
1010 new (math-add new (math-mul value (car (car ulist))))
1011 expr (math-sub expr (math-mul value (nth 1 (car ulist))))
1012 ulist (cdr ulist)))
1013 (math-add new (math-mul (math-div expr (nth 1 (car ulist)))
1014 (car (car ulist)))))
2e2b4fbe
JB
1015 (if pure
1016 expr
1017 (math-simplify-units (list '* expr units)))))
136211a9 1018
8cd8ee52 1019(defvar math-decompose-units-cache nil)
136211a9
EZ
1020(defun math-decompose-units (units)
1021 (let ((u (math-check-unit-name units)))
1022 (and u (eq (car-safe (nth 1 u)) '+)
1023 (setq units (nth 1 u))))
1024 (setq units (calcFunc-expand units))
1025 (and (eq (car-safe units) '+)
1026 (let ((entry (list units calc-internal-prec calc-prefer-frac)))
1027 (or (equal entry (car math-decompose-units-cache))
1028 (let ((ulist nil)
1029 (utemp units)
1030 qty unit)
1031 (while (eq (car-safe utemp) '+)
1032 (setq ulist (cons (math-decompose-unit-part (nth 2 utemp))
1033 ulist)
1034 utemp (nth 1 utemp)))
1035 (setq ulist (cons (math-decompose-unit-part utemp) ulist)
1036 utemp ulist)
1037 (while (setq utemp (cdr utemp))
8cd8ee52
CW
1038 (unless (equal (nth 2 (car utemp)) (nth 2 (car ulist)))
1039 (error "Inconsistent units in sum")))
136211a9
EZ
1040 (setq math-decompose-units-cache
1041 (cons entry
1042 (sort ulist
1043 (function
1044 (lambda (x y)
1045 (not (Math-lessp (nth 1 x)
1046 (nth 1 y))))))))))
c9aef719 1047 (cdr math-decompose-units-cache))))
136211a9
EZ
1048
1049(defun math-decompose-unit-part (unit)
1050 (cons unit
1051 (math-is-multiple (math-simplify-units (math-to-standard-units
1052 unit nil))
c9aef719 1053 t)))
136211a9 1054
27971241
JB
1055;; The variable math-fcu-u is local to math-find-compatible-unit,
1056;; but is used by math-find-compatible-rec which is called by
1057;; math-find-compatible-unit.
1058(defvar math-fcu-u)
1059
136211a9 1060(defun math-find-compatible-unit (expr unit)
27971241
JB
1061 (let ((math-fcu-u (math-check-unit-name unit)))
1062 (if math-fcu-u
c9aef719 1063 (math-find-compatible-unit-rec expr 1))))
136211a9
EZ
1064
1065(defun math-find-compatible-unit-rec (expr pow)
1066 (cond ((eq (car-safe expr) '*)
1067 (or (math-find-compatible-unit-rec (nth 1 expr) pow)
1068 (math-find-compatible-unit-rec (nth 2 expr) pow)))
1069 ((eq (car-safe expr) '/)
1070 (or (math-find-compatible-unit-rec (nth 1 expr) pow)
1071 (math-find-compatible-unit-rec (nth 2 expr) (- pow))))
1072 ((and (eq (car-safe expr) '^)
1073 (integerp (nth 2 expr)))
1074 (math-find-compatible-unit-rec (nth 1 expr) (* pow (nth 2 expr))))
1075 (t
1076 (let ((u2 (math-check-unit-name expr)))
27971241 1077 (if (equal (nth 4 math-fcu-u) (nth 4 u2))
c9aef719 1078 (cons expr pow))))))
136211a9 1079
09040c8d
JB
1080;; The variables math-cu-new-units and math-cu-pure are local to
1081;; math-convert-units, but are used by math-convert-units-rec,
27971241
JB
1082;; which is called by math-convert-units.
1083(defvar math-cu-new-units)
1084(defvar math-cu-pure)
1085
1086(defun math-convert-units (expr math-cu-new-units &optional math-cu-pure)
65b62d0a
JB
1087 (if (eq (car-safe math-cu-new-units) 'var)
1088 (let ((unew (assq (nth 1 math-cu-new-units)
1089 (math-build-units-table))))
1090 (if (eq (car-safe (nth 1 unew)) '+)
1091 (setq math-cu-new-units (nth 1 unew)))))
136211a9 1092 (math-with-extra-prec 2
09040c8d 1093 (let ((compat (and (not math-cu-pure)
27971241
JB
1094 (math-find-compatible-unit expr math-cu-new-units)))
1095 (math-cu-unit-list nil)
136211a9
EZ
1096 (math-combining-units nil))
1097 (if compat
1098 (math-simplify-units
1099 (math-mul (math-mul (math-simplify-units
1100 (math-div expr (math-pow (car compat)
1101 (cdr compat))))
27971241 1102 (math-pow math-cu-new-units (cdr compat)))
136211a9
EZ
1103 (math-simplify-units
1104 (math-to-standard-units
27971241 1105 (math-pow (math-div (car compat) math-cu-new-units)
136211a9
EZ
1106 (cdr compat))
1107 nil))))
27971241
JB
1108 (when (setq math-cu-unit-list (math-decompose-units math-cu-new-units))
1109 (setq math-cu-new-units (nth 2 (car math-cu-unit-list))))
8cd8ee52
CW
1110 (when (eq (car-safe expr) '+)
1111 (setq expr (math-simplify-units expr)))
136211a9
EZ
1112 (if (math-units-in-expr-p expr t)
1113 (math-convert-units-rec expr)
1114 (math-apply-units (math-to-standard-units
27971241
JB
1115 (list '/ expr math-cu-new-units) nil)
1116 math-cu-new-units math-cu-unit-list math-cu-pure))))))
136211a9
EZ
1117
1118(defun math-convert-units-rec (expr)
1119 (if (math-units-in-expr-p expr nil)
09040c8d 1120 (math-apply-units (math-to-standard-units
27971241
JB
1121 (list '/ expr math-cu-new-units) nil)
1122 math-cu-new-units math-cu-unit-list math-cu-pure)
136211a9
EZ
1123 (if (Math-primp expr)
1124 expr
1125 (cons (car expr)
c9aef719 1126 (mapcar 'math-convert-units-rec (cdr expr))))))
136211a9
EZ
1127
1128(defun math-convert-temperature (expr old new &optional pure)
1129 (let* ((units (math-single-units-in-expr-p expr))
1130 (uold (if old
1131 (if (or (null units)
1132 (equal (nth 1 old) (car units)))
1133 (math-check-unit-name old)
1134 (error "Inconsistent temperature units"))
1135 units))
1136 (unew (math-check-unit-name new)))
8cd8ee52
CW
1137 (unless (and (consp unew) (nth 3 unew))
1138 (error "Not a valid temperature unit"))
1139 (unless (and (consp uold) (nth 3 uold))
1140 (error "Not a pure temperature expression"))
136211a9
EZ
1141 (let ((v (car uold)))
1142 (setq expr (list '/ expr (list 'var v
1143 (intern (concat "var-"
1144 (symbol-name v)))))))
1145 (or (eq (nth 3 uold) (nth 3 unew))
1146 (cond ((eq (nth 3 uold) 'K)
e6cd99dc 1147 (setq expr (list '- expr '(/ 27315 100)))
136211a9 1148 (if (eq (nth 3 unew) 'F)
e6cd99dc 1149 (setq expr (list '+ (list '* expr '(/ 9 5)) 32))))
136211a9
EZ
1150 ((eq (nth 3 uold) 'C)
1151 (if (eq (nth 3 unew) 'F)
e6cd99dc
JB
1152 (setq expr (list '+ (list '* expr '(/ 9 5)) 32))
1153 (setq expr (list '+ expr '(/ 27315 100)))))
136211a9 1154 (t
e6cd99dc 1155 (setq expr (list '* (list '- expr 32) '(/ 5 9)))
136211a9 1156 (if (eq (nth 3 unew) 'K)
e6cd99dc 1157 (setq expr (list '+ expr '(/ 27315 100)))))))
136211a9
EZ
1158 (if pure
1159 expr
c9aef719 1160 (list '* expr new))))
136211a9
EZ
1161
1162
1163
1164(defun math-simplify-units (a)
1165 (let ((math-simplifying-units t)
1166 (calc-matrix-mode 'scalar))
c9aef719 1167 (math-simplify a)))
8cd8ee52 1168(defalias 'calcFunc-usimplify 'math-simplify-units)
136211a9 1169
f095c6c9
JB
1170;; The function created by math-defsimplify uses the variable
1171;; math-simplify-expr, and so is used by functions in math-defsimplify
1172(defvar math-simplify-expr)
1173
136211a9
EZ
1174(math-defsimplify (+ -)
1175 (and math-simplifying-units
f095c6c9
JB
1176 (math-units-in-expr-p (nth 1 math-simplify-expr) nil)
1177 (let* ((units (math-extract-units (nth 1 math-simplify-expr)))
136211a9 1178 (ratio (math-simplify (math-to-standard-units
f095c6c9 1179 (list '/ (nth 2 math-simplify-expr) units) nil))))
136211a9
EZ
1180 (if (math-units-in-expr-p ratio nil)
1181 (progn
f095c6c9
JB
1182 (calc-record-why "*Inconsistent units" math-simplify-expr)
1183 math-simplify-expr)
1184 (list '* (math-add (math-remove-units (nth 1 math-simplify-expr))
09040c8d 1185 (if (eq (car math-simplify-expr) '-)
f095c6c9 1186 (math-neg ratio) ratio))
c9aef719 1187 units)))))
136211a9
EZ
1188
1189(math-defsimplify *
c9aef719 1190 (math-simplify-units-prod))
136211a9
EZ
1191
1192(defun math-simplify-units-prod ()
1193 (and math-simplifying-units
1194 calc-autorange-units
f095c6c9
JB
1195 (Math-realp (nth 1 math-simplify-expr))
1196 (let* ((num (math-float (nth 1 math-simplify-expr)))
136211a9 1197 (xpon (calcFunc-xpon num))
f095c6c9 1198 (unitp (cdr (cdr math-simplify-expr)))
136211a9 1199 (unit (car unitp))
f095c6c9 1200 (pow (if (eq (car math-simplify-expr) '*) 1 -1))
136211a9
EZ
1201 u)
1202 (and (eq (car-safe unit) '*)
1203 (setq unitp (cdr unit)
1204 unit (car unitp)))
1205 (and (eq (car-safe unit) '^)
1206 (integerp (nth 2 unit))
1207 (setq pow (* pow (nth 2 unit))
1208 unitp (cdr unit)
1209 unit (car unitp)))
1210 (and (setq u (math-check-unit-name unit))
1211 (integerp xpon)
1212 (or (< xpon 0)
1213 (>= xpon (if (eq (car u) 'm) 1 3)))
1214 (let* ((uxpon 0)
1215 (pref (if (< pow 0)
1216 (reverse math-unit-prefixes)
1217 math-unit-prefixes))
1218 (p pref)
1219 pxpon pname)
1220 (or (eq (car u) (nth 1 unit))
1221 (setq uxpon (* pow
1222 (nth 2 (nth 1 (assq
1223 (aref (symbol-name
1224 (nth 1 unit)) 0)
1225 math-unit-prefixes))))))
1226 (setq xpon (+ xpon uxpon))
1227 (while (and p
1228 (or (memq (car (car p)) '(?d ?D ?h ?H))
1229 (and (eq (car (car p)) ?c)
1230 (not (eq (car u) 'm)))
1231 (< xpon (setq pxpon (* (nth 2 (nth 1 (car p)))
1232 pow)))
1233 (progn
1234 (setq pname (math-build-var-name
1235 (if (eq (car (car p)) 0)
1236 (car u)
1237 (concat (char-to-string
1238 (car (car p)))
1239 (symbol-name
1240 (car u))))))
1241 (and (/= (car (car p)) 0)
1242 (assq (nth 1 pname)
1243 math-units-table)))))
1244 (setq p (cdr p)))
1245 (and p
1246 (/= pxpon uxpon)
1247 (or (not (eq p pref))
1248 (< xpon (+ pxpon (* (math-abs pow) 3))))
1249 (progn
f095c6c9 1250 (setcar (cdr math-simplify-expr)
136211a9 1251 (let ((calc-prefer-frac nil))
f095c6c9 1252 (calcFunc-scf (nth 1 math-simplify-expr)
136211a9
EZ
1253 (- uxpon pxpon))))
1254 (setcar unitp pname)
f095c6c9 1255 math-simplify-expr)))))))
136211a9 1256
27971241
JB
1257(defvar math-try-cancel-units)
1258
136211a9
EZ
1259(math-defsimplify /
1260 (and math-simplifying-units
f095c6c9 1261 (let ((np (cdr math-simplify-expr))
27971241 1262 (math-try-cancel-units 0)
136211a9 1263 n nn)
f095c6c9
JB
1264 (setq n (if (eq (car-safe (nth 2 math-simplify-expr)) '*)
1265 (cdr (nth 2 math-simplify-expr))
1266 (nthcdr 2 math-simplify-expr)))
136211a9
EZ
1267 (if (math-realp (car n))
1268 (progn
f095c6c9 1269 (setcar (cdr math-simplify-expr) (math-mul (nth 1 math-simplify-expr)
136211a9
EZ
1270 (let ((calc-prefer-frac nil))
1271 (math-div 1 (car n)))))
1272 (setcar n 1)))
1273 (while (eq (car-safe (setq n (car np))) '*)
f095c6c9 1274 (math-simplify-units-divisor (cdr n) (cdr (cdr math-simplify-expr)))
136211a9 1275 (setq np (cdr (cdr n))))
f095c6c9 1276 (math-simplify-units-divisor np (cdr (cdr math-simplify-expr)))
27971241 1277 (if (eq math-try-cancel-units 0)
136211a9 1278 (let* ((math-simplifying-units nil)
09040c8d 1279 (base (math-simplify
f095c6c9 1280 (math-to-standard-units math-simplify-expr nil))))
136211a9 1281 (if (Math-numberp base)
f095c6c9
JB
1282 (setq math-simplify-expr base))))
1283 (if (eq (car-safe math-simplify-expr) '/)
136211a9 1284 (math-simplify-units-prod))
f095c6c9 1285 math-simplify-expr)))
136211a9
EZ
1286
1287(defun math-simplify-units-divisor (np dp)
1288 (let ((n (car np))
1289 d dd temp)
1290 (while (eq (car-safe (setq d (car dp))) '*)
8cd8ee52
CW
1291 (when (setq temp (math-simplify-units-quotient n (nth 1 d)))
1292 (setcar np (setq n temp))
1293 (setcar (cdr d) 1))
136211a9 1294 (setq dp (cdr (cdr d))))
8cd8ee52
CW
1295 (when (setq temp (math-simplify-units-quotient n d))
1296 (setcar np (setq n temp))
1297 (setcar dp 1))))
136211a9
EZ
1298
1299;; Simplify, e.g., "in / cm" to "2.54" in a units expression.
1300(defun math-simplify-units-quotient (n d)
1301 (let ((pow1 1)
1302 (pow2 1))
8cd8ee52
CW
1303 (when (and (eq (car-safe n) '^)
1304 (integerp (nth 2 n)))
1305 (setq pow1 (nth 2 n) n (nth 1 n)))
1306 (when (and (eq (car-safe d) '^)
1307 (integerp (nth 2 d)))
1308 (setq pow2 (nth 2 d) d (nth 1 d)))
136211a9
EZ
1309 (let ((un (math-check-unit-name n))
1310 (ud (math-check-unit-name d)))
1311 (and un ud
1312 (if (and (equal (nth 4 un) (nth 4 ud))
1313 (eq pow1 pow2))
0317ca78
JB
1314 (if (eq pow1 1)
1315 (math-to-standard-units (list '/ n d) nil)
1316 (list '^ (math-to-standard-units (list '/ n d) nil) pow1))
136211a9
EZ
1317 (let (ud1)
1318 (setq un (nth 4 un)
1319 ud (nth 4 ud))
1320 (while un
1321 (setq ud1 ud)
1322 (while ud1
1323 (and (eq (car (car un)) (car (car ud1)))
27971241
JB
1324 (setq math-try-cancel-units
1325 (+ math-try-cancel-units
136211a9
EZ
1326 (- (* (cdr (car un)) pow1)
1327 (* (cdr (car ud)) pow2)))))
1328 (setq ud1 (cdr ud1)))
1329 (setq un (cdr un)))
c9aef719 1330 nil))))))
136211a9
EZ
1331
1332(math-defsimplify ^
1333 (and math-simplifying-units
f095c6c9
JB
1334 (math-realp (nth 2 math-simplify-expr))
1335 (if (memq (car-safe (nth 1 math-simplify-expr)) '(* /))
1336 (list (car (nth 1 math-simplify-expr))
09040c8d 1337 (list '^ (nth 1 (nth 1 math-simplify-expr))
f095c6c9 1338 (nth 2 math-simplify-expr))
09040c8d 1339 (list '^ (nth 2 (nth 1 math-simplify-expr))
f095c6c9 1340 (nth 2 math-simplify-expr)))
09040c8d 1341 (math-simplify-units-pow (nth 1 math-simplify-expr)
f095c6c9 1342 (nth 2 math-simplify-expr)))))
136211a9
EZ
1343
1344(math-defsimplify calcFunc-sqrt
1345 (and math-simplifying-units
f095c6c9
JB
1346 (if (memq (car-safe (nth 1 math-simplify-expr)) '(* /))
1347 (list (car (nth 1 math-simplify-expr))
1348 (list 'calcFunc-sqrt (nth 1 (nth 1 math-simplify-expr)))
1349 (list 'calcFunc-sqrt (nth 2 (nth 1 math-simplify-expr))))
1350 (math-simplify-units-pow (nth 1 math-simplify-expr) '(frac 1 2)))))
136211a9
EZ
1351
1352(math-defsimplify (calcFunc-floor
1353 calcFunc-ceil
1354 calcFunc-round
1355 calcFunc-rounde
1356 calcFunc-roundu
1357 calcFunc-trunc
1358 calcFunc-float
1359 calcFunc-frac
1360 calcFunc-abs
1361 calcFunc-clean)
1362 (and math-simplifying-units
f095c6c9
JB
1363 (= (length math-simplify-expr) 2)
1364 (if (math-only-units-in-expr-p (nth 1 math-simplify-expr))
1365 (nth 1 math-simplify-expr)
1366 (if (and (memq (car-safe (nth 1 math-simplify-expr)) '(* /))
136211a9 1367 (or (math-only-units-in-expr-p
f095c6c9 1368 (nth 1 (nth 1 math-simplify-expr)))
136211a9 1369 (math-only-units-in-expr-p
f095c6c9
JB
1370 (nth 2 (nth 1 math-simplify-expr)))))
1371 (list (car (nth 1 math-simplify-expr))
1372 (cons (car math-simplify-expr)
1373 (cons (nth 1 (nth 1 math-simplify-expr))
1374 (cdr (cdr math-simplify-expr))))
1375 (cons (car math-simplify-expr)
1376 (cons (nth 2 (nth 1 math-simplify-expr))
1377 (cdr (cdr math-simplify-expr)))))))))
136211a9
EZ
1378
1379(defun math-simplify-units-pow (a pow)
1380 (if (and (eq (car-safe a) '^)
1381 (math-check-unit-name (nth 1 a))
1382 (math-realp (nth 2 a)))
1383 (list '^ (nth 1 a) (math-mul pow (nth 2 a)))
1384 (let* ((u (math-check-unit-name a))
1385 (pf (math-to-simple-fraction pow))
1386 (d (and (eq (car-safe pf) 'frac) (nth 2 pf))))
1387 (and u d
1388 (math-units-are-multiple u d)
c9aef719 1389 (list '^ (math-to-standard-units a nil) pow)))))
136211a9
EZ
1390
1391
1392(defun math-units-are-multiple (u n)
1393 (setq u (nth 4 u))
1394 (while (and u (= (% (cdr (car u)) n) 0))
1395 (setq u (cdr u)))
c9aef719 1396 (null u))
136211a9
EZ
1397
1398(math-defsimplify calcFunc-sin
1399 (and math-simplifying-units
f095c6c9 1400 (math-units-in-expr-p (nth 1 math-simplify-expr) nil)
136211a9
EZ
1401 (let ((rad (math-simplify-units
1402 (math-evaluate-expr
f095c6c9 1403 (math-to-standard-units (nth 1 math-simplify-expr) nil))))
136211a9
EZ
1404 (calc-angle-mode 'rad))
1405 (and (eq (car-safe rad) '*)
1406 (math-realp (nth 1 rad))
1407 (eq (car-safe (nth 2 rad)) 'var)
1408 (eq (nth 1 (nth 2 rad)) 'rad)
c9aef719 1409 (list 'calcFunc-sin (nth 1 rad))))))
136211a9
EZ
1410
1411(math-defsimplify calcFunc-cos
1412 (and math-simplifying-units
f095c6c9 1413 (math-units-in-expr-p (nth 1 math-simplify-expr) nil)
136211a9
EZ
1414 (let ((rad (math-simplify-units
1415 (math-evaluate-expr
f095c6c9 1416 (math-to-standard-units (nth 1 math-simplify-expr) nil))))
136211a9
EZ
1417 (calc-angle-mode 'rad))
1418 (and (eq (car-safe rad) '*)
1419 (math-realp (nth 1 rad))
1420 (eq (car-safe (nth 2 rad)) 'var)
1421 (eq (nth 1 (nth 2 rad)) 'rad)
c9aef719 1422 (list 'calcFunc-cos (nth 1 rad))))))
136211a9
EZ
1423
1424(math-defsimplify calcFunc-tan
1425 (and math-simplifying-units
f095c6c9 1426 (math-units-in-expr-p (nth 1 math-simplify-expr) nil)
136211a9
EZ
1427 (let ((rad (math-simplify-units
1428 (math-evaluate-expr
f095c6c9 1429 (math-to-standard-units (nth 1 math-simplify-expr) nil))))
136211a9
EZ
1430 (calc-angle-mode 'rad))
1431 (and (eq (car-safe rad) '*)
1432 (math-realp (nth 1 rad))
1433 (eq (car-safe (nth 2 rad)) 'var)
1434 (eq (nth 1 (nth 2 rad)) 'rad)
c9aef719 1435 (list 'calcFunc-tan (nth 1 rad))))))
136211a9 1436
40b444ac
JB
1437(math-defsimplify calcFunc-sec
1438 (and math-simplifying-units
1439 (math-units-in-expr-p (nth 1 math-simplify-expr) nil)
1440 (let ((rad (math-simplify-units
1441 (math-evaluate-expr
1442 (math-to-standard-units (nth 1 math-simplify-expr) nil))))
1443 (calc-angle-mode 'rad))
1444 (and (eq (car-safe rad) '*)
1445 (math-realp (nth 1 rad))
1446 (eq (car-safe (nth 2 rad)) 'var)
1447 (eq (nth 1 (nth 2 rad)) 'rad)
1448 (list 'calcFunc-sec (nth 1 rad))))))
1449
1450(math-defsimplify calcFunc-csc
1451 (and math-simplifying-units
1452 (math-units-in-expr-p (nth 1 math-simplify-expr) nil)
1453 (let ((rad (math-simplify-units
1454 (math-evaluate-expr
1455 (math-to-standard-units (nth 1 math-simplify-expr) nil))))
1456 (calc-angle-mode 'rad))
1457 (and (eq (car-safe rad) '*)
1458 (math-realp (nth 1 rad))
1459 (eq (car-safe (nth 2 rad)) 'var)
1460 (eq (nth 1 (nth 2 rad)) 'rad)
1461 (list 'calcFunc-csc (nth 1 rad))))))
1462
1463(math-defsimplify calcFunc-cot
1464 (and math-simplifying-units
1465 (math-units-in-expr-p (nth 1 math-simplify-expr) nil)
1466 (let ((rad (math-simplify-units
1467 (math-evaluate-expr
1468 (math-to-standard-units (nth 1 math-simplify-expr) nil))))
1469 (calc-angle-mode 'rad))
1470 (and (eq (car-safe rad) '*)
1471 (math-realp (nth 1 rad))
1472 (eq (car-safe (nth 2 rad)) 'var)
1473 (eq (nth 1 (nth 2 rad)) 'rad)
1474 (list 'calcFunc-cot (nth 1 rad))))))
1475
136211a9
EZ
1476
1477(defun math-remove-units (expr)
1478 (if (math-check-unit-name expr)
1479 1
1480 (if (Math-primp expr)
1481 expr
1482 (cons (car expr)
c9aef719 1483 (mapcar 'math-remove-units (cdr expr))))))
136211a9
EZ
1484
1485(defun math-extract-units (expr)
4c9e9550
JB
1486 (cond
1487 ((memq (car-safe expr) '(* /))
1488 (cons (car expr)
1489 (mapcar 'math-extract-units (cdr expr))))
1490 ((and
1491 (eq (car-safe expr) '^)
1492 (math-check-unit-name (nth 1 expr)))
1493 expr)
1494 ((math-check-unit-name expr) expr)
1495 (t 1)))
136211a9
EZ
1496
1497(defun math-build-units-table-buffer (enter-buffer)
1498 (if (not (and math-units-table math-units-table-buffer-valid
1499 (get-buffer "*Units Table*")))
1500 (let ((buf (get-buffer-create "*Units Table*"))
1501 (uptr (math-build-units-table))
1502 (calc-language (if (eq calc-language 'big) nil calc-language))
1503 (calc-float-format '(float 0))
1504 (calc-group-digits nil)
1505 (calc-number-radix 10)
7b999abb 1506 (calc-twos-complement-mode nil)
136211a9
EZ
1507 (calc-point-char ".")
1508 (std nil)
1509 u name shadowed)
1510 (save-excursion
1511 (message "Formatting units table...")
1512 (set-buffer buf)
ed8060d9
JB
1513 (let ((inhibit-read-only t))
1514 (erase-buffer)
1515 (insert "Calculator Units Table:\n\n")
ac318df0 1516 (insert "(All definitions are exact unless marked with an asterisk (*).)\n\n")
ed8060d9
JB
1517 (insert "Unit Type Definition Description\n\n")
1518 (while uptr
1519 (setq u (car uptr)
1520 name (nth 2 u))
1521 (when (eq (car u) 'm)
1522 (setq std t))
1523 (setq shadowed (and std (assq (car u) math-additional-units)))
1524 (when (and name
1525 (> (length name) 1)
1526 (eq (aref name 0) ?\*))
1527 (unless (eq uptr math-units-table)
1528 (insert "\n"))
1529 (setq name (substring name 1)))
1530 (insert " ")
1531 (and shadowed (insert "("))
1532 (insert (symbol-name (car u)))
1533 (and shadowed (insert ")"))
1534 (if (nth 3 u)
1535 (progn
1536 (indent-to 10)
1537 (insert (symbol-name (nth 3 u))))
1538 (or std
1539 (progn
1540 (indent-to 10)
1541 (insert "U"))))
1542 (indent-to 14)
1543 (and shadowed (insert "("))
19bdc4d8
JB
1544 (if (nth 5 u)
1545 (insert (nth 5 u))
1546 (if (nth 1 u)
1547 (insert (math-format-value (nth 1 u) 80))
1548 (insert (symbol-name (car u)))))
ed8060d9
JB
1549 (and shadowed (insert ")"))
1550 (indent-to 41)
1551 (insert " ")
1552 (when name
1553 (insert name))
1554 (if shadowed
1555 (insert " (redefined above)")
1556 (unless (nth 1 u)
1557 (insert " (base unit)")))
1558 (insert "\n")
1559 (setq uptr (cdr uptr)))
1560 (insert "\n\nUnit Prefix Table:\n\n")
1561 (setq uptr math-unit-prefixes)
1562 (while uptr
1563 (setq u (car uptr))
1564 (insert " " (char-to-string (car u)))
1565 (if (equal (nth 1 u) (nth 1 (nth 1 uptr)))
1566 (insert " " (char-to-string (car (car (setq uptr (cdr uptr)))))
1567 " ")
1568 (insert " "))
1569 (insert "10^" (int-to-string (nth 2 (nth 1 u))))
1570 (indent-to 15)
1571 (insert " " (nth 2 u) "\n")
1572 (while (eq (car (car (setq uptr (cdr uptr)))) 0)))
1265829e
JB
1573 (insert "\n\n")
1574 (insert "(**) When in TeX or LaTeX display mode, the TeX specific unit\n"
1575 "names will not use the `tex' prefix; the unit name for a\n"
1576 "TeX point will be `pt' instead of `texpt', for example.\n"
1577 "To avoid conflicts, the unit names for pint and parsec will\n"
1578 "be `pint' and `parsec' instead of `pt' and `pc'."))
ed8060d9 1579 (view-mode)
136211a9
EZ
1580 (message "Formatting units table...done"))
1581 (setq math-units-table-buffer-valid t)
1582 (let ((oldbuf (current-buffer)))
1583 (set-buffer buf)
1584 (goto-char (point-min))
1585 (set-buffer oldbuf))
1586 (if enter-buffer
1587 (pop-to-buffer buf)
1588 (display-buffer buf)))
1589 (if enter-buffer
1590 (pop-to-buffer (get-buffer "*Units Table*"))
c9aef719 1591 (display-buffer (get-buffer "*Units Table*")))))
136211a9 1592
603823f5
JB
1593;;; Logarithmic units functions
1594
1595(defvar math-logunits '((var dB var-dB)
1596 (var Np var-Np)))
1597
771fc75e
JB
1598(defun math-conditional-apply (fn &rest args)
1599 "Evaluate f(args) unless in symbolic mode.
1600In symbolic mode, return the list (fn args)."
1601 (if calc-symbolic-mode
1602 (cons fn args)
1603 (apply fn args)))
1604
1605(defun math-conditional-pow (a b)
1606 "Evaluate a^b unless in symbolic mode.
1607In symbolic mode, return the list (^ a b)."
1608 (if calc-symbolic-mode
1609 (list '^ a b)
1610 (math-pow a b)))
1611
603823f5
JB
1612(defun math-extract-logunits (expr)
1613 (if (memq (car-safe expr) '(* /))
1614 (cons (car expr)
1615 (mapcar 'math-extract-logunits (cdr expr)))
1616 (if (memq (car-safe expr) '(^))
1617 (list '^ (math-extract-logunits (nth 1 expr)) (nth 2 expr))
1618 (if (member expr math-logunits) expr 1))))
1619
ec6ad6f2 1620(defun math-logunits-add (a b neg power)
603823f5
JB
1621 (let ((aunit (math-simplify (math-extract-logunits a))))
1622 (if (not (eq (car-safe aunit) 'var))
1623 (calc-record-why "*Improper logarithmic unit" aunit)
1624 (let* ((units (math-extract-units a))
1625 (acoeff (math-simplify (math-remove-units a)))
1626 (bcoeff (math-simplify (math-to-standard-units
1627 (list '/ b units) nil))))
1628 (if (math-units-in-expr-p bcoeff nil)
1629 (calc-record-why "*Inconsistent units" nil)
1630 (if (and neg
1631 (or (math-lessp acoeff bcoeff)
1632 (math-equal acoeff bcoeff)))
1633 (calc-record-why "*Improper coefficients" nil)
da6062e6 1634 (math-mul
603823f5 1635 (if (equal aunit '(var dB var-dB))
6f16becc
JB
1636 (let ((coef (if power 10 20)))
1637 (math-mul coef
771fc75e 1638 (math-conditional-apply 'calcFunc-log10
6f16becc
JB
1639 (if neg
1640 (math-sub
771fc75e
JB
1641 (math-conditional-pow 10 (math-div acoeff coef))
1642 (math-conditional-pow 10 (math-div bcoeff coef)))
6f16becc 1643 (math-add
771fc75e
JB
1644 (math-conditional-pow 10 (math-div acoeff coef))
1645 (math-conditional-pow 10 (math-div bcoeff coef)))))))
6f16becc
JB
1646 (let ((coef (if power 2 1)))
1647 (math-div
771fc75e 1648 (math-conditional-apply 'calcFunc-ln
6f16becc
JB
1649 (if neg
1650 (math-sub
771fc75e
JB
1651 (math-conditional-apply 'calcFunc-exp (math-mul coef acoeff))
1652 (math-conditional-apply 'calcFunc-exp (math-mul coef bcoeff)))
6f16becc 1653 (math-add
771fc75e
JB
1654 (math-conditional-apply 'calcFunc-exp (math-mul coef acoeff))
1655 (math-conditional-apply 'calcFunc-exp (math-mul coef bcoeff)))))
6f16becc 1656 coef)))
603823f5
JB
1657 units)))))))
1658
d71990a1 1659(defun calcFunc-lufadd (a b)
ec6ad6f2 1660 (math-logunits-add a b nil nil))
603823f5 1661
d71990a1 1662(defun calcFunc-lupadd (a b)
ec6ad6f2 1663 (math-logunits-add a b nil t))
603823f5 1664
d71990a1 1665(defun calcFunc-lufsub (a b)
ec6ad6f2 1666 (math-logunits-add a b t nil))
6f16becc 1667
d71990a1 1668(defun calcFunc-lupsub (a b)
ec6ad6f2 1669 (math-logunits-add a b t t))
6f16becc 1670
d71990a1 1671(defun calc-lu-plus (arg)
603823f5
JB
1672 (interactive "P")
1673 (calc-slow-wrapper
1674 (if (calc-is-inverse)
6f16becc 1675 (if (calc-is-hyperbolic)
d71990a1
JB
1676 (calc-binary-op "lu-" 'calcFunc-lufsub arg)
1677 (calc-binary-op "lu-" 'calcFunc-lupsub arg))
6f16becc 1678 (if (calc-is-hyperbolic)
d71990a1
JB
1679 (calc-binary-op "lu+" 'calcFunc-lufadd arg)
1680 (calc-binary-op "lu+" 'calcFunc-lupadd arg)))))
6f16becc 1681
d71990a1 1682(defun calc-lu-minus (arg)
6f16becc
JB
1683 (interactive "P")
1684 (calc-slow-wrapper
1685 (if (calc-is-inverse)
1686 (if (calc-is-hyperbolic)
d71990a1
JB
1687 (calc-binary-op "lu+" 'calcFunc-lufadd arg)
1688 (calc-binary-op "lu+" 'calcFunc-lupadd arg))
6f16becc 1689 (if (calc-is-hyperbolic)
d71990a1
JB
1690 (calc-binary-op "lu-" 'calcFunc-lufsub arg)
1691 (calc-binary-op "lu-" 'calcFunc-lupsub arg)))))
6f16becc 1692
ec6ad6f2 1693(defun math-logunits-mul (a b power)
6f16becc
JB
1694 (let (logunit coef units number)
1695 (cond
1696 ((and
1697 (setq logunit (math-simplify (math-extract-logunits a)))
1698 (eq (car-safe logunit) 'var)
1699 (eq (math-simplify (math-extract-units b)) 1))
1700 (setq coef (math-simplify (math-remove-units a))
1701 units (math-extract-units a)
1702 number b))
1703 ((and
1704 (setq logunit (math-simplify (math-extract-logunits b)))
1705 (eq (car-safe logunit) 'var)
1706 (eq (math-simplify (math-extract-units a)) 1))
1707 (setq coef (math-simplify (math-remove-units b))
1708 units (math-extract-units b)
1709 number a))
1710 (t (setq logunit nil)))
1711 (if logunit
1712 (cond
1713 ((equal logunit '(var dB var-dB))
1714 (math-simplify
1715 (math-mul
1716 (math-add
da6062e6 1717 coef
6f16becc 1718 (math-mul (if power 10 20)
771fc75e 1719 (math-conditional-apply 'calcFunc-log10 number)))
6f16becc
JB
1720 units)))
1721 (t
1722 (math-simplify
1723 (math-mul
1724 (math-add
da6062e6 1725 coef
771fc75e 1726 (math-div (math-conditional-apply 'calcFunc-ln number) (if power 2 1)))
6f16becc
JB
1727 units))))
1728 (calc-record-why "*Improper units" nil))))
1729
1730(defun math-logunits-divide (a b power)
1731 (let ((logunit (math-simplify (math-extract-logunits a))))
1732 (if (not (eq (car-safe logunit) 'var))
1733 (calc-record-why "*Improper logarithmic unit" logunit)
1734 (if (math-units-in-expr-p b nil)
1735 (calc-record-why "*Improper units quantity" b)
1736 (let* ((units (math-extract-units a))
1737 (coef (math-simplify (math-remove-units a))))
1738 (cond
1739 ((equal logunit '(var dB var-dB))
1740 (math-simplify
1741 (math-mul
1742 (math-sub
da6062e6 1743 coef
6f16becc 1744 (math-mul (if power 10 20)
771fc75e 1745 (math-conditional-apply 'calcFunc-log10 b)))
6f16becc
JB
1746 units)))
1747 (t
1748 (math-simplify
1749 (math-mul
1750 (math-sub
da6062e6 1751 coef
771fc75e 1752 (math-div (math-conditional-apply 'calcFunc-ln b) (if power 2 1)))
6f16becc
JB
1753 units)))))))))
1754
d71990a1 1755(defun calcFunc-lufmul (a b)
ec6ad6f2 1756 (math-logunits-mul a b nil))
6f16becc 1757
d71990a1 1758(defun calcFunc-lupmul (a b)
ec6ad6f2 1759 (math-logunits-mul a b t))
6f16becc 1760
d71990a1 1761(defun calc-lu-times (arg)
603823f5
JB
1762 (interactive "P")
1763 (calc-slow-wrapper
1764 (if (calc-is-inverse)
6f16becc 1765 (if (calc-is-hyperbolic)
d71990a1
JB
1766 (calc-binary-op "lu/" 'calcFunc-lufdiv arg)
1767 (calc-binary-op "lu/" 'calcFunc-lupdiv arg))
6f16becc 1768 (if (calc-is-hyperbolic)
d71990a1
JB
1769 (calc-binary-op "lu*" 'calcFunc-lufmul arg)
1770 (calc-binary-op "lu*" 'calcFunc-lupmul arg)))))
603823f5 1771
d71990a1 1772(defun calcFunc-lufdiv (a b)
6f16becc 1773 (math-logunits-divide a b nil))
603823f5 1774
d71990a1 1775(defun calcFunc-lupdiv (a b)
6f16becc 1776 (math-logunits-divide a b t))
603823f5 1777
d71990a1 1778(defun calc-lu-divide (arg)
6f16becc
JB
1779 (interactive "P")
1780 (calc-slow-wrapper
1781 (if (calc-is-inverse)
1782 (if (calc-is-hyperbolic)
d71990a1
JB
1783 (calc-binary-op "lu*" 'calcFunc-lufmul arg)
1784 (calc-binary-op "lu*" 'calcFunc-lupmul arg))
6f16becc 1785 (if (calc-is-hyperbolic)
d71990a1
JB
1786 (calc-binary-op "lu/" 'calcFunc-lufdiv arg)
1787 (calc-binary-op "lu/" 'calcFunc-lupdiv arg)))))
6f16becc
JB
1788
1789(defun math-logunits-quant (val ref power)
226590f8
JB
1790 (let* ((units (math-simplify (math-extract-units val)))
1791 (lunit (math-simplify (math-extract-logunits units))))
603823f5
JB
1792 (if (not (eq (car-safe lunit) 'var))
1793 (calc-record-why "*Improper logarithmic unit" lunit)
226590f8
JB
1794 (let ((runits (math-simplify (math-div units lunit)))
1795 (coeff (math-simplify (math-div val units))))
1796 (math-mul
1797 (if (equal lunit '(var dB var-dB))
da6062e6 1798 (math-mul
226590f8 1799 ref
da6062e6 1800 (math-conditional-pow
226590f8
JB
1801 10
1802 (math-div
1803 coeff
1804 (if power 10 20))))
da6062e6 1805 (math-mul
226590f8 1806 ref
771fc75e 1807 (math-conditional-apply 'calcFunc-exp
da6062e6 1808 (if power
226590f8
JB
1809 (math-mul 2 coeff)
1810 coeff))))
1811 runits)))))
603823f5 1812
d71990a1
JB
1813(defvar calc-lu-field-reference)
1814(defvar calc-lu-power-reference)
603823f5 1815
580b66d8 1816(defun calcFunc-lufquant (val &optional ref)
603823f5 1817 (unless ref
d71990a1 1818 (setq ref (math-read-expr calc-lu-field-reference)))
6f16becc 1819 (math-logunits-quant val ref nil))
603823f5 1820
580b66d8 1821(defun calcFunc-lupquant (val &optional ref)
603823f5 1822 (unless ref
d71990a1 1823 (setq ref (math-read-expr calc-lu-power-reference)))
6f16becc
JB
1824 (math-logunits-quant val ref t))
1825
d71990a1 1826(defun calc-lu-quant (arg)
6f16becc
JB
1827 (interactive "P")
1828 (calc-slow-wrapper
1829 (if (calc-is-hyperbolic)
1830 (if (calc-is-option)
580b66d8
JB
1831 (calc-binary-op "lupq" 'calcFunc-lufquant arg)
1832 (calc-unary-op "lupq" 'calcFunc-lufquant arg))
6f16becc 1833 (if (calc-is-option)
580b66d8
JB
1834 (calc-binary-op "lufq" 'calcFunc-lupquant arg)
1835 (calc-unary-op "lufq" 'calcFunc-lupquant arg)))))
6f16becc
JB
1836
1837(defun math-logunits-level (val ref db power)
1838 "Compute the value of VAL in decibels or nepers."
1839 (let* ((ratio (math-simplify-units (math-div val ref)))
771fc75e 1840 (ratiou (math-simplify-units (math-remove-units ratio)))
6f16becc
JB
1841 (units (math-simplify (math-extract-units ratio))))
1842 (math-mul
1843 (if db
1844 (math-mul
1845 (math-mul (if power 10 20)
771fc75e 1846 (math-conditional-apply 'calcFunc-log10 ratiou))
6f16becc
JB
1847 '(var dB var-dB))
1848 (math-mul
771fc75e 1849 (math-div (math-conditional-apply 'calcFunc-ln ratiou) (if power 2 1))
6f16becc
JB
1850 '(var Np var-Np)))
1851 units)))
1852
d71990a1 1853(defun calcFunc-dbfield (val &optional ref)
6f16becc 1854 (unless ref
d71990a1 1855 (setq ref (math-read-expr calc-lu-field-reference)))
6f16becc
JB
1856 (math-logunits-level val ref t nil))
1857
d71990a1 1858(defun calcFunc-dbpower (val &optional ref)
6f16becc 1859 (unless ref
d71990a1 1860 (setq ref (math-read-expr calc-lu-power-reference)))
6f16becc
JB
1861 (math-logunits-level val ref t t))
1862
d71990a1 1863(defun calcFunc-npfield (val &optional ref)
6f16becc 1864 (unless ref
d71990a1 1865 (setq ref (math-read-expr calc-lu-field-reference)))
6f16becc
JB
1866 (math-logunits-level val ref nil nil))
1867
d71990a1 1868(defun calcFunc-nppower (val &optional ref)
6f16becc 1869 (unless ref
d71990a1 1870 (setq ref (math-read-expr calc-lu-power-reference)))
6f16becc
JB
1871 (math-logunits-level val ref nil t))
1872
d71990a1 1873(defun calc-db (arg)
6f16becc
JB
1874 (interactive "P")
1875 (calc-slow-wrapper
1876 (if (calc-is-hyperbolic)
1877 (if (calc-is-option)
d71990a1
JB
1878 (calc-binary-op "ludb" 'calcFunc-dbfield arg)
1879 (calc-unary-op "ludb" 'calcFunc-dbfield arg))
6f16becc 1880 (if (calc-is-option)
d71990a1
JB
1881 (calc-binary-op "ludb" 'calcFunc-dbpower arg)
1882 (calc-unary-op "ludb" 'calcFunc-dbpower arg)))))
603823f5 1883
d71990a1 1884(defun calc-np (arg)
603823f5
JB
1885 (interactive "P")
1886 (calc-slow-wrapper
1887 (if (calc-is-hyperbolic)
1888 (if (calc-is-option)
d71990a1
JB
1889 (calc-binary-op "lunp" 'calcFunc-npfield arg)
1890 (calc-unary-op "lunp" 'calcFunc-npfield arg))
603823f5 1891 (if (calc-is-option)
d71990a1
JB
1892 (calc-binary-op "lunp" 'calcFunc-nppower arg)
1893 (calc-unary-op "lunp" 'calcFunc-nppower arg)))))
603823f5 1894
05a29101
JB
1895;;; Musical notes
1896
1897
1898(defvar calc-note-threshold)
1899
1900(defun math-midi-round (num)
1901 "Round NUM to an integer N if NUM is within calc-note-threshold cents of N."
1902 (let* ((n (math-round num))
1903 (diff (math-abs
1904 (math-sub num n))))
da6062e6 1905 (if (< (math-compare diff
8f60c820 1906 (math-div (math-read-expr calc-note-threshold) 100)) 0)
05a29101
JB
1907 n
1908 num)))
1909
1910(defconst math-notes
1911 '(((var C var-C) . 0)
1912 ((var Csharp var-Csharp) . 1)
1913; ((var C♯ var-C♯) . 1)
1914 ((var Dflat var-Dflat) . 1)
1915; ((var D♭ var-D♭) . 1)
1916 ((var D var-D) . 2)
1917 ((var Dsharp var-Dsharp) . 3)
1918; ((var D♯ var-D♯) . 3)
1919 ((var E var-E) . 4)
1920 ((var F var-F) . 5)
1921 ((var Fsharp var-Fsharp) . 6)
1922; ((var F♯ var-F♯) . 6)
1923 ((var Gflat var-Gflat) . 6)
1924; ((var G♭ var-G♭) . 6)
1925 ((var G var-G) . 7)
1926 ((var Gsharp var-Gsharp) . 8)
1927; ((var G♯ var-G♯) . 8)
1928 ((var A var-A) . 9)
1929 ((var Asharp var-Asharp) . 10)
1930; ((var A♯ var-A♯) . 10)
1931 ((var Bflat var-Bflat) . 10)
1932; ((var B♭ var-B♭) . 10)
1933 ((var B var-B) . 11))
1934 "An alist of notes with their number of semitones above C.")
1935
1936(defun math-freqp (freq)
1937 "Non-nil if FREQ is a positive number times the unit Hz.
1938If non-nil, return the coefficient of Hz."
1939 (let ((freqcoef (math-simplify-units
1940 (math-div freq '(var Hz var-Hz)))))
1941 (if (Math-posp freqcoef) freqcoef)))
1942
1943(defun math-midip (num)
1944 "Non-nil if NUM is a possible MIDI note number.
1945If non-nil, return NUM."
1946 (if (Math-numberp num) num))
1947
1948(defun math-spnp (spn)
1949 "Non-nil if NUM is a scientific pitch note (note + cents).
1950If non-nil, return a list consisting of the note and the cents coefficient."
1951 (let (note cents rnote rcents)
1952 (if (eq (car-safe spn) '+)
1953 (setq note (nth 1 spn)
1954 cents (nth 2 spn))
1955 (setq note spn
1956 cents nil))
1957 (cond
1958 ((and ;; NOTE is a note, CENTS is nil or cents.
1959 (eq (car-safe note) 'calcFunc-subscr)
1960 (assoc (nth 1 note) math-notes)
1961 (integerp (nth 2 note))
1962 (setq rnote note)
da6062e6 1963 (or
05a29101
JB
1964 (not cents)
1965 (Math-numberp (setq rcents
da6062e6 1966 (math-simplify
05a29101
JB
1967 (math-div cents '(var cents var-cents)))))))
1968 (list rnote rcents))
1969 ((and ;; CENTS is a note, NOTE is cents.
1970 (eq (car-safe cents) 'calcFunc-subscr)
1971 (assoc (nth 1 cents) math-notes)
1972 (integerp (nth 2 cents))
1973 (setq rnote cents)
da6062e6 1974 (or
05a29101
JB
1975 (not note)
1976 (Math-numberp (setq rcents
da6062e6 1977 (math-simplify
05a29101
JB
1978 (math-div note '(var cents var-cents)))))))
1979 (list rnote rcents)))))
1980
1981(defun math-freq-to-midi (freq)
1982 "Return the midi note number corresponding to FREQ Hz."
1983 (let ((midi (math-add
1984 69
1985 (math-mul
1986 12
1987 (calcFunc-log
1988 (math-div freq 440)
1989 2)))))
1990 (math-midi-round midi)))
1991
1992(defun math-spn-to-midi (spn)
1993 "Return the MIDI number corresponding to SPN."
1994 (let* ((note (cdr (assoc (nth 1 (car spn)) math-notes)))
1995 (octave (math-add (nth 2 (car spn)) 1))
1996 (cents (nth 1 spn))
1997 (midi (math-add
1998 (math-mul 12 octave)
1999 note)))
2000 (if cents
2001 (math-add midi (math-div cents 100))
2002 midi)))
2003
2004(defun math-midi-to-spn (midi)
2005 "Return the scientific pitch notation corresponding to midi number MIDI."
2006 (let (midin cents)
2007 (if (math-integerp midi)
da6062e6 2008 (setq midin midi
05a29101
JB
2009 cents nil)
2010 (setq midin (math-floor midi)
2011 cents (math-mul 100 (math-sub midi midin))))
2012 (let* ((nr ;; This should be (math-idivmod midin 12), but with
2013 ;; better behavior for negative midin.
2014 (if (Math-negp midin)
2015 (let ((dm (math-idivmod (math-neg midin) 12)))
2016 (if (= (cdr dm) 0)
2017 (cons (math-neg (car dm)) 0)
2018 (cons
2019 (math-sub (math-neg (car dm)) 1)
2020 (math-sub 12 (cdr dm)))))
2021 (math-idivmod midin 12)))
2022 (n (math-sub (car nr) 1))
2023 (note (car (rassoc (cdr nr) math-notes))))
2024 (if cents
da6062e6 2025 (list '+ (list 'calcFunc-subscr note n)
05a29101
JB
2026 (list '* cents '(var cents var-cents)))
2027 (list 'calcFunc-subscr note n)))))
2028
2029(defun math-freq-to-spn (freq)
2030 "Return the scientific pitch notation corresponding to FREQ Hz."
2031 (math-with-extra-prec 3
2032 (math-midi-to-spn (math-freq-to-midi freq))))
2033
2034(defun math-midi-to-freq (midi)
2035 "Return the frequency of the note with midi number MIDI."
2036 (list '*
2037 (math-mul
2038 440
2039 (math-pow
2040 2
da6062e6 2041 (math-div
05a29101
JB
2042 (math-sub
2043 midi
2044 69)
2045 12)))
2046 '(var Hz var-Hz)))
2047
2048(defun math-spn-to-freq (spn)
2049 "Return the frequency of the note with scientific pitch notation SPN."
2050 (math-midi-to-freq (math-spn-to-midi spn)))
2051
2052(defun calcFunc-spn (expr)
2053 "Return EXPR written as scientific pitch notation + cents."
da6062e6 2054 ;; Get the coefficient of Hz
05a29101
JB
2055 (let (note)
2056 (cond
2057 ((setq note (math-freqp expr))
2058 (math-freq-to-spn note))
2059 ((setq note (math-midip expr))
2060 (math-midi-to-spn note))
2061 ((math-spnp expr)
2062 expr)
2063 (t
2064 (math-reject-arg expr "*Improper expression")))))
2065
2066(defun calcFunc-midi (expr)
2067 "Return EXPR written as a MIDI number."
2068 (let (note)
2069 (cond
2070 ((setq note (math-freqp expr))
2071 (math-freq-to-midi note))
2072 ((setq note (math-spnp expr))
2073 (math-spn-to-midi note))
2074 ((math-midip expr)
2075 expr)
2076 (t
2077 (math-reject-arg expr "*Improper expression")))))
2078
2079(defun calcFunc-freq (expr)
2080 "Return the frequency corresponding to EXPR."
2081 (let (note)
2082 (cond
2083 ((setq note (math-midip expr))
2084 (math-midi-to-freq note))
2085 ((setq note (math-spnp expr))
2086 (math-spn-to-freq note))
2087 ((math-freqp expr)
2088 expr)
2089 (t
2090 (math-reject-arg expr "*Improper expression")))))
2091
2092(defun calc-freq (arg)
2093 "Return the frequency corresponding to the expression on the stack."
2094 (interactive "P")
2095 (calc-slow-wrapper
2096 (calc-unary-op "freq" 'calcFunc-freq arg)))
2097
2098(defun calc-midi (arg)
2099 "Return the MIDI number corresponding to the expression on the stack."
2100 (interactive "P")
2101 (calc-slow-wrapper
2102 (calc-unary-op "midi" 'calcFunc-midi arg)))
2103
2104(defun calc-spn (arg)
2105 "Return the scientific pitch notation corresponding to the expression on the stack."
2106 (interactive "P")
2107 (calc-slow-wrapper
2108 (calc-unary-op "spn" 'calcFunc-spn arg)))
2109
2110
7d02e8cd
JB
2111(provide 'calc-units)
2112
be19ef0b
GM
2113;; Local variables:
2114;; coding: utf-8
2115;; End:
2116
c9aef719 2117;;; calc-units.el ends here