* calc/calc-math.el (calcFunc-log10): Check for symbolic mode
[bpt/emacs.git] / lisp / calc / calc-units.el
index 0c622b1..e6a6fb0 100644 (file)
@@ -1,7 +1,6 @@
 ;;; calc-units.el --- unit conversion functions for Calc
 
-;; Copyright (C) 1990, 1991, 1992, 1993, 2001, 2002, 2003, 2004,
-;;   2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+;; Copyright (C) 1990-1993, 2001-2011  Free Software Foundation, Inc.
 
 ;; Author: David Gillespie <daveg@synaptics.com>
 ;; Maintainer: Jay Belanger <jay.p.belanger@gmail.com>
     ( R0      "8.314472 J/(mol K)"          "Molar gas constant" nil
               "8.314472 J/(mol K) (*)")
     ( V0      "22.710981*10^(-3) m^3/mol"   "Standard volume of ideal gas" nil
-              "22.710981 10^-3 m^3/mol (*)")))
+              "22.710981 10^-3 m^3/mol (*)")
+    ;; Logarithmic units
+    ( Np      nil    "*Neper")
+    ( dB      "(ln(10)/20) Np" "decibel")))
 
 
 (defvar math-additional-units nil
@@ -871,6 +873,7 @@ If EXPR is nil, return nil."
           (or (eq (nth 1 expr) 'pi)
               (error "Unknown name %s in defining expression for unit %s"
                      (nth 1 expr) (car math-fbu-entry))))
+          ((equal expr '(calcFunc-ln 10)))
          (t (error "Malformed defining expression for unit %s" (car math-fbu-entry))))))
 
 
@@ -1551,11 +1554,312 @@ If EXPR is nil, return nil."
        (pop-to-buffer (get-buffer "*Units Table*"))
       (display-buffer (get-buffer "*Units Table*")))))
 
+;;; Logarithmic units functions
+
+(defvar math-logunits '((var dB var-dB)
+                        (var Np var-Np)))
+
+(defun math-conditional-apply (fn &rest args)
+  "Evaluate f(args) unless in symbolic mode.
+In symbolic mode, return the list (fn args)."
+  (if calc-symbolic-mode
+      (cons fn args)
+    (apply fn args)))
+
+(defun math-conditional-pow (a b)
+  "Evaluate a^b unless in symbolic mode.
+In symbolic mode, return the list (^ a b)."
+  (if calc-symbolic-mode
+      (list '^ a b)
+    (math-pow a b)))
+
+(defun math-extract-logunits (expr)
+  (if (memq (car-safe expr) '(* /))
+      (cons (car expr)
+           (mapcar 'math-extract-logunits (cdr expr)))
+    (if (memq (car-safe expr) '(^))
+        (list '^ (math-extract-logunits (nth 1 expr)) (nth 2 expr))
+      (if (member expr math-logunits) expr 1))))
+
+(defun math-logunits-add (a b neg power)
+  (let ((aunit (math-simplify (math-extract-logunits a))))
+    (if (not (eq (car-safe aunit) 'var))
+        (calc-record-why "*Improper logarithmic unit" aunit)
+      (let* ((units (math-extract-units a))
+            (acoeff (math-simplify (math-remove-units a)))
+            (bcoeff (math-simplify (math-to-standard-units
+                                    (list '/ b units) nil))))
+        (if (math-units-in-expr-p bcoeff nil)
+            (calc-record-why "*Inconsistent units" nil)
+          (if (and neg
+                   (or (math-lessp acoeff bcoeff)
+                       (math-equal acoeff bcoeff)))
+              (calc-record-why "*Improper coefficients" nil)
+            (math-mul 
+             (if (equal aunit '(var dB var-dB))
+                 (let ((coef (if power 10 20)))
+                   (math-mul coef
+                             (math-conditional-apply 'calcFunc-log10
+                              (if neg
+                                  (math-sub
+                                   (math-conditional-pow 10 (math-div acoeff coef))
+                                   (math-conditional-pow 10 (math-div bcoeff coef)))
+                                (math-add
+                                 (math-conditional-pow 10 (math-div acoeff coef))
+                                 (math-conditional-pow 10 (math-div bcoeff coef)))))))
+               (let ((coef (if power 2 1)))
+                 (math-div
+                  (math-conditional-apply 'calcFunc-ln
+                   (if neg
+                       (math-sub
+                        (math-conditional-apply 'calcFunc-exp (math-mul coef acoeff))
+                        (math-conditional-apply 'calcFunc-exp (math-mul coef bcoeff)))
+                     (math-add
+                      (math-conditional-apply 'calcFunc-exp (math-mul coef acoeff))
+                      (math-conditional-apply 'calcFunc-exp (math-mul coef bcoeff)))))
+                  coef)))
+             units)))))))
+
+(defun calcFunc-lufieldplus (a b)
+  (math-logunits-add a b nil nil))
+
+(defun calcFunc-lupowerplus (a b)
+  (math-logunits-add a b nil t))
+
+(defun calcFunc-lufieldminus (a b)
+  (math-logunits-add a b t nil))
+
+(defun calcFunc-lupowerminus (a b)
+  (math-logunits-add a b t t))
+
+(defun calc-logunits-add (arg)
+  (interactive "P")
+  (calc-slow-wrapper
+   (if (calc-is-inverse)
+       (if (calc-is-hyperbolic)
+           (calc-binary-op "lu-" 'calcFunc-lufieldminus arg)
+         (calc-binary-op "lu-" 'calcFunc-lupowerminus arg))
+     (if (calc-is-hyperbolic)
+         (calc-binary-op "lu+" 'calcFunc-lufieldplus arg)
+       (calc-binary-op "lu+" 'calcFunc-lupowerplus arg)))))
+
+(defun calc-logunits-sub (arg)
+  (interactive "P")
+  (calc-slow-wrapper
+   (if (calc-is-inverse)
+       (if (calc-is-hyperbolic)
+           (calc-binary-op "lu+" 'calcFunc-lufieldplus arg)
+         (calc-binary-op "lu+" 'calcFunc-lupowerplus arg))
+     (if (calc-is-hyperbolic)
+         (calc-binary-op "lu-" 'calcFunc-lufieldminus arg)
+       (calc-binary-op "lu-" 'calcFunc-lupowerminus arg)))))
+
+(defun math-logunits-mul (a b power)
+  (let (logunit coef units number)
+    (cond
+     ((and
+       (setq logunit (math-simplify (math-extract-logunits a)))
+       (eq (car-safe logunit) 'var)
+       (eq (math-simplify (math-extract-units b)) 1))
+      (setq coef (math-simplify (math-remove-units a))
+            units (math-extract-units a)
+            number b))
+     ((and
+       (setq logunit (math-simplify (math-extract-logunits b)))
+       (eq (car-safe logunit) 'var)
+       (eq (math-simplify (math-extract-units a)) 1))
+      (setq coef (math-simplify (math-remove-units b))
+            units (math-extract-units b)
+            number a))
+     (t (setq logunit nil)))
+    (if logunit
+        (cond
+         ((equal logunit '(var dB var-dB))
+          (math-simplify
+           (math-mul
+            (math-add
+             coef 
+             (math-mul (if power 10 20)
+                       (math-conditional-apply 'calcFunc-log10 number)))
+            units)))
+         (t
+          (math-simplify
+           (math-mul
+            (math-add
+             coef 
+             (math-div (math-conditional-apply 'calcFunc-ln number) (if power 2 1)))
+            units))))
+      (calc-record-why "*Improper units" nil))))
+
+(defun math-logunits-divide (a b power)
+  (let ((logunit (math-simplify (math-extract-logunits a))))
+    (if (not (eq (car-safe logunit) 'var))
+        (calc-record-why "*Improper logarithmic unit" logunit)
+      (if (math-units-in-expr-p b nil)
+          (calc-record-why "*Improper units quantity" b)
+        (let* ((units (math-extract-units a))
+               (coef (math-simplify (math-remove-units a))))
+          (cond
+           ((equal logunit '(var dB var-dB))
+            (math-simplify
+             (math-mul
+              (math-sub
+               coef 
+               (math-mul (if power 10 20)
+                         (math-conditional-apply 'calcFunc-log10 b)))
+              units)))
+         (t
+          (math-simplify
+           (math-mul
+            (math-sub
+             coef 
+             (math-div (math-conditional-apply 'calcFunc-ln b) (if power 2 1)))
+            units)))))))))
+
+(defun calcFunc-lufieldtimes (a b)
+  (math-logunits-mul a b nil))
+
+(defun calcFunc-lupowertimes (a b)
+  (math-logunits-mul a b t))
+
+(defun calc-logunits-mul (arg)
+  (interactive "P")
+  (calc-slow-wrapper
+   (if (calc-is-inverse)
+       (if (calc-is-hyperbolic)
+           (calc-binary-op "lu/" 'calcFunc-lufielddiv arg)
+         (calc-binary-op "lu/" 'calcFunc-lupowerdiv arg))
+     (if (calc-is-hyperbolic)
+         (calc-binary-op "lu*" 'calcFunc-lufieldtimes arg)
+       (calc-binary-op "lu*" 'calcFunc-lupowertimes arg)))))
+
+(defun calcFunc-lufielddiv (a b)
+  (math-logunits-divide a b nil))
+
+(defun calcFunc-lupowerdiv (a b)
+  (math-logunits-divide a b t))
+
+(defun calc-logunits-divide (arg)
+  (interactive "P")
+  (calc-slow-wrapper
+   (if (calc-is-inverse)
+       (if (calc-is-hyperbolic)
+           (calc-binary-op "lu*" 'calcFunc-lufieldtimes arg)
+         (calc-binary-op "lu*" 'calcFunc-lupowertimes arg))
+     (if (calc-is-hyperbolic)
+         (calc-binary-op "lu/" 'calcFunc-lufielddiv arg)
+       (calc-binary-op "lu/" 'calcFunc-lupowerdiv arg)))))
+
+(defun math-logunits-quant (val ref power)
+  (let* ((units (math-simplify (math-extract-units val)))
+         (lunit (math-simplify (math-extract-logunits units))))
+    (if (not (eq (car-safe lunit) 'var))
+        (calc-record-why "*Improper logarithmic unit" lunit)
+      (let ((runits (math-simplify (math-div units lunit)))
+            (coeff (math-simplify (math-div val units))))
+        (math-mul
+         (if (equal lunit '(var dB var-dB))
+             (math-mul 
+              ref
+              (math-conditional-pow 
+               10
+               (math-div
+                coeff
+                (if power 10 20))))
+           (math-mul 
+            ref
+            (math-conditional-apply 'calcFunc-exp
+             (if power 
+                 (math-mul 2 coeff)
+               coeff))))
+         runits)))))
+
+(defvar calc-logunits-field-reference)
+(defvar calc-logunits-power-reference)
+
+(defun calcFunc-fieldquant (val &optional ref)
+  (unless ref
+    (setq ref (math-read-expr calc-logunits-field-reference)))
+  (math-logunits-quant val ref nil))
+
+(defun calcFunc-powerquant (val &optional ref)
+  (unless ref
+    (setq ref (math-read-expr calc-logunits-power-reference)))
+  (math-logunits-quant val ref t))
+
+(defun calc-logunits-quantity (arg)
+  (interactive "P")
+  (calc-slow-wrapper
+   (if (calc-is-hyperbolic)
+       (if (calc-is-option)
+           (calc-binary-op "lupq" 'calcFunc-fieldquant arg)
+         (calc-unary-op "lupq" 'calcFunc-fieldquant arg))
+     (if (calc-is-option)
+         (calc-binary-op "lufq" 'calcFunc-powerquant arg)
+       (calc-unary-op "lufq" 'calcFunc-powerquant arg)))))
+
+(defun math-logunits-level (val ref db power)
+  "Compute the value of VAL in decibels or nepers."
+      (let* ((ratio (math-simplify-units (math-div val ref)))
+             (ratiou (math-simplify-units (math-remove-units ratio)))
+             (units (math-simplify (math-extract-units ratio))))
+        (math-mul
+         (if db
+             (math-mul
+              (math-mul (if power 10 20)
+                        (math-conditional-apply 'calcFunc-log10 ratiou))
+              '(var dB var-dB))
+           (math-mul
+            (math-div (math-conditional-apply 'calcFunc-ln ratiou) (if power 2 1))
+            '(var Np var-Np)))
+         units)))
+
+(defun calcFunc-dbfieldlevel (val &optional ref)
+  (unless ref
+    (setq ref (math-read-expr calc-logunits-field-reference)))
+  (math-logunits-level val ref t nil))
+
+(defun calcFunc-dbpowerlevel (val &optional ref)
+  (unless ref
+    (setq ref (math-read-expr calc-logunits-power-reference)))
+  (math-logunits-level val ref t t))
+
+(defun calcFunc-npfieldlevel (val &optional ref)
+  (unless ref
+    (setq ref (math-read-expr calc-logunits-field-reference)))
+  (math-logunits-level val ref nil nil))
+
+(defun calcFunc-nppowerlevel (val &optional ref)
+  (unless ref
+    (setq ref (math-read-expr calc-logunits-power-reference)))
+  (math-logunits-level val ref nil t))
+
+(defun calc-logunits-dblevel (arg)
+  (interactive "P")
+  (calc-slow-wrapper
+   (if (calc-is-hyperbolic)
+       (if (calc-is-option)
+           (calc-binary-op "ludb" 'calcFunc-dbfieldlevel arg)
+         (calc-unary-op "ludb" 'calcFunc-dbfieldlevel arg))
+     (if (calc-is-option)
+         (calc-binary-op "ludb" 'calcFunc-dbpowerlevel arg)
+       (calc-unary-op "ludb" 'calcFunc-dbpowerlevel arg)))))
+
+(defun calc-logunits-nplevel (arg)
+  (interactive "P")
+  (calc-slow-wrapper
+   (if (calc-is-hyperbolic)
+       (if (calc-is-option)
+           (calc-binary-op "lunp" 'calcFunc-npfieldlevel arg)
+         (calc-unary-op "lunp" 'calcFunc-npfieldlevel arg))
+     (if (calc-is-option)
+         (calc-binary-op "lunp" 'calcFunc-nppowerlevel arg)
+       (calc-unary-op "lunp" 'calcFunc-nppowerlevel arg)))))
+
 (provide 'calc-units)
 
 ;; Local variables:
 ;; coding: utf-8
 ;; End:
 
-;; arch-tag: e993314f-3adc-4191-be61-4ef8874881c4
 ;;; calc-units.el ends here