/**
* Copyright (c) 2008 Jeremy Hinegardner
* All rights reserved. See LICENSE and/or COPYING for details.
*
* vim: shiftwidth=4
*/
#include "hitimes_stats.h"
/* classes defined here */
VALUE cH_Stats; /* Hitimes::Stats */
/**
* Allocator and Deallocator for Stats classes
*/
VALUE hitimes_stats_free(hitimes_stats_t* s)
{
xfree( s );
return Qnil;
}
VALUE hitimes_stats_alloc(VALUE klass)
{
VALUE obj;
hitimes_stats_t* s = xmalloc( sizeof( hitimes_stats_t ) );
s->min = 0.0;
s->max = 0.0;
s->count = 0;
s->sum = 0.0;
s->sumsq = 0.0;
obj = Data_Wrap_Struct(klass, NULL, hitimes_stats_free, s);
return obj;
}
/**
* call-seq:
* stat.update( val ) -> val
*
* Update the running stats with the new value.
* Return the input value.
*/
VALUE hitimes_stats_update( VALUE self, VALUE v )
{
long double new_v;
hitimes_stats_t *stats;
Data_Get_Struct( self, hitimes_stats_t, stats );
new_v = NUM2DBL( v );
if ( 0 == stats->count ) {
stats->min = new_v;
stats->max = new_v;
} else {
stats->min = ( new_v < stats->min) ? ( new_v ) : ( stats->min );
stats->max = ( new_v > stats->max) ? ( new_v ) : ( stats->max );
}
stats->count += 1;
stats->sum += new_v;
stats->sumsq += ( new_v * new_v );
return v;
}
/**
* call-seq:
* stat.mean -> Float
*
* Return the arithmetic mean of the values put into the Stats object. If no
* values have passed through the stats object then 0.0 is returned;
*/
VALUE hitimes_stats_mean( VALUE self )
{
hitimes_stats_t *stats;
long double mean = 0.0;
Data_Get_Struct( self, hitimes_stats_t, stats );
if ( stats->count > 0 ) {
mean = stats->sum / stats->count ;
}
return rb_float_new( mean );
}
/**
* call-seq:
* stat.rate -> Float
*
* Return the +count+ divided by +sum+.
*
* In many cases when Stats#update( _value_ ) is called, the _value_ is a unit
* of time, typically seconds or microseconds. #rate is a convenience for those
* times. In this case, where _value_ is a unit if time, then count divided by
* sum is a useful value, i.e. +something per unit of time+.
*
* In the case where _value_ is a non-time related value, then the value
* returned by _rate_ is not really useful.
*
*/
VALUE hitimes_stats_rate( VALUE self )
{
hitimes_stats_t *stats;
long double rate = 0.0;
Data_Get_Struct( self, hitimes_stats_t, stats );
if ( stats->sum > 0.0 ) {
rate = stats->count / stats->sum;
}
return rb_float_new( rate );
}
/**
* call-seq:
* stat.max -> Float
*
* Return the maximum value that has passed through the Stats object
*/
VALUE hitimes_stats_max( VALUE self )
{
hitimes_stats_t *stats;
Data_Get_Struct( self, hitimes_stats_t, stats );
return rb_float_new( stats->max );
}
/**
* call-seq:
* stat.min -> Float
*
* Return the minimum value that has passed through the Stats object
*/
VALUE hitimes_stats_min( VALUE self )
{
hitimes_stats_t *stats;
Data_Get_Struct( self, hitimes_stats_t, stats );
return rb_float_new( stats->min );
}
/**
* call-seq:
* stat.count -> Integer
*
* Return the number of values that have passed through the Stats object.
*/
VALUE hitimes_stats_count( VALUE self )
{
hitimes_stats_t *stats;
Data_Get_Struct( self, hitimes_stats_t, stats );
return LONG2NUM( stats->count );
}
/**
* call-seq:
* stat.sum -> Float
*
* Return the sum of all the values that have passed through the Stats object.
*/
VALUE hitimes_stats_sum( VALUE self )
{
hitimes_stats_t *stats;
Data_Get_Struct( self, hitimes_stats_t, stats );
return rb_float_new( stats->sum );
}
/**
* call-seq:
* stat.sumsq -> Float
*
* Return the sum of the squars of all the values that passed through the Stats
* object.
*/
VALUE hitimes_stats_sumsq( VALUE self )
{
hitimes_stats_t *stats;
Data_Get_Struct( self, hitimes_stats_t, stats );
return rb_float_new( stats->sumsq );
}
/**
* call-seq:
* stat.stddev -> Float
*
* Return the standard deviation of all the values that have passed through the
* Stats object. The standard deviation has no meaning unless the count is > 1,
* therefore if the current _stat.count_ is < 1 then 0.0 will be returned;
*/
VALUE hitimes_stats_stddev ( VALUE self )
{
hitimes_stats_t *stats;
long double stddev = 0.0;
Data_Get_Struct( self, hitimes_stats_t, stats );
if ( stats->count > 1 ) {
stddev = sqrt( ( stats->sumsq - ( stats->sum * stats->sum / stats->count ) ) / ( stats->count - 1 ) );
}
return rb_float_new( stddev );
}
/**
* Document-class: Hitimes::Stats
*
* The Stats class encapulsates capturing and reporting statistics. It is
* modeled after the RFuzz::Sampler class, but implemented in C. For general use
* you allocate a new Stats object, and then update it with new values. The
* Stats object will keep track of the _min_, _max_, _count_, _sum_ and _sumsq_
* and when you want you may also retrieve the _mean_, _stddev_ and _rate_.
*
* this contrived example shows getting a list of all the files in a directory
* and running stats on file sizes.
*
* s = Hitimes::Stats.new
* dir = ARGV.shift || Dir.pwd
* Dir.entries( dir ).each do |entry|
* fs = File.stat( entry )
* if fs.file? then
* s.update( fs.size )
* end
* end
*
* %w[ count min max mean sum stddev rate ].each do |m|
* puts "#{m.rjust(6)} : #{s.send( m ) }"
* end
*/
void Init_hitimes_stats()
{
mH = rb_define_module("Hitimes");
cH_Stats = rb_define_class_under( mH, "Stats", rb_cObject ); /* in hitimes_stats.c */
rb_define_alloc_func( cH_Stats, hitimes_stats_alloc );
rb_define_method( cH_Stats, "update", hitimes_stats_update, 1 ); /* in hitimes_stats.c */
rb_define_method( cH_Stats, "count", hitimes_stats_count, 0 ); /* in hitimes_stats.c */
rb_define_method( cH_Stats, "max", hitimes_stats_max, 0 ); /* in hitimes_stats.c */
rb_define_method( cH_Stats, "mean", hitimes_stats_mean, 0 ); /* in hitimes_stats.c */
rb_define_method( cH_Stats, "min", hitimes_stats_min, 0 ); /* in hitimes_stats.c */
rb_define_method( cH_Stats, "rate", hitimes_stats_rate, 0 ); /* in hitimes_stats.c */
rb_define_method( cH_Stats, "sum", hitimes_stats_sum, 0 ); /* in hitimes_stats.c */
rb_define_method( cH_Stats, "sumsq", hitimes_stats_sumsq, 0 ); /* in hitimes_stats.c */
rb_define_method( cH_Stats, "stddev", hitimes_stats_stddev, 0 ); /* in hitimes_stats.c */
}