Add license and copyright to all source files in System.Data
[mono.git] / mcs / class / System.Data / Mono.Data.SqlExpressions / Aggregation.cs
1 //
2 // Aggregation.cs
3 //
4 // Author:
5 //   Juraj Skripsky (juraj@hotfeet.ch)
6 //
7 // (C) 2004 HotFeet GmbH (http://www.hotfeet.ch)
8 //
9
10 //
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System;
34 using System.Collections;
35 using System.Data;
36
37 namespace Mono.Data.SqlExpressions {
38         internal enum AggregationFunction {
39                 Count, Sum, Min, Max, Avg, StDev, Var
40         }
41
42         internal class Aggregation : IExpression {
43                 bool cacheResults;
44                 DataRow[] rows;
45                 ColumnReference column;
46                 AggregationFunction function;
47                 int count;
48                 IConvertible result;
49         
50                 public Aggregation (bool cacheResults, DataRow[] rows, AggregationFunction function, ColumnReference column)
51                 {
52                         this.cacheResults = cacheResults;
53                         this.rows = rows;
54                         this.column = column;
55                         this.function = function;
56                         this.result = null;
57                 }
58         
59                 public object Eval (DataRow row)
60                 {
61                         //TODO: implement a better caching strategy and a mechanism for cache invalidation.
62                         //for now only aggregation over the table owning 'row' (e.g. 'sum(parts)'
63                         //in constrast to 'sum(parent.parts)' and 'sum(child.parts)') is cached.
64                         if (cacheResults && result != null && column.ReferencedTable == ReferencedTable.Self)
65                                 return result;
66                                 
67                         count = 0;
68                         result = null;
69                         
70                         object[] values;
71                         if (rows == null)
72                                 values = column.GetValues (column.GetReferencedRows (row));
73                         else
74                                 values = column.GetValues (rows);
75                         
76                         foreach (object val in values) {
77                                 if (val == null)
78                                         continue;
79                                         
80                                 count++;
81                                 Aggregate ((IConvertible)val);
82                         }
83                         
84                         switch (function) {
85                         case AggregationFunction.StDev:
86                         case AggregationFunction.Var:
87                                 result = CalcStatisticalFunction (values);
88                                 break;
89                                         
90                         case AggregationFunction.Avg:
91                                 result = Numeric.Divide (result, count);
92                                 break;
93                         
94                         case AggregationFunction.Count:
95                                 result = count;
96                                 break;
97                         }
98                         
99                         if (result == null)
100                                 result = 0;
101                                 
102                         return result;
103                 }
104                 
105                 private void Aggregate (IConvertible val)
106                 {
107                         switch (function) {
108                         case AggregationFunction.Min:
109                                 result = (result != null ? Numeric.Min (result, val) : val);
110                                 return;
111                         
112                         case AggregationFunction.Max:
113                                 result = (result != null ? Numeric.Max (result, val) : val);
114                                 return;
115
116                         case AggregationFunction.Sum:
117                         case AggregationFunction.Avg:
118                         case AggregationFunction.StDev:
119                         case AggregationFunction.Var:
120                                 result = (result != null ? Numeric.Add (result, val) : val);
121                                 return;
122                         }
123                 }
124                 
125                 private IConvertible CalcStatisticalFunction (object[] values)
126                 {
127                         double average = (double)Convert.ChangeType(result, TypeCode.Double) / count;
128                         double res = 0.0;
129                                                 
130                         foreach (object val in values) {
131                                 if (val == null)
132                                         continue;
133                                         
134                                 double diff = average - (double)Convert.ChangeType(val, TypeCode.Double);
135                                 res += System.Math.Pow (diff, 2);
136                         }
137                         res /= (count - 1);
138                         
139                         if (function == AggregationFunction.StDev)
140                                 res = System.Math.Sqrt (res);
141                         
142                         return res;
143                 }
144         }
145 }