#!/usr/bin/env ruby

# Copyright (c) 2015-2017 Aleksey Cheusov <vle@gmx.net>
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

# This is an internal herisvm script.  It takes output "heri-stat -R"
# on input and outputs maximum deviations

require 'optparse'

@options = {}

OptionParser.new do |opts|
  opts.banner = <<EOF
heri-stat-addons calculates mean and daviations for statistics
   calculated by heri-stat
Usage:
    heri-stat-addons [OPTIONS] [files...]
OPTIONS:
EOF

  opts.on('-h', '--help','display this message and exit') do
    puts opts
    exit 0
  end

  @options[:raw] = false
  opts.on('-R', '--raw','raw tab-separated output') do
    @options[:raw] = true
  end

  opts.separator " "
end.parse!

lines = []
while line = gets do
  lines << line.split("\t")
end

module Enumerable
  def sum
    return self.inject(0){|accum, i| accum + i }
  end

  def mean
    return self.sum / self.length.to_f
  end

  def sample_variance
    m = self.mean
    sum = self.inject(0){|accum, i| accum + (i - m) ** 2 }
    return sum / (self.length - 1).to_f
  end

  def standard_deviation
    return Math.sqrt(self.sample_variance)
  end

end

def hash2hash2array
  return Hash.new do
    |h,k| h [k] = Hash.new do
      |h,k| h [k] = []
    end
  end
end

def hash2hash
  return Hash.new do
    |h,k| h [k] = {}
  end
end

def print_value_raw(t, f, value)
  puts "%s\t%s\t%s" % [t, f, value]
end

def print_value(t, f, metrics, mean, stddev, maxdev)
  puts "%-13s %8s> %-26s: %s %s %s" % [t, metrics, f, mean, stddev, maxdev]
end

values = hash2hash2array

lines.each do |tokens|
  values [tokens[1]][tokens[0]] << tokens[2].to_f
end

mi = hash2hash
ma = hash2hash
mean = hash2hash
max_deviation = hash2hash
std_deviation = hash2hash

values.each do |key1, hash|
  hash.each do |key2, arr|
    mi [key1] [key2] = arr.min
    ma [key1] [key2] = arr.max
    mean [key1] [key2] = arr.mean
    max_deviation [key1] [key2] = [arr.max - arr.mean, arr.mean - arr.min].max
    std_deviation [key1] [key2] = arr.standard_deviation
  end
end

#puts std_deviation.inspect
#exit 0

TYPES  = {"" => 1, "Macro average" => 1}
mi.each do |f, h|
  pairs = []
  max_deviation[f].each do |t, max_dev|
    pairs << [f, t] if max_dev && TYPES.include?(t)
  end
  max_deviation[f].each do |t, max_dev|
    pairs << [f, t] if max_dev && ! TYPES.include?(t)
  end

  pairs.each do |ft|
    max_dev = max_deviation [ft[0]][ft[1]] * 100
    std_dev = std_deviation [ft[0]][ft[1]] * 100
    mean_value = mean [ft[0]][ft[1]] * 100
    if @options[:raw]
      max_dev = "%#.3g" % [max_dev]
      std_dev = "%#.3g" % [std_dev]
      mean_value = "%#.3g" % [mean_value]

      print_value_raw(ft[1], "#{f}: mean", mean_value)
      print_value_raw(ft[1], "#{f}: maxdev", max_dev)
      print_value_raw(ft[1], "#{f}: stddev", std_dev)
    else
      max_dev = "%#6.3g" % [max_dev]
      std_dev = "%#6.3g" % [std_dev]
      mean_value = "%-#.3g" % [mean_value]

      print_value(ft[1], "mean, maxdev, stddev", f, mean_value, max_dev, std_dev)
    end
  end
  puts ''
end
