;;; stat.el --- Small Set of Statistical Functions -*- coding: euc-jp -*-
;;; (C)2005,2014 by HIROSE, Yuuji [yuuji>at<gentei.org]
;;; $Id: stat.el,v 1.6 2019/08/06 00:39:13 yuuji Exp yuuji $
;;; Last Modified Mon Nov  4 10:58:06 2019 on firestorm
;;; Update Count: 34

;; Usage in ~/.emacs
;; ~~~~~~~~~~~~~~~~~
;; (autoload 'stat "stat" "Statistical Functions" nil)
;; (autoload 'stat-auto-sum "stat" "Add all numbers in preceding lines" t)
;; (global-set-key "\C-c+" 'stat-auto-sum)
;;
;; Usage in *scratch* buffer
;; ~~~~~~~~~~~~~~~~~~~~~~~~~
;; Call 'stat function with arbitrary length numbers and type C-j.
;; Ex.
;; (stat 1 2 3 4 5.9) [C-j]
;;
;; Usage of stat-auto-sum
;; ~~~~~~~~~~~~~~~~~~~~~~
;; The function stat-auto-sum insert a summation of all numbers in the
;; end of preceding lines.  If no numbers found in a contiguous line,
;; calculation is ceased at that line.
;; You can call 'stat-auto-sum repeatedly without erasing old summation
;; because this function removes old summation around the point
;; automatically.  In the case below, move to 12.99 on the `Sum:' line
;; and call M-x stat-auto-sum to get real summation.
;; 
;; 2014-06-14  HDD Case			| $12.99
;; 2014-06-14  HDD 3.5inch		| $50.00
;; 2014-06-15  USB Stick 32GB		| $20.00
;; Sum: 12.99
;;      ^^^^here!
;; 
;; 行末に数値、という連続した行の次の行で 'stat-auto-sum を呼ぶと、ポイ
;; ント位置にそれらの数値の合計が自動挿入される。現在のポイント位置に既
;; に数値がある場合はそれを消してから合計を計算する。たとえば、上の例の
;; Sum: の後ろの12.99にカーソルをおいて、M-x stat-auto-sum を呼ぶと、そ
;; れより上の行の連続した行末数値をすべて足した値に置き換えてくれる。

(defun stat1 (p)
  (cond
   ((and (listp p) (atom (car p)))
    (let* ((n (length p))
	   (a (/ (apply '+ p) (float n)))
	   (s2 (apply '+ (mapcar (function(lambda (i) (expt (- a i) 2))) p))))
      (list (cons "n" n)
	    (cons "min" (apply 'min p))
	    (cons "max" (apply 'max p))
	    (cons "avg" a)
	    (cons "stdd" (sqrt (/ s2 (1- n)))))))))

(defun stat (&rest p)
  (mapc (function
	 (lambda (i)
	   (insert (format "%s\t%s\n" (car i) (prin1-to-string (cdr i))))))
	(stat1 p)))

(defun stat-commaize (n)
  (let ((d (int-to-string (floor n)))
	(s (number-to-string n))
	(r ""))
    (while (string-match "[0-9][0-9][0-9][0-9]$" d)
      (setq r (concat "," (substring d -3) r)
	    d (substring d 0 -3)))
    (concat d r (if (string-match "\\." s) (substring s (match-beginning 0))))))

(defun stat-ignore-comma-number (str)
  "Return the numeric value of STR ignorring comma"
  (save-match-data
    (let ((r ""))
      (while (and str (string-match "^\\([-.0-9]+\\),?\\(.*\\)" str))
	(setq r (concat r (substring str (match-beginning 1) (match-end 1)))
	      str (substring str (match-beginning 2) (match-end 2))))
      (string-to-number r))))
;; (stat-ignore-comma-number "1,234,567.45")

(defun stat-auto-sum ()
  "Insert the summation of all numbers at the end of preceding lines."
  (interactive)
  (let ((sum 0) lim nstr comma)
    (save-excursion
      (skip-chars-backward "-0-9.,")
      (if (looking-at "[-0-9.,]+")
	  (delete-region (point) (match-end 0)))
      (while (and (not (bobp))
		  (progn
		    (forward-line -1)
		    (setq lim (point))
		    (end-of-line)
		    (re-search-backward "[-0-9,.]+" lim t)
		    (skip-chars-backward "-0-9,.")
		    (setq comma (or comma (looking-at "[-0-9]+,[0-9]")))
		    (looking-at "[-0-9,.]+")))
	(setq sum (+ sum
		     (stat-ignore-comma-number
		      (buffer-substring (match-beginning 0) (match-end 0)))))
	))
    (insert (if comma (stat-commaize sum) (number-to-string sum)))))

(defun stat-get-recent-regexp-value (regexp)
  "Get the numeric value at the end of line where REGEXP found recently."
  (save-excursion
    (save-match-data
      (forward-line -1)
      (if (re-search-backward regexp nil)
	  (progn
	    (end-of-line)
	    (skip-chars-backward "-0-9,.")
	    (looking-at "[-.,0-9]+")
	    (string-to-number
	     (buffer-substring-no-properties
	      (match-beginning 0) (match-end 0))))
	0))))

; Local variables: 
; fill-prefix: ";; " 
; paragraph-start: "^$\\|\\|;;$" 
; paragraph-separate: "^$\\|\\|;;$" 
; End: