1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
30 #define WINAPI __stdcall
31 #define LoadInverseLib FALSE
32 #define LoadLanguageLib FALSE
33 #include <lpsolve/lp_lib.h>
39 #include <com/sun/star/beans/XPropertySet.hpp>
40 #include <com/sun/star/container/XIndexAccess.hpp>
41 #include <com/sun/star/frame/XModel.hpp>
42 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
43 #include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
44 #include <com/sun/star/sheet/XSpreadsheet.hpp>
45 #include <com/sun/star/table/CellAddress.hpp>
46 #include <com/sun/star/table/CellRangeAddress.hpp>
47 #include <com/sun/star/text/XTextRange.hpp>
49 #include <rtl/math.hxx>
50 #include <rtl/ustrbuf.hxx>
51 #include <cppuhelper/factory.hxx>
53 #include <boost/unordered_map.hpp>
55 #include <tools/resmgr.hxx>
57 using namespace com::sun::star
;
59 using ::rtl::OUString
;
61 #define C2U(constAsciiStr) (::rtl::OUString( constAsciiStr ))
63 #define STR_NONNEGATIVE "NonNegative"
64 #define STR_INTEGER "Integer"
65 #define STR_TIMEOUT "Timeout"
66 #define STR_EPSILONLEVEL "EpsilonLevel"
67 #define STR_LIMITBBDEPTH "LimitBBDepth"
69 // -----------------------------------------------------------------------
70 // Resources from tools are used for translated strings
72 static ResMgr
* pSolverResMgr
= NULL
;
74 OUString
lcl_GetResourceString( sal_uInt32 nId
)
77 pSolverResMgr
= ResMgr::CreateResMgr("solver");
79 return String( ResId( nId
, *pSolverResMgr
) );
82 // -----------------------------------------------------------------------
96 // -----------------------------------------------------------------------
98 // hash map for the coefficients of a dependent cell (objective or constraint)
99 // The size of each vector is the number of columns (variable cells) plus one, first entry is initial value.
101 struct ScSolverCellHash
103 size_t operator()( const table::CellAddress
& rAddress
) const
105 return ( rAddress
.Sheet
<< 24 ) | ( rAddress
.Column
<< 16 ) | rAddress
.Row
;
109 inline bool AddressEqual( const table::CellAddress
& rAddr1
, const table::CellAddress
& rAddr2
)
111 return rAddr1
.Sheet
== rAddr2
.Sheet
&& rAddr1
.Column
== rAddr2
.Column
&& rAddr1
.Row
== rAddr2
.Row
;
114 struct ScSolverCellEqual
116 bool operator()( const table::CellAddress
& rAddr1
, const table::CellAddress
& rAddr2
) const
118 return AddressEqual( rAddr1
, rAddr2
);
122 typedef boost::unordered_map
< table::CellAddress
, std::vector
<double>, ScSolverCellHash
, ScSolverCellEqual
> ScSolverCellHashMap
;
124 // -----------------------------------------------------------------------
126 uno::Reference
<table::XCell
> lcl_GetCell( const uno::Reference
<sheet::XSpreadsheetDocument
>& xDoc
,
127 const table::CellAddress
& rPos
)
129 uno::Reference
<container::XIndexAccess
> xSheets( xDoc
->getSheets(), uno::UNO_QUERY
);
130 uno::Reference
<sheet::XSpreadsheet
> xSheet( xSheets
->getByIndex( rPos
.Sheet
), uno::UNO_QUERY
);
131 return xSheet
->getCellByPosition( rPos
.Column
, rPos
.Row
);
134 void lcl_SetValue( const uno::Reference
<sheet::XSpreadsheetDocument
>& xDoc
,
135 const table::CellAddress
& rPos
, double fValue
)
137 lcl_GetCell( xDoc
, rPos
)->setValue( fValue
);
140 double lcl_GetValue( const uno::Reference
<sheet::XSpreadsheetDocument
>& xDoc
,
141 const table::CellAddress
& rPos
)
143 return lcl_GetCell( xDoc
, rPos
)->getValue();
146 // -------------------------------------------------------------------------
148 SolverComponent::SolverComponent( const uno::Reference
<uno::XComponentContext
>& /* rSMgr */ ) :
149 OPropertyContainer( GetBroadcastHelper() ),
150 mbMaximize( sal_True
),
151 mbNonNegative( sal_False
),
152 mbInteger( sal_False
),
155 mbLimitBBDepth( sal_True
),
156 mbSuccess( sal_False
),
159 // for XPropertySet implementation:
160 registerProperty( C2U(STR_NONNEGATIVE
), PROP_NONNEGATIVE
, 0, &mbNonNegative
, getCppuType( &mbNonNegative
) );
161 registerProperty( C2U(STR_INTEGER
), PROP_INTEGER
, 0, &mbInteger
, getCppuType( &mbInteger
) );
162 registerProperty( C2U(STR_TIMEOUT
), PROP_TIMEOUT
, 0, &mnTimeout
, getCppuType( &mnTimeout
) );
163 registerProperty( C2U(STR_EPSILONLEVEL
), PROP_EPSILONLEVEL
, 0, &mnEpsilonLevel
, getCppuType( &mnEpsilonLevel
) );
164 registerProperty( C2U(STR_LIMITBBDEPTH
), PROP_LIMITBBDEPTH
, 0, &mbLimitBBDepth
, getCppuType( &mbLimitBBDepth
) );
167 SolverComponent::~SolverComponent()
171 IMPLEMENT_FORWARD_XINTERFACE2( SolverComponent
, SolverComponent_Base
, OPropertyContainer
)
172 IMPLEMENT_FORWARD_XTYPEPROVIDER2( SolverComponent
, SolverComponent_Base
, OPropertyContainer
)
174 cppu::IPropertyArrayHelper
* SolverComponent::createArrayHelper() const
176 uno::Sequence
<beans::Property
> aProps
;
177 describeProperties( aProps
);
178 return new cppu::OPropertyArrayHelper( aProps
);
181 cppu::IPropertyArrayHelper
& SAL_CALL
SolverComponent::getInfoHelper()
183 return *getArrayHelper();
186 uno::Reference
<beans::XPropertySetInfo
> SAL_CALL
SolverComponent::getPropertySetInfo() throw(uno::RuntimeException
)
188 return createPropertySetInfo( getInfoHelper() );
191 // XSolverDescription
193 OUString SAL_CALL
SolverComponent::getComponentDescription() throw (uno::RuntimeException
)
195 return lcl_GetResourceString( RID_SOLVER_COMPONENT
);
198 OUString SAL_CALL
SolverComponent::getStatusDescription() throw (uno::RuntimeException
)
203 OUString SAL_CALL
SolverComponent::getPropertyDescription( const OUString
& rPropertyName
) throw (uno::RuntimeException
)
205 sal_uInt32 nResId
= 0;
206 sal_Int32 nHandle
= getInfoHelper().getHandleByName( rPropertyName
);
209 case PROP_NONNEGATIVE
:
210 nResId
= RID_PROPERTY_NONNEGATIVE
;
213 nResId
= RID_PROPERTY_INTEGER
;
216 nResId
= RID_PROPERTY_TIMEOUT
;
218 case PROP_EPSILONLEVEL
:
219 nResId
= RID_PROPERTY_EPSILONLEVEL
;
221 case PROP_LIMITBBDEPTH
:
222 nResId
= RID_PROPERTY_LIMITBBDEPTH
;
226 // unknown - leave empty
231 aRet
= lcl_GetResourceString( nResId
);
237 uno::Reference
<sheet::XSpreadsheetDocument
> SAL_CALL
SolverComponent::getDocument() throw(uno::RuntimeException
)
242 void SAL_CALL
SolverComponent::setDocument( const uno::Reference
<sheet::XSpreadsheetDocument
>& _document
)
243 throw(uno::RuntimeException
)
248 table::CellAddress SAL_CALL
SolverComponent::getObjective() throw(uno::RuntimeException
)
253 void SAL_CALL
SolverComponent::setObjective( const table::CellAddress
& _objective
) throw(uno::RuntimeException
)
255 maObjective
= _objective
;
258 uno::Sequence
<table::CellAddress
> SAL_CALL
SolverComponent::getVariables() throw(uno::RuntimeException
)
263 void SAL_CALL
SolverComponent::setVariables( const uno::Sequence
<table::CellAddress
>& _variables
)
264 throw(uno::RuntimeException
)
266 maVariables
= _variables
;
269 uno::Sequence
<sheet::SolverConstraint
> SAL_CALL
SolverComponent::getConstraints() throw(uno::RuntimeException
)
271 return maConstraints
;
274 void SAL_CALL
SolverComponent::setConstraints( const uno::Sequence
<sheet::SolverConstraint
>& _constraints
)
275 throw(uno::RuntimeException
)
277 maConstraints
= _constraints
;
280 sal_Bool SAL_CALL
SolverComponent::getMaximize() throw(uno::RuntimeException
)
285 void SAL_CALL
SolverComponent::setMaximize( sal_Bool _maximize
) throw(uno::RuntimeException
)
287 mbMaximize
= _maximize
;
290 // XSolver: get results
292 sal_Bool SAL_CALL
SolverComponent::getSuccess() throw(uno::RuntimeException
)
297 double SAL_CALL
SolverComponent::getResultValue() throw(uno::RuntimeException
)
299 return mfResultValue
;
302 uno::Sequence
<double> SAL_CALL
SolverComponent::getSolution() throw(uno::RuntimeException
)
307 // -------------------------------------------------------------------------
309 void SAL_CALL
SolverComponent::solve() throw(uno::RuntimeException
)
311 uno::Reference
<frame::XModel
> xModel( mxDoc
, uno::UNO_QUERY
);
313 throw uno::RuntimeException();
315 maStatus
= OUString();
318 if ( mnEpsilonLevel
< EPS_TIGHT
|| mnEpsilonLevel
> EPS_BAGGY
)
320 maStatus
= lcl_GetResourceString( RID_ERROR_EPSILONLEVEL
);
324 xModel
->lockControllers();
326 // collect variables in vector (?)
328 std::vector
<table::CellAddress
> aVariableCells
;
329 for (sal_Int32 nPos
=0; nPos
<maVariables
.getLength(); nPos
++)
330 aVariableCells
.push_back( maVariables
[nPos
] );
331 size_t nVariables
= aVariableCells
.size();
334 // collect all dependent cells
336 ScSolverCellHashMap aCellsHash
;
337 aCellsHash
[maObjective
].reserve( nVariables
+ 1 ); // objective function
339 for (sal_Int32 nConstrPos
= 0; nConstrPos
< maConstraints
.getLength(); ++nConstrPos
)
341 table::CellAddress aCellAddr
= maConstraints
[nConstrPos
].Left
;
342 aCellsHash
[aCellAddr
].reserve( nVariables
+ 1 ); // constraints: left hand side
344 if ( maConstraints
[nConstrPos
].Right
>>= aCellAddr
)
345 aCellsHash
[aCellAddr
].reserve( nVariables
+ 1 ); // constraints: right hand side
348 // set all variables to zero
349 //! store old values?
350 //! use old values as initial values?
351 std::vector
<table::CellAddress
>::const_iterator aVarIter
;
352 for ( aVarIter
= aVariableCells
.begin(); aVarIter
!= aVariableCells
.end(); ++aVarIter
)
354 lcl_SetValue( mxDoc
, *aVarIter
, 0.0 );
357 // read initial values from all dependent cells
358 ScSolverCellHashMap::iterator aCellsIter
;
359 for ( aCellsIter
= aCellsHash
.begin(); aCellsIter
!= aCellsHash
.end(); ++aCellsIter
)
361 double fValue
= lcl_GetValue( mxDoc
, aCellsIter
->first
);
362 aCellsIter
->second
.push_back( fValue
); // store as first element, as-is
365 // loop through variables
366 for ( aVarIter
= aVariableCells
.begin(); aVarIter
!= aVariableCells
.end(); ++aVarIter
)
368 lcl_SetValue( mxDoc
, *aVarIter
, 1.0 ); // set to 1 to examine influence
370 // read value change from all dependent cells
371 for ( aCellsIter
= aCellsHash
.begin(); aCellsIter
!= aCellsHash
.end(); ++aCellsIter
)
373 double fChanged
= lcl_GetValue( mxDoc
, aCellsIter
->first
);
374 double fInitial
= aCellsIter
->second
.front();
375 aCellsIter
->second
.push_back( fChanged
- fInitial
);
378 lcl_SetValue( mxDoc
, *aVarIter
, 2.0 ); // minimal test for linearity
380 for ( aCellsIter
= aCellsHash
.begin(); aCellsIter
!= aCellsHash
.end(); ++aCellsIter
)
382 double fInitial
= aCellsIter
->second
.front();
383 double fCoeff
= aCellsIter
->second
.back(); // last appended: coefficient for this variable
384 double fTwo
= lcl_GetValue( mxDoc
, aCellsIter
->first
);
386 bool bLinear
= rtl::math::approxEqual( fTwo
, fInitial
+ 2.0 * fCoeff
) ||
387 rtl::math::approxEqual( fInitial
, fTwo
- 2.0 * fCoeff
);
388 // second comparison is needed in case fTwo is zero
390 maStatus
= lcl_GetResourceString( RID_ERROR_NONLINEAR
);
393 lcl_SetValue( mxDoc
, *aVarIter
, 0.0 ); // set back to zero for examining next variable
396 xModel
->unlockControllers();
398 if ( !maStatus
.isEmpty() )
402 // build lp_solve model
405 lprec
* lp
= make_lp( 0, nVariables
);
409 set_outputfile( lp
, const_cast<char*>( "" ) ); // no output
411 // set objective function
413 const std::vector
<double>& rObjCoeff
= aCellsHash
[maObjective
];
414 REAL
* pObjVal
= new REAL
[nVariables
+1];
415 pObjVal
[0] = 0.0; // ignored
416 for (nVar
=0; nVar
<nVariables
; nVar
++)
417 pObjVal
[nVar
+1] = rObjCoeff
[nVar
+1];
418 set_obj_fn( lp
, pObjVal
);
420 set_rh( lp
, 0, rObjCoeff
[0] ); // constant term of objective
424 set_add_rowmode(lp
, TRUE
);
426 for (sal_Int32 nConstrPos
= 0; nConstrPos
< maConstraints
.getLength(); ++nConstrPos
)
428 // integer constraints are set later
429 sheet::SolverConstraintOperator eOp
= maConstraints
[nConstrPos
].Operator
;
430 if ( eOp
== sheet::SolverConstraintOperator_LESS_EQUAL
||
431 eOp
== sheet::SolverConstraintOperator_GREATER_EQUAL
||
432 eOp
== sheet::SolverConstraintOperator_EQUAL
)
434 double fDirectValue
= 0.0;
435 bool bRightCell
= false;
436 table::CellAddress aRightAddr
;
437 const uno::Any
& rRightAny
= maConstraints
[nConstrPos
].Right
;
438 if ( rRightAny
>>= aRightAddr
)
439 bRightCell
= true; // cell specified as right-hand side
441 rRightAny
>>= fDirectValue
; // constant value
443 table::CellAddress aLeftAddr
= maConstraints
[nConstrPos
].Left
;
445 const std::vector
<double>& rLeftCoeff
= aCellsHash
[aLeftAddr
];
446 REAL
* pValues
= new REAL
[nVariables
+1];
447 pValues
[0] = 0.0; // ignored?
448 for (nVar
=0; nVar
<nVariables
; nVar
++)
449 pValues
[nVar
+1] = rLeftCoeff
[nVar
+1];
451 // if left hand cell has a constant term, put into rhs value
452 double fRightValue
= -rLeftCoeff
[0];
456 const std::vector
<double>& rRightCoeff
= aCellsHash
[aRightAddr
];
457 // modify pValues with rhs coefficients
458 for (nVar
=0; nVar
<nVariables
; nVar
++)
459 pValues
[nVar
+1] -= rRightCoeff
[nVar
+1];
461 fRightValue
+= rRightCoeff
[0]; // constant term
464 fRightValue
+= fDirectValue
;
466 int nConstrType
= LE
;
469 case sheet::SolverConstraintOperator_LESS_EQUAL
: nConstrType
= LE
; break;
470 case sheet::SolverConstraintOperator_GREATER_EQUAL
: nConstrType
= GE
; break;
471 case sheet::SolverConstraintOperator_EQUAL
: nConstrType
= EQ
; break;
473 OSL_FAIL( "unexpected enum type" );
475 add_constraint( lp
, pValues
, nConstrType
, fRightValue
);
481 set_add_rowmode(lp
, FALSE
);
483 // apply settings to all variables
485 for (nVar
=0; nVar
<nVariables
; nVar
++)
487 if ( !mbNonNegative
)
488 set_unbounded(lp
, nVar
+1); // allow negative (default is non-negative)
489 //! collect bounds from constraints?
491 set_int(lp
, nVar
+1, TRUE
);
494 // apply single-var integer constraints
496 for (sal_Int32 nConstrPos
= 0; nConstrPos
< maConstraints
.getLength(); ++nConstrPos
)
498 sheet::SolverConstraintOperator eOp
= maConstraints
[nConstrPos
].Operator
;
499 if ( eOp
== sheet::SolverConstraintOperator_INTEGER
||
500 eOp
== sheet::SolverConstraintOperator_BINARY
)
502 table::CellAddress aLeftAddr
= maConstraints
[nConstrPos
].Left
;
503 // find variable index for cell
504 for (nVar
=0; nVar
<nVariables
; nVar
++)
505 if ( AddressEqual( aVariableCells
[nVar
], aLeftAddr
) )
507 if ( eOp
== sheet::SolverConstraintOperator_INTEGER
)
508 set_int(lp
, nVar
+1, TRUE
);
510 set_binary(lp
, nVar
+1, TRUE
);
520 if ( !mbLimitBBDepth
)
521 set_bb_depthlimit( lp
, 0 );
523 set_epslevel( lp
, mnEpsilonLevel
);
524 set_timeout( lp
, mnTimeout
);
528 int nResult
= ::solve( lp
);
530 mbSuccess
= ( nResult
== OPTIMAL
);
535 maSolution
.realloc( nVariables
);
537 REAL
* pResultVar
= NULL
;
538 get_ptr_variables( lp
, &pResultVar
);
539 for (nVar
=0; nVar
<nVariables
; nVar
++)
540 maSolution
[nVar
] = pResultVar
[nVar
];
542 mfResultValue
= get_objective( lp
);
544 else if ( nResult
== INFEASIBLE
)
545 maStatus
= lcl_GetResourceString( RID_ERROR_INFEASIBLE
);
546 else if ( nResult
== UNBOUNDED
)
547 maStatus
= lcl_GetResourceString( RID_ERROR_UNBOUNDED
);
548 else if ( nResult
== TIMEOUT
|| nResult
== SUBOPTIMAL
)
549 maStatus
= lcl_GetResourceString( RID_ERROR_TIMEOUT
);
550 // SUBOPTIMAL is assumed to be caused by a timeout, and reported as an error
555 // -------------------------------------------------------------------------
559 uno::Sequence
< OUString
> SolverComponent_getSupportedServiceNames()
561 uno::Sequence
< OUString
> aServiceNames( 1 );
562 aServiceNames
[ 0 ] = OUString("com.sun.star.sheet.Solver" );
563 return aServiceNames
;
566 OUString
SolverComponent_getImplementationName()
568 return OUString("com.sun.star.comp.Calc.Solver" );
571 OUString SAL_CALL
SolverComponent::getImplementationName() throw(uno::RuntimeException
)
573 return SolverComponent_getImplementationName();
576 sal_Bool SAL_CALL
SolverComponent::supportsService( const OUString
& rServiceName
) throw(uno::RuntimeException
)
578 const uno::Sequence
< OUString
> aServices
= SolverComponent_getSupportedServiceNames();
579 const OUString
* pArray
= aServices
.getConstArray();
580 const OUString
* pArrayEnd
= pArray
+ aServices
.getLength();
581 return ::std::find( pArray
, pArrayEnd
, rServiceName
) != pArrayEnd
;
584 uno::Sequence
<OUString
> SAL_CALL
SolverComponent::getSupportedServiceNames() throw(uno::RuntimeException
)
586 return SolverComponent_getSupportedServiceNames();
589 uno::Reference
<uno::XInterface
> SolverComponent_createInstance( const uno::Reference
<uno::XComponentContext
>& rSMgr
)
590 throw(uno::Exception
)
592 return (cppu::OWeakObject
*) new SolverComponent( rSMgr
);
595 // -------------------------------------------------------------------------
599 SAL_DLLPUBLIC_EXPORT
void* SAL_CALL
solver_component_getFactory( const sal_Char
* pImplName
, void * pServiceManager
, void * /*pRegistryKey*/ )
601 OUString
aImplName( OUString::createFromAscii( pImplName
) );
604 if( pServiceManager
)
606 uno::Reference
< lang::XSingleComponentFactory
> xFactory
;
607 if( aImplName
.equals( SolverComponent_getImplementationName() ) )
608 xFactory
= cppu::createSingleComponentFactory(
609 SolverComponent_createInstance
,
610 OUString::createFromAscii( pImplName
),
611 SolverComponent_getSupportedServiceNames() );
616 pRet
= xFactory
.get();
623 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */