* src/eval.c (Fbind_symbol): New function.
[bpt/emacs.git] / lisp / calc / calc-units.el
CommitLineData
8cd8ee52
CW
1;;; calc-units.el --- unit conversion functions for Calc
2
ba318903 3;; Copyright (C) 1990-1993, 2001-2014 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
0ccecc08 440(defvar calc-allow-units-as-numbers t)
5d9c6f17 441
136211a9
EZ
442(defun calc-convert-units (&optional old-units new-units)
443 (interactive)
444 (calc-slow-wrapper
445 (let ((expr (calc-top-n 1))
446 (uoldname nil)
7c3fb76f 447 (unitscancel nil)
5d9c6f17 448 (nouold nil)
27971241 449 unew
5360ea16
JB
450 units
451 defunits)
7c3fb76f 452 (if (or (not (math-units-in-expr-p expr t))
5d9c6f17 453 (setq unitscancel (and
0ccecc08
JB
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)
5d9c6f17 457 (eq (math-get-standard-units expr) 1))))
8cd8ee52
CW
458 (let ((uold (or old-units
459 (progn
7c3fb76f
JB
460 (setq uoldname
461 (if unitscancel
462 (read-string
463 "(The expression is unitless when simplified) Old Units: ")
464 (read-string "Old units: ")))
8cd8ee52
CW
465 (if (equal uoldname "")
466 (progn
5d9c6f17 467 (setq nouold unitscancel)
8cd8ee52
CW
468 (setq uoldname "1")
469 1)
470 (if (string-match "\\` */" uoldname)
471 (setq uoldname (concat "1" uoldname)))
472 (math-read-expr uoldname))))))
473 (when (eq (car-safe uold) 'error)
474 (error "Bad format in units expression: %s" (nth 1 uold)))
475 (setq expr (math-mul expr uold))))
0fd09128 476 (setq defunits (math-get-default-units expr))
7c3fb76f
JB
477 (unless new-units
478 (setq new-units
479 (read-string (concat
5d9c6f17 480 (if (and uoldname (not nouold))
7c3fb76f
JB
481 (concat "Old units: "
482 uoldname
483 ", new units")
484 "New units")
485 (if defunits
486 (concat
487 " (default "
488 defunits
489 "): ")
490 ": "))))
491 (if (and
492 (string= new-units "")
493 defunits)
494 (setq new-units defunits)))
495 (when (string-match "\\` */" new-units)
496 (setq new-units (concat "1" new-units)))
497 (setq units (math-read-expr new-units))
498 (when (eq (car-safe units) 'error)
499 (error "Bad format in units expression: %s" (nth 2 units)))
500 (if calc-ensure-consistent-units
501 (math-check-unit-consistency expr units))
502 (let ((unew (math-units-in-expr-p units t))
503 (std (and (eq (car-safe units) 'var)
504 (assq (nth 1 units) math-standard-units-systems)))
505 (comp (eq (car-safe units) '+)))
506 (unless (or unew std)
507 (error "No units specified"))
6d7ebb72
JB
508 (let* ((noold (and uoldname (not (equal uoldname "1"))))
509 (res
510 (if std
511 (math-simplify-units (math-to-standard-units expr (nth 1 std)))
512 (math-convert-units expr units noold))))
513 (unless std
514 (math-put-default-units (if noold units res) (if comp units)))
7c3fb76f 515 (calc-enter-result 1 "cvun" res))))))
136211a9
EZ
516
517(defun calc-autorange-units (arg)
518 (interactive "P")
519 (calc-wrapper
520 (calc-change-mode 'calc-autorange-units arg nil t)
521 (message (if calc-autorange-units
8cd8ee52
CW
522 "Adjusting target unit prefix automatically"
523 "Using target units exactly"))))
136211a9
EZ
524
525(defun calc-convert-temperature (&optional old-units new-units)
526 (interactive)
527 (calc-slow-wrapper
528 (let ((expr (calc-top-n 1))
529 (uold nil)
530 (uoldname nil)
5360ea16
JB
531 unew
532 defunits)
136211a9
EZ
533 (setq uold (or old-units
534 (let ((units (math-single-units-in-expr-p expr)))
535 (if units
536 (if (consp units)
537 (list 'var (car units)
538 (intern (concat "var-"
539 (symbol-name
540 (car units)))))
541 (error "Not a pure temperature expression"))
542 (math-read-expr
543 (setq uoldname (read-string
544 "Old temperature units: ")))))))
8cd8ee52
CW
545 (when (eq (car-safe uold) 'error)
546 (error "Bad format in units expression: %s" (nth 2 uold)))
136211a9
EZ
547 (or (math-units-in-expr-p expr nil)
548 (setq expr (math-mul expr uold)))
5360ea16 549 (setq defunits (math-get-default-units expr))
136211a9 550 (setq unew (or new-units
fa786329
JB
551 (read-string
552 (concat
553 (if uoldname
554 (concat "Old temperature units: "
555 uoldname
556 ", new units")
557 "New temperature units")
558 (if defunits
559 (concat " (default "
560 defunits
561 "): ")
562 ": ")))))
563 (setq unew (math-read-expr (if (string= unew "") defunits unew)))
8cd8ee52
CW
564 (when (eq (car-safe unew) 'error)
565 (error "Bad format in units expression: %s" (nth 2 unew)))
5360ea16 566 (math-put-default-units unew)
a34f800f
JB
567 (let ((ntemp (calc-normalize
568 (math-simplify-units
569 (math-convert-temperature expr uold unew
570 uoldname)))))
571 (if (Math-zerop ntemp)
572 (setq ntemp (list '* ntemp unew)))
573 (let ((calc-simplify-mode 'none))
574 (calc-enter-result 1 "cvtm" ntemp))))))
136211a9
EZ
575
576(defun calc-remove-units ()
577 (interactive)
578 (calc-slow-wrapper
579 (calc-enter-result 1 "rmun" (math-simplify-units
c9aef719 580 (math-remove-units (calc-top-n 1))))))
136211a9
EZ
581
582(defun calc-extract-units ()
583 (interactive)
584 (calc-slow-wrapper
d14b0029 585 (calc-enter-result 1 "exun" (math-simplify-units
c9aef719 586 (math-extract-units (calc-top-n 1))))))
136211a9 587
09040c8d 588;; The variables calc-num-units and calc-den-units are local to
27971241
JB
589;; calc-explain-units, but are used by calc-explain-units-rec,
590;; which is called by calc-explain-units.
591(defvar calc-num-units)
592(defvar calc-den-units)
593
136211a9
EZ
594(defun calc-explain-units ()
595 (interactive)
596 (calc-wrapper
27971241
JB
597 (let ((calc-num-units nil)
598 (calc-den-units nil))
136211a9 599 (calc-explain-units-rec (calc-top-n 1) 1)
27971241
JB
600 (and calc-den-units (string-match "^[^(].* .*[^)]$" calc-den-units)
601 (setq calc-den-units (concat "(" calc-den-units ")")))
602 (if calc-num-units
603 (if calc-den-units
604 (message "%s per %s" calc-num-units calc-den-units)
605 (message "%s" calc-num-units))
606 (if calc-den-units
607 (message "1 per %s" calc-den-units)
c9aef719 608 (message "No units in expression"))))))
136211a9
EZ
609
610(defun calc-explain-units-rec (expr pow)
611 (let ((u (math-check-unit-name expr))
612 pos)
613 (if (and u (not (math-zerop pow)))
614 (let ((name (or (nth 2 u) (symbol-name (car u)))))
615 (if (eq (aref name 0) ?\*)
616 (setq name (substring name 1)))
ae6bc504
JB
617 (if (string-match "[^a-zA-Zα-ωΑ-Ω0-9']" name)
618 (if (string-match "^[a-zA-Zα-ωΑ-Ω0-9' ()]*$" name)
136211a9
EZ
619 (while (setq pos (string-match "[ ()]" name))
620 (setq name (concat (substring name 0 pos)
621 (if (eq (aref name pos) 32) "-" "")
622 (substring name (1+ pos)))))
623 (setq name (concat "(" name ")"))))
624 (or (eq (nth 1 expr) (car u))
625 (setq name (concat (nth 2 (assq (aref (symbol-name
626 (nth 1 expr)) 0)
627 math-unit-prefixes))
ae6bc504 628 (if (and (string-match "[^a-zA-Zα-ωΑ-Ω0-9']" name)
136211a9
EZ
629 (not (memq (car u) '(mHg gf))))
630 (concat "-" name)
631 (downcase name)))))
632 (cond ((or (math-equal-int pow 1)
633 (math-equal-int pow -1)))
634 ((or (math-equal-int pow 2)
635 (math-equal-int pow -2))
636 (if (equal (nth 4 u) '((m . 1)))
637 (setq name (concat "Square-" name))
638 (setq name (concat name "-squared"))))
639 ((or (math-equal-int pow 3)
640 (math-equal-int pow -3))
641 (if (equal (nth 4 u) '((m . 1)))
642 (setq name (concat "Cubic-" name))
643 (setq name (concat name "-cubed"))))
644 (t
645 (setq name (concat name "^"
646 (math-format-number (math-abs pow))))))
647 (if (math-posp pow)
27971241
JB
648 (setq calc-num-units (if calc-num-units
649 (concat calc-num-units " " name)
136211a9 650 name))
27971241
JB
651 (setq calc-den-units (if calc-den-units
652 (concat calc-den-units " " name)
136211a9
EZ
653 name))))
654 (cond ((eq (car-safe expr) '*)
655 (calc-explain-units-rec (nth 1 expr) pow)
656 (calc-explain-units-rec (nth 2 expr) pow))
657 ((eq (car-safe expr) '/)
658 (calc-explain-units-rec (nth 1 expr) pow)
659 (calc-explain-units-rec (nth 2 expr) (- pow)))
660 ((memq (car-safe expr) '(neg + -))
661 (calc-explain-units-rec (nth 1 expr) pow))
662 ((and (eq (car-safe expr) '^)
663 (math-realp (nth 2 expr)))
664 (calc-explain-units-rec (nth 1 expr)
c9aef719 665 (math-mul pow (nth 2 expr))))))))
136211a9
EZ
666
667(defun calc-simplify-units ()
668 (interactive)
669 (calc-slow-wrapper
670 (calc-with-default-simplification
c9aef719 671 (calc-enter-result 1 "smun" (math-simplify-units (calc-top-n 1))))))
136211a9
EZ
672
673(defun calc-view-units-table (n)
674 (interactive "P")
675 (and n (setq math-units-table-buffer-valid nil))
676 (let ((win (get-buffer-window "*Units Table*")))
677 (if (and win
678 math-units-table
679 math-units-table-buffer-valid)
680 (progn
681 (bury-buffer (window-buffer win))
682 (let ((curwin (selected-window)))
683 (select-window win)
684 (switch-to-buffer nil)
685 (select-window curwin)))
c9aef719 686 (math-build-units-table-buffer nil))))
136211a9
EZ
687
688(defun calc-enter-units-table (n)
689 (interactive "P")
690 (and n (setq math-units-table-buffer-valid nil))
691 (math-build-units-table-buffer t)
5c08522d 692 (message "%s" (substitute-command-keys "Type \\[calc] to return to the Calculator")))
136211a9 693
19bdc4d8
JB
694(defun calc-define-unit (uname desc &optional disp)
695 (interactive "SDefine unit name: \nsDescription: \nP")
696 (if disp (setq disp (read-string "Display definition: ")))
136211a9
EZ
697 (calc-wrapper
698 (let ((form (calc-top-n 1))
699 (unit (assq uname math-additional-units)))
700 (or unit
701 (setq math-additional-units
19bdc4d8 702 (cons (setq unit (list uname nil nil nil nil))
136211a9
EZ
703 math-additional-units)
704 math-units-table nil))
705 (setcar (cdr unit) (and (not (and (eq (car-safe form) 'var)
706 (eq (nth 1 form) uname)))
707 (not (math-equal-int form 1))
708 (math-format-flat-expr form 0)))
709 (setcar (cdr (cdr unit)) (and (not (equal desc ""))
19bdc4d8
JB
710 desc))
711 (if disp
712 (setcar (cdr (cdr (cdr (cdr unit)))) disp))))
c9aef719 713 (calc-invalidate-units-table))
136211a9
EZ
714
715(defun calc-undefine-unit (uname)
716 (interactive "SUndefine unit name: ")
717 (calc-wrapper
718 (let ((unit (assq uname math-additional-units)))
719 (or unit
720 (if (assq uname math-standard-units)
721 (error "\"%s\" is a predefined unit name" uname)
722 (error "Unit name \"%s\" not found" uname)))
723 (setq math-additional-units (delq unit math-additional-units)
724 math-units-table nil)))
c9aef719 725 (calc-invalidate-units-table))
136211a9
EZ
726
727(defun calc-invalidate-units-table ()
728 (setq math-units-table nil)
729 (let ((buf (get-buffer "*Units Table*")))
730 (and buf
6df9b6d7 731 (with-current-buffer buf
136211a9
EZ
732 (save-excursion
733 (goto-char (point-min))
734 (if (looking-at "Calculator Units Table")
ed8060d9 735 (let ((inhibit-read-only t))
c9aef719 736 (insert "(Obsolete) "))))))))
136211a9
EZ
737
738(defun calc-get-unit-definition (uname)
739 (interactive "SGet definition for unit: ")
740 (calc-wrapper
741 (math-build-units-table)
742 (let ((unit (assq uname math-units-table)))
743 (or unit
744 (error "Unit name \"%s\" not found" uname))
745 (let ((msg (nth 2 unit)))
746 (if (stringp msg)
747 (if (string-match "^\\*" msg)
748 (setq msg (substring msg 1)))
749 (setq msg (symbol-name uname)))
750 (if (nth 1 unit)
751 (progn
752 (calc-enter-result 0 "ugdf" (nth 1 unit))
753 (message "Derived unit: %s" msg))
754 (calc-enter-result 0 "ugdf" (list 'var uname
755 (intern
756 (concat "var-"
757 (symbol-name uname)))))
c9aef719 758 (message "Base unit: %s" msg))))))
136211a9
EZ
759
760(defun calc-permanent-units ()
761 (interactive)
762 (calc-wrapper
763 (let (pos)
764 (set-buffer (find-file-noselect (substitute-in-file-name
765 calc-settings-file)))
766 (goto-char (point-min))
767 (if (and (search-forward ";;; Custom units stored by Calc" nil t)
768 (progn
769 (beginning-of-line)
770 (setq pos (point))
771 (search-forward "\n;;; End of custom units" nil t)))
772 (progn
773 (beginning-of-line)
774 (forward-line 1)
775 (delete-region pos (point)))
776 (goto-char (point-max))
777 (insert "\n\n")
778 (forward-char -1))
779 (insert ";;; Custom units stored by Calc on " (current-time-string) "\n")
780 (if math-additional-units
781 (progn
782 (insert "(setq math-additional-units '(\n")
783 (let ((list math-additional-units))
784 (while list
785 (insert " (" (symbol-name (car (car list))) " "
786 (if (nth 1 (car list))
787 (if (stringp (nth 1 (car list)))
788 (prin1-to-string (nth 1 (car list)))
789 (prin1-to-string (math-format-flat-expr
790 (nth 1 (car list)) 0)))
791 "nil")
792 " "
793 (prin1-to-string (nth 2 (car list)))
794 ")\n")
795 (setq list (cdr list))))
796 (insert "))\n"))
797 (insert ";;; (no custom units defined)\n"))
798 (insert ";;; End of custom units\n")
c9aef719 799 (save-buffer))))
136211a9
EZ
800
801
27971241
JB
802;; The variable math-cu-unit-list is local to math-build-units-table,
803;; but is used by math-compare-unit-names, which is called (indirectly)
804;; by math-build-units-table.
805;; math-cu-unit-list is also local to math-convert-units, but is used
806;; by math-convert-units-rec, which is called by math-convert-units.
807(defvar math-cu-unit-list)
136211a9 808
136211a9
EZ
809(defun math-build-units-table ()
810 (or math-units-table
811 (let* ((combined-units (append math-additional-units
812 math-standard-units))
27971241 813 (math-cu-unit-list (mapcar 'car combined-units))
136211a9
EZ
814 tab)
815 (message "Building units table...")
816 (setq math-units-table-buffer-valid nil)
817 (setq tab (mapcar (function
818 (lambda (x)
819 (list (car x)
820 (and (nth 1 x)
821 (if (stringp (nth 1 x))
822 (let ((exp (math-read-plain-expr
823 (nth 1 x))))
824 (if (eq (car-safe exp) 'error)
825 (error "Format error in definition of %s in units table: %s"
826 (car x) (nth 2 exp))
827 exp))
828 (nth 1 x)))
829 (nth 2 x)
830 (nth 3 x)
831 (and (not (nth 1 x))
19bdc4d8
JB
832 (list (cons (car x) 1)))
833 (nth 4 x))))
136211a9
EZ
834 combined-units))
835 (let ((math-units-table tab))
09040c8d 836 (mapc 'math-find-base-units tab))
136211a9 837 (message "Building units table...done")
c9aef719 838 (setq math-units-table tab))))
136211a9 839
27971241
JB
840;; The variables math-fbu-base and math-fbu-entry are local to
841;; math-find-base-units, but are used by math-find-base-units-rec,
842;; which is called by math-find-base-units.
843(defvar math-fbu-base)
844(defvar math-fbu-entry)
845
3effaa28
JB
846(defun math-find-base-units (math-fbu-entry)
847 (if (eq (nth 4 math-fbu-entry) 'boom)
848 (error "Circular definition involving unit %s" (car math-fbu-entry)))
849 (or (nth 4 math-fbu-entry)
850 (let (math-fbu-base)
851 (setcar (nthcdr 4 math-fbu-entry) 'boom)
852 (math-find-base-units-rec (nth 1 math-fbu-entry) 1)
853 '(or math-fbu-base
854 (error "Dimensionless definition for unit %s" (car math-fbu-entry)))
855 (while (eq (cdr (car math-fbu-base)) 0)
856 (setq math-fbu-base (cdr math-fbu-base)))
857 (let ((b math-fbu-base))
136211a9
EZ
858 (while (cdr b)
859 (if (eq (cdr (car (cdr b))) 0)
860 (setcdr b (cdr (cdr b)))
861 (setq b (cdr b)))))
3effaa28
JB
862 (setq math-fbu-base (sort math-fbu-base 'math-compare-unit-names))
863 (setcar (nthcdr 4 math-fbu-entry) math-fbu-base)
864 math-fbu-base)))
136211a9
EZ
865
866(defun math-compare-unit-names (a b)
27971241 867 (memq (car b) (cdr (memq (car a) math-cu-unit-list))))
136211a9
EZ
868
869(defun math-find-base-units-rec (expr pow)
870 (let ((u (math-check-unit-name expr)))
871 (cond (u
872 (let ((ulist (math-find-base-units u)))
873 (while ulist
874 (let ((p (* (cdr (car ulist)) pow))
3effaa28 875 (old (assq (car (car ulist)) math-fbu-base)))
136211a9
EZ
876 (if old
877 (setcdr old (+ (cdr old) p))
09040c8d 878 (setq math-fbu-base
3effaa28 879 (cons (cons (car (car ulist)) p) math-fbu-base))))
136211a9
EZ
880 (setq ulist (cdr ulist)))))
881 ((math-scalarp expr))
882 ((and (eq (car expr) '^)
883 (integerp (nth 2 expr)))
884 (math-find-base-units-rec (nth 1 expr) (* pow (nth 2 expr))))
885 ((eq (car expr) '*)
886 (math-find-base-units-rec (nth 1 expr) pow)
887 (math-find-base-units-rec (nth 2 expr) pow))
888 ((eq (car expr) '/)
889 (math-find-base-units-rec (nth 1 expr) pow)
890 (math-find-base-units-rec (nth 2 expr) (- pow)))
891 ((eq (car expr) 'neg)
892 (math-find-base-units-rec (nth 1 expr) pow))
893 ((eq (car expr) '+)
894 (math-find-base-units-rec (nth 1 expr) pow))
895 ((eq (car expr) 'var)
896 (or (eq (nth 1 expr) 'pi)
897 (error "Unknown name %s in defining expression for unit %s"
3effaa28 898 (nth 1 expr) (car math-fbu-entry))))
603823f5 899 ((equal expr '(calcFunc-ln 10)))
3effaa28 900 (t (error "Malformed defining expression for unit %s" (car math-fbu-entry))))))
136211a9
EZ
901
902
903(defun math-units-in-expr-p (expr sub-exprs)
904 (and (consp expr)
905 (if (eq (car expr) 'var)
906 (math-check-unit-name expr)
907 (and (or sub-exprs
908 (memq (car expr) '(* / ^)))
909 (or (math-units-in-expr-p (nth 1 expr) sub-exprs)
c9aef719 910 (math-units-in-expr-p (nth 2 expr) sub-exprs))))))
136211a9
EZ
911
912(defun math-only-units-in-expr-p (expr)
913 (and (consp expr)
914 (if (eq (car expr) 'var)
915 (math-check-unit-name expr)
916 (if (memq (car expr) '(* /))
917 (and (math-only-units-in-expr-p (nth 1 expr))
918 (math-only-units-in-expr-p (nth 2 expr)))
919 (and (eq (car expr) '^)
920 (and (math-only-units-in-expr-p (nth 1 expr))
c9aef719 921 (math-realp (nth 2 expr))))))))
136211a9
EZ
922
923(defun math-single-units-in-expr-p (expr)
924 (cond ((math-scalarp expr) nil)
925 ((eq (car expr) 'var)
926 (math-check-unit-name expr))
927 ((eq (car expr) '*)
928 (let ((u1 (math-single-units-in-expr-p (nth 1 expr)))
929 (u2 (math-single-units-in-expr-p (nth 2 expr))))
930 (or (and u1 u2 'wrong)
931 u1
932 u2)))
933 ((eq (car expr) '/)
934 (if (math-units-in-expr-p (nth 2 expr) nil)
935 'wrong
936 (math-single-units-in-expr-p (nth 1 expr))))
c9aef719 937 (t 'wrong)))
136211a9 938
76bc2ec7
JB
939(defun math-consistent-units-p (expr newunits)
940 "Non-nil if EXPR and NEWUNITS have consistent units."
941 (or
942 (and (eq (car-safe newunits) 'var)
943 (assq (nth 1 newunits) math-standard-units-systems))
944 (math-numberp (math-get-units (list '/ expr newunits)))))
d14b0029
JB
945
946(defun math-check-unit-consistency (expr units)
947 "Give an error if EXPR and UNITS do not have consistent units."
948 (unless (math-consistent-units-p expr units)
949 (error "New units (%s) are inconsistent with current units (%s)"
950 (math-format-value units)
951 (math-format-value (math-get-units expr)))))
952
136211a9
EZ
953(defun math-check-unit-name (v)
954 (and (eq (car-safe v) 'var)
955 (or (assq (nth 1 v) (or math-units-table (math-build-units-table)))
956 (let ((name (symbol-name (nth 1 v))))
957 (and (> (length name) 1)
958 (assq (aref name 0) math-unit-prefixes)
959 (or (assq (intern (substring name 1)) math-units-table)
960 (and (eq (aref name 0) ?M)
961 (> (length name) 3)
962 (eq (aref name 1) ?e)
963 (eq (aref name 2) ?g)
964 (assq (intern (substring name 3))
c9aef719 965 math-units-table))))))))
136211a9 966
27971241
JB
967;; The variable math-which-standard is local to math-to-standard-units,
968;; but is used by math-to-standard-rec, which is called by
969;; math-to-standard-units.
970(defvar math-which-standard)
136211a9 971
27971241 972(defun math-to-standard-units (expr math-which-standard)
c9aef719 973 (math-to-standard-rec expr))
136211a9
EZ
974
975(defun math-to-standard-rec (expr)
976 (if (eq (car-safe expr) 'var)
977 (let ((u (math-check-unit-name expr))
978 (base (nth 1 expr)))
979 (if u
980 (progn
981 (if (nth 1 u)
982 (setq expr (math-to-standard-rec (nth 1 u)))
27971241 983 (let ((st (assq (car u) math-which-standard)))
136211a9
EZ
984 (if st
985 (setq expr (nth 1 st))
986 (setq expr (list 'var (car u)
987 (intern (concat "var-"
988 (symbol-name
989 (car u)))))))))
990 (or (null u)
991 (eq base (car u))
992 (setq expr (list '*
993 (nth 1 (assq (aref (symbol-name base) 0)
994 math-unit-prefixes))
995 expr)))
996 expr)
997 (if (eq base 'pi)
998 (math-pi)
999 expr)))
d6ffd3f8
JB
1000 (if (or
1001 (Math-primp expr)
1002 (and (eq (car-safe expr) 'calcFunc-subscr)
1003 (eq (car-safe (nth 1 expr)) 'var)))
136211a9
EZ
1004 expr
1005 (cons (car expr)
c9aef719 1006 (mapcar 'math-to-standard-rec (cdr expr))))))
136211a9
EZ
1007
1008(defun math-apply-units (expr units ulist &optional pure)
2e2b4fbe 1009 (setq expr (math-simplify-units expr))
136211a9
EZ
1010 (if ulist
1011 (let ((new 0)
1012 value)
136211a9
EZ
1013 (or (math-numberp expr)
1014 (error "Incompatible units"))
1015 (while (cdr ulist)
1016 (setq value (math-div expr (nth 1 (car ulist)))
1017 value (math-floor (let ((calc-internal-prec
1018 (1- calc-internal-prec)))
1019 (math-normalize value)))
1020 new (math-add new (math-mul value (car (car ulist))))
1021 expr (math-sub expr (math-mul value (nth 1 (car ulist))))
1022 ulist (cdr ulist)))
1023 (math-add new (math-mul (math-div expr (nth 1 (car ulist)))
1024 (car (car ulist)))))
2e2b4fbe
JB
1025 (if pure
1026 expr
1027 (math-simplify-units (list '* expr units)))))
136211a9 1028
8cd8ee52 1029(defvar math-decompose-units-cache nil)
136211a9
EZ
1030(defun math-decompose-units (units)
1031 (let ((u (math-check-unit-name units)))
1032 (and u (eq (car-safe (nth 1 u)) '+)
1033 (setq units (nth 1 u))))
1034 (setq units (calcFunc-expand units))
1035 (and (eq (car-safe units) '+)
1036 (let ((entry (list units calc-internal-prec calc-prefer-frac)))
1037 (or (equal entry (car math-decompose-units-cache))
1038 (let ((ulist nil)
1039 (utemp units)
1040 qty unit)
1041 (while (eq (car-safe utemp) '+)
1042 (setq ulist (cons (math-decompose-unit-part (nth 2 utemp))
1043 ulist)
1044 utemp (nth 1 utemp)))
1045 (setq ulist (cons (math-decompose-unit-part utemp) ulist)
1046 utemp ulist)
1047 (while (setq utemp (cdr utemp))
8cd8ee52
CW
1048 (unless (equal (nth 2 (car utemp)) (nth 2 (car ulist)))
1049 (error "Inconsistent units in sum")))
136211a9
EZ
1050 (setq math-decompose-units-cache
1051 (cons entry
1052 (sort ulist
1053 (function
1054 (lambda (x y)
1055 (not (Math-lessp (nth 1 x)
1056 (nth 1 y))))))))))
c9aef719 1057 (cdr math-decompose-units-cache))))
136211a9
EZ
1058
1059(defun math-decompose-unit-part (unit)
1060 (cons unit
1061 (math-is-multiple (math-simplify-units (math-to-standard-units
1062 unit nil))
c9aef719 1063 t)))
136211a9 1064
27971241
JB
1065;; The variable math-fcu-u is local to math-find-compatible-unit,
1066;; but is used by math-find-compatible-rec which is called by
1067;; math-find-compatible-unit.
1068(defvar math-fcu-u)
1069
136211a9 1070(defun math-find-compatible-unit (expr unit)
27971241
JB
1071 (let ((math-fcu-u (math-check-unit-name unit)))
1072 (if math-fcu-u
c9aef719 1073 (math-find-compatible-unit-rec expr 1))))
136211a9
EZ
1074
1075(defun math-find-compatible-unit-rec (expr pow)
1076 (cond ((eq (car-safe expr) '*)
1077 (or (math-find-compatible-unit-rec (nth 1 expr) pow)
1078 (math-find-compatible-unit-rec (nth 2 expr) pow)))
1079 ((eq (car-safe expr) '/)
1080 (or (math-find-compatible-unit-rec (nth 1 expr) pow)
1081 (math-find-compatible-unit-rec (nth 2 expr) (- pow))))
1082 ((and (eq (car-safe expr) '^)
1083 (integerp (nth 2 expr)))
1084 (math-find-compatible-unit-rec (nth 1 expr) (* pow (nth 2 expr))))
1085 (t
1086 (let ((u2 (math-check-unit-name expr)))
27971241 1087 (if (equal (nth 4 math-fcu-u) (nth 4 u2))
c9aef719 1088 (cons expr pow))))))
136211a9 1089
09040c8d
JB
1090;; The variables math-cu-new-units and math-cu-pure are local to
1091;; math-convert-units, but are used by math-convert-units-rec,
27971241
JB
1092;; which is called by math-convert-units.
1093(defvar math-cu-new-units)
1094(defvar math-cu-pure)
1095
1096(defun math-convert-units (expr math-cu-new-units &optional math-cu-pure)
65b62d0a
JB
1097 (if (eq (car-safe math-cu-new-units) 'var)
1098 (let ((unew (assq (nth 1 math-cu-new-units)
1099 (math-build-units-table))))
1100 (if (eq (car-safe (nth 1 unew)) '+)
1101 (setq math-cu-new-units (nth 1 unew)))))
136211a9 1102 (math-with-extra-prec 2
09040c8d 1103 (let ((compat (and (not math-cu-pure)
27971241
JB
1104 (math-find-compatible-unit expr math-cu-new-units)))
1105 (math-cu-unit-list nil)
136211a9
EZ
1106 (math-combining-units nil))
1107 (if compat
1108 (math-simplify-units
1109 (math-mul (math-mul (math-simplify-units
1110 (math-div expr (math-pow (car compat)
1111 (cdr compat))))
27971241 1112 (math-pow math-cu-new-units (cdr compat)))
136211a9
EZ
1113 (math-simplify-units
1114 (math-to-standard-units
27971241 1115 (math-pow (math-div (car compat) math-cu-new-units)
136211a9
EZ
1116 (cdr compat))
1117 nil))))
27971241
JB
1118 (when (setq math-cu-unit-list (math-decompose-units math-cu-new-units))
1119 (setq math-cu-new-units (nth 2 (car math-cu-unit-list))))
8cd8ee52
CW
1120 (when (eq (car-safe expr) '+)
1121 (setq expr (math-simplify-units expr)))
136211a9
EZ
1122 (if (math-units-in-expr-p expr t)
1123 (math-convert-units-rec expr)
1124 (math-apply-units (math-to-standard-units
27971241
JB
1125 (list '/ expr math-cu-new-units) nil)
1126 math-cu-new-units math-cu-unit-list math-cu-pure))))))
136211a9
EZ
1127
1128(defun math-convert-units-rec (expr)
1129 (if (math-units-in-expr-p expr nil)
09040c8d 1130 (math-apply-units (math-to-standard-units
27971241
JB
1131 (list '/ expr math-cu-new-units) nil)
1132 math-cu-new-units math-cu-unit-list math-cu-pure)
136211a9
EZ
1133 (if (Math-primp expr)
1134 expr
1135 (cons (car expr)
c9aef719 1136 (mapcar 'math-convert-units-rec (cdr expr))))))
136211a9
EZ
1137
1138(defun math-convert-temperature (expr old new &optional pure)
1139 (let* ((units (math-single-units-in-expr-p expr))
1140 (uold (if old
1141 (if (or (null units)
1142 (equal (nth 1 old) (car units)))
1143 (math-check-unit-name old)
1144 (error "Inconsistent temperature units"))
1145 units))
1146 (unew (math-check-unit-name new)))
8cd8ee52
CW
1147 (unless (and (consp unew) (nth 3 unew))
1148 (error "Not a valid temperature unit"))
1149 (unless (and (consp uold) (nth 3 uold))
1150 (error "Not a pure temperature expression"))
136211a9
EZ
1151 (let ((v (car uold)))
1152 (setq expr (list '/ expr (list 'var v
1153 (intern (concat "var-"
1154 (symbol-name v)))))))
1155 (or (eq (nth 3 uold) (nth 3 unew))
1156 (cond ((eq (nth 3 uold) 'K)
e6cd99dc 1157 (setq expr (list '- expr '(/ 27315 100)))
136211a9 1158 (if (eq (nth 3 unew) 'F)
e6cd99dc 1159 (setq expr (list '+ (list '* expr '(/ 9 5)) 32))))
136211a9
EZ
1160 ((eq (nth 3 uold) 'C)
1161 (if (eq (nth 3 unew) 'F)
e6cd99dc
JB
1162 (setq expr (list '+ (list '* expr '(/ 9 5)) 32))
1163 (setq expr (list '+ expr '(/ 27315 100)))))
136211a9 1164 (t
e6cd99dc 1165 (setq expr (list '* (list '- expr 32) '(/ 5 9)))
136211a9 1166 (if (eq (nth 3 unew) 'K)
e6cd99dc 1167 (setq expr (list '+ expr '(/ 27315 100)))))))
136211a9
EZ
1168 (if pure
1169 expr
c9aef719 1170 (list '* expr new))))
136211a9
EZ
1171
1172
1173
1174(defun math-simplify-units (a)
1175 (let ((math-simplifying-units t)
1176 (calc-matrix-mode 'scalar))
c9aef719 1177 (math-simplify a)))
8cd8ee52 1178(defalias 'calcFunc-usimplify 'math-simplify-units)
136211a9 1179
f095c6c9
JB
1180;; The function created by math-defsimplify uses the variable
1181;; math-simplify-expr, and so is used by functions in math-defsimplify
1182(defvar math-simplify-expr)
1183
136211a9
EZ
1184(math-defsimplify (+ -)
1185 (and math-simplifying-units
f095c6c9
JB
1186 (math-units-in-expr-p (nth 1 math-simplify-expr) nil)
1187 (let* ((units (math-extract-units (nth 1 math-simplify-expr)))
136211a9 1188 (ratio (math-simplify (math-to-standard-units
f095c6c9 1189 (list '/ (nth 2 math-simplify-expr) units) nil))))
136211a9
EZ
1190 (if (math-units-in-expr-p ratio nil)
1191 (progn
f095c6c9
JB
1192 (calc-record-why "*Inconsistent units" math-simplify-expr)
1193 math-simplify-expr)
1194 (list '* (math-add (math-remove-units (nth 1 math-simplify-expr))
09040c8d 1195 (if (eq (car math-simplify-expr) '-)
f095c6c9 1196 (math-neg ratio) ratio))
c9aef719 1197 units)))))
136211a9
EZ
1198
1199(math-defsimplify *
c9aef719 1200 (math-simplify-units-prod))
136211a9
EZ
1201
1202(defun math-simplify-units-prod ()
1203 (and math-simplifying-units
1204 calc-autorange-units
f095c6c9
JB
1205 (Math-realp (nth 1 math-simplify-expr))
1206 (let* ((num (math-float (nth 1 math-simplify-expr)))
136211a9 1207 (xpon (calcFunc-xpon num))
f095c6c9 1208 (unitp (cdr (cdr math-simplify-expr)))
136211a9 1209 (unit (car unitp))
f095c6c9 1210 (pow (if (eq (car math-simplify-expr) '*) 1 -1))
136211a9
EZ
1211 u)
1212 (and (eq (car-safe unit) '*)
1213 (setq unitp (cdr unit)
1214 unit (car unitp)))
1215 (and (eq (car-safe unit) '^)
1216 (integerp (nth 2 unit))
1217 (setq pow (* pow (nth 2 unit))
1218 unitp (cdr unit)
1219 unit (car unitp)))
1220 (and (setq u (math-check-unit-name unit))
1221 (integerp xpon)
1222 (or (< xpon 0)
1223 (>= xpon (if (eq (car u) 'm) 1 3)))
1224 (let* ((uxpon 0)
1225 (pref (if (< pow 0)
1226 (reverse math-unit-prefixes)
1227 math-unit-prefixes))
1228 (p pref)
1229 pxpon pname)
1230 (or (eq (car u) (nth 1 unit))
1231 (setq uxpon (* pow
1232 (nth 2 (nth 1 (assq
1233 (aref (symbol-name
1234 (nth 1 unit)) 0)
1235 math-unit-prefixes))))))
1236 (setq xpon (+ xpon uxpon))
1237 (while (and p
1238 (or (memq (car (car p)) '(?d ?D ?h ?H))
1239 (and (eq (car (car p)) ?c)
1240 (not (eq (car u) 'm)))
1241 (< xpon (setq pxpon (* (nth 2 (nth 1 (car p)))
1242 pow)))
1243 (progn
1244 (setq pname (math-build-var-name
1245 (if (eq (car (car p)) 0)
1246 (car u)
1247 (concat (char-to-string
1248 (car (car p)))
1249 (symbol-name
1250 (car u))))))
1251 (and (/= (car (car p)) 0)
1252 (assq (nth 1 pname)
1253 math-units-table)))))
1254 (setq p (cdr p)))
1255 (and p
1256 (/= pxpon uxpon)
1257 (or (not (eq p pref))
1258 (< xpon (+ pxpon (* (math-abs pow) 3))))
1259 (progn
f095c6c9 1260 (setcar (cdr math-simplify-expr)
136211a9 1261 (let ((calc-prefer-frac nil))
f095c6c9 1262 (calcFunc-scf (nth 1 math-simplify-expr)
136211a9
EZ
1263 (- uxpon pxpon))))
1264 (setcar unitp pname)
f095c6c9 1265 math-simplify-expr)))))))
136211a9 1266
27971241
JB
1267(defvar math-try-cancel-units)
1268
136211a9
EZ
1269(math-defsimplify /
1270 (and math-simplifying-units
f095c6c9 1271 (let ((np (cdr math-simplify-expr))
27971241 1272 (math-try-cancel-units 0)
136211a9 1273 n nn)
f095c6c9
JB
1274 (setq n (if (eq (car-safe (nth 2 math-simplify-expr)) '*)
1275 (cdr (nth 2 math-simplify-expr))
1276 (nthcdr 2 math-simplify-expr)))
136211a9
EZ
1277 (if (math-realp (car n))
1278 (progn
f095c6c9 1279 (setcar (cdr math-simplify-expr) (math-mul (nth 1 math-simplify-expr)
136211a9
EZ
1280 (let ((calc-prefer-frac nil))
1281 (math-div 1 (car n)))))
1282 (setcar n 1)))
1283 (while (eq (car-safe (setq n (car np))) '*)
f095c6c9 1284 (math-simplify-units-divisor (cdr n) (cdr (cdr math-simplify-expr)))
136211a9 1285 (setq np (cdr (cdr n))))
f095c6c9 1286 (math-simplify-units-divisor np (cdr (cdr math-simplify-expr)))
27971241 1287 (if (eq math-try-cancel-units 0)
136211a9 1288 (let* ((math-simplifying-units nil)
09040c8d 1289 (base (math-simplify
f095c6c9 1290 (math-to-standard-units math-simplify-expr nil))))
136211a9 1291 (if (Math-numberp base)
f095c6c9
JB
1292 (setq math-simplify-expr base))))
1293 (if (eq (car-safe math-simplify-expr) '/)
136211a9 1294 (math-simplify-units-prod))
f095c6c9 1295 math-simplify-expr)))
136211a9
EZ
1296
1297(defun math-simplify-units-divisor (np dp)
1298 (let ((n (car np))
1299 d dd temp)
1300 (while (eq (car-safe (setq d (car dp))) '*)
8cd8ee52
CW
1301 (when (setq temp (math-simplify-units-quotient n (nth 1 d)))
1302 (setcar np (setq n temp))
1303 (setcar (cdr d) 1))
136211a9 1304 (setq dp (cdr (cdr d))))
8cd8ee52
CW
1305 (when (setq temp (math-simplify-units-quotient n d))
1306 (setcar np (setq n temp))
1307 (setcar dp 1))))
136211a9
EZ
1308
1309;; Simplify, e.g., "in / cm" to "2.54" in a units expression.
1310(defun math-simplify-units-quotient (n d)
1311 (let ((pow1 1)
1312 (pow2 1))
8cd8ee52
CW
1313 (when (and (eq (car-safe n) '^)
1314 (integerp (nth 2 n)))
1315 (setq pow1 (nth 2 n) n (nth 1 n)))
1316 (when (and (eq (car-safe d) '^)
1317 (integerp (nth 2 d)))
1318 (setq pow2 (nth 2 d) d (nth 1 d)))
136211a9
EZ
1319 (let ((un (math-check-unit-name n))
1320 (ud (math-check-unit-name d)))
1321 (and un ud
1322 (if (and (equal (nth 4 un) (nth 4 ud))
1323 (eq pow1 pow2))
0317ca78
JB
1324 (if (eq pow1 1)
1325 (math-to-standard-units (list '/ n d) nil)
1326 (list '^ (math-to-standard-units (list '/ n d) nil) pow1))
136211a9
EZ
1327 (let (ud1)
1328 (setq un (nth 4 un)
1329 ud (nth 4 ud))
1330 (while un
1331 (setq ud1 ud)
1332 (while ud1
1333 (and (eq (car (car un)) (car (car ud1)))
27971241
JB
1334 (setq math-try-cancel-units
1335 (+ math-try-cancel-units
136211a9
EZ
1336 (- (* (cdr (car un)) pow1)
1337 (* (cdr (car ud)) pow2)))))
1338 (setq ud1 (cdr ud1)))
1339 (setq un (cdr un)))
c9aef719 1340 nil))))))
136211a9
EZ
1341
1342(math-defsimplify ^
1343 (and math-simplifying-units
f095c6c9
JB
1344 (math-realp (nth 2 math-simplify-expr))
1345 (if (memq (car-safe (nth 1 math-simplify-expr)) '(* /))
1346 (list (car (nth 1 math-simplify-expr))
09040c8d 1347 (list '^ (nth 1 (nth 1 math-simplify-expr))
f095c6c9 1348 (nth 2 math-simplify-expr))
09040c8d 1349 (list '^ (nth 2 (nth 1 math-simplify-expr))
f095c6c9 1350 (nth 2 math-simplify-expr)))
09040c8d 1351 (math-simplify-units-pow (nth 1 math-simplify-expr)
f095c6c9 1352 (nth 2 math-simplify-expr)))))
136211a9
EZ
1353
1354(math-defsimplify calcFunc-sqrt
1355 (and math-simplifying-units
f095c6c9
JB
1356 (if (memq (car-safe (nth 1 math-simplify-expr)) '(* /))
1357 (list (car (nth 1 math-simplify-expr))
1358 (list 'calcFunc-sqrt (nth 1 (nth 1 math-simplify-expr)))
1359 (list 'calcFunc-sqrt (nth 2 (nth 1 math-simplify-expr))))
1360 (math-simplify-units-pow (nth 1 math-simplify-expr) '(frac 1 2)))))
136211a9
EZ
1361
1362(math-defsimplify (calcFunc-floor
1363 calcFunc-ceil
1364 calcFunc-round
1365 calcFunc-rounde
1366 calcFunc-roundu
1367 calcFunc-trunc
1368 calcFunc-float
1369 calcFunc-frac
1370 calcFunc-abs
1371 calcFunc-clean)
1372 (and math-simplifying-units
f095c6c9
JB
1373 (= (length math-simplify-expr) 2)
1374 (if (math-only-units-in-expr-p (nth 1 math-simplify-expr))
1375 (nth 1 math-simplify-expr)
1376 (if (and (memq (car-safe (nth 1 math-simplify-expr)) '(* /))
136211a9 1377 (or (math-only-units-in-expr-p
f095c6c9 1378 (nth 1 (nth 1 math-simplify-expr)))
136211a9 1379 (math-only-units-in-expr-p
f095c6c9
JB
1380 (nth 2 (nth 1 math-simplify-expr)))))
1381 (list (car (nth 1 math-simplify-expr))
1382 (cons (car math-simplify-expr)
1383 (cons (nth 1 (nth 1 math-simplify-expr))
1384 (cdr (cdr math-simplify-expr))))
1385 (cons (car math-simplify-expr)
1386 (cons (nth 2 (nth 1 math-simplify-expr))
1387 (cdr (cdr math-simplify-expr)))))))))
136211a9
EZ
1388
1389(defun math-simplify-units-pow (a pow)
1390 (if (and (eq (car-safe a) '^)
1391 (math-check-unit-name (nth 1 a))
1392 (math-realp (nth 2 a)))
1393 (list '^ (nth 1 a) (math-mul pow (nth 2 a)))
1394 (let* ((u (math-check-unit-name a))
1395 (pf (math-to-simple-fraction pow))
1396 (d (and (eq (car-safe pf) 'frac) (nth 2 pf))))
1397 (and u d
1398 (math-units-are-multiple u d)
c9aef719 1399 (list '^ (math-to-standard-units a nil) pow)))))
136211a9
EZ
1400
1401
1402(defun math-units-are-multiple (u n)
1403 (setq u (nth 4 u))
1404 (while (and u (= (% (cdr (car u)) n) 0))
1405 (setq u (cdr u)))
c9aef719 1406 (null u))
136211a9
EZ
1407
1408(math-defsimplify calcFunc-sin
1409 (and math-simplifying-units
f095c6c9 1410 (math-units-in-expr-p (nth 1 math-simplify-expr) nil)
136211a9
EZ
1411 (let ((rad (math-simplify-units
1412 (math-evaluate-expr
f095c6c9 1413 (math-to-standard-units (nth 1 math-simplify-expr) nil))))
136211a9
EZ
1414 (calc-angle-mode 'rad))
1415 (and (eq (car-safe rad) '*)
1416 (math-realp (nth 1 rad))
1417 (eq (car-safe (nth 2 rad)) 'var)
1418 (eq (nth 1 (nth 2 rad)) 'rad)
c9aef719 1419 (list 'calcFunc-sin (nth 1 rad))))))
136211a9
EZ
1420
1421(math-defsimplify calcFunc-cos
1422 (and math-simplifying-units
f095c6c9 1423 (math-units-in-expr-p (nth 1 math-simplify-expr) nil)
136211a9
EZ
1424 (let ((rad (math-simplify-units
1425 (math-evaluate-expr
f095c6c9 1426 (math-to-standard-units (nth 1 math-simplify-expr) nil))))
136211a9
EZ
1427 (calc-angle-mode 'rad))
1428 (and (eq (car-safe rad) '*)
1429 (math-realp (nth 1 rad))
1430 (eq (car-safe (nth 2 rad)) 'var)
1431 (eq (nth 1 (nth 2 rad)) 'rad)
c9aef719 1432 (list 'calcFunc-cos (nth 1 rad))))))
136211a9
EZ
1433
1434(math-defsimplify calcFunc-tan
1435 (and math-simplifying-units
f095c6c9 1436 (math-units-in-expr-p (nth 1 math-simplify-expr) nil)
136211a9
EZ
1437 (let ((rad (math-simplify-units
1438 (math-evaluate-expr
f095c6c9 1439 (math-to-standard-units (nth 1 math-simplify-expr) nil))))
136211a9
EZ
1440 (calc-angle-mode 'rad))
1441 (and (eq (car-safe rad) '*)
1442 (math-realp (nth 1 rad))
1443 (eq (car-safe (nth 2 rad)) 'var)
1444 (eq (nth 1 (nth 2 rad)) 'rad)
c9aef719 1445 (list 'calcFunc-tan (nth 1 rad))))))
136211a9 1446
40b444ac
JB
1447(math-defsimplify calcFunc-sec
1448 (and math-simplifying-units
1449 (math-units-in-expr-p (nth 1 math-simplify-expr) nil)
1450 (let ((rad (math-simplify-units
1451 (math-evaluate-expr
1452 (math-to-standard-units (nth 1 math-simplify-expr) nil))))
1453 (calc-angle-mode 'rad))
1454 (and (eq (car-safe rad) '*)
1455 (math-realp (nth 1 rad))
1456 (eq (car-safe (nth 2 rad)) 'var)
1457 (eq (nth 1 (nth 2 rad)) 'rad)
1458 (list 'calcFunc-sec (nth 1 rad))))))
1459
1460(math-defsimplify calcFunc-csc
1461 (and math-simplifying-units
1462 (math-units-in-expr-p (nth 1 math-simplify-expr) nil)
1463 (let ((rad (math-simplify-units
1464 (math-evaluate-expr
1465 (math-to-standard-units (nth 1 math-simplify-expr) nil))))
1466 (calc-angle-mode 'rad))
1467 (and (eq (car-safe rad) '*)
1468 (math-realp (nth 1 rad))
1469 (eq (car-safe (nth 2 rad)) 'var)
1470 (eq (nth 1 (nth 2 rad)) 'rad)
1471 (list 'calcFunc-csc (nth 1 rad))))))
1472
1473(math-defsimplify calcFunc-cot
1474 (and math-simplifying-units
1475 (math-units-in-expr-p (nth 1 math-simplify-expr) nil)
1476 (let ((rad (math-simplify-units
1477 (math-evaluate-expr
1478 (math-to-standard-units (nth 1 math-simplify-expr) nil))))
1479 (calc-angle-mode 'rad))
1480 (and (eq (car-safe rad) '*)
1481 (math-realp (nth 1 rad))
1482 (eq (car-safe (nth 2 rad)) 'var)
1483 (eq (nth 1 (nth 2 rad)) 'rad)
1484 (list 'calcFunc-cot (nth 1 rad))))))
1485
136211a9
EZ
1486
1487(defun math-remove-units (expr)
1488 (if (math-check-unit-name expr)
1489 1
1490 (if (Math-primp expr)
1491 expr
1492 (cons (car expr)
c9aef719 1493 (mapcar 'math-remove-units (cdr expr))))))
136211a9
EZ
1494
1495(defun math-extract-units (expr)
4c9e9550
JB
1496 (cond
1497 ((memq (car-safe expr) '(* /))
1498 (cons (car expr)
1499 (mapcar 'math-extract-units (cdr expr))))
1db165f0
JB
1500 ((eq (car-safe expr) '^)
1501 (list '^ (math-extract-units (nth 1 expr)) (nth 2 expr)))
4c9e9550
JB
1502 ((math-check-unit-name expr) expr)
1503 (t 1)))
136211a9
EZ
1504
1505(defun math-build-units-table-buffer (enter-buffer)
1506 (if (not (and math-units-table math-units-table-buffer-valid
1507 (get-buffer "*Units Table*")))
1508 (let ((buf (get-buffer-create "*Units Table*"))
1509 (uptr (math-build-units-table))
1510 (calc-language (if (eq calc-language 'big) nil calc-language))
1511 (calc-float-format '(float 0))
1512 (calc-group-digits nil)
1513 (calc-number-radix 10)
7b999abb 1514 (calc-twos-complement-mode nil)
136211a9
EZ
1515 (calc-point-char ".")
1516 (std nil)
1517 u name shadowed)
1518 (save-excursion
1519 (message "Formatting units table...")
1520 (set-buffer buf)
ed8060d9
JB
1521 (let ((inhibit-read-only t))
1522 (erase-buffer)
1523 (insert "Calculator Units Table:\n\n")
ac318df0 1524 (insert "(All definitions are exact unless marked with an asterisk (*).)\n\n")
ed8060d9
JB
1525 (insert "Unit Type Definition Description\n\n")
1526 (while uptr
1527 (setq u (car uptr)
1528 name (nth 2 u))
1529 (when (eq (car u) 'm)
1530 (setq std t))
1531 (setq shadowed (and std (assq (car u) math-additional-units)))
1532 (when (and name
1533 (> (length name) 1)
1534 (eq (aref name 0) ?\*))
1535 (unless (eq uptr math-units-table)
1536 (insert "\n"))
1537 (setq name (substring name 1)))
1538 (insert " ")
1539 (and shadowed (insert "("))
1540 (insert (symbol-name (car u)))
1541 (and shadowed (insert ")"))
1542 (if (nth 3 u)
1543 (progn
1544 (indent-to 10)
1545 (insert (symbol-name (nth 3 u))))
1546 (or std
1547 (progn
1548 (indent-to 10)
1549 (insert "U"))))
1550 (indent-to 14)
1551 (and shadowed (insert "("))
19bdc4d8
JB
1552 (if (nth 5 u)
1553 (insert (nth 5 u))
1554 (if (nth 1 u)
1555 (insert (math-format-value (nth 1 u) 80))
1556 (insert (symbol-name (car u)))))
ed8060d9
JB
1557 (and shadowed (insert ")"))
1558 (indent-to 41)
1559 (insert " ")
1560 (when name
1561 (insert name))
1562 (if shadowed
1563 (insert " (redefined above)")
1564 (unless (nth 1 u)
1565 (insert " (base unit)")))
1566 (insert "\n")
1567 (setq uptr (cdr uptr)))
1568 (insert "\n\nUnit Prefix Table:\n\n")
1569 (setq uptr math-unit-prefixes)
1570 (while uptr
1571 (setq u (car uptr))
1572 (insert " " (char-to-string (car u)))
1573 (if (equal (nth 1 u) (nth 1 (nth 1 uptr)))
1574 (insert " " (char-to-string (car (car (setq uptr (cdr uptr)))))
1575 " ")
1576 (insert " "))
1577 (insert "10^" (int-to-string (nth 2 (nth 1 u))))
1578 (indent-to 15)
1579 (insert " " (nth 2 u) "\n")
1580 (while (eq (car (car (setq uptr (cdr uptr)))) 0)))
1265829e
JB
1581 (insert "\n\n")
1582 (insert "(**) When in TeX or LaTeX display mode, the TeX specific unit\n"
1583 "names will not use the `tex' prefix; the unit name for a\n"
1584 "TeX point will be `pt' instead of `texpt', for example.\n"
1585 "To avoid conflicts, the unit names for pint and parsec will\n"
1586 "be `pint' and `parsec' instead of `pt' and `pc'."))
ed8060d9 1587 (view-mode)
136211a9
EZ
1588 (message "Formatting units table...done"))
1589 (setq math-units-table-buffer-valid t)
1590 (let ((oldbuf (current-buffer)))
1591 (set-buffer buf)
1592 (goto-char (point-min))
1593 (set-buffer oldbuf))
1594 (if enter-buffer
1595 (pop-to-buffer buf)
1596 (display-buffer buf)))
1597 (if enter-buffer
1598 (pop-to-buffer (get-buffer "*Units Table*"))
c9aef719 1599 (display-buffer (get-buffer "*Units Table*")))))
136211a9 1600
603823f5
JB
1601;;; Logarithmic units functions
1602
1603(defvar math-logunits '((var dB var-dB)
1604 (var Np var-Np)))
1605
771fc75e
JB
1606(defun math-conditional-apply (fn &rest args)
1607 "Evaluate f(args) unless in symbolic mode.
1608In symbolic mode, return the list (fn args)."
1609 (if calc-symbolic-mode
1610 (cons fn args)
1611 (apply fn args)))
1612
1613(defun math-conditional-pow (a b)
1614 "Evaluate a^b unless in symbolic mode.
1615In symbolic mode, return the list (^ a b)."
1616 (if calc-symbolic-mode
1617 (list '^ a b)
1618 (math-pow a b)))
1619
603823f5
JB
1620(defun math-extract-logunits (expr)
1621 (if (memq (car-safe expr) '(* /))
1622 (cons (car expr)
1623 (mapcar 'math-extract-logunits (cdr expr)))
1624 (if (memq (car-safe expr) '(^))
1625 (list '^ (math-extract-logunits (nth 1 expr)) (nth 2 expr))
1626 (if (member expr math-logunits) expr 1))))
1627
ec6ad6f2 1628(defun math-logunits-add (a b neg power)
603823f5
JB
1629 (let ((aunit (math-simplify (math-extract-logunits a))))
1630 (if (not (eq (car-safe aunit) 'var))
1631 (calc-record-why "*Improper logarithmic unit" aunit)
1632 (let* ((units (math-extract-units a))
1633 (acoeff (math-simplify (math-remove-units a)))
1634 (bcoeff (math-simplify (math-to-standard-units
1635 (list '/ b units) nil))))
1636 (if (math-units-in-expr-p bcoeff nil)
1637 (calc-record-why "*Inconsistent units" nil)
1638 (if (and neg
1639 (or (math-lessp acoeff bcoeff)
1640 (math-equal acoeff bcoeff)))
1641 (calc-record-why "*Improper coefficients" nil)
da6062e6 1642 (math-mul
603823f5 1643 (if (equal aunit '(var dB var-dB))
6f16becc
JB
1644 (let ((coef (if power 10 20)))
1645 (math-mul coef
771fc75e 1646 (math-conditional-apply 'calcFunc-log10
6f16becc
JB
1647 (if neg
1648 (math-sub
771fc75e
JB
1649 (math-conditional-pow 10 (math-div acoeff coef))
1650 (math-conditional-pow 10 (math-div bcoeff coef)))
6f16becc 1651 (math-add
771fc75e
JB
1652 (math-conditional-pow 10 (math-div acoeff coef))
1653 (math-conditional-pow 10 (math-div bcoeff coef)))))))
6f16becc
JB
1654 (let ((coef (if power 2 1)))
1655 (math-div
771fc75e 1656 (math-conditional-apply 'calcFunc-ln
6f16becc
JB
1657 (if neg
1658 (math-sub
771fc75e
JB
1659 (math-conditional-apply 'calcFunc-exp (math-mul coef acoeff))
1660 (math-conditional-apply 'calcFunc-exp (math-mul coef bcoeff)))
6f16becc 1661 (math-add
771fc75e
JB
1662 (math-conditional-apply 'calcFunc-exp (math-mul coef acoeff))
1663 (math-conditional-apply 'calcFunc-exp (math-mul coef bcoeff)))))
6f16becc 1664 coef)))
603823f5
JB
1665 units)))))))
1666
d71990a1 1667(defun calcFunc-lufadd (a b)
ec6ad6f2 1668 (math-logunits-add a b nil nil))
603823f5 1669
d71990a1 1670(defun calcFunc-lupadd (a b)
ec6ad6f2 1671 (math-logunits-add a b nil t))
603823f5 1672
d71990a1 1673(defun calcFunc-lufsub (a b)
ec6ad6f2 1674 (math-logunits-add a b t nil))
6f16becc 1675
d71990a1 1676(defun calcFunc-lupsub (a b)
ec6ad6f2 1677 (math-logunits-add a b t t))
6f16becc 1678
d71990a1 1679(defun calc-lu-plus (arg)
603823f5
JB
1680 (interactive "P")
1681 (calc-slow-wrapper
1682 (if (calc-is-inverse)
6f16becc 1683 (if (calc-is-hyperbolic)
d71990a1
JB
1684 (calc-binary-op "lu-" 'calcFunc-lufsub arg)
1685 (calc-binary-op "lu-" 'calcFunc-lupsub arg))
6f16becc 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
d71990a1 1690(defun calc-lu-minus (arg)
6f16becc
JB
1691 (interactive "P")
1692 (calc-slow-wrapper
1693 (if (calc-is-inverse)
1694 (if (calc-is-hyperbolic)
d71990a1
JB
1695 (calc-binary-op "lu+" 'calcFunc-lufadd arg)
1696 (calc-binary-op "lu+" 'calcFunc-lupadd arg))
6f16becc 1697 (if (calc-is-hyperbolic)
d71990a1
JB
1698 (calc-binary-op "lu-" 'calcFunc-lufsub arg)
1699 (calc-binary-op "lu-" 'calcFunc-lupsub arg)))))
6f16becc 1700
ec6ad6f2 1701(defun math-logunits-mul (a b power)
6f16becc
JB
1702 (let (logunit coef units number)
1703 (cond
1704 ((and
1705 (setq logunit (math-simplify (math-extract-logunits a)))
1706 (eq (car-safe logunit) 'var)
1707 (eq (math-simplify (math-extract-units b)) 1))
1708 (setq coef (math-simplify (math-remove-units a))
1709 units (math-extract-units a)
1710 number b))
1711 ((and
1712 (setq logunit (math-simplify (math-extract-logunits b)))
1713 (eq (car-safe logunit) 'var)
1714 (eq (math-simplify (math-extract-units a)) 1))
1715 (setq coef (math-simplify (math-remove-units b))
1716 units (math-extract-units b)
1717 number a))
1718 (t (setq logunit nil)))
1719 (if logunit
1720 (cond
1721 ((equal logunit '(var dB var-dB))
1722 (math-simplify
1723 (math-mul
1724 (math-add
da6062e6 1725 coef
6f16becc 1726 (math-mul (if power 10 20)
771fc75e 1727 (math-conditional-apply 'calcFunc-log10 number)))
6f16becc
JB
1728 units)))
1729 (t
1730 (math-simplify
1731 (math-mul
1732 (math-add
da6062e6 1733 coef
771fc75e 1734 (math-div (math-conditional-apply 'calcFunc-ln number) (if power 2 1)))
6f16becc
JB
1735 units))))
1736 (calc-record-why "*Improper units" nil))))
1737
1738(defun math-logunits-divide (a b power)
1739 (let ((logunit (math-simplify (math-extract-logunits a))))
1740 (if (not (eq (car-safe logunit) 'var))
1741 (calc-record-why "*Improper logarithmic unit" logunit)
1742 (if (math-units-in-expr-p b nil)
1743 (calc-record-why "*Improper units quantity" b)
1744 (let* ((units (math-extract-units a))
1745 (coef (math-simplify (math-remove-units a))))
1746 (cond
1747 ((equal logunit '(var dB var-dB))
1748 (math-simplify
1749 (math-mul
1750 (math-sub
da6062e6 1751 coef
6f16becc 1752 (math-mul (if power 10 20)
771fc75e 1753 (math-conditional-apply 'calcFunc-log10 b)))
6f16becc
JB
1754 units)))
1755 (t
1756 (math-simplify
1757 (math-mul
1758 (math-sub
da6062e6 1759 coef
771fc75e 1760 (math-div (math-conditional-apply 'calcFunc-ln b) (if power 2 1)))
6f16becc
JB
1761 units)))))))))
1762
d71990a1 1763(defun calcFunc-lufmul (a b)
ec6ad6f2 1764 (math-logunits-mul a b nil))
6f16becc 1765
d71990a1 1766(defun calcFunc-lupmul (a b)
ec6ad6f2 1767 (math-logunits-mul a b t))
6f16becc 1768
d71990a1 1769(defun calc-lu-times (arg)
603823f5
JB
1770 (interactive "P")
1771 (calc-slow-wrapper
1772 (if (calc-is-inverse)
6f16becc 1773 (if (calc-is-hyperbolic)
d71990a1
JB
1774 (calc-binary-op "lu/" 'calcFunc-lufdiv arg)
1775 (calc-binary-op "lu/" 'calcFunc-lupdiv arg))
6f16becc 1776 (if (calc-is-hyperbolic)
d71990a1
JB
1777 (calc-binary-op "lu*" 'calcFunc-lufmul arg)
1778 (calc-binary-op "lu*" 'calcFunc-lupmul arg)))))
603823f5 1779
d71990a1 1780(defun calcFunc-lufdiv (a b)
6f16becc 1781 (math-logunits-divide a b nil))
603823f5 1782
d71990a1 1783(defun calcFunc-lupdiv (a b)
6f16becc 1784 (math-logunits-divide a b t))
603823f5 1785
d71990a1 1786(defun calc-lu-divide (arg)
6f16becc
JB
1787 (interactive "P")
1788 (calc-slow-wrapper
1789 (if (calc-is-inverse)
1790 (if (calc-is-hyperbolic)
d71990a1
JB
1791 (calc-binary-op "lu*" 'calcFunc-lufmul arg)
1792 (calc-binary-op "lu*" 'calcFunc-lupmul arg))
6f16becc 1793 (if (calc-is-hyperbolic)
d71990a1
JB
1794 (calc-binary-op "lu/" 'calcFunc-lufdiv arg)
1795 (calc-binary-op "lu/" 'calcFunc-lupdiv arg)))))
6f16becc
JB
1796
1797(defun math-logunits-quant (val ref power)
226590f8
JB
1798 (let* ((units (math-simplify (math-extract-units val)))
1799 (lunit (math-simplify (math-extract-logunits units))))
603823f5
JB
1800 (if (not (eq (car-safe lunit) 'var))
1801 (calc-record-why "*Improper logarithmic unit" lunit)
226590f8
JB
1802 (let ((runits (math-simplify (math-div units lunit)))
1803 (coeff (math-simplify (math-div val units))))
1804 (math-mul
1805 (if (equal lunit '(var dB var-dB))
da6062e6 1806 (math-mul
226590f8 1807 ref
da6062e6 1808 (math-conditional-pow
226590f8
JB
1809 10
1810 (math-div
1811 coeff
1812 (if power 10 20))))
da6062e6 1813 (math-mul
226590f8 1814 ref
771fc75e 1815 (math-conditional-apply 'calcFunc-exp
da6062e6 1816 (if power
226590f8
JB
1817 (math-mul 2 coeff)
1818 coeff))))
1819 runits)))))
603823f5 1820
d71990a1
JB
1821(defvar calc-lu-field-reference)
1822(defvar calc-lu-power-reference)
603823f5 1823
580b66d8 1824(defun calcFunc-lufquant (val &optional ref)
603823f5 1825 (unless ref
d71990a1 1826 (setq ref (math-read-expr calc-lu-field-reference)))
6f16becc 1827 (math-logunits-quant val ref nil))
603823f5 1828
580b66d8 1829(defun calcFunc-lupquant (val &optional ref)
603823f5 1830 (unless ref
d71990a1 1831 (setq ref (math-read-expr calc-lu-power-reference)))
6f16becc
JB
1832 (math-logunits-quant val ref t))
1833
d71990a1 1834(defun calc-lu-quant (arg)
6f16becc
JB
1835 (interactive "P")
1836 (calc-slow-wrapper
1837 (if (calc-is-hyperbolic)
1838 (if (calc-is-option)
580b66d8
JB
1839 (calc-binary-op "lupq" 'calcFunc-lufquant arg)
1840 (calc-unary-op "lupq" 'calcFunc-lufquant arg))
6f16becc 1841 (if (calc-is-option)
580b66d8
JB
1842 (calc-binary-op "lufq" 'calcFunc-lupquant arg)
1843 (calc-unary-op "lufq" 'calcFunc-lupquant arg)))))
6f16becc
JB
1844
1845(defun math-logunits-level (val ref db power)
1846 "Compute the value of VAL in decibels or nepers."
1847 (let* ((ratio (math-simplify-units (math-div val ref)))
771fc75e 1848 (ratiou (math-simplify-units (math-remove-units ratio)))
6f16becc
JB
1849 (units (math-simplify (math-extract-units ratio))))
1850 (math-mul
1851 (if db
1852 (math-mul
1853 (math-mul (if power 10 20)
771fc75e 1854 (math-conditional-apply 'calcFunc-log10 ratiou))
6f16becc
JB
1855 '(var dB var-dB))
1856 (math-mul
771fc75e 1857 (math-div (math-conditional-apply 'calcFunc-ln ratiou) (if power 2 1))
6f16becc
JB
1858 '(var Np var-Np)))
1859 units)))
1860
d71990a1 1861(defun calcFunc-dbfield (val &optional ref)
6f16becc 1862 (unless ref
d71990a1 1863 (setq ref (math-read-expr calc-lu-field-reference)))
6f16becc
JB
1864 (math-logunits-level val ref t nil))
1865
d71990a1 1866(defun calcFunc-dbpower (val &optional ref)
6f16becc 1867 (unless ref
d71990a1 1868 (setq ref (math-read-expr calc-lu-power-reference)))
6f16becc
JB
1869 (math-logunits-level val ref t t))
1870
d71990a1 1871(defun calcFunc-npfield (val &optional ref)
6f16becc 1872 (unless ref
d71990a1 1873 (setq ref (math-read-expr calc-lu-field-reference)))
6f16becc
JB
1874 (math-logunits-level val ref nil nil))
1875
d71990a1 1876(defun calcFunc-nppower (val &optional ref)
6f16becc 1877 (unless ref
d71990a1 1878 (setq ref (math-read-expr calc-lu-power-reference)))
6f16becc
JB
1879 (math-logunits-level val ref nil t))
1880
d71990a1 1881(defun calc-db (arg)
6f16becc
JB
1882 (interactive "P")
1883 (calc-slow-wrapper
1884 (if (calc-is-hyperbolic)
1885 (if (calc-is-option)
d71990a1
JB
1886 (calc-binary-op "ludb" 'calcFunc-dbfield arg)
1887 (calc-unary-op "ludb" 'calcFunc-dbfield arg))
6f16becc 1888 (if (calc-is-option)
d71990a1
JB
1889 (calc-binary-op "ludb" 'calcFunc-dbpower arg)
1890 (calc-unary-op "ludb" 'calcFunc-dbpower arg)))))
603823f5 1891
d71990a1 1892(defun calc-np (arg)
603823f5
JB
1893 (interactive "P")
1894 (calc-slow-wrapper
1895 (if (calc-is-hyperbolic)
1896 (if (calc-is-option)
d71990a1
JB
1897 (calc-binary-op "lunp" 'calcFunc-npfield arg)
1898 (calc-unary-op "lunp" 'calcFunc-npfield arg))
603823f5 1899 (if (calc-is-option)
d71990a1
JB
1900 (calc-binary-op "lunp" 'calcFunc-nppower arg)
1901 (calc-unary-op "lunp" 'calcFunc-nppower arg)))))
603823f5 1902
05a29101
JB
1903;;; Musical notes
1904
1905
1906(defvar calc-note-threshold)
1907
1908(defun math-midi-round (num)
1909 "Round NUM to an integer N if NUM is within calc-note-threshold cents of N."
1910 (let* ((n (math-round num))
1911 (diff (math-abs
1912 (math-sub num n))))
da6062e6 1913 (if (< (math-compare diff
8f60c820 1914 (math-div (math-read-expr calc-note-threshold) 100)) 0)
05a29101
JB
1915 n
1916 num)))
1917
1918(defconst math-notes
1919 '(((var C var-C) . 0)
1920 ((var Csharp var-Csharp) . 1)
1921; ((var C♯ var-C♯) . 1)
1922 ((var Dflat var-Dflat) . 1)
1923; ((var D♭ var-D♭) . 1)
1924 ((var D var-D) . 2)
1925 ((var Dsharp var-Dsharp) . 3)
1926; ((var D♯ var-D♯) . 3)
1927 ((var E var-E) . 4)
1928 ((var F var-F) . 5)
1929 ((var Fsharp var-Fsharp) . 6)
1930; ((var F♯ var-F♯) . 6)
1931 ((var Gflat var-Gflat) . 6)
1932; ((var G♭ var-G♭) . 6)
1933 ((var G var-G) . 7)
1934 ((var Gsharp var-Gsharp) . 8)
1935; ((var G♯ var-G♯) . 8)
1936 ((var A var-A) . 9)
1937 ((var Asharp var-Asharp) . 10)
1938; ((var A♯ var-A♯) . 10)
1939 ((var Bflat var-Bflat) . 10)
1940; ((var B♭ var-B♭) . 10)
1941 ((var B var-B) . 11))
1942 "An alist of notes with their number of semitones above C.")
1943
1944(defun math-freqp (freq)
1945 "Non-nil if FREQ is a positive number times the unit Hz.
1946If non-nil, return the coefficient of Hz."
1947 (let ((freqcoef (math-simplify-units
1948 (math-div freq '(var Hz var-Hz)))))
1949 (if (Math-posp freqcoef) freqcoef)))
1950
1951(defun math-midip (num)
1952 "Non-nil if NUM is a possible MIDI note number.
1953If non-nil, return NUM."
1954 (if (Math-numberp num) num))
1955
1956(defun math-spnp (spn)
1957 "Non-nil if NUM is a scientific pitch note (note + cents).
1958If non-nil, return a list consisting of the note and the cents coefficient."
1959 (let (note cents rnote rcents)
1960 (if (eq (car-safe spn) '+)
1961 (setq note (nth 1 spn)
1962 cents (nth 2 spn))
1963 (setq note spn
1964 cents nil))
1965 (cond
1966 ((and ;; NOTE is a note, CENTS is nil or cents.
1967 (eq (car-safe note) 'calcFunc-subscr)
1968 (assoc (nth 1 note) math-notes)
1969 (integerp (nth 2 note))
1970 (setq rnote note)
da6062e6 1971 (or
05a29101
JB
1972 (not cents)
1973 (Math-numberp (setq rcents
da6062e6 1974 (math-simplify
05a29101
JB
1975 (math-div cents '(var cents var-cents)))))))
1976 (list rnote rcents))
1977 ((and ;; CENTS is a note, NOTE is cents.
1978 (eq (car-safe cents) 'calcFunc-subscr)
1979 (assoc (nth 1 cents) math-notes)
1980 (integerp (nth 2 cents))
1981 (setq rnote cents)
da6062e6 1982 (or
05a29101
JB
1983 (not note)
1984 (Math-numberp (setq rcents
da6062e6 1985 (math-simplify
05a29101
JB
1986 (math-div note '(var cents var-cents)))))))
1987 (list rnote rcents)))))
1988
1989(defun math-freq-to-midi (freq)
1990 "Return the midi note number corresponding to FREQ Hz."
1991 (let ((midi (math-add
1992 69
1993 (math-mul
1994 12
1995 (calcFunc-log
1996 (math-div freq 440)
1997 2)))))
1998 (math-midi-round midi)))
1999
2000(defun math-spn-to-midi (spn)
2001 "Return the MIDI number corresponding to SPN."
2002 (let* ((note (cdr (assoc (nth 1 (car spn)) math-notes)))
2003 (octave (math-add (nth 2 (car spn)) 1))
2004 (cents (nth 1 spn))
2005 (midi (math-add
2006 (math-mul 12 octave)
2007 note)))
2008 (if cents
2009 (math-add midi (math-div cents 100))
2010 midi)))
2011
2012(defun math-midi-to-spn (midi)
2013 "Return the scientific pitch notation corresponding to midi number MIDI."
2014 (let (midin cents)
2015 (if (math-integerp midi)
da6062e6 2016 (setq midin midi
05a29101
JB
2017 cents nil)
2018 (setq midin (math-floor midi)
2019 cents (math-mul 100 (math-sub midi midin))))
2020 (let* ((nr ;; This should be (math-idivmod midin 12), but with
2021 ;; better behavior for negative midin.
2022 (if (Math-negp midin)
2023 (let ((dm (math-idivmod (math-neg midin) 12)))
2024 (if (= (cdr dm) 0)
2025 (cons (math-neg (car dm)) 0)
2026 (cons
2027 (math-sub (math-neg (car dm)) 1)
2028 (math-sub 12 (cdr dm)))))
2029 (math-idivmod midin 12)))
2030 (n (math-sub (car nr) 1))
2031 (note (car (rassoc (cdr nr) math-notes))))
2032 (if cents
da6062e6 2033 (list '+ (list 'calcFunc-subscr note n)
05a29101
JB
2034 (list '* cents '(var cents var-cents)))
2035 (list 'calcFunc-subscr note n)))))
2036
2037(defun math-freq-to-spn (freq)
2038 "Return the scientific pitch notation corresponding to FREQ Hz."
2039 (math-with-extra-prec 3
2040 (math-midi-to-spn (math-freq-to-midi freq))))
2041
2042(defun math-midi-to-freq (midi)
2043 "Return the frequency of the note with midi number MIDI."
2044 (list '*
2045 (math-mul
2046 440
2047 (math-pow
2048 2
da6062e6 2049 (math-div
05a29101
JB
2050 (math-sub
2051 midi
2052 69)
2053 12)))
2054 '(var Hz var-Hz)))
2055
2056(defun math-spn-to-freq (spn)
2057 "Return the frequency of the note with scientific pitch notation SPN."
2058 (math-midi-to-freq (math-spn-to-midi spn)))
2059
2060(defun calcFunc-spn (expr)
2061 "Return EXPR written as scientific pitch notation + cents."
da6062e6 2062 ;; Get the coefficient of Hz
05a29101
JB
2063 (let (note)
2064 (cond
2065 ((setq note (math-freqp expr))
2066 (math-freq-to-spn note))
2067 ((setq note (math-midip expr))
2068 (math-midi-to-spn note))
2069 ((math-spnp expr)
2070 expr)
2071 (t
2072 (math-reject-arg expr "*Improper expression")))))
2073
2074(defun calcFunc-midi (expr)
2075 "Return EXPR written as a MIDI number."
2076 (let (note)
2077 (cond
2078 ((setq note (math-freqp expr))
2079 (math-freq-to-midi note))
2080 ((setq note (math-spnp expr))
2081 (math-spn-to-midi note))
2082 ((math-midip expr)
2083 expr)
2084 (t
2085 (math-reject-arg expr "*Improper expression")))))
2086
2087(defun calcFunc-freq (expr)
2088 "Return the frequency corresponding to EXPR."
2089 (let (note)
2090 (cond
2091 ((setq note (math-midip expr))
2092 (math-midi-to-freq note))
2093 ((setq note (math-spnp expr))
2094 (math-spn-to-freq note))
2095 ((math-freqp expr)
2096 expr)
2097 (t
2098 (math-reject-arg expr "*Improper expression")))))
2099
2100(defun calc-freq (arg)
2101 "Return the frequency corresponding to the expression on the stack."
2102 (interactive "P")
2103 (calc-slow-wrapper
2104 (calc-unary-op "freq" 'calcFunc-freq arg)))
2105
2106(defun calc-midi (arg)
2107 "Return the MIDI number corresponding to the expression on the stack."
2108 (interactive "P")
2109 (calc-slow-wrapper
2110 (calc-unary-op "midi" 'calcFunc-midi arg)))
2111
2112(defun calc-spn (arg)
2113 "Return the scientific pitch notation corresponding to the expression on the stack."
2114 (interactive "P")
2115 (calc-slow-wrapper
2116 (calc-unary-op "spn" 'calcFunc-spn arg)))
2117
2118
7d02e8cd
JB
2119(provide 'calc-units)
2120
be19ef0b
GM
2121;; Local variables:
2122;; coding: utf-8
2123;; End:
2124
c9aef719 2125;;; calc-units.el ends here