;; -*- mode: emacs-lisp; coding: hebrew-iso-8bit-unix; -*- ;; compute.el --- Compute simple arithmetical expression ;; Copyright (C) 1992-2003 Ehud karni ;; This file is NOT part of GNU Emacs, distribution conditions below. ;; ;; EHUD KARNI ינרק דוהא ;; Ben Gurion st' 14 ןוירוג ןב 'חר ;; Kfar - Sava 44 257 אבס - רפכ ;; =================================== ;; 972-(0)9-7659599 ;; =================================== ;; RCS: $Id: compute.el,v 1.105 2003/04/24 11:52:41 ehud Exp $ ;; ;; $Log: compute.el,v $ ;; Revision 1.105 2003/04/24 11:52:41 ehud ;; Use `random' (emacs 21), defalias to `ran' otherwise. ;; Add functions: compute-region, compute-str-to-signd-dec, ;; operate-on-numbers, sum-numbers-region, insert-sum-total ;; Change `mult-numbers' and add: increment-numbers, divide-numbers, ;; subtract-numbers - All use `operate-on-numbers'. ;; ;; Revision 1.104 2000/03/05 12:17:18 ehud ;; Change function calling in compute, add compute-functions-list var. ;; Change exponent to **, add octal as \nnn, hexa as \xnnn. ;; ;; Revision 1.103 1998/03/15 17:09:44 ehud ;; Last revision for 19.34 ;; ;; Revision 1.102 1996/02/19 10:15:55 ehud ;; Emacs 19.30 version ;; ;; Revision 1.101 1995/05/17 15:56:06 ehud ;; adding of mult-numbers defun ;; ;; Revision 1.100 1995/01/19 17:13:20 ehud ;; SW initial version control for all el's ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2 of the License, or ;; (at your option) any later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software ;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ;; The updated package can be got by email: ;; Send email to: auto_mail@unix.mvs.co.il. ;; Subject: "files" (one word, no quotes). ;; 1st line of the content: "compute.el.gz" (one word, no quotes). ;; The file will be then automaticly sent to the reply address. ;; To work with this package load it and define keys of your choice ;; to the needed commands. I use the following commands/keys: ;; ;; Command Does My Key ;; ======= ===================== ====== ;; compute Evaluate numeric value of a STRING Alt-c ;; compute-region Region as an arithmetic expression Alt-Shift-A ;; ;; sum-numbers-region Sum all decimal numbers in region Alt-Shift-t ;; ;; increment-numbers Query increment numbers by constant ;; divide-numbers Query divide numbers into constant ;; mult-numbers Query multiply numbers by constant ;; subtract-numbers Query subtract numbers from constant ;; ;; insert-sum-total Insert `sum-total' as decimal number C-# (defvar compute-random-called nil "random already called switch, set to t after 1st call") (defvar compute-history nil "history list for compute") (defvar compute-string "" "saved last computed (by compute) string") (defvar compute-string-auto "" "saved last auto added (by compute-repeat) string") (defvar rslt 0 "value of result computed (by compute)") (defvar sum-total 0 "value of total computed by `sum-total-text' (accumulated) or `sum-marked'") (or (fboundp 'random) ;; check for random existence (defalias 'random 'ran)) ;; for Emacs before 21.1 (defvar compute-functions-list '( ("abs" abs 1 nil) ("exp" exp 1 nil) ("ln" log 1 nil) ("log" log10 1 nil) ("int" truncate 1 nil) ("ran" random 0 nil) ("sqrt" sqrt 1 nil) ;; trigonometric functions ("acos" acos 1 r2d) ("asin" asin 1 r2d) ("atan" atan 1 r2d) ("cos" cos 1 d2r) ("sin" sin 1 d2r) ("tan" tan 1 d2r) ("d2r" identity 1 d2r) ("r2d" identity 1 r2d) ) "functions to call by compute. List of lists. Each list has the structure: (NAME FUNC ARGS TRIG). The meaning of the 4 values are: NAME is the function name in `compute' - a string which the user types. FUNC is the lisp function to call - predefined or newly defined. ARGS is the number of arguments to the function (must be 0 or 1). TRIG is special treatment for trigonometric functions. It can be: d2r - convert the argument to radians before calling r2d - convert the function result to degrees after calling. nil (or any other value) - no special treatment. example: Add a new trigonometric function `cotan' by using @ctg and converting its argument to radians do: (or (assoc \"ctg\" compute-functions-list) ;do not add more than once (setq compute-functions-list (append compute-functions-list (list (list \"ctg\" 'cotan 1 'd2r))))) In Emacs 21 and later you can do (add-to-list 'compute-functions-list '(\"ctg\" cotan 1 d2r) t) ") (defun compute (&optional STRING NOMSG) "Compute numeric value of optional STRING and put result into variable 'rslt' the result (or error message) is also displayed in the minibuffer. If there is no parameter then the user is prompted to edit the last computed string in the minibuffer. Optional 2nd arg NOMSG if non nil - return computed value without message. A number may be decimal, or octal (it should begin with [ or \\) or hexadecimal (it should begin with ] or \\x the hex digits can be upper A-F or lower a-f). 2748 (decimal) = [5274 = \\5274 (octal) = ]AbC = \\xaBC (hex) The operations and syntax that can be used: Highest operations: - (unary minus), = (set variable), # (square root), ~ (not) or @func (see list of functions below). 1st level operations: ** (exponent) 2nd level operations: * (multiply), / (divide), ! (int divide), % (modulo) 3rd level operations: + (add, plus), - (subtract) 4th level operations: & (and), | (or), ^ (xor) 5th level operations: < (left shift), > (right shift) - must be separated by spaces on both sides. parentheses can be used to change precedence. e.g. abcd=(3*eee) - set variable abcd (and rslt) to 3 times value of variable eee. (3+6)*(5-7) - set rslt to -18 a=-12*b=(c=5-d=7) - sets a=-12, c=5, d=7, b=-2, rslt to 24. All the functions are defined in the `compute-functions-list' variable, which can be changed at run time. Here is the list of predefined functions. Calling is by: @func(expr), e.g. @abs(-7), @sin(a*5). abs absolute value. log logarithm (base 10). acos arc cosine (degrees). ln natural logarithm. asin arc sine (degrees). r2d radians to degrees. atan arc tangent (degrees). ran () random number (next) (0-1). cos cosine (degrees). sin sine (degrees). d2r degrees to radians. sqrt square root. exp exponential (e ** ARG). tan tangent (degrees). int truncate to integer. " (interactive) ;; (ek-debug-msg "===== compute main start =====") (if (not STRING) (setq STRING (setq compute-string (read-string "string to compute: " compute-string '(compute-history . 1))))) (let ((lng (length STRING)) ; string length (plvl 0) ; parentheses lvl start from 0 (ix 0) ; index in STRING (standard-output (if NOMSG 'ignore (get-buffer-create "*compute-buf*"))) (saved-buf (buffer-name))) (if (not NOMSG) (progn (pop-to-buffer standard-output) (goto-char (point-max)) (princ "\nCompute: ") (princ STRING))) (setq rslt (compute-1 99)) ; compute exepression up to its end ! (if (> plvl 0) ;unbalanced (compute-err "unbalanced parentheses, more '(' than ')'.")) (if (and (floatp rslt) (< (logb rslt) 25) (= rslt (ftruncate rslt))) (setq rslt (truncate rslt))) (if NOMSG rslt ;return rslt only if NOMSG requested (progn (if (floatp rslt) (princ (format "\n result: %f.\n" rslt)) (princ (format "\n result: %d, \\x%x.\n" rslt rslt))) (pop-to-buffer saved-buf) (if (floatp rslt) (message "result: %f (in rslt). input: %s." rslt STRING) (message "result: %g, \\x%x (in rslt). input: %s." rslt rslt STRING)))))) (defun compute-repeat (&optional AUTO-OP) "Repeat calling compute, prepending rslt before string typed, optional AUTO-OP (string) is automatically added." (interactive) (let ((rstr "") (cont t) (saved-buf (buffer-name))) (if (not (and AUTO-OP (stringp AUTO-OP))) (setq compute-string-auto (setq AUTO-OP (read-string "Automatic op: " compute-string-auto '(compute-history . 1))))) (while cont (setq rstr (read-string (format "rslt=%d, comp: " rslt) (concat "rslt " AUTO-OP) '(compute-history . 1))) (if (string= rstr "") (progn (pop-to-buffer saved-buf) (setq cont nil)) (compute rstr t))))) (defun compute-region (beg end &optional NOMSG) "Compute region as expression from BEG to END. Optional arg NOMSG: nil display result in `*compute-buf*' 'REP replace region with the computed result (no message) t just return result, no user message." (interactive) (let ((sum (compute (buffer-substring beg end) NOMSG))) (if (eq NOMSG 'REP) (save-excursion (goto-char beg) (delete-region beg end) (insert (format (if (floatp sum-total) "%f" "%d") sum)))) sum)) (defun compute-1 (lvl) "Compute expression at one level. Level is: 0 (unary -,~,=,#) 1 (**), 2 (*,/,!,%), 3 (+,-), 4 (&,^,|), 5 (<,>) 9 (parentheses)." ;; (ek-debug-msg "===== compute-1 =====") (let ((tmp nil) ; temp result (nil - none) (nvar nil) ; var for setting (var=) (var "") ; var name or number (nvl nil) ; var name or number (xlng lng)) ; internal length for checking ;; (ek-debug-msg "===== compute-1 1st var =====") (setq var (compute-nxt-arg)) ;get next arg (op or var or nil) (if (not var) ;end of string error (compute-err "end of input (number expected)")) (if (stringp var) ;number or var (if (= (aref var 0) ?@) ;function call ? (setq nvl (compute-func (substring var 1))) (progn (setq nvl (compute-str-to-val var)) ;only numbers, convert to decimal (if nvl ;it was number (nvl is not nil) (setq nvar nil) (progn (setq nvar (intern var nil)) (setq nvl 0) ;variable name (condition-case () (setq nvl (symbol-value nvar)) ;variable name (error)) ;; (ek-debug-msg (format "--- |%s| read value is %d " var nvl)) (if (compute-nxt-arg-is-op ?= ) ;get next arg only if it is = (progn (setq nvl (set nvar (compute-1 0))) (if (floatp nvl) (princ (format " %s set to %g " var nvl)) (princ (format " %s set to %d \\x%x " var nvl nvl)))) (if (floatp nvl) (princ (format " %s was %g " var nvl)) (princ (format " %s was %d \\x%x " var nvl nvl)))))))) (cond ; not variable or number, must be (, - or ~ ((= var ?( ) ;start of () (setq plvl (1+ plvl)) (setq nvl (compute-1 9))) ((= var ?- ) ;unary - (setq nvl (- 0 (compute-1 0)))) ((= var ?~ ) ;unary logical not (setq nvl (lognot (truncate (compute-1 0))))) ((= var ?# ) ;square root function (setq nvl (sqrt (compute-1 0)))) (t (compute-err "number or variable or unary operator or '(' expected")))) ;syntax error missing var or unary op (at ix) ;; (ek-debug-msg (format "--- current value is %d " nvl)) (if (= lvl 0) ;lvl 0 is unary (- ~ # or =) (setq xlng 0)) ;no more checking (while (< ix xlng) ;; (ek-debug-msg "===== compute-1 loop (op) =====") (setq var (compute-nxt-arg)) ;get next arg (op or var or nil) (if var ;end of string error (progn (if (stringp var) ;number or var - error, should be op only ! (setq var ?( )) ;change to '(' (cond ((= var ?)) ;allowed only after var name (progn (if (< lvl 9) ; check if other op intervention (setq ix (1- ix)) ; yes, push op back in (if (= plvl 0) ; were there "(" ? (any number) (compute-err "unbalanced parentheses, more ')' than '('.") ;no, ERROR ! (setq plvl (1- plvl)))) ;yes, decrease parentheses level (setq xlng 0))) ; exit from loop IN ANY CASE !!! ((and (= var ?*) (= (aref STRING ix) ?*)) ;; (ek-debug-msg "- - - compute-1 **") (if (<= lvl 1) ; check if preceded (setq ix (1- ix) ; push op back in xlng 0) ; exit from loop (setq ix (1+ ix) ; skip 2nd * nvl (expt nvl (compute-1 1))) ;nvl=nvl**(nxt val) )) ((compute-chk-op var "*/!%") ;; (ek-debug-msg "- - - compute-1 * / ! %") (if (<= lvl 2) ; check if preceded (progn (setq ix (1- ix)) ; push op back in (setq xlng 0)) ; exit from loop (cond ((= var ?*) ; multiply (setq nvl (* nvl (compute-1 2)))) ; return value * next val ((= var ?/) ; divide (float) (setq nvl (/ nvl (compute-1 2)))) ; return value / next val ((= var ?!) ; divide (convert to integer) (setq nvl (truncate (/ nvl (compute-1 2))))) ; return value / next val ((= var ?%) ; modulo (setq nvl (% (truncate nvl) (truncate (compute-1 2))))) ;value modulo next-val ))) ((compute-chk-op var "-+") ;; (ek-debug-msg "- - - compute-1 - +") (if (<= lvl 3) ; check if preceded (progn (setq ix (1- ix)) ; push op back in (setq xlng 0)) ; exit from loop (if (= var ?+) ; add (plus) (setq nvl (+ nvl (compute-1 3))) ; return value + next val (setq nvl (- nvl (compute-1 3)))))) ; return value - next-val ((compute-chk-op var "&|^") ;; (ek-debug-msg "- - - compute-1 & | ^") (if (<= lvl 4) ; check if preceded (progn (setq ix (1- ix)) ; push op back in (setq xlng 0)) ; exit from loop (cond ((= var ?&) ; logical and (setq nvl (logand (truncate nvl) (truncate (compute-1 4))))) ;return and (value next val) ((= var ?|) (setq nvl (logior (truncate nvl) (truncate (compute-1 4))))) ;return value .or. next val ((= var ?^) (setq nvl (logxor (truncate nvl) (truncate (compute-1 4)))))))) ;return value .xor. next val ((compute-chk-op var "<>") ;; (ek-debug-msg "- - - compute-1 < >") (if (<= lvl 5) ; check if preceded (progn (setq ix (1- ix)) ; push op back in (setq xlng 0)) ; exit from loop (let ((avl (abs (truncate nvl))) (shft (truncate (compute-1 5)))) (if (= var ?>) ;right shift (setq shft (- 0 shft))) ;change shft sign for right shift (setq avl (ash avl shft)) ;avl is shifted absolute value (if (< nvl 0) (setq avl (- avl))) ;change sign for initial negative (setq nvl avl)))) ;return final result (t (compute-err "operator or ')' expected")))))) ;syntax error - missing op (at ix) nvl)) ; return nvl (defun compute-chk-op (arg allow) "return nil if ARG is not one of the ALLOW chars, otherwise return its index." ;; (ek-debug-msg "===== compute-chk-op =====") (let ((ix -1) (lng (1- (length allow)))) (while (< ix lng) (setq ix (1+ ix)) (if (= arg (aref allow ix)) (setq lng -1))) (if (< lng 0) ix nil))) (defun compute-err (err) "display error with ERR message" (princ (format " ===syntax error at char %d===\ncompute:%s! %s" ix (make-string ix ? ) err)) (error "syntax error at %d, %s" ix err)) (defun compute-func (func) "Check syntax (name and'(') and Compute the value of the function FUNC." (or (compute-nxt-arg-is-op ?( ) ;get next arg only if it is ( (compute-err "function without arguments (missing '(')" )) (let (farg ;argument (fnc-prm (assoc func compute-functions-list)) ;function params trig) ;TRIG special treatment (setq plvl (1+ plvl)) (or fnc-prm (compute-err (concat "unknown function name: " func))) (if (compute-nxt-arg-is-op ?) ) ;get next arg only if it is not ) (setq plvl (1- plvl)) ;no args, assume 0 (setq farg (compute-1 9))) ;compute arg value (or farg (= (nth 2 fnc-prm) 0) (compute-err (concat "argument must be given for: " func))) (setq trig (nth 3 fnc-prm)) (and (equal trig 'd2r) (setq farg (degrees-to-radians farg))) (setq farg (funcall (nth 1 fnc-prm) farg)) (and (equal trig 'r2d) (setq farg (radians-to-degrees farg))) farg)) (defun compute-func-random () "return random number, set seed if called for first time" (if (not compute-random-called) (progn (setq compute-random-called t) (random t))) (+ (/ (float (random)) 6.7108864e7) .5)) (defun compute-nxt-arg () "returns next argument. Which can be: number (op or '(',')'), string (var or func name or digits), nil (end of input)." ;; (ek-debug-msg "===== compute-nxt-arg =====") (let ((var "") ; var name or number (chr ? ) ; current char (opsw nil)) ; loop switch (operator) (while (and chr (not opsw)) ; while there is input not operator (if (or (= chr ? ) (= chr ?\n)) ; space or NL (setq chr nil) ; change it to nil (may be stop switch) (setq var (concat var (char-to-string chr)))) ; add it to var (if (>= ix lng) (setq chr nil)) ; set to nil (stop switch, no more input) (if (or chr (string= var "")) ; not spc or empty var (progn (setq chr (aref STRING ix)) (setq opsw (compute-chk-op chr "()-=#~*/!%+&|^<>")))) (setq ix (1+ ix))) ;inc ix (in while loop) (if (string= var "") chr ; not variable, return op or nil (progn (setq ix (1- ix)) ; dec ix (once !) var)))) ; not op, return variable (defun compute-nxt-arg-is-op (op) "If next arg is the character OP returns t (and skip op), nil (next arg not read) otherwise" ;; (ek-debug-msg "===== compute-nxt-arg-is-op =====") (let ((iix ix) ; var name or number (chr 0) ; chr in STRING (eqf nil)) ; current char (while (< iix lng) (setq chr (aref STRING ix)) (setq iix (1+ iix)) (cond ((= chr op) (setq eqf t) ; = found (setq ix iix) ; set new index (setq iix lng)) ; end loop ((or (= chr ? ) (= chr ?\n)) ) ; space or NL: noop (t (setq iix lng)))) ; end loop eqf )) ; return equal flag (t or nil) (defun compute-str-to-signd-dec (STR) "convert STRING to (signed) decimal number (integer/float), if not numeric return nil, STRING may optionally begin with - or +." (cond ((= ?- (aref STR 0)) (- 0 (compute-str-to-val (substring STR 1)))) ((= ?+ (aref STR 0)) (compute-str-to-val (substring STR 1))) (t (compute-str-to-val STR)))) (defun compute-str-to-val (STR) "convert string to (positive) number (integer/float), if not numeric return nil string that begins with [ is octal string that begins with ] is hexadecimal" ;; (ek-debug-msg "===== compute-str-to-val =====") (let ((STR (downcase STR)) (len (length STR))) (cond ((= (aref STR 0) ?[) ; [ or \ is octal (compute-str-to-val-lp 1 8)) ((= (aref STR 0) ?]) ; ] or \x is hexa (compute-str-to-val-lp 1 16)) ((= (aref STR 0) ?\\) (if (= (aref STR 1) ?x) (compute-str-to-val-lp 2 16) (compute-str-to-val-lp 1 8))) (t (compute-str-to-val-lp 0 10))))) (defun compute-str-to-val-lp (ix base) "convert string to (positive) number (integer/float), if not numeric return nil ix is string index (0 1 or 2), base is 8, 10, or 16" ;; (ek-debug-msg "===== compute-str-to-int-lp =====") (let ((num 0) (fac 0) (digits (substring ".0123456789abcdef" 0 (1+ base))) (dig nil)) (while (< ix len) (setq dig (compute-chk-op (aref STR ix) digits)) (if dig (progn (setq dig (1- dig)) (setq fac (* fac base)) (if (= dig -1) (if (> fac 0) (setq len 0) (setq fac 1.0)) (setq num (+ (* num base) (float dig))))) (setq len 0)) (setq ix (1+ ix))) (if (= len 0) nil (if (> fac 0) (/ num fac) num)))) (defun operate-on-numbers (AOP ANAME NUM) "Internal function. Operate repeatedly on numbers in the buffer. AOP is an arithmetic operation, it may be +, -, *, /. ANAME is the arithmetic operation name (displayed in the echo area). NUM is the 1st operand. i.e. the operation is: NUM (AOP) ." (let (beg end val (uop 0)) (while (and NUM (re-search-forward "[+-]?[0-9]*[\\.]?[0-9]+" nil 1)) (setq beg (match-beginning 0)) (setq end (match-end 0)) (setq val (buffer-substring beg end)) (if (/= uop ?a) (progn (message "Number is \"%s\", type S(kip), Q(uit), A(ll) or other to %s :" val ANAME) (setq uop (downcase (read-char))))) (cond ((= uop ?q) (setq NUM nil)) ((= uop ?s) ) (t (setq val (compute-str-to-signd-dec val)) (cond ((= AOP ?+) (setq val (+ NUM (float val)))) ((= AOP ?-) (setq val (- NUM (float val)))) ((= AOP ?*) (setq val (* NUM (float val)))) ((= AOP ?/) (setq val (/ NUM (float val)))) ) (delete-region beg end) (goto-char beg) (if (= (ftruncate val) val) (setq val (truncate val))) (insert (if (floatp val) (let* ((str (format "%f" val)) (ix (1- (length str)))) (while (= (aref str ix) ?0) (setq ix (1- ix))) (substring str 0 (1+ ix))) (format "%d" val)))))))) (defun increment-numbers (INC) "replace next number by increasing it by INC (which may be negative)" (interactive "Nincrease by ") (operate-on-numbers ?+ "Increase" INC)) (defun divide-numbers (DIV) "replace next number by dividing it into DIV (which may be fractional)" (interactive "Ndividend is ") (operate-on-numbers ?/ "Divide by" DIV)) (defun mult-numbers (MULT) "replace next number by multiplying it by MULT (which may be fractional)" (interactive "Nmultiply by ") (operate-on-numbers ?* "Multiply" MULT)) (defun subtract-numbers (SUB) "replace next number by subtracting it from SUB (which may be negative)" (interactive "Nsubtract from ") (operate-on-numbers ?- "Subtract" SUB)) (defun sum-numbers-region (beg end &optional NOMSG) "Sum all decimal numbers in region from BEG to END. Optional NOMSG - just return result, no user message." (interactive) (sum-numbers (buffer-substring beg end) NOMSG)) (defun sum-numbers (STRING &optional NOMSG) "Sum all decimal numbers in input STRING. Optional NOMSG, if non nil - return computed value without message. The sum is always put in `sum-total'" (interactive) ;; (ek-debug-msg "===== sum-numbers start =====") (let ((sum 0) ; sum of all numbers (num 0) ; current number (strt 0) ; start search in STRING (ix 0)) ; index in STRING (while (setq ix (string-match "[-]?[0-9]*[\\.]?[0-9]+" STRING strt)) (and (= ?- (aref STRING ix)) (setq ix (1+ ix))) (setq num (compute-str-to-val (substring STRING ix (match-end 0)))) (if (> ix (match-beginning 0)) (setq num (- num))) (setq sum (+ sum num)) (setq strt (match-end 0))) (if (and (floatp sum) (< (logb sum) 25) (= sum (ftruncate sum))) (setq sum (truncate sum))) (setq sum-total sum) (if NOMSG sum ;return sum only if NOMSG requested (if (floatp sum) (message "The sum of all numbers is: %f" sum) (message "The sum of all numbers is: %g" sum))))) (defun sum-total-text (&optional INITV) "Total decimal numbers in text cursor is in (from previous space to next space). Optional INITV (number), if non nil - set total to it before adding number, otherwise use `sum-total' as initial value. Result is saved in `sum-total'" (interactive "P") ;; (ek-debug-msg "===== sum-total-text start =====") (and INITV (setq sum-total INITV)) (let (pt (pnt (point)) (sum sum-total) ) (forward-char 1) (re-search-backward "[^0-9]" nil 1) (setq pt (point)) (goto-char (1+ pnt)) (re-search-forward "[^0-9]" nil 1) (setq pt (buffer-substring pt (point))) (goto-char pnt) (setq sum-total (+ sum (sum-numbers pt t)))) (message "Total is %d." sum-total)) (defun insert-sum-total () "Insert `sum-total' value" (interactive) (insert (format (if (floatp sum-total) "%f" "%g") sum-total))) (define-key global-map '[?\C-#] 'insert-sum-total) ;;============================ end of compute (EK) ============================