/*
 * cpu_time() は、プロセスの CPU 消費時間を秒単位で返します。
 *
 * プログラム開始直後に、ダミーの呼び出しが必要です。
 * 関数の型は double なので、小数付きの値が返ります。
 * エラーがあれば、0.0 を返しますが、そんなことはまず起こらないでしょう。
 * gcc/egcs (Sun,Linux,FreeBSD), Turbo C/LSI-C (MS-DOS) で作動確認済み。
 * 最新版は以下で公開します。
 * https://tutimura.ath.cx/~nob/c/
 * 2023/ 7/28 (C) tutimura(a)nn.iij4u.or.jp (^^) ご自由にお使い下さい。
 */

/* 分割コンパイルしない時の使い方
 *
 *  #include "cpu_time.c"
 *  ...
 *  main() {
 *      cpu_time(); <== これはダミーの呼出し
 *      ...
 *      printf( "time:%.2f(sec)\n", cpu_time() );
 *  }
 */

/* 分割コンパイルする時の使い方
 *
 * 共通ヘッダに以下を記述する。
 *  double cpu_time( void );
 */


#ifdef TEST
#include <stdio.h>
#define TEST_MESSAGE(s) printf("by %s\n",s)
#else
#define TEST_MESSAGE(s)
#endif /* TEST */

#if defined(__STDC__) || defined(__STDC_VERSION__)
double cpu_time( void );
#endif /* __STDC__ */


#if !(defined(unix) || defined(__unix))

	/* ↓MS-DOS, Windows ↓ */
#include <time.h>
#if !defined(CLOCKS_PER_SEC) && defined(CLK_TCK)
#define CLOCKS_PER_SEC CLK_TCK
#endif
double cpu_time( void ) {
    clock_t t;  static clock_t last = (clock_t)-1;
    TEST_MESSAGE("clock()");
    t = clock();
    if (last == (clock_t)-1) last = t;
    return (double)(t-last)/CLOCKS_PER_SEC;
}
	/* ↑MS-DOS, Windows↑ */

#else /* unix */

#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#ifdef RUSAGE_SELF

	/* ↓Sun 5.5, Linux, FreeBSD, DJGPP↓ */
double cpu_time() {
    struct rusage tmp;
    TEST_MESSAGE("getrusage()");
    if ( getrusage( RUSAGE_SELF, &tmp ) ) return 0.0;
    return (double)(tmp.ru_utime.tv_sec)
          +(double)(tmp.ru_utime.tv_usec)/1000000;
}
	/* ↑Sun 5.5, Linux, FreeBSD, DJGPP↑ */
#else /* RUSAGE_SELF */
	/* ↓Sun 5.3↓ */
#include <sys/types.h>
#include <sys/times.h>
double cpu_time() {
    struct tms tmp;
    TEST_MESSAGE("times()");
    if ( times( &tmp ) == -1 ) return 0.0;
    return (double)(tmp.tms_utime)/CLK_TCK;
}
	/* ↑Sun 5.3↑ */
#endif /* RUSAGE_SELF */

#endif /* unix */


#ifdef TEST
int main() {
    int i, j, k;

    cpu_time(); /* dummy */
    for ( i=0; i<25; i++ ) {
        for ( j=1; j<300000; j++ ) {
            k = j*j*j*j;
            k /= j;
            k /= j;
            k /= j;
        }
        j=k;
        printf( "time:%.4f(sec)\n", cpu_time() );
    }
    return 0;
}
#endif /* TEST */


/* 詳細説明
 *
 * このプログラムは、ANSI C 規格で
 *     (double) clock()/CLOCKS_PER_SEC
 * に相当する計算を行いますが、
 * 以下のような不具合を解消しています。
 *
 *  -- 古いANSI規格では CLOCKS_PER_SEC ではなく CLK_TCK が
 *     定義されていた。
 *  --（主にUnix系の）処理系によっては CLOCKS_PER_SEC が
 *     1000000 と定義されていて、 long(32bit)の clock() が
 *     30分ちょっとで桁溢れを起こす。
 *  -- CPU timeではなく、現在時刻を返す処理系がある
 *     （プログラム開始直後に0でない）。
 *     半面、初めての呼び出しでは（開始直後でなくても）
 *     必ず 0 を返す処理系もある。
 */

/* 動作確認環境
 *
 * SUN 系
 * gcc 2.5.8 (SunOS 4.1.3) -- getrusage() で警告あり
 * gcc 2.6.3 (SunOS 5.5/5.5.1) -- getrusage() で警告あり
 * egcs 1.1.2 (SunOS 5.5/5.5.1)
 *
 * Linux 系
 * gcc 2.7.2.3 (Linux 2.0/2.2, glibc2.0.7)
 * egcs 1.0.3 (Linux 2.0/2.2, glibc2.0.7)
 * gcc 4.8.5 (CentOS 7)
 *
 * FreeBSD 系
 * gcc 2.7.2.1 (FreeBSD 3.2-RELEASE)
 *
 * MS-DOS 系
 * DJGPP
 * Turbo C 2nd Ver.1.0
 * Turbo C++ Ver.2.0
 * Turbo C++ Ver.4.0J
 * LSI-C86 Ver.3.3c
 *
 * Windows 系
 * Visual C++ Ver 6.0, 2017, 2022
 * Borland C compiler 5.5
 * Cygwin gcc 11.3.0
 * Cygwin i686-w64-mingw32-gcc 11.3.0
 */

/* 更新履歴
 * 1997/ 2/20  my_time() として登場。
 * 2000/ 2/ 6  cpu_time() に改名。
 *             大幅なスタイル変更するも、処理内容はそのまま。
 * 2002/ 3/11  MS-DOS 部分で初回呼び出しを見分けるために
 *             last に 0 を代入していたが、よく考えると
 *             これでは初回と見分けがつかないことがあるので
 *             -1 を代入することに変更した。
 * 2002/11/29  Visual C++ に対応した。
 * 2006/ 1/10  Borland C++, WATCOM C/C++ 用のマクロを書き加えた。
 *             テストルーチンのループ回数を増やして、割算も追加した。
 * 2006/ 1/14  Borland C++ の動作報告をいただいた。
 * 2023/ 7/28  Cygwin のクロスコンパイル環境で、OSの判定に失敗していた。
 *             MS-DOS+Win を UNIX 以外に分類することにした。
 */
