public function aspectsMatch( $testableAspects, $uncoveredAspectFacts, $variableSet, &$facts, $negate = false, $resetCaches = true )
	{
		if ( count( $variableSet->factVariableBindings ) == 1 )
		{
			// TODO If there is just one variable binding in the variable set and there is a dimensional
			// 		uncovered aspect and the dimensional aspect fact is a tuple handle it
			// 		(see Arelle:FormulaEvaluator.py.ImplicitFilters line 629)
			// Need an example before implementing this
		}

		// Indexes to prevent redundant lookups
		if ( $resetCaches )
		{
			$this->factComponentsCache = array();
			$this->factElementCache = array();
			$this->factQNameCache = array();
			$this->factParentCache = array();
			$this->factNamespaceCache = array();
			$this->factCache = array();
			$this->dimensionElementCache = array();
			$this->contextDimensionCache = array();
			$this->equalityDefinitionsCache = array();
		}

		$matches = array();
		$misMatches = array();
		$types = \XBRL_Types::getInstance();

		foreach ( $facts as /** @var DOMXPathNavigator $fact */ $fact )
		{
			if ( is_null( $fact ) ) continue;

			foreach ( $testableAspects as $aspect )
			{
				$aspectFact = $uncoveredAspectFacts[ $aspect ];
				// BMS 2018-02-11 Changed from 'continue' when the aspect fact is null.
				// This will tend only to occur where there is a an uncovered dimension
				// such as in test 43210 V-05.  The Arelle equivalent is aspectMatches()
				// line 670 in FormulaEvaluator.py
				// BMS 2018-02-16 This is not correct and no longer necessary
				if ( is_null( $aspectFact ) )
				{
					// $misMatches[] = $fact;
					continue;
				}

				// The fact will always match itself
				if ( $aspectFact->getUnderlyingObject()->isSameNode( $fact->getUnderlyingObject() ) )
				{
					continue;
				}

				switch ( $aspect )
				{
					case ASPECT_LOCATION:

						if ( $fact->getUnderlyingObject()->parentNode->isSameNode( $aspectFact->getUnderlyingObject()->parentNode ) )
						{
							continue 2;
						}

						break;

					case ASPECT_CONCEPT:

						if ( $fact->getNamespaceURI() == $aspectFact->getNamespaceURI() &&
							 $fact->getLocalName() == $aspectFact->getLocalName() )
						{
							continue 2;
						}

						break;

					default:

						if ( FactValues::isTuple( $fact ) ) break;

						$factComponents = $this->getFactDetails( $variableSet, $types, $fact );
						$aspectComponents = $this->getFactDetails( $variableSet, $types, $aspectFact );

						$result = $this->aspectMatch( $variableSet, $types, $aspect, $aspectComponents, $factComponents );
						if ( $result ) continue 2;

						break;
				}

				$misMatches[] = $fact;
				continue 2;
			}

			$matches[] = $fact;
		}

		return $negate ? $misMatches : $matches;
	}