This repository has been archived on 2024-04-10. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
elektron/utilities/runAvg/runAvg.go

127 lines
4 KiB
Go
Raw Normal View History

2018-10-06 20:03:14 -07:00
// Copyright (C) 2018 spdf
//
// This file is part of Elektron.
//
// Elektron is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Elektron is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Elektron. If not, see <http://www.gnu.org/licenses/>.
//
2016-12-19 16:30:03 -05:00
/*
A utility to calculate the running average.
2017-01-28 19:43:12 -05:00
One should implement Val() and ID() to use this utility.
2016-12-19 16:30:03 -05:00
*/
package runAvg
import (
"container/list"
2017-01-03 20:57:25 -05:00
"errors"
)
2016-12-19 16:30:03 -05:00
type Interface interface {
// Value to use for running average calculation.
Val() float64
// Unique ID.
ID() string
2016-12-19 16:30:03 -05:00
}
type runningAverageCalculator struct {
2017-01-28 18:29:00 -05:00
considerationWindow list.List
considerationWindowSize int
currentSum float64
2016-12-19 16:30:03 -05:00
}
// Singleton instance.
2016-12-19 16:30:03 -05:00
var racSingleton *runningAverageCalculator
// Return single instance.
func getInstance(curSum float64, wSize int) *runningAverageCalculator {
2016-12-19 16:30:03 -05:00
if racSingleton == nil {
2017-01-03 20:57:25 -05:00
racSingleton = &runningAverageCalculator{
2017-01-28 18:29:00 -05:00
considerationWindowSize: wSize,
2017-01-28 19:45:47 -05:00
currentSum: curSum,
2016-12-19 16:30:03 -05:00
}
return racSingleton
} else {
// Updating window size if a new window size is given.
2017-01-28 18:29:00 -05:00
if wSize != racSingleton.considerationWindowSize {
racSingleton.considerationWindowSize = wSize
2016-12-19 16:30:03 -05:00
}
return racSingleton
}
}
// Compute the running average by adding 'data' to the window.
// Updating currentSum to get constant time complexity for every running average computation.
func (rac *runningAverageCalculator) calculate(data Interface) float64 {
2017-01-28 18:29:00 -05:00
if rac.considerationWindow.Len() < rac.considerationWindowSize {
rac.considerationWindow.PushBack(data)
2016-12-19 16:30:03 -05:00
rac.currentSum += data.Val()
} else {
// Removing the element at the front of the window.
2017-01-28 18:29:00 -05:00
elementToRemove := rac.considerationWindow.Front()
2016-12-19 16:30:03 -05:00
rac.currentSum -= elementToRemove.Value.(Interface).Val()
2017-01-28 18:29:00 -05:00
rac.considerationWindow.Remove(elementToRemove)
2017-01-03 20:57:25 -05:00
// Adding new element to the window.
2017-01-28 18:29:00 -05:00
rac.considerationWindow.PushBack(data)
2016-12-19 16:30:03 -05:00
rac.currentSum += data.Val()
}
2017-01-28 18:29:00 -05:00
return rac.currentSum / float64(rac.considerationWindow.Len())
2016-12-19 16:30:03 -05:00
}
/*
If element with given ID present in the window, then remove it and return (removeElement, nil).
Else, return (nil, error).
*/
func (rac *runningAverageCalculator) removeFromWindow(id string) (interface{}, error) {
2017-01-28 18:29:00 -05:00
for element := rac.considerationWindow.Front(); element != nil; element = element.Next() {
if elementToRemove := element.Value.(Interface); elementToRemove.ID() == id {
2017-01-28 18:29:00 -05:00
rac.considerationWindow.Remove(element)
rac.currentSum -= elementToRemove.Val()
return elementToRemove, nil
}
}
return nil, errors.New("Error: Element not found in the window.")
}
2016-12-19 16:30:03 -05:00
// Taking windowSize as a parameter to allow for sliding window implementation.
func Calc(data Interface, windowSize int) float64 {
rac := getInstance(0.0, windowSize)
2016-12-19 16:30:03 -05:00
return rac.calculate(data)
}
// Remove element from the window if it is present.
func Remove(id string) (interface{}, error) {
// Checking if racSingleton has been instantiated.
if racSingleton == nil {
return nil, errors.New("Error: Not instantiated. Please call Init() to instantiate.")
} else {
return racSingleton.removeFromWindow(id)
}
}
// Initialize the parameters of the running average calculator.
2016-12-19 16:30:03 -05:00
func Init() {
// Checking to see if racSingleton needs top be instantiated.
if racSingleton == nil {
racSingleton = getInstance(0.0, 0)
}
2016-12-19 16:30:03 -05:00
// Setting parameters to default values. Could also set racSingleton to nil but this leads to unnecessary overhead of creating
// another instance when Calc is called.
2017-01-28 18:29:00 -05:00
racSingleton.considerationWindow.Init()
racSingleton.considerationWindowSize = 0
2016-12-19 16:30:03 -05:00
racSingleton.currentSum = 0.0
2017-01-03 20:57:25 -05:00
}