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
2016-12-19 20:43:25 -05:00
import (
"container/list"
2017-01-03 20:57:25 -05:00
"errors"
2016-12-19 20:43:25 -05:00
)
2016-12-19 16:30:03 -05:00
type Interface interface {
// Value to use for running average calculation.
Val ( ) float64
2017-09-28 15:36:47 -04:00
// Unique ID.
2016-12-19 20:43:25 -05:00
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
}
2017-09-28 15:36:47 -04:00
// Singleton instance.
2016-12-19 16:30:03 -05:00
var racSingleton * runningAverageCalculator
2017-09-28 15:36:47 -04:00
// Return single instance.
2016-12-20 15:03:43 -05:00
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 {
2017-09-28 15:36:47 -04:00
// 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
2017-09-28 15:36:47 -04: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
}
2016-12-19 20:43:25 -05:00
/ *
If element with given ID present in the window , then remove it and return ( removeElement , nil ) .
2017-09-28 15:36:47 -04:00
Else , return ( nil , error ) .
2016-12-19 20:43:25 -05:00
* /
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 ( ) {
2016-12-19 20:43:25 -05:00
if elementToRemove := element . Value . ( Interface ) ; elementToRemove . ID ( ) == id {
2017-01-28 18:29:00 -05:00
rac . considerationWindow . Remove ( element )
2016-12-20 13:53:28 -05:00
rac . currentSum -= elementToRemove . Val ( )
2016-12-19 20:43:25 -05:00
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 {
2016-12-20 15:03:43 -05:00
rac := getInstance ( 0.0 , windowSize )
2016-12-19 16:30:03 -05:00
return rac . calculate ( data )
}
2016-12-19 20:43:25 -05:00
// Remove element from the window if it is present.
func Remove ( id string ) ( interface { } , error ) {
2017-09-28 15:36:47 -04:00
// Checking if racSingleton has been instantiated.
2016-12-19 20:43:25 -05:00
if racSingleton == nil {
return nil , errors . New ( "Error: Not instantiated. Please call Init() to instantiate." )
} else {
return racSingleton . removeFromWindow ( id )
}
}
2017-09-28 15:36:47 -04:00
// Initialize the parameters of the running average calculator.
2016-12-19 16:30:03 -05:00
func Init ( ) {
2017-09-28 15:36:47 -04:00
// Checking to see if racSingleton needs top be instantiated.
2016-12-19 20:43:25 -05:00
if racSingleton == nil {
2016-12-20 15:03:43 -05:00
racSingleton = getInstance ( 0.0 , 0 )
2016-12-19 20:43:25 -05:00
}
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
}