OTS_TimeSeries: Add the AddDataPoint() method.
[otsdb-go.git] / ots_timeseries.go
index efa8931..fd22468 100644 (file)
@@ -16,6 +16,11 @@ type OTS_TimeSeries struct {
   DataPoints []OTS_DataPoint
 }
 
+func timestampToInterval (a float64, b float64) float64 {
+  tmp := int (a / b)
+  return b * float64 (tmp)
+}
+
 /* Functions for the sort interface. */
 func (obj *OTS_TimeSeries) Len () int {
   return (len (obj.DataPoints))
@@ -29,14 +34,7 @@ func (obj *OTS_TimeSeries) Less (i, j int) bool {
 }
 
 func (obj *OTS_TimeSeries) Swap (i, j int) {
-  tmp := obj.DataPoints[i]
-  obj.DataPoints[i] = obj.DataPoints[j]
-  obj.DataPoints[j] = tmp
-}
-
-func Fmod64 (a float64, b float64) float64 {
-  tmp := int (a / b)
-  return b * float64 (tmp)
+  obj.DataPoints[i], obj.DataPoints[j] = obj.DataPoints[j], obj.DataPoints[i]
 }
 
 func (obj *OTS_TimeSeries) Write (name string) os.Error {
@@ -56,6 +54,45 @@ func (obj *OTS_TimeSeries) Write (name string) os.Error {
   return nil
 }
 
+func (obj *OTS_TimeSeries) AddDataPoint (timestamp, rate float64) int {
+  if math.IsNaN (timestamp) || (timestamp < 0.0) {
+    return -1
+  }
+
+  /* Handle the usual case first. */
+  if (timestamp > obj.TimestampLast ()) || (obj.DataPoints == nil) {
+    obj.DataPoints = append (obj.DataPoints, OTS_DataPoint{timestamp, rate})
+    return 0
+  }
+
+  /* Find the first index where the timestamp is greater than or equal to the
+   * new timestamp. This is an O(log n) operation. */
+  index := sort.Search (len (obj.DataPoints), func (i int) bool {
+      if obj.DataPoints[i].TimeStamp >= timestamp {
+        return true
+      }
+      return false
+  })
+
+  /* Check for a duplicate time. */
+  if obj.DataPoints[index].TimeStamp == timestamp {
+    obj.DataPoints[index].Rate = rate
+    return 0
+  }
+
+  /* Insert the new datapoint at "index". Currently, this is a O(n) operation.
+   * First, add the new datapoint at the end. */
+  obj.DataPoints = append (obj.DataPoints, OTS_DataPoint{timestamp, rate})
+  /* Now move the datapoint to the position "index". */
+  for i := len (obj.DataPoints) - 2; i >= index; i-- {
+    /* TODO: Is there a faster way to manipulate arrays in Go than to move
+     * elements in bubblesort fashion? */
+    obj.Swap (i, i + 1)
+  }
+
+  return 0
+}
+
 func ReadFile (name string) (obj *OTS_TimeSeries, err os.Error) {
   fd, err := os.Open (name)
   if err != nil {
@@ -65,7 +102,7 @@ func ReadFile (name string) (obj *OTS_TimeSeries, err os.Error) {
   /* dp_list := make ([]OTS_DataPoint, intervals_num */
   obj = new (OTS_TimeSeries)
 
-  for ;; {
+  for {
     var timestamp float64
     var rate float64
 
@@ -78,7 +115,7 @@ func ReadFile (name string) (obj *OTS_TimeSeries, err os.Error) {
 
     fmt.Printf ("timestamp = %.3f; rate = %g;\n", timestamp, rate)
 
-    obj.DataPoints = append (obj.DataPoints, OTS_DataPoint{timestamp, rate})
+    obj.AddDataPoint (timestamp, rate)
   }
 
   fd.Close ()
@@ -157,6 +194,20 @@ func (obj *OTS_TimeSeries) ConsolidatePointAverage (ts_start, ts_end float64) OT
   return dp
 } /* ConsolidatePointAverage */
 
+func (obj *OTS_TimeSeries) TimestampFirst () float64 {
+  if obj.DataPoints == nil {
+    return math.NaN ()
+  }
+  return obj.DataPoints[0].TimeStamp
+}
+
+func (obj *OTS_TimeSeries) TimestampLast () float64 {
+  if obj.DataPoints == nil {
+    return math.NaN ()
+  }
+  return obj.DataPoints[len (obj.DataPoints) - 1].TimeStamp
+}
+
 func (obj *OTS_TimeSeries) ConsolidateAverage (interval float64) *OTS_TimeSeries {
   if interval <= 0.0 {
     return nil
@@ -169,8 +220,8 @@ func (obj *OTS_TimeSeries) ConsolidateAverage (interval float64) *OTS_TimeSeries
       ts_raw_first, ts_raw_last)
 
   /* Determine the timespan the consolidated data will span. */
-  ts_csl_first := Fmod64 (ts_raw_first, interval)
-  ts_csl_last  := Fmod64 (ts_raw_last,  interval)
+  ts_csl_first := timestampToInterval (ts_raw_first, interval)
+  ts_csl_last  := timestampToInterval (ts_raw_last,  interval)
   if ts_csl_first < ts_raw_first {
     ts_csl_first += interval
   }