All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.endoscope.core.Stat Maven / Gradle / Ivy

There is a newer version: 1.0.9
Show newest version
package com.github.endoscope.core;

import java.beans.Transient;
import java.util.HashMap;
import java.util.Map;

@com.fasterxml.jackson.annotation.JsonPropertyOrder({ "hits", "max", "min", "avg", "ah10", "children" })
public class Stat {
    public static final Stat EMPTY_STAT;

    static {
        EMPTY_STAT = new Stat();
        EMPTY_STAT.setMax(0);
    }

    private long hits = 0;
    private long max = -1;//-1 means it's not set
    private long min = 0;
    private double avg = 0;

    /*
        Average hits per parent. For example:
        1) parentMethod calls childMethod 2 times
        2) parentMethod calls childMethod 4 times

        In such case we should get:
            parentCount==2
            avgParent==3.0

        Notice that we increment parentCount by one - thats the difference from hits
        and allows us to calulate average number of hits
     */
    private long parentCount = 0;
    double avgParent = 0;

    private Map children;

    public Stat(){}

    public long getHits() {
        return hits;
    }

    public void setHits(long hits) {
        this.hits = hits;
    }

    public long getMax() {
        return max;
    }

    public void setMax(long max) {
        this.max = max;
    }

    public long getMin() {
        return min;
    }

    public void setMin(long min) {
        this.min = min;
    }

    public long getAvg() {
        return Math.round(avg);
    }

    public void setAvg(long avg) {
        this.avg = avg;
    }

    /**
     * Average hits per parent x10 (1 digit of precision)
     * Short name for JSON - no @JsonProperty in this module
     * @return
     */
    public long getAh10() {
        return Math.round(avgParent*10.0f);
    }

    public void setAh10(long ah10) {
        avgParent = ((float)ah10)/10f;
    }

    public Map getChildren() {
        return children;
    }

    public void setChildren(Map children) {
        this.children = children;
    }

    @Transient
    public long getParentCount() {
        return parentCount;
    }

    @Transient
    public void setParentCount(long parentCount) {
        this.parentCount = parentCount;
    }

    @Transient
    public double getAvgParent() {
        return avgParent;
    }

    @Transient
    public void setAvgParent(double avgParent) {
        this.avgParent = avgParent;
    }

    public void ensureChildrenMap(){
        if(children == null){
            children = new HashMap<>();
        }
    }

    @Transient
    public Stat getChild(String id){
        ensureChildrenMap();
        return children.get(id);
    }

    @Transient
    public Stat createChild(String id){
        ensureChildrenMap();
        Stat child = new Stat();
        children.put(id, child);
        return child;
    }

    public void update(long time){
        if( time < 0 ) return;
        if( max < 0 ){
            avg = max = min = time;
        } else {
            max = Math.max(max, time);
            min = Math.min(min, time);
            avg = (avg* hits + time)/(hits +1);
        }
        hits++;
    }

    public void updateAvgHits(long hitsPerParent) {
        avgParent = (avgParent * parentCount + hitsPerParent)/(parentCount+1);
        parentCount++;
    }

    @Transient
    public void merge(Stat inc){
        merge(inc, true);
    }

    @Transient
    public void merge(Stat inc, boolean withChildren){
        max = Math.max(max, inc.max);
        min = Math.min(min, inc.min);
        if( hits + inc.hits > 0 ){
            avg = (avg*hits + inc.avg*inc.hits)/(hits + inc.hits);
            hits += inc.hits;
        }

        if( parentCount + inc.parentCount > 0 ){
            avgParent = (avgParent * parentCount + inc.avgParent * inc.parentCount)/(parentCount + inc.parentCount);
            parentCount += inc.parentCount;
        }

        if( withChildren ){
            mergeChildren(inc);
        } else {
            //we want to mark that there are children
            if( inc.getChildren() != null ){
                ensureChildrenMap();
            }
        }
    }

    @Transient
    private void mergeChildren(Stat s2){
        if( s2.getChildren() == null ){
            return;
        }
        ensureChildrenMap();

        s2.children.forEach((k2, v2) -> {
            Stat v1 = children.get(k2);
            if( v1 == null ){
                children.put(k2, v2);
            } else {
                v1.merge(v2);
            }
        });
    }

    @Transient
    public Stat deepCopy(){
        return deepCopy(true);
    }

    @Transient
    public Stat deepCopy(boolean withChildren){
        Stat s = new Stat();
        s.merge(this, withChildren);
        s.setMin(min);
        return s;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Stat)) return false;

        Stat stat = (Stat) o;

        if (hits != stat.hits) return false;
        if (max != stat.max) return false;
        if (min != stat.min) return false;
        if ( compareDoubleLowPrecision(stat.avg, avg) != 0) return false;
        if (parentCount != stat.parentCount) return false;
        if ( compareDoubleLowPrecision(stat.avgParent, avgParent) != 0) return false;
        return children != null ? children.equals(stat.children) : stat.children == null;
    }

    public static int compareDoubleLowPrecision(double d1, double d2){
        long l1 = Math.round(d1 * 1000);
        long l2 = Math.round(d2 * 1000);
        return Long.compare(l1, l2);
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = (int) (hits ^ (hits >>> 32));
        result = 31 * result + (int) (max ^ (max >>> 32));
        result = 31 * result + (int) (min ^ (min >>> 32));
        temp = Double.doubleToLongBits(avg);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        result = 31 * result + (int) (parentCount ^ (parentCount >>> 32));
        temp = Double.doubleToLongBits(avgParent);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        result = 31 * result + (children != null ? children.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        return "Stat{" +
                "hits=" + hits +
                ", max=" + max +
                ", min=" + min +
                ", avg=" + avg +
                ", parentCount=" + parentCount +
                ", avgParent=" + avgParent +
                ", children=" + children +
                '}';
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy