/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #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; #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 = ResMgr::CreateResMgr( "solver" ); return 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::unordered_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(); } // ------------------------------------------------------------------------- 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, std::exception) { return createPropertySetInfo( getInfoHelper() ); } // XSolverDescription OUString SAL_CALL SolverComponent::getComponentDescription() throw (uno::RuntimeException, std::exception) { return lcl_GetResourceString( RID_SOLVER_COMPONENT ); } OUString SAL_CALL SolverComponent::getStatusDescription() throw (uno::RuntimeException, std::exception) { return maStatus; } OUString SAL_CALL SolverComponent::getPropertyDescription( const OUString& rPropertyName ) throw (uno::RuntimeException, std::exception) { 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, std::exception) { return mxDoc; } void SAL_CALL SolverComponent::setDocument( const uno::Reference& _document ) throw(uno::RuntimeException, std::exception) { mxDoc = _document; } table::CellAddress SAL_CALL SolverComponent::getObjective() throw(uno::RuntimeException, std::exception) { return maObjective; } void SAL_CALL SolverComponent::setObjective( const table::CellAddress& _objective ) throw(uno::RuntimeException, std::exception) { maObjective = _objective; } uno::Sequence SAL_CALL SolverComponent::getVariables() throw(uno::RuntimeException, std::exception) { return maVariables; } void SAL_CALL SolverComponent::setVariables( const uno::Sequence& _variables ) throw(uno::RuntimeException, std::exception) { maVariables = _variables; } uno::Sequence SAL_CALL SolverComponent::getConstraints() throw(uno::RuntimeException, std::exception) { return maConstraints; } void SAL_CALL SolverComponent::setConstraints( const uno::Sequence& _constraints ) throw(uno::RuntimeException, std::exception) { maConstraints = _constraints; } sal_Bool SAL_CALL SolverComponent::getMaximize() throw(uno::RuntimeException, std::exception) { return mbMaximize; } void SAL_CALL SolverComponent::setMaximize( sal_Bool _maximize ) throw(uno::RuntimeException, std::exception) { mbMaximize = _maximize; } // XSolver: get results sal_Bool SAL_CALL SolverComponent::getSuccess() throw(uno::RuntimeException, std::exception) { return mbSuccess; } double SAL_CALL SolverComponent::getResultValue() throw(uno::RuntimeException, std::exception) { return mfResultValue; } uno::Sequence SAL_CALL SolverComponent::getSolution() throw(uno::RuntimeException, std::exception) { return maSolution; } // ------------------------------------------------------------------------- void SAL_CALL SolverComponent::solve() throw(uno::RuntimeException, std::exception) { uno::Reference xModel( mxDoc, uno::UNO_QUERY ); if ( !xModel.is() ) throw uno::RuntimeException(); maStatus = OUString(); mbSuccess = false; 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 parameter arrays for CoinMP // // set objective function const std::vector& rObjCoeff = aCellsHash[maObjective]; double* pObjectCoeffs = new double[nVariables]; 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]; double* pValues = &pCompMatrix[nConstrPos * nVariables]; 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, std::exception) { return SolverComponent_getImplementationName(); } sal_Bool SAL_CALL SolverComponent::supportsService( const OUString& rServiceName ) throw(uno::RuntimeException, std::exception) { 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, std::exception) { 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 solver_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; } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */