OTS_TimeSeries: Add the AddDataPoint() method. master
authorFlorian Forster <ff@octo.it>
Tue, 7 Jun 2011 20:54:43 +0000 (22:54 +0200)
committerFlorian Forster <ff@octo.it>
Tue, 7 Jun 2011 20:54:43 +0000 (22:54 +0200)
ots_timeseries.go
ots_timeseries_test.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
   }
index 8df850e..8b69eb8 100644 (file)
@@ -45,7 +45,7 @@ var consolidatePointAverageTests = []consolidatePointAverageTest {
   consolidatePointAverageTest{27.0,   7.0,  5.1},
 }
 
-func FloatsDiffer (a, b float64) bool {
+func floatsDiffer (a, b float64) bool {
   if math.Fabs (a - b) > 1.0e-6 {
     return true
   }
@@ -60,7 +60,7 @@ func TestConsolidatePointAverage (t *testing.T) {
     testCase := consolidatePointAverageTests[i]
 
     dp := obj.ConsolidatePointAverage (testCase.tsStart, testCase.tsEnd);
-    if FloatsDiffer (dp.Rate, testCase.rate) {
+    if floatsDiffer (dp.Rate, testCase.rate) {
       t.Errorf ("ConsolidatePointAverage (%g, %g) failed: Expected %g, got %g",
           testCase.tsStart, testCase.tsEnd, testCase.rate, dp.Rate);
     }