float TextAutosizer::inflate(LayoutObject* parent, InflateBehavior behavior, float multiplier)
{
    Cluster* cluster = currentCluster();
    bool hasTextChild = false;

    LayoutObject* child = nullptr;
    if (parent->isLayoutBlock() && (parent->childrenInline() || behavior == DescendToInnerBlocks))
        child = toLayoutBlock(parent)->firstChild();
    else if (parent->isLayoutInline())
        child = toLayoutInline(parent)->firstChild();

    while (child) {
        if (child->isText()) {
            hasTextChild = true;
            // We only calculate this multiplier on-demand to ensure the parent block of this text
            // has entered layout.
            if (!multiplier)
                multiplier = cluster->m_flags & SUPPRESSING ? 1.0f : clusterMultiplier(cluster);
            applyMultiplier(child, multiplier);

            // FIXME: Investigate why MarkOnlyThis is sufficient.
            if (parent->isLayoutInline())
                child->setPreferredLogicalWidthsDirty(MarkOnlyThis);
        } else if (child->isLayoutInline()) {
            multiplier = inflate(child, behavior, multiplier);
        } else if (child->isLayoutBlock() && behavior == DescendToInnerBlocks
            && !classifyBlock(child, INDEPENDENT | EXPLICIT_WIDTH | SUPPRESSING)) {
            multiplier = inflate(child, behavior, multiplier);
        }
        child = child->nextSibling();
    }

    if (hasTextChild) {
        applyMultiplier(parent, multiplier); // Parent handles line spacing.
    } else if (!parent->isListItem()) {
        // For consistency, a block with no immediate text child should always have a
        // multiplier of 1.
        applyMultiplier(parent, 1);
    }

    if (parent->isListItem()) {
        float multiplier = clusterMultiplier(cluster);
        applyMultiplier(parent, multiplier);

        // The list item has to be treated special because we can have a tree such that you have
        // a list item for a form inside it. The list marker then ends up inside the form and when
        // we try to get the clusterMultiplier we have the wrong cluster root to work from and get
        // the wrong value.
        LayoutListItem* item = toLayoutListItem(parent);
        if (LayoutListMarker* marker = item->marker()) {
            applyMultiplier(marker, multiplier);
            marker->setPreferredLogicalWidthsDirty(MarkOnlyThis);
        }
    }

    return multiplier;
}
void TextAutosizer::record(const LayoutBlock* block)
{
    if (!m_pageInfo.m_settingEnabled)
        return;

    ASSERT(!m_blocksThatHaveBegunLayout.contains(block));

    if (!classifyBlock(block, INDEPENDENT | EXPLICIT_WIDTH))
        return;

    if (Fingerprint fingerprint = computeFingerprint(block))
        m_fingerprintMapper.addTentativeClusterRoot(block, fingerprint);
}
static CSSValueID classifyVariableRange(CSSParserTokenRange range, bool& hasReferences)
{
    hasReferences = false;

    range.consumeWhitespace();
    if (range.peek().type() == IdentToken) {
        CSSValueID id = range.consumeIncludingWhitespace().id();
        if (range.atEnd() && (id == CSSValueInherit || id == CSSValueInitial || id == CSSValueUnset))
            return id;
    }

    if (classifyBlock(range, hasReferences))
        return CSSValueInternalVariableValue;
    return CSSValueInvalid;
}
bool TextAutosizer::clusterHasEnoughTextToAutosize(Cluster* cluster, const LayoutBlock* widthProvider)
{
    if (cluster->m_hasEnoughTextToAutosize != UnknownAmountOfText)
        return cluster->m_hasEnoughTextToAutosize == HasEnoughText;

    const LayoutBlock* root = cluster->m_root;
    if (!widthProvider)
        widthProvider = clusterWidthProvider(root);

    // TextAreas and user-modifiable areas get a free pass to autosize regardless of text content.
    if (root->isTextArea() || (root->style() && root->style()->userModify() != READ_ONLY)) {
        cluster->m_hasEnoughTextToAutosize = HasEnoughText;
        return true;
    }

    if (cluster->m_flags & SUPPRESSING) {
        cluster->m_hasEnoughTextToAutosize = NotEnoughText;
        return false;
    }

    // 4 lines of text is considered enough to autosize.
    float minimumTextLengthToAutosize = widthFromBlock(widthProvider) * 4;

    float length = 0;
    LayoutObject* descendant = root->firstChild();
    while (descendant) {
        if (descendant->isLayoutBlock()) {
            if (classifyBlock(descendant, INDEPENDENT | SUPPRESSING)) {
                descendant = descendant->nextInPreOrderAfterChildren(root);
                continue;
            }
        } else if (descendant->isText()) {
            // Note: Using text().stripWhiteSpace().length() instead of resolvedTextLength() because
            // the lineboxes will not be built until layout. These values can be different.
            // Note: This is an approximation assuming each character is 1em wide.
            length += toLayoutText(descendant)->text().stripWhiteSpace().length() * descendant->style()->specifiedFontSize();

            if (length >= minimumTextLengthToAutosize) {
                cluster->m_hasEnoughTextToAutosize = HasEnoughText;
                return true;
            }
        }
        descendant = descendant->nextInPreOrder(root);
    }

    cluster->m_hasEnoughTextToAutosize = NotEnoughText;
    return false;
}
bool isValidVariableReference(CSSParserTokenRange range)
{
    range.consumeWhitespace();
    if (!CSSVariableParser::isValidVariableName(range.consumeIncludingWhitespace()))
        return false;
    if (range.atEnd())
        return true;

    if (range.consume().type() != CommaToken)
        return false;
    if (range.atEnd())
        return false;

    bool hasReferences = false;
    return classifyBlock(range, hasReferences);
}
bool classifyBlock(CSSParserTokenRange range, bool& hasReferences, bool isTopLevelBlock = true)
{
    while (!range.atEnd()) {
        if (range.peek().blockType() == CSSParserToken::BlockStart) {
            const CSSParserToken& token = range.peek();
            CSSParserTokenRange block = range.consumeBlock();
            if (token.functionId() == CSSValueVar) {
                if (!isValidVariableReference(block))
                    return false; // Bail if any references are invalid
                hasReferences = true;
                continue;
            }
            if (!classifyBlock(block, hasReferences, false))
                return false;
            continue;
        }

        ASSERT(range.peek().blockType() != CSSParserToken::BlockEnd);

        const CSSParserToken& token = range.consume();
        switch (token.type()) {
        case DelimiterToken: {
            if (token.delimiter() == '!' && isTopLevelBlock)
                return false;
            break;
        }
        case RightParenthesisToken:
        case RightBraceToken:
        case RightBracketToken:
        case BadStringToken:
        case BadUrlToken:
            return false;
        case SemicolonToken:
            if (isTopLevelBlock)
                return false;
            break;
        default:
            break;
        }
    }
    return true;
}
TextAutosizer::Cluster* TextAutosizer::maybeCreateCluster(const LayoutBlock* block)
{
    BlockFlags flags = classifyBlock(block);
    if (!(flags & POTENTIAL_ROOT))
        return nullptr;

    Cluster* parentCluster = m_clusterStack.isEmpty() ? nullptr : currentCluster();
    ASSERT(parentCluster || block->isLayoutView());

    // If a non-independent block would not alter the SUPPRESSING flag, it doesn't need to be a cluster.
    bool parentSuppresses = parentCluster && (parentCluster->m_flags & SUPPRESSING);
    if (!(flags & INDEPENDENT) && !(flags & EXPLICIT_WIDTH) && !!(flags & SUPPRESSING) == parentSuppresses)
        return nullptr;

    Cluster* cluster = new Cluster(block, flags, parentCluster, getSupercluster(block));
#ifdef AUTOSIZING_DOM_DEBUG_INFO
    // Non-SUPPRESSING clusters are annotated in clusterMultiplier.
    if (flags & SUPPRESSING)
        writeClusterDebugInfo(cluster);
#endif
    return cluster;
}
const LayoutObject* TextAutosizer::findTextLeaf(const LayoutObject* parent, size_t& depth, TextLeafSearch firstOrLast) const
{
    // List items are treated as text due to the marker.
    if (parent->isListItem())
        return parent;

    if (parent->isText())
        return parent;

    ++depth;
    const LayoutObject* child = (firstOrLast == First) ? parent->slowFirstChild() : parent->slowLastChild();
    while (child) {
        // Note: At this point clusters may not have been created for these blocks so we cannot rely
        //       on m_clusters. Instead, we use a best-guess about whether the block will become a cluster.
        if (!classifyBlock(child, INDEPENDENT)) {
            if (const LayoutObject* leaf = findTextLeaf(child, depth, firstOrLast))
                return leaf;
        }
        child = (firstOrLast == First) ? child->nextSibling() : child->previousSibling();
    }
    --depth;

    return nullptr;
}
bool TextAutosizer::clusterWouldHaveEnoughTextToAutosize(const LayoutBlock* root, const LayoutBlock* widthProvider)
{
    Cluster hypotheticalCluster(root, classifyBlock(root), nullptr);
    return clusterHasEnoughTextToAutosize(&hypotheticalCluster, widthProvider);
}
//
//	Perform classification
//
//	Input:	trainSet, testSet
//	Output:	vector of assigned labels
//
void CacheEfficient_kNCN::classify(const SampleSet& trainSet, const SampleSet& testSet) {	  
	for (int testBlockIndex = 0; testBlockIndex < nrTestSetBlocks - 1; testBlockIndex++) {
		SampleSet testSetBlock(testSet.nrClasses, testSet.nrDims, nrSamplesInTestBlock);
		for (int samIndex = 0; samIndex < nrSamplesInTestBlock; samIndex++)	{
			testSetBlock[samIndex] = testSet[testBlockIndex*nrSamplesInTestBlock + samIndex]; 
		}
		for (int trainBlockIndex = 0; trainBlockIndex < nrTrainSetBlocks - 1; trainBlockIndex++) {
			SampleSet trainSetBlock(trainSet.nrClasses, trainSet.nrDims, nrSamplesInTrainBlock);
			for (int samIndex = 0; samIndex < nrSamplesInTrainBlock; samIndex++) {
				trainSetBlock[samIndex] = trainSet[trainBlockIndex*nrSamplesInTrainBlock + samIndex]; 
			}
			classifyBlock(trainSetBlock, testSetBlock, this->blockDistances[testBlockIndex][trainBlockIndex], 
				this->blockNCNDists[testBlockIndex][trainBlockIndex], trainBlockIndex*nrSamplesInTrainBlock, testBlockIndex*nrSamplesInTestBlock);
		}
		// last train block
		SampleSet trainSetBlock(trainSet.nrClasses, trainSet.nrDims, nrRemainingTrainSamples);
		for (int samIndex = 0; samIndex < trainSetBlock.nrSamples; samIndex++) {
			trainSetBlock[samIndex] = trainSet[firstRemainingTrainSampleIndex + samIndex]; 
		}
		classifyBlock(trainSetBlock, testSetBlock, this->blockDistances[testBlockIndex][nrTrainSetBlocks - 1], 
			this->blockNCNDists[testBlockIndex][nrTrainSetBlocks - 1], firstRemainingTrainSampleIndex, testBlockIndex*nrSamplesInTestBlock);
		
		// store NCNs for every testSample from current testSetBlock from all trainBlocks for every k
		for (int samIndex = 0; samIndex < testSetBlock.nrSamples; samIndex++) {
			for (int trainBlockIndex = 0; trainBlockIndex < nrTrainSetBlocks; trainBlockIndex++) {
				for (int kIndex = 0; kIndex < k; kIndex++)	{
					closestNCNDists[samIndex][trainBlockIndex*k + kIndex] = blockNCNDists[testBlockIndex][trainBlockIndex][samIndex][kIndex];
				}
			}
		}
		for (int samIndex = 0; samIndex < testSetBlock.nrSamples; samIndex++) {
			// sort NCNs on basis of distance from sample
			qsort(closestNCNDists[samIndex], nrTrainSetBlocks*k, sizeof(NCNDistance), compareNCNDistances);
			// assign label on basis of closestNCNDists
			results[testBlockIndex*nrSamplesInTestBlock + samIndex] = assignLabel(closestNCNDists[samIndex], k);
			//LOG4CXX_DEBUG(logger, "" << closestNCNDists[samIndex][0].sampleIndex << " " << closestNCNDists[samIndex][0].distValue
			//	<< " " << closestNCNDists[samIndex][1].sampleIndex << " " << closestNCNDists[samIndex][1].distValue
			//	<< " " << closestNCNDists[samIndex][2].sampleIndex << " " << closestNCNDists[samIndex][2].distValue
			//	<< " " << closestNCNDists[samIndex][3].sampleIndex << " " << closestNCNDists[samIndex][3].distValue
			//	<< " " << closestNCNDists[samIndex][4].sampleIndex << " " << closestNCNDists[samIndex][4].distValue
			//	);
		}
	}

	// last test block
	SampleSet testSetBlock(testSet.nrClasses, testSet.nrDims, nrRemainingTestSamples);
	for (int samIndex = 0; samIndex < nrRemainingTestSamples; samIndex++) {
		testSetBlock[samIndex] = testSet[firstRemainingTestSampleIndex + samIndex]; 
	}
	for (int trainBlockIndex = 0; trainBlockIndex < nrTrainSetBlocks - 1; trainBlockIndex++) {
		SampleSet trainSetBlock(trainSet.nrClasses, trainSet.nrDims, nrSamplesInTrainBlock);
		for (int samIndex = 0; samIndex < nrSamplesInTrainBlock; samIndex++) {
			trainSetBlock[samIndex] = trainSet[trainBlockIndex*nrSamplesInTrainBlock + samIndex]; 
		}
		classifyBlock(trainSetBlock, testSetBlock, this->blockDistances[nrTestSetBlocks - 1][trainBlockIndex], 
			this->blockNCNDists[nrTestSetBlocks - 1][trainBlockIndex], trainBlockIndex*nrSamplesInTrainBlock, firstRemainingTestSampleIndex);
	}
	// last train block for last test block
	SampleSet trainSetBlock(trainSet.nrClasses, trainSet.nrDims, nrRemainingTrainSamples);
	for (int samIndex = 0; samIndex < nrRemainingTrainSamples; samIndex++) {
		trainSetBlock[samIndex] = trainSet[firstRemainingTrainSampleIndex + samIndex]; 
	}
	classifyBlock(trainSetBlock, testSetBlock, this->blockDistances[nrTestSetBlocks - 1][nrTrainSetBlocks - 1], 
		this->blockNCNDists[nrTestSetBlocks - 1][nrTrainSetBlocks - 1], firstRemainingTrainSampleIndex, firstRemainingTestSampleIndex);

	// find the closest candidate for every testSample from current testSetBlock for kNCN from all trainBlocks for every k
	for (int samIndex = 0; samIndex < testSetBlock.nrSamples; samIndex++) {
		for (int trainBlockIndex = 0; trainBlockIndex < nrTrainSetBlocks; trainBlockIndex++) {
			for (int kIndex = 0; kIndex < k; kIndex++)	{
				closestNCNDists[samIndex][trainBlockIndex*k + kIndex] = blockNCNDists[nrTestSetBlocks - 1][trainBlockIndex][samIndex][kIndex];
			}
		}
	}
	for (int samIndex = 0; samIndex < testSetBlock.nrSamples; samIndex++) {
		// sort NCNs on basis of distance from sample
		qsort(closestNCNDists[samIndex], nrTrainSetBlocks*k, sizeof(NCNDistance), compareNCNDistances);
		// assign label on basis of closestNCNDists
		results[firstRemainingTestSampleIndex + samIndex] = assignLabel(closestNCNDists[samIndex], k);
		//LOG4CXX_DEBUG(logger, "" << closestNCNDists[samIndex][0].sampleIndex << " " << closestNCNDists[samIndex][0].distValue
		//	<< " " << closestNCNDists[samIndex][1].sampleIndex << " " << closestNCNDists[samIndex][1].distValue
		//	<< " " << closestNCNDists[samIndex][2].sampleIndex << " " << closestNCNDists[samIndex][2].distValue
		//	<< " " << closestNCNDists[samIndex][3].sampleIndex << " " << closestNCNDists[samIndex][3].distValue
		//	<< " " << closestNCNDists[samIndex][4].sampleIndex << " " << closestNCNDists[samIndex][4].distValue
		//	);
	}
}