/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2008 by Sun Microsystems, Inc. * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: solver.cxx,v $ * $Revision: 1.3 $ * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ #undef LANGUAGE_NONE #define WINAPI __stdcall #define LoadInverseLib FALSE #define LoadLanguageLib FALSE #include #undef LANGUAGE_NONE #include "solver.hxx" #include "solver.hrc" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace com::sun::star; using ::rtl::OUString; #define C2U(constAsciiStr) (::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( constAsciiStr ) )) #define STR_NONNEGATIVE "NonNegative" #define STR_INTEGER "Integer" #define STR_TIMEOUT "Timeout" #define STR_EPSILONLEVEL "EpsilonLevel" #define STR_LIMITBBDEPTH "LimitBBDepth" // ----------------------------------------------------------------------- // Resources from tools are used for translated strings static ResMgr* pSolverResMgr = NULL; OUString lcl_GetResourceString( sal_uInt32 nId ) { if (!pSolverResMgr) pSolverResMgr = CREATEVERSIONRESMGR( solver ); return String( ResId( nId, *pSolverResMgr ) ); } // ----------------------------------------------------------------------- namespace { enum { PROP_NONNEGATIVE, PROP_INTEGER, PROP_TIMEOUT, PROP_EPSILONLEVEL, PROP_LIMITBBDEPTH }; } // ----------------------------------------------------------------------- // hash map for the coefficients of a dependent cell (objective or constraint) // The size of each vector is the number of columns (variable cells) plus one, first entry is initial value. struct ScSolverCellHash { size_t operator()( const table::CellAddress& rAddress ) const { return ( rAddress.Sheet << 24 ) | ( rAddress.Column << 16 ) | rAddress.Row; } }; inline bool AddressEqual( const table::CellAddress& rAddr1, const table::CellAddress& rAddr2 ) { return rAddr1.Sheet == rAddr2.Sheet && rAddr1.Column == rAddr2.Column && rAddr1.Row == rAddr2.Row; } struct ScSolverCellEqual { bool operator()( const table::CellAddress& rAddr1, const table::CellAddress& rAddr2 ) const { return AddressEqual( rAddr1, rAddr2 ); } }; typedef std::hash_map< table::CellAddress, std::vector, ScSolverCellHash, ScSolverCellEqual > ScSolverCellHashMap; // ----------------------------------------------------------------------- uno::Reference lcl_GetCell( const uno::Reference& xDoc, const table::CellAddress& rPos ) { uno::Reference xSheets( xDoc->getSheets(), uno::UNO_QUERY ); uno::Reference xSheet( xSheets->getByIndex( rPos.Sheet ), uno::UNO_QUERY ); return xSheet->getCellByPosition( rPos.Column, rPos.Row ); } void lcl_SetValue( const uno::Reference& xDoc, const table::CellAddress& rPos, double fValue ) { lcl_GetCell( xDoc, rPos )->setValue( fValue ); } double lcl_GetValue( const uno::Reference& xDoc, const table::CellAddress& rPos ) { return lcl_GetCell( xDoc, rPos )->getValue(); } OUString lcl_FormatCellRef( const uno::Reference& xDoc, const table::CellAddress& rPos ) { uno::Reference xFact( xDoc, uno::UNO_QUERY ); uno::Reference xConv( xFact->createInstance( OUString::createFromAscii( "com.sun.star.table.CellAddressConversion" ) ), uno::UNO_QUERY ); xConv->setPropertyValue( OUString::createFromAscii( "Address" ), uno::makeAny( rPos ) ); OUString aRet; xConv->getPropertyValue( OUString::createFromAscii( "UserInterfaceRepresentation" ) ) >>= aRet; return aRet; } bool lcl_ParseCellRef( table::CellAddress& rPos, const uno::Reference& xDoc, const OUString& rStr ) { uno::Reference xFact( xDoc, uno::UNO_QUERY ); uno::Reference xConv( xFact->createInstance( OUString::createFromAscii( "com.sun.star.table.CellAddressConversion" ) ), uno::UNO_QUERY ); try { xConv->setPropertyValue( OUString::createFromAscii( "UserInterfaceRepresentation" ), uno::makeAny( rStr ) ); } catch ( lang::IllegalArgumentException& ) { return false; // no valid ref } xConv->getPropertyValue( OUString::createFromAscii( "Address" ) ) >>= rPos; return true; } bool lcl_ParseRangeRef( table::CellRangeAddress& rRange, const uno::Reference& xDoc, const OUString& rStr ) { uno::Reference xFact( xDoc, uno::UNO_QUERY ); uno::Reference xConv( xFact->createInstance( OUString::createFromAscii( "com.sun.star.table.CellRangeAddressConversion" ) ), uno::UNO_QUERY ); try { xConv->setPropertyValue( OUString::createFromAscii( "UserInterfaceRepresentation" ), uno::makeAny( rStr ) ); } catch ( lang::IllegalArgumentException& ) { return false; // no valid ref } xConv->getPropertyValue( OUString::createFromAscii( "Address" ) ) >>= rRange; return true; } // ------------------------------------------------------------------------- SolverComponent::SolverComponent( const uno::Reference& /* rSMgr */ ) : OPropertyContainer( GetBroadcastHelper() ), mbMaximize( sal_True ), mbNonNegative( sal_False ), mbInteger( sal_False ), mnTimeout( 100 ), mnEpsilonLevel( 0 ), mbLimitBBDepth( sal_True ), mbSuccess( sal_False ), mfResultValue( 0.0 ) { // for XPropertySet implementation: registerProperty( C2U(STR_NONNEGATIVE), PROP_NONNEGATIVE, 0, &mbNonNegative, getCppuType( &mbNonNegative ) ); registerProperty( C2U(STR_INTEGER), PROP_INTEGER, 0, &mbInteger, getCppuType( &mbInteger ) ); registerProperty( C2U(STR_TIMEOUT), PROP_TIMEOUT, 0, &mnTimeout, getCppuType( &mnTimeout ) ); registerProperty( C2U(STR_EPSILONLEVEL), PROP_EPSILONLEVEL, 0, &mnEpsilonLevel, getCppuType( &mnEpsilonLevel ) ); registerProperty( C2U(STR_LIMITBBDEPTH), PROP_LIMITBBDEPTH, 0, &mbLimitBBDepth, getCppuType( &mbLimitBBDepth ) ); } SolverComponent::~SolverComponent() { } IMPLEMENT_FORWARD_XINTERFACE2( SolverComponent, SolverComponent_Base, OPropertyContainer ) IMPLEMENT_FORWARD_XTYPEPROVIDER2( SolverComponent, SolverComponent_Base, OPropertyContainer ) cppu::IPropertyArrayHelper* SolverComponent::createArrayHelper() const { uno::Sequence aProps; describeProperties( aProps ); return new cppu::OPropertyArrayHelper( aProps ); } cppu::IPropertyArrayHelper& SAL_CALL SolverComponent::getInfoHelper() { return *getArrayHelper(); } uno::Reference SAL_CALL SolverComponent::getPropertySetInfo() throw(uno::RuntimeException) { return createPropertySetInfo( getInfoHelper() ); } // XSolverDescription OUString SAL_CALL SolverComponent::getComponentDescription() throw (uno::RuntimeException) { return lcl_GetResourceString( RID_SOLVER_COMPONENT ); } OUString SAL_CALL SolverComponent::getStatusDescription() throw (uno::RuntimeException) { return maStatus; } OUString SAL_CALL SolverComponent::getPropertyDescription( const OUString& rPropertyName ) throw (uno::RuntimeException) { sal_uInt32 nResId = 0; sal_Int32 nHandle = getInfoHelper().getHandleByName( rPropertyName ); switch (nHandle) { case PROP_NONNEGATIVE: nResId = RID_PROPERTY_NONNEGATIVE; break; case PROP_INTEGER: nResId = RID_PROPERTY_INTEGER; break; case PROP_TIMEOUT: nResId = RID_PROPERTY_TIMEOUT; break; case PROP_EPSILONLEVEL: nResId = RID_PROPERTY_EPSILONLEVEL; break; case PROP_LIMITBBDEPTH: nResId = RID_PROPERTY_LIMITBBDEPTH; break; default: { // unknown - leave empty } } OUString aRet; if ( nResId ) aRet = lcl_GetResourceString( nResId ); return aRet; } // XSolver: settings uno::Reference SAL_CALL SolverComponent::getDocument() throw(uno::RuntimeException) { return mxDoc; } void SAL_CALL SolverComponent::setDocument( const uno::Reference& _document ) throw(uno::RuntimeException) { mxDoc = _document; } table::CellAddress SAL_CALL SolverComponent::getObjective() throw(uno::RuntimeException) { return maObjective; } void SAL_CALL SolverComponent::setObjective( const table::CellAddress& _objective ) throw(uno::RuntimeException) { maObjective = _objective; } uno::Sequence SAL_CALL SolverComponent::getVariables() throw(uno::RuntimeException) { return maVariables; } void SAL_CALL SolverComponent::setVariables( const uno::Sequence& _variables ) throw(uno::RuntimeException) { maVariables = _variables; } uno::Sequence SAL_CALL SolverComponent::getConstraints() throw(uno::RuntimeException) { return maConstraints; } void SAL_CALL SolverComponent::setConstraints( const uno::Sequence& _constraints ) throw(uno::RuntimeException) { maConstraints = _constraints; } sal_Bool SAL_CALL SolverComponent::getMaximize() throw(uno::RuntimeException) { return mbMaximize; } void SAL_CALL SolverComponent::setMaximize( sal_Bool _maximize ) throw(uno::RuntimeException) { mbMaximize = _maximize; } // XSolver: get results sal_Bool SAL_CALL SolverComponent::getSuccess() throw(uno::RuntimeException) { return mbSuccess; } double SAL_CALL SolverComponent::getResultValue() throw(uno::RuntimeException) { return mfResultValue; } uno::Sequence SAL_CALL SolverComponent::getSolution() throw(uno::RuntimeException) { return maSolution; } // ------------------------------------------------------------------------- void SAL_CALL SolverComponent::solve() throw(uno::RuntimeException) { uno::Reference xModel( mxDoc, uno::UNO_QUERY ); if ( !xModel.is() ) throw uno::RuntimeException(); maStatus = OUString(); mbSuccess = false; if ( mnEpsilonLevel < EPS_TIGHT || mnEpsilonLevel > EPS_BAGGY ) { maStatus = lcl_GetResourceString( RID_ERROR_EPSILONLEVEL ); return; } xModel->lockControllers(); // collect variables in vector (?) std::vector aVariableCells; for (sal_Int32 nPos=0; nPos>= aCellAddr ) aCellsHash[aCellAddr].reserve( nVariables + 1 ); // constraints: right hand side } // set all variables to zero //! store old values? //! use old values as initial values? std::vector::const_iterator aVarIter; for ( aVarIter = aVariableCells.begin(); aVarIter != aVariableCells.end(); ++aVarIter ) { lcl_SetValue( mxDoc, *aVarIter, 0.0 ); } // read initial values from all dependent cells ScSolverCellHashMap::iterator aCellsIter; for ( aCellsIter = aCellsHash.begin(); aCellsIter != aCellsHash.end(); ++aCellsIter ) { double fValue = lcl_GetValue( mxDoc, aCellsIter->first ); aCellsIter->second.push_back( fValue ); // store as first element, as-is } // loop through variables for ( aVarIter = aVariableCells.begin(); aVarIter != aVariableCells.end(); ++aVarIter ) { lcl_SetValue( mxDoc, *aVarIter, 1.0 ); // set to 1 to examine influence // read value change from all dependent cells for ( aCellsIter = aCellsHash.begin(); aCellsIter != aCellsHash.end(); ++aCellsIter ) { double fChanged = lcl_GetValue( mxDoc, aCellsIter->first ); double fInitial = aCellsIter->second.front(); aCellsIter->second.push_back( fChanged - fInitial ); } lcl_SetValue( mxDoc, *aVarIter, 2.0 ); // minimal test for linearity for ( aCellsIter = aCellsHash.begin(); aCellsIter != aCellsHash.end(); ++aCellsIter ) { double fInitial = aCellsIter->second.front(); double fCoeff = aCellsIter->second.back(); // last appended: coefficient for this variable double fTwo = lcl_GetValue( mxDoc, aCellsIter->first ); bool bLinear = rtl::math::approxEqual( fTwo, fInitial + 2.0 * fCoeff ) || rtl::math::approxEqual( fInitial, fTwo - 2.0 * fCoeff ); // second comparison is needed in case fTwo is zero if ( !bLinear ) maStatus = lcl_GetResourceString( RID_ERROR_NONLINEAR ); } lcl_SetValue( mxDoc, *aVarIter, 0.0 ); // set back to zero for examining next variable } xModel->unlockControllers(); if ( maStatus.getLength() ) return; // // build lp_solve model // lprec* lp = make_lp( 0, nVariables ); if ( !lp ) return; set_outputfile( lp, const_cast( "" ) ); // no output // set objective function const std::vector& rObjCoeff = aCellsHash[maObjective]; REAL* pObjVal = new REAL[nVariables+1]; pObjVal[0] = 0.0; // ignored for (nVar=0; nVar>= aRightAddr ) bRightCell = true; // cell specified as right-hand side else rRightAny >>= fDirectValue; // constant value table::CellAddress aLeftAddr = maConstraints[nConstrPos].Left; const std::vector& rLeftCoeff = aCellsHash[aLeftAddr]; REAL* pValues = new REAL[nVariables+1]; pValues[0] = 0.0; // ignored? for (nVar=0; nVar& rRightCoeff = aCellsHash[aRightAddr]; // modify pValues with rhs coefficients for (nVar=0; nVar SolverComponent_getSupportedServiceNames() { uno::Sequence< OUString > aServiceNames( 1 ); aServiceNames[ 0 ] = OUString::createFromAscii( "com.sun.star.sheet.Solver" ); return aServiceNames; } OUString SolverComponent_getImplementationName() { return OUString::createFromAscii( "com.sun.star.comp.Calc.Solver" ); } OUString SAL_CALL SolverComponent::getImplementationName() throw(uno::RuntimeException) { return SolverComponent_getImplementationName(); } sal_Bool SAL_CALL SolverComponent::supportsService( const OUString& rServiceName ) throw(uno::RuntimeException) { const uno::Sequence< OUString > aServices = SolverComponent_getSupportedServiceNames(); const OUString* pArray = aServices.getConstArray(); const OUString* pArrayEnd = pArray + aServices.getLength(); return ::std::find( pArray, pArrayEnd, rServiceName ) != pArrayEnd; } uno::Sequence SAL_CALL SolverComponent::getSupportedServiceNames() throw(uno::RuntimeException) { return SolverComponent_getSupportedServiceNames(); } uno::Reference SolverComponent_createInstance( const uno::Reference& rSMgr ) throw(uno::Exception) { return (cppu::OWeakObject*) new SolverComponent( rSMgr ); } // ------------------------------------------------------------------------- extern "C" { SAL_DLLPUBLIC_EXPORT void SAL_CALL component_getImplementationEnvironment( const sal_Char ** ppEnvTypeName, uno_Environment ** ) { *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME; } // ------------------------------------------------------------------------- SAL_DLLPUBLIC_EXPORT sal_Bool SAL_CALL component_writeInfo( void* /*pServiceManager*/, void* pRegistryKey ) { if (pRegistryKey) { try { uno::Reference xNewKey; sal_Int32 nPos; xNewKey = reinterpret_cast< registry::XRegistryKey * >( pRegistryKey )->createKey( SolverComponent_getImplementationName() ); xNewKey = xNewKey->createKey( OUString::createFromAscii( "/UNO/SERVICES" ) ); const uno::Sequence< OUString > & rSNL1 = SolverComponent_getSupportedServiceNames(); const OUString * pArray1 = rSNL1.getConstArray(); for ( nPos = rSNL1.getLength(); nPos--; ) xNewKey->createKey( pArray1[nPos] ); return sal_True; } catch (registry::InvalidRegistryException &) { OSL_ENSURE( sal_False, "### InvalidRegistryException!" ); } } return sal_False; } // ------------------------------------------------------------------------- SAL_DLLPUBLIC_EXPORT void* SAL_CALL component_getFactory( const sal_Char * pImplName, void * pServiceManager, void * /*pRegistryKey*/ ) { OUString aImplName( OUString::createFromAscii( pImplName ) ); void* pRet = 0; if( pServiceManager ) { uno::Reference< lang::XSingleComponentFactory > xFactory; if( aImplName.equals( SolverComponent_getImplementationName() ) ) xFactory = cppu::createSingleComponentFactory( SolverComponent_createInstance, OUString::createFromAscii( pImplName ), SolverComponent_getSupportedServiceNames() ); if( xFactory.is() ) { xFactory->acquire(); pRet = xFactory.get(); } } return pRet; } }