ItemEqual
@param XPathItem $item1
@param XPathItem $item2
@return bool
541245
protected function ItemEqual( $item1, $item2 )
	{
		$xTypeCode = $item1->getSchemaType()->TypeCode;
		$yTypeCode = $item2->getSchemaType()->TypeCode;
		$types = array( XmlTypeCode::Decimal, XmlTypeCode::Float, XmlTypeCode::Double, XmlTypeCode::Integer );

		if ( $xTypeCode == XmlTypeCode::Boolean && $yTypeCode == XmlTypeCode::Boolean )
		{
			$x = $item1->GetTypedValue();
			$y = $item2->GetTypedValue();

			return $x == $y;
		}

		$xIsNumeric = in_array( $xTypeCode, $types );
		$yIsNumeric = in_array( $yTypeCode, $types );

		if ( $xIsNumeric != $yIsNumeric )
		{
			return false;
		}

		if ( ! $xIsNumeric )
		{
			$x = $item1->GetValue();
			$y = $item2->GetValue();

			if ( ! is_null( $this->collation ) && $this->collation == XmlReservedNs::collationCodepoint )
			{
				$x = \normalizer_normalize( $x, \Normalizer::FORM_C );
				$y = \normalizer_normalize( $y, \Normalizer::FORM_C );

				return strcmp( $x, $y );
			}
			else
			{
				return strcoll( $x, $y ) == 0;
			}
		}

		$x = $item1->GetTypedValue();
		$y = $item2->GetTypedValue();

		// Perform comparison of the typed values
		if ( is_double( $x ) && is_nan( $x ) && is_double( $y ) && is_nan( $y ) )
		{
			// BMS 2017-09-13 XFI requires that NaN == NaN returns false
			return false;
		}
		if ( is_double( $x ) && is_infinite( $x ) && is_double( $y ) && is_infinite( $y ) )
		{
			return true;
		}

		if ( $xTypeCode != $yTypeCode )
		{
			// Handle the exceptions
			$exceptions = (
				( $xTypeCode == XmlTypeCode::Integer && in_array( $yTypeCode, $types ) ) ||
				( $yTypeCode == XmlTypeCode::Integer && in_array( $xTypeCode, $types ) ) ||
				( $xTypeCode == XmlTypeCode::Decimal && in_array( $yTypeCode, $types ) ) ||
				( $yTypeCode == XmlTypeCode::Decimal && in_array( $xTypeCode, $types ) )
			);

			if ( ! $exceptions )
			{
				return false;
			}
		}

		if ( ! is_object( $x ) && ! is_object( $y ) )
		{
			if ( $x == $y )
			{
				return true;
			}
		}
		else if ( is_object( $x ) && is_object( $y ) && method_exists( $x, "Equals" ) && method_exists( $y, "Equals" ) )
		{
			if ( $x->Equals( $y ) )
			{
				return true;
			}
		}

		$res;
		$result = ValueProxy::EqValues( $x, $y, $res) && $res;
		return $result;

	}
43619
public function isPrimaryItem( $item )
	{
		if ( ! isset( $item['substitutionGroup'] ) ) return false;
		if ( $item['substitutionGroup'] == XBRL_Constants::$xbrldtHypercubeItem /* 'hypercubeItem' */ ) return false;
		if ( $item['substitutionGroup'] == XBRL_Constants::$xbrldtDimensionItem /* 'dimensionItem' */ ) return false;
		if ( XBRL_Types::getInstance()->resolveToSubstitutionGroup( $item['substitutionGroup'] , array( XBRL_Constants::$xbrldtHypercubeItem  ) ) ) return false;
		if ( XBRL_Types::getInstance()->resolveToSubstitutionGroup( $item['substitutionGroup'] , array( XBRL_Constants::$xbrldtDimensionItem  ) ) ) return false;
		return true;
	}
1124876
protected function AttributeEqual($nav1, $nav2)
	{
		if ( $nav1->getLocalName() != $nav2->getLocalName() || $nav1->getNamespaceURI() != $nav2->getNamespaceURI() ) return false;
		// BMS 2018-03-21 Add because a query like:
		//	xfi:nodes-correspond( //t:P1[@id='t1'], //t:P1[@id='t2'] )
		// passes a pair of nodes including the attribute 'id' to the nodes-correspond
		// function but by definition the id values are different
		// The attributeToIgnore function allows the caller to define the selection attribute that should not affect equality.
		if ( ! is_null( $this->attributeToIgnore  ) )
		{
			if ( $nav1->getLocalName() == $this->attributeToIgnore ) return true;
		}
		return $this->ItemEqual( $nav1, $nav2 );
		// return $nav1->GetTypedValue() == $nav2->GetTypedValue();
	}
775836
public function Equals($item1, $item2)
    {
        if ($item1 === null && $item2 !== null)
        {
            return false;
        }
        else if ($item1 !== null && $item2 === null)
        {
            return false;
        }
        else if ($item1 === null && $item2 === null)
        {
            return true;
        }
        return $item1->Equals($item2);
    }
967467
private function compare(Item $itemA, Item $itemB): int
    {
        $itemAVolume = $itemA->getWidth() * $itemA->getLength() * $itemA->getDepth();
        $itemBVolume = $itemB->getWidth() * $itemB->getLength() * $itemB->getDepth();
        $volumeDecider = $itemAVolume <=> $itemBVolume;
        $weightDecider = $itemA->getWeight() - $itemB->getWeight();
        if ($volumeDecider !== 0) {
            return $volumeDecider;
        }
        if ($weightDecider !== 0) {
            return $weightDecider;
        }

        return $itemB->getDescription() <=> $itemA->getDescription();
    }
883638
function contains($item, callable $callback = null) {
        if ($callback === null) {
            $callback = __NAMESPACE__ . '\\compare';
        }
        foreach ($this->avl as $pair) {
            /**
             * @var Pair $pair
             */
            if (call_user_func($callback, $pair->second, $item) === 0) {
                return true;
            }
        }

        return false;
    }
737877
public function ItemEqual( $item1, $item2 )
	{
		if ( $item1 instanceof DOMXPathNavigator &&
			 $item1->getNamespaceURI() == \XBRL_Constants::$standardPrefixes[ STANDARD_PREFIX_XBRLI ] &&
			 ( $item1->getLocalName() == 'instant' || $item1->getLocalName() == 'endDate' ) )
		{
			// Parent MUST be period
			$parent = $item1->CloneInstance();
			if ( $parent->MoveToParent() )
			{
				if ( $parent->getLocalName() == 'period' )
				{
					// OK, got two dates.  If one has the format <date>T<time> and the other does not then compare them carefully.
					$item1Value = trim( $item1->getValue() );
					$item2Value = trim( $item2->getValue() );
					if ( ( strpos( $item1Value, "T" ) !== false && strpos( $item2Value, "T" ) === false ) ||
						 ( strpos( $item2Value, "T" ) !== false && strpos( $item1Value, "T" ) === false )
					)
					{
						$item1 = $item1->getTypedValue();
						$item2 = $item2->getTypedValue();

						// One of the values has a 'T' format.  If one of them has 00:00:00 then create a revised date
						if ( strpos( $item1Value, "00:00:00" ) !== false )
						{
							$item1 = DateTimeValue::AddDayTimeDuration( $item1, DayTimeDurationValue::Parse("-P1D") );
							$item1 = DateValue::fromDate( $item1->S, $item1->getValue(), ! $item1->IsLocal );
						}
						else if ( strpos( $item2Value, "00:00:00" ) !== false )
						{
							$item2 = DateTimeValue::AddDayTimeDuration( $item2, DayTimeDurationValue::Parse("-P1D") );
							$item2 = DateValue::fromDate( $item2->S, $item2->getValue(), ! $item2->IsLocal );
						}

						$item1 = XPath2Item::fromValue( $item1 );
						$item2 = XPath2Item::fromValue( $item2 );
					}
				}
			}
		}

		return parent::ItemEqual( $item1, $item2 );
	}
747776
public function DeepEqualByIterator( $iter1, $iter2, $elementsOnly = false )
	{
		$iter1 = $iter1->CloneInstance();
		$iter2 = $iter2->CloneInstance();
		$flag1 = false;
		$flag2 = false;

		do
		{
			$flag1 = $iter1->MoveNext();
			$flag2 = $iter2->MoveNext();
			if ( $flag1 != $flag2 ) return false;

			if (! $flag1 && ! $flag2) return true;

			$iter1Current = $iter1->getCurrent();
			$iter2Current = $iter2->getCurrent();

			// If one of the iterators is a ForIterator and it returns an ExprIterator handle it
			if ( $iter1Current instanceof ExprIterator || $iter2Current instanceof ExprIterator )
			{
				if ( ! $iter1Current instanceof ExprIterator ) $iter1Current = $iter1;
				if ( ! $iter2Current instanceof ExprIterator ) $iter2Current = $iter2;
				return $this->DeepEqualByIterator( $iter1Current->CloneInstance(), $iter2Current->CloneInstance() );
			}

			if ( $iter1Current->getIsNode() != $iter2Current->getIsNode() )
			{
				return false;
			}
			else
			{
				if ( $iter1Current->getIsNode() && $iter2Current->getIsNode() )
				{
					if ( $elementsOnly && $iter1Current->getNodeType() != XPathNodeType::Element )
					{
						continue;
					}
					if ( ! $this->NodeEqual( $iter1Current, $iter2Current ) )
					{
						return false;
					}
				}
				else
				{
					if ( ! $this->ItemEqual( $iter1Current, $iter2Current ) )
					{
						return false;
					}
				}
			}
		}
		while ( true );
		return true;
	}
236195
public function Match( $item, $context )
	{
		if ( $this->TypeCode == $item->getSchemaType()->TypeCode ) return true;

		switch ( $this->TypeCode )
		{
			case XmlTypeCode::None:
				return false;

			case XmlTypeCode::Item:
				return true;

			case XmlTypeCode::Node:
				return $item->getIsNode();

			case XmlTypeCode::AnyAtomicType:
				return ! $item->getIsNode();

			case XmlTypeCode::UntypedAtomic:
				return ! $item->getIsNode() && $item->GetSchemaType() == XmlSchema::$UntypedAtomic;

			case XmlTypeCode::Document:

				if ( $item instanceof XPathNavigator )
				{
					/**
					 * @var XPathNavigator $nav
					 */
					$nav = $item;
					if ( $nav->getNodeType() == XPathNodeType::Root)
					{
						/**
						 * @var XPathNavigator $cur
						 */
						$cur = $nav->CloneInstance();
						if ( is_null( $this->SchemaElement ) )
						{
							if ( $cur->MoveToChild( XPathNodeType::Element ) && $this->MatchName( $cur, $context ) )
							{
								if ( is_null( $this->SchemaType ) || $this->SchemaType == XmlSchema::$UntypedAtomic )
								{
									return true;
								}
								/**
								 * @var IXmlSchemaInfo $schemaInfo
								 */
								$schemaInfo = $cur->getSchemaInfo();
								if ( ! is_null( $this->schemaInfo ) )
								{
									if ( DOMSchemaType::IsDerivedFrom( $schemaInfo->getSchemaType(), $this->SchemaType, XmlSchemaDerivationMethod::Empty_ ) )
										return ! $schemaInfo->getIsNil() || $this->Nillable;
								}
								else
								{
									return DOMSchemaType::IsDerivedFrom( XmlSchema::$UntypedAtomic, $this->SchemaType, XmlSchemaDerivationMethod::Empty_ );
								}
							}
						}
						else
						{
							if ( ! $cur->MoveToChild( XPathNodeType::Element ) )
							{
								return false;
							}

							/**
							 * @var IXmlSchemaInfo $schemaInfo
							 */
							$schemaInfo = $cur->getSchemaInfo();
							if ( ! is_null( $schemaInfo ) )
							{
								return $schemaInfo->getSchemaElement()->QualifiedName->equals( $this->SchemaElement->QualifiedName );
							}
						}
					}
				}
				break;

			case XmlTypeCode::Element:

				if ( $item instanceof XPathNavigator )
				{
					/**
				   	 * @var XPathNavigator $nav
				   	 */
					$nav = $item;
					if ( $nav->getNodeType() == XPathNodeType::Element )
					{
						if ( is_null( $this->SchemaElement ) )
						{
							if ( $this->MatchName( $nav, $context ) )
							{
								if ( is_null( $this->SchemaType ) || $this->SchemaType == XmlSchema::$UntypedAtomic )
								{
									return true;
								}

								/**
								 * @var IXmlSchemaInfo $schemaInfo
								 */
								$schemaInfo = $nav->getSchemaInfo();
								if ( is_null( $schemaInfo ) )
								{
									return DOMSchemaType::IsDerivedFrom( XmlSchema::$UntypedAtomic, $this->SchemaType, XmlSchemaDerivationMethod::Empty_ );
								}
								else
								{
									if ( DOMSchemaType::IsDerivedFrom( $schemaInfo->getSchemaType(), $this->SchemaType, XmlSchemaDerivationMethod::Empty_ ) )
									{
										return ! $schemaInfo->getIsNil() || $this->Nillable;
									}
								}
							}
						}
						else
						{
							/**
							 * @var IXmlSchemaInfo $schemaInfo
							 */
							$schemaInfo = $nav->getSchemaInfo();
							if ( ! is_null( $schemaInfo ) )
								return $schemaInfo->getSchemaElement()->QualifiedName->equals( $this->SchemaElement->QualifiedName );
						}
					}
				}
				break;

			case XmlTypeCode::Attribute:

				if ( $item instanceof XPathNavigator )
				{
					/**
					 * @var XPathNavigator $nav
					 */
					$nav = $item;
					if ( $nav->getNodeType() == XPathNodeType::Attribute )
					{
						if ( is_null( $this->SchemaAttribute ) )
						{
							if ( $this->MatchName( $nav, $context ) )
							{
								if ( is_null( $this->SchemaType ) || $this->SchemaType == XmlSchema::$UntypedAtomic )
								{
									return true;
								}
								/**
								 * @var IXmlSchemaInfo $schemaInfo
								 */
								$schemaInfo = $nav->getSchemaInfo();
								if ( is_null( $schemaInfo ) )
									return DOMSchemaType::IsDerivedFrom( XmlSchema::$UntypedAtomic, $this->SchemaType, XmlSchemaDerivationMethod::Empty_ );
								else
									return DOMSchemaType::IsDerivedFrom( $schemaInfo->getSchemaType(), $this->SchemaType, XmlSchemaDerivationMethod::Empty_ );
							}
						}
						else
						{
							/**
							 * @var IXmlSchemaInfo $schemaInfo
							 */
							$schemaInfo = $nav->getSchemaInfo();
							if ( ! is_null( $schemaInfo ) )
								return $schemaInfo->getSchemaAttribute()->QualifiedName->equals( $this->SchemaAttribute->QualifiedName );
						}
					}
				}
				break;

			case XmlTypeCode::ProcessingInstruction:

				if ( $item instanceof XPathNavigator )
				{
					/**
					 * @var XPathNavigator $nav
					 */
					$nav = $item;
					return ( $nav->getNodeType() == XPathNodeType::ProcessingInstruction && ( $this->NameTest->IsNameWildcard() || $this->NameTest->localName == $nav->getLocalName() ) );
				}
				break;

			case XmlTypeCode::Comment:

				if ( $item instanceof XPathNavigator )
				{
					/**
					 * @var XPathNavigator $nav
					 */
					$nav = $item;
					return $nav->getNodeType() == XPathNodeType::Comment;
				}
				break;

			case XmlTypeCode::Text:

				if ( $item instanceof XPathNavigator )
				{
					/**
					 * @var XPathNavigator $nav
					 */
					$nav = $item;
					return $nav->getNodeType() == XPathNodeType::Text || $nav->getNodeType() == XPathNodeType::SignificantWhitespace;
				}
				break;

			case XmlTypeCode::PositiveInteger:
				switch ( $item->GetSchemaType()->TypeCode )
				{
					case XmlTypeCode::Byte:
					case XmlTypeCode::Short:
					case XmlTypeCode::Int:
					case XmlTypeCode::Integer:
						return $item->getValueAsInt() >= 0;

					case XmlTypeCode::Long:
						return false;
				}
				break;

			case XmlTypeCode::NegativeInteger:
				switch ( $item->GetSchemaType()->TypeCode )
				{
					case XmlTypeCode::Byte:
					case XmlTypeCode::Short:
					case XmlTypeCode::Int:
					case XmlTypeCode::Integer:
						return $item->getValueAsInt() <= 0;

					case XmlTypeCode::Long:
						return false;
				}
				break;

			case XmlTypeCode::NonPositiveInteger:
				switch ( $item->GetSchemaType()->TypeCode )
				{
					case XmlTypeCode::Byte:
					case XmlTypeCode::Short:
					case XmlTypeCode::Int:
					case XmlTypeCode::Long:
					case XmlTypeCode::Integer:
						return $item->getValueAsInt() <= 0;
				}
				break;

			case XmlTypeCode::NonNegativeInteger:
				switch ( $item->GetSchemaType()->TypeCode )
				{
					case XmlTypeCode::Byte:
					case XmlTypeCode::Short:
					case XmlTypeCode::Int:
					case XmlTypeCode::Integer:
						return $item->getValueAsInt() >= 0;

					case XmlTypeCode::Long:
						return $item->getValueAsLong() == 0;

					case XmlTypeCode::UnsignedByte:
					case XmlTypeCode::UnsignedShort:
					case XmlTypeCode::UnsignedInt:
					case XmlTypeCode::UnsignedLong:
						return true;
				}
				break;

			case XmlTypeCode::Int:
			case XmlTypeCode::Byte:
			case XmlTypeCode::Short:
			case XmlTypeCode::Long:
			case XmlTypeCode::Integer:
			case XmlTypeCode::UnsignedLong:
			case XmlTypeCode::UnsignedInt:
			case XmlTypeCode::UnsignedByte:
			case XmlTypeCode::UnsignedShort:
				switch ( $item->GetSchemaType()->TypeCode )
				{
					case XmlTypeCode::Byte:
					case XmlTypeCode::Short:
					case XmlTypeCode::Int:
					case XmlTypeCode::Long:
					case XmlTypeCode::Integer:
					case XmlTypeCode::UnsignedByte:
					case XmlTypeCode::UnsignedShort:
					case XmlTypeCode::UnsignedInt:
					case XmlTypeCode::UnsignedLong:
						return true;

					case XmlTypeCode::Decimal:
						// BMS 2017-07-20 Changed result
						/**
						 * @var DecimalValue $dec
						 */
						$dec = $item->getTypedValue();
						return $dec->getIntegerPart() == $dec->getValue();
						// BMS 2017-07-10 Changed result
						return false;
						return ! $item->ValueAs( Types::$DecimalType, $context->NamespaceManager )->getIsDecimal();

					case XmlTypeCode::Double:
						// BMS 2017-07-10 Changed result
						return false;
						$value = $item->getTypedValue(); // ->ValueAs( Types::$DoubleType );
						return $value == floor( $value );
				}
				break;

			case XmlTypeCode::Entity:
				return	( $item->GetSchemaType()->TypeCode == XmlTypeCode::String ) ||
						( $item->GetSchemaType()->TypeCode == XmlTypeCode::Entity );

			// In PHP these are synonymous
			// BMS 2017-07-10 Changed result
			case XmlTypeCode::Float:
				switch ( $item->GetSchemaType()->TypeCode )
				{
					// Note the order of these cases is significant
					// If the order is different then float will be
					// reported as an instance of double and will fail
					// test instanceof49 in the exprSeqTypes test set.
					case XmlTypeCode::Float:
						return true;

					case XmlTypeCode::Double:
						if ( is_nan( $item->getTypedValue() ) )
						{
							return true;
						}
				}
				break;

			// BMS 2017-07-10 Changed result
			case XmlTypeCode::Double:
				switch ( $item->GetSchemaType()->TypeCode )
				{
					case XmlTypeCode::Double:
						return true;
				}
				break;

			// BMS 2017-07-10 Changed result
			case XmlTypeCode::Decimal:
				switch ( $item->GetSchemaType()->TypeCode )
				{
					case XmlTypeCode::Byte:
					case XmlTypeCode::Short:
					case XmlTypeCode::Int:
					case XmlTypeCode::Long:
					case XmlTypeCode::Integer:
					case XmlTypeCode::UnsignedByte:
					case XmlTypeCode::UnsignedShort:
					case XmlTypeCode::UnsignedInt:
					case XmlTypeCode::UnsignedLong:
					case XmlTypeCode::Decimal:
						return true;
				}
				break;

			default:
				{
					$schemaType = $item->getSchemaType();
					if ( ! is_null( $schemaType ) )
					{
						// BMS 2017-09-03	Added this condition. The XPath documentation indicates that an
						//					untyped type can be matched with any other type.
						// https://www.w3.org/TR/xpath-functions/#casting-from-primitive-to-primitive
						if ( $schemaType->TypeCode == XmlTypeCode::UntypedAtomic )
						{
							return true;
						}
						return DOMSchemaType::IsDerivedFrom( $schemaType, $this->SchemaType, XmlSchemaDerivationMethod::Empty_ );
					}
				}
				break;
		}
		return false;
	}
428577
public function containsItem(string $itemType, int $itemUid) : bool
    {
        return (bool)$this->getQueryBuilderForContainsMethods($itemType, $itemUid)->execute()->fetchColumn(0);
    }
959798
public function DeepEqualByNavigator( $nav1, $nav2 )
	{
		/**
		 * @var XPathNodeIterator $ni1
		 * @var XPathNodeIterator $ni2
		 */
		$ni1 = ElementOrderNodeIterator::fromNavigator( $this->context, $nav1 );
		$ni2 = ElementOrderNodeIterator::fromNavigator( $this->context, $nav2 );

		$count1 = $ni1->getCount();
		$count2 = $ni2->getCount();

		if ( $count1 != $count2 ) return false;

		if ( $count1 )
		{
			return $this->DeepEqualByIterator( $ni1, $ni2 );
		}
		else
		{
			$doni1 = ChildNodeIterator::fromNodeTest( $this->context, null, XPath2NodeIterator::Create( $nav1 ) );
			$doni2 = ChildNodeIterator::fromNodeTest( $this->context, null, XPath2NodeIterator::Create( $nav2 ) );

			return $this->DeepEqualByIterator( $doni1, $doni2 );

			return $this->NodeEqual( $nav1, $nav2 );
		}
	}
512267
public function isAncestor(ItemInterface $item): bool
    {
        $ancestors = $this->getBreadcrumbs();
        array_pop($ancestors);

        return in_array(
            $item,
            array_map(
                function (ParentNode $node) {
                    return $node->getItem();
                },
                $ancestors
            )
        );
    }
645751
static function equals( $entity1, $entity2 ) {
			if ( is_null( $entity1 ) && is_null( $entity2 ) ) {
				return true;
			} else if ( is_object( $entity1 ) && is_object( $entity2 ) ) {
				return ( $entity1->id == $entity2->id );
			} else if ( is_object( $entity1 ) ) {
				return is_null( $entity1->id );
			} else {
				return is_null( $entity2->id );
			}
		}
1333545
public function isSatisfiedBy($item): bool
    {
        return $this->one->isSatisfiedBy($item) && $this->two->isSatisfiedBy($item);
    }
83252
public static function mergeNodes($items1, $items2)
    {
        $ret = array();
        $items = array_merge($items1, $items2);
        $len = count($items);

        // collects non-repeated elements
        for ($i = 0; $i < $len; $i++) {
            $item = $items[$i];
            $position = Dom::searchNode($item, $items, $i + 1);
            if ($position === false) {
                array_push($ret, $item);
            }
        }
        
        return $ret;
    }
290524
private function equals(array $set1, array $set2): bool
    {
        return array_diff($set1, $set2) == array_diff($set2, $set1);
    }
878595
public static function equal(array $array1, array $array2)
    {
        if (static::count($array1) != static::count($array2)) {
            return false;
        } else {
            for ($i = 0; $i < static::count($array1); $i++) {
                if ($array1[$i] != $array2[$i]) {
                    return false;
                }
            }
        }

        return true;
    }