1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_chart2.hxx"
30 #include "ExponentialRegressionCurveCalculator.hxx"
31 #include "macros.hxx"
32 #include "RegressionCalculationHelper.hxx"
33 
34 #include <rtl/math.hxx>
35 #include <rtl/ustrbuf.hxx>
36 
37 using namespace ::com::sun::star;
38 
39 using ::rtl::OUString;
40 using ::rtl::OUStringBuffer;
41 
42 namespace chart
43 {
44 
45 ExponentialRegressionCurveCalculator::ExponentialRegressionCurveCalculator() :
46         m_fLogSlope( 0.0 ),
47         m_fLogIntercept( 0.0 )
48 {
49     ::rtl::math::setNan( & m_fLogSlope );
50     ::rtl::math::setNan( & m_fLogIntercept );
51 }
52 
53 ExponentialRegressionCurveCalculator::~ExponentialRegressionCurveCalculator()
54 {}
55 
56 // ____ XRegressionCurveCalculator ____
57 void SAL_CALL ExponentialRegressionCurveCalculator::recalculateRegression(
58     const uno::Sequence< double >& aXValues,
59     const uno::Sequence< double >& aYValues )
60     throw (uno::RuntimeException)
61 {
62     RegressionCalculationHelper::tDoubleVectorPair aValues(
63         RegressionCalculationHelper::cleanup(
64             aXValues, aYValues,
65             RegressionCalculationHelper::isValidAndYPositive()));
66 
67     const size_t nMax = aValues.first.size();
68     if( nMax == 0 )
69     {
70         ::rtl::math::setNan( & m_fLogSlope );
71         ::rtl::math::setNan( & m_fLogIntercept );
72         ::rtl::math::setNan( & m_fCorrelationCoeffitient );// actual it is coefficient of determination
73         return;
74     }
75 
76     double fAverageX = 0.0, fAverageY = 0.0;
77     size_t i = 0;
78     for( i = 0; i < nMax; ++i )
79     {
80         fAverageX += aValues.first[i];
81         fAverageY += log( aValues.second[i] );
82     }
83 
84     const double fN = static_cast< double >( nMax );
85     fAverageX /= fN;
86     fAverageY /= fN;
87 
88     double fQx = 0.0, fQy = 0.0, fQxy = 0.0;
89     for( i = 0; i < nMax; ++i )
90     {
91         double fDeltaX = aValues.first[i] - fAverageX;
92         double fDeltaY = log( aValues.second[i] ) - fAverageY;
93 
94         fQx  += fDeltaX * fDeltaX;
95         fQy  += fDeltaY * fDeltaY;
96         fQxy += fDeltaX * fDeltaY;
97     }
98 
99     m_fLogSlope = fQxy / fQx;
100     m_fLogIntercept = fAverageY - m_fLogSlope * fAverageX;
101     m_fCorrelationCoeffitient = fQxy / sqrt( fQx * fQy );
102 
103 }
104 
105 double SAL_CALL ExponentialRegressionCurveCalculator::getCurveValue( double x )
106     throw (lang::IllegalArgumentException,
107            uno::RuntimeException)
108 {
109     double fResult;
110     ::rtl::math::setNan( & fResult );
111 
112     if( ! ( ::rtl::math::isNan( m_fLogSlope ) ||
113             ::rtl::math::isNan( m_fLogIntercept )))
114     {
115         fResult = exp(m_fLogIntercept + x * m_fLogSlope);
116     }
117 
118     return fResult;
119 }
120 
121 uno::Sequence< geometry::RealPoint2D > SAL_CALL ExponentialRegressionCurveCalculator::getCurveValues(
122     double min, double max, ::sal_Int32 nPointCount,
123     const uno::Reference< chart2::XScaling >& xScalingX,
124     const uno::Reference< chart2::XScaling >& xScalingY,
125     ::sal_Bool bMaySkipPointsInCalculation )
126     throw (lang::IllegalArgumentException,
127            uno::RuntimeException)
128 {
129     if( bMaySkipPointsInCalculation &&
130         isLinearScaling( xScalingX ) &&
131         isLogarithmicScaling( xScalingY ))
132     {
133         // optimize result
134         uno::Sequence< geometry::RealPoint2D > aResult( 2 );
135         aResult[0].X = min;
136         aResult[0].Y = this->getCurveValue( min );
137         aResult[1].X = max;
138         aResult[1].Y = this->getCurveValue( max );
139 
140         return aResult;
141     }
142 
143     return RegressionCurveCalculator::getCurveValues( min, max, nPointCount, xScalingX, xScalingY, bMaySkipPointsInCalculation );
144 }
145 
146 
147 OUString ExponentialRegressionCurveCalculator::ImplGetRepresentation(
148     const uno::Reference< util::XNumberFormatter >& xNumFormatter,
149     ::sal_Int32 nNumberFormatKey ) const
150 {
151     double fIntercept = exp(m_fLogIntercept);
152     double fSlope = exp(m_fLogSlope);
153     bool bHasSlope = !rtl::math::approxEqual( fSlope, 1.0 );
154     bool bHasIntercept = !rtl::math::approxEqual( fIntercept, 1.0 );
155 
156     OUStringBuffer aBuf( C2U( "f(x) = " ));
157 
158     if ( fIntercept == 0.0)
159     {
160         // underflow, a true zero is impossible
161         aBuf.append( C2U( "exp( " ));
162         aBuf.append( getFormattedString( xNumFormatter, nNumberFormatKey, m_fLogIntercept) );
163         aBuf.append( (m_fLogSlope < 0.0) ? C2U( " - " ) : C2U( " + " ));
164         aBuf.append( getFormattedString( xNumFormatter, nNumberFormatKey, fabs(m_fLogSlope)) );
165         aBuf.append( C2U( " x )" ));
166     }
167     else
168     {
169         if (bHasIntercept)
170         {
171             aBuf.append( getFormattedString( xNumFormatter, nNumberFormatKey, fIntercept) );
172             aBuf.append( C2U( " exp( " ));
173             aBuf.append( getFormattedString( xNumFormatter, nNumberFormatKey, m_fLogSlope) );
174             aBuf.append( C2U( " x )" ));
175         }
176         else
177         {
178             // show logarithmic output, if intercept and slope both are near one
179             // otherwise drop output of intercept, which is 1 here
180             aBuf.append( C2U( " exp( " ));
181             if (!bHasSlope)
182             {
183                 aBuf.append( getFormattedString( xNumFormatter, nNumberFormatKey, m_fLogIntercept) );
184                 aBuf.append( (m_fLogSlope < 0.0) ? C2U( " - " ) : C2U( " + " ));
185                 aBuf.append( getFormattedString( xNumFormatter, nNumberFormatKey, fabs(m_fLogSlope)) );
186             }
187             else
188             {
189                 aBuf.append( getFormattedString( xNumFormatter, nNumberFormatKey, m_fLogSlope) );
190             }
191             aBuf.append( C2U( " x )" ));
192         }
193     }
194 
195     return aBuf.makeStringAndClear();
196 }
197 
198 } //  namespace chart
199