```
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Find Most Ancestral Elements with Progress Feedback</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 20px;
            position: relative; /* To position the target rectangle absolutely within the body */
        }

        /* Button Styles */
        .buttons {
            margin-bottom: 20px;
        }

        button {
            padding: 10px 20px;
            margin-right: 10px;
            font-size: 16px;
            cursor: pointer;
        }

        /* Progress Bar Styles */
        #progress-container {
            width: 100%;
            background-color: #f3f3f3;
            border: 1px solid #ccc;
            border-radius: 5px;
            overflow: hidden;
            margin-bottom: 10px;
            height: 25px;
        }

        #progress-bar {
            width: 0%;
            height: 100%;
            background-color: #4caf50;
            transition: width 0.2s ease;
        }

        #progress-text {
            display: block;
            text-align: right;
            margin-top: -25px;
            margin-bottom: 20px;
            font-weight: bold;
        }

        /* Elements Container */
        #elements-container {
            display: flex;
            flex-wrap: wrap;
            gap: 5px;
            max-height: 400px;
            overflow: auto;
            border: 1px solid #ccc;
            padding: 10px;
            position: relative; /* To position nested elements if needed */
        }

        /* Generated Elements */
        .generated-element {
            width: 50px;
            height: 50px;
            background-color: #add8e6;
            border: 1px solid #000;
            box-sizing: border-box;
        }

        /* Results */
        #results {
            margin-top: 20px;
        }

        #results ul {
            list-style-type: none;
            padding-left: 0;
        }

        #results li {
            padding: 5px 0;
        }

        /* Target Rectangle Styles */
        #target-rect {
            position: fixed;
            border: 2px solid red;
            pointer-events: none; /* Allows clicks to pass through the rectangle */
            z-index: 1000; /* Ensure it's on top of other elements */
            display: none; /* Hidden by default */
        }
    </style>
</head>
<body>

    <h1>Find Most Ancestral Elements with Progress Feedback</h1>

    <div class="buttons">
        <button id="generate-btn">Generate Elements</button>
        <button id="search-btn" disabled>Start Search</button>
    </div>

    <div id="progress-container">
        <div id="progress-bar"></div>
    </div>
    <span id="progress-text">0%</span>

    <h2>Generated Elements</h2>
    <div id="elements-container"></div>

    <!-- Target Rectangle -->
    <div id="target-rect"></div>

    <h2>Search Results</h2>
    <div id="results">
        <ul id="results-list"></ul>
    </div>

    <script>
        // Optimized findMostAncestralElements function using closure for caching
        const findMostAncestralElements = (function () {
            // Cached helper functions
            function isMatch(rect, target, z) {
                const { left, top, width: rectWidth, height: rectHeight } = rect;
                const { x, y, width: targetWidth, height: targetHeight } = target;
                const { abs } = Math;

                return (
                    abs(left - x) <= targetWidth * z &&
                    abs(top - y) <= targetHeight * z &&
                    abs(rectWidth - targetWidth) <= targetWidth * z &&
                    abs(rectHeight - targetHeight) <= targetHeight * z
                );
            }

            function isOverlap(rect, target) {
                const { left, top, width: rectWidth, height: rectHeight } = rect;
                const { x, y, width: targetWidth, height: targetHeight } = target;

                return (
                    left < x + targetWidth &&
                    left + rectWidth > x &&
                    top < y + targetHeight &&
                    top + rectHeight > y
                );
            }

            function createProcessBatchAsync(queue, target, z, exhaustive, iterationBatch, onProgress, head, results) {
                return function processBatchAsync() {
                    return new Promise((resolve) => {
                        let processed = 0;

                        while (head.value < queue.length && processed < iterationBatch) {
                            const elem = queue[head.value++];
                            const rect = elem.getBoundingClientRect();

                            if (isMatch(rect, target, z)) {
                                results.add(elem);
                                // ==== for debugging ====
                                console.log(elem);
                                debugger;
                                const res  = isMatch(rect, target, z);
                                // ==== for debugging ====
                                // Skip enqueuing children to maintain ancestry
                            } else if (exhaustive || isOverlap(rect, target)) {
                                const children = elem.children;
                                for (let i = 0, len = children.length; i < len; i++) {
                                    queue.push(children[i]);
                                }
                            }

                            processed++;
                        }

                        if (onProgress) {
                            onProgress({
                                processedElements: head.value,
                                totalQueued: queue.length,
                                uniqueResults: results.size,
                            });
                        }

                        resolve();
                    });
                };
            }

            /**
             * The optimized findMostAncestralElements function.
             *
             * @param {number} x - The X-coordinate of the target rectangle (relative to the viewport).
             * @param {number} y - The Y-coordinate of the target rectangle (relative to the viewport).
             * @param {number} width - The width of the target rectangle.
             * @param {number} height - The height of the target rectangle.
             * @param {number} errorRatio - The allowed error ratio (0 to 1) for matching.
             * @param {Object} [options] - Optional settings.
             * @param {boolean} [options.exhaustive=true] - If true, performs an exhaustive search.
             * @param {number} [options.asyncIterations] - Number of iterations per asynchronous batch.
             *                                             If null, undefined, or <= 0, executes synchronously.
             * @param {Function} [options.onProgress] - Callback function to receive progress updates.
             * @returns {Promise<Element[]>|Element[]} A promise that resolves to an array of the most ancestral DOM elements matching the criteria, or the array directly if not asynchronous.
             */
            return function findMostAncestralElements(
                x,
                y,
                width,
                height,
                errorRatio,
                options = {}
            ) {
                const {
                    exhaustive = true,
                    asyncIterations, // Renamed from 'async' to 'asyncIterations'
                    onProgress,
                } = options;

                // Determine if the function should execute asynchronously
                const isAsync = typeof asyncIterations === 'number' && asyncIterations > 0;
                const iterationBatch = isAsync ? asyncIterations : null;

                // Clamp errorRatio between 0 and 1
                const z = Math.max(0, Math.min(errorRatio, 1));

                const target = { x, y, width, height };
                const results = new Set();
                const queue = [document.body];
                const head = { value: 0 }; // Using an object to allow mutation inside closure

                if (isAsync) {
                    const processBatch = createProcessBatchAsync(
                        queue,
                        target,
                        z,
                        exhaustive,
                        iterationBatch,
                        onProgress,
                        head,
                        results
                    );

                    return new Promise((resolve) => {
                        function iterate() {
                            processBatch().then(() => {
                                if (head.value < queue.length) {
                                    setTimeout(iterate, 0);
                                } else {
                                    resolve(Array.from(results));
                                }
                            });
                        }
                        iterate();
                    });
                } else {
                    // Synchronous Processing
                    while (head.value < queue.length) {
                        const elem = queue[head.value++];
                        const rect = elem.getBoundingClientRect();

                        if (isMatch(rect, target, z)) {
                            results.add(elem);
                            continue; // Skip enqueuing children to maintain ancestry
                        }

                        if (exhaustive || isOverlap(rect, target)) {
                            const children = elem.children;
                            for (let i = 0, len = children.length; i < len; i++) {
                                queue.push(children[i]);
                            }
                        }

                        if (onProgress) {
                            onProgress({
                                processedElements: head.value,
                                totalQueued: queue.length,
                                uniqueResults: results.size,
                            });
                        }
                    }
                    return Array.from(results);
                }
            };
        })();

        // Function to update the progress bar and percentage text
        function updateProgressBar(progress) {
            const { processedElements, totalQueued } = progress;
            const progressBar = document.getElementById('progress-bar');
            const progressText = document.getElementById('progress-text');

            // Estimate percentage based on processed vs total queued
            const percentage = totalQueued > 0 ? Math.min((processedElements / (processedElements + totalQueued)) * 100, 100) : 100;

            progressBar.style.width = `${percentage}%`;
            progressText.textContent = `${percentage.toFixed(2)}%`;
        }

        // Function to generate many nested empty elements
        function generateElements(count = 1000, maxDepth = 5) {
            const container = document.getElementById('elements-container');
            container.innerHTML = ''; // Clear previous elements if any

            function createNestedElements(parent, currentDepth) {
                if (currentDepth > maxDepth) return;

                for (let i = 0; i < count / Math.pow(10, currentDepth); i++) {
                    const div = document.createElement('div');
                    div.classList.add('generated-element');
                    // Optional: Set styles or attributes to make elements identifiable
                    parent.appendChild(div);

                    // Randomly decide to nest further
                    if (Math.random() > 0.8) { // 20% chance to nest
                        createNestedElements(div, currentDepth + 1);
                    }
                }
            }

            // Start generating from the root container
            createNestedElements(container, 1);
        }

        // Function to display search results
        function displayResults(elements) {
            const resultsList = document.getElementById('results-list');
            resultsList.innerHTML = ''; // Clear previous results

            if (elements.length === 0) {
                resultsList.innerHTML = '<li>No matching elements found.</li>';
                return;
            }

            elements.forEach((elem, index) => {
                const li = document.createElement('li');
                li.textContent = `Element ${index + 1}: ${elem.tagName}${elem.id ? `#${elem.id}` : ''}${elem.className ? `.${elem.className}` : ''}`;
                resultsList.appendChild(li);
                console.log(elem);
            });
            window.results = Array.from(elements);
        }

        // Function to draw or update the target rectangle
        function drawTargetRect(targetRect) {
            const targetDiv = document.getElementById('target-rect');
            // Set the position and size based on targetRect
            targetDiv.style.left = `${targetRect.x}px`;
            targetDiv.style.top = `${targetRect.y}px`;
            targetDiv.style.width = `${targetRect.width}px`;
            targetDiv.style.height = `${targetRect.height}px`;
            // Display the rectangle
            targetDiv.style.display = 'block';
        }

        // Event listeners for buttons
        document.getElementById('generate-btn').addEventListener('click', () => {
            const generateBtn = document.getElementById('generate-btn');
            const searchBtn = document.getElementById('search-btn');
            const progressBar = document.getElementById('progress-bar');
            const progressText = document.getElementById('progress-text');
            const resultsList = document.getElementById('results-list');

            // Disable buttons during generation
            generateBtn.disabled = true;
            searchBtn.disabled = true;
            resultsList.innerHTML = '';
            progressBar.style.width = '0%';
            progressText.textContent = '0%';

            // Hide the target rectangle when generating new elements
            const targetDiv = document.getElementById('target-rect');
            targetDiv.style.display = 'none';

            // Generate elements
            setTimeout(() => { // Use setTimeout to allow UI to update
                generateElements(10000, 3); // Generate 10,000 elements with max depth 3
                generateBtn.disabled = false;
                searchBtn.disabled = false;
                alert('Element generation complete!');
            }, 100); // Slight delay to ensure buttons get disabled first
        });

        document.getElementById('search-btn').addEventListener('click', () => {
            const searchBtn = document.getElementById('search-btn');
            const resultsList = document.getElementById('results-list');

            // Clear previous results
            resultsList.innerHTML = '';
            // Reset progress bar
            const progressBar = document.getElementById('progress-bar');
            const progressText = document.getElementById('progress-text');
            progressBar.style.width = '0%';
            progressText.textContent = '0%';

            // Disable buttons during search
            const generateBtn = document.getElementById('generate-btn');
            generateBtn.disabled = true;
            searchBtn.disabled = true;

            // Define target rectangle (e.g., part of the viewport)
            const targetRect = {
                x: Math.random() * (window.innerWidth - 100), // Random x within viewport minus target width
                y: Math.random() * (window.innerHeight - 100), // Random y within viewport minus target height
                width: 100, // Fixed width
                height: 100 // Fixed height
            };
            const errorRatio = 0.5; // 50% error tolerance

            // Log or visually indicate target rectangle (optional)
            console.log('Target Rectangle:', targetRect);

            // Draw or update the target rectangle on the page
            drawTargetRect(targetRect);

            // Start search
            findMostAncestralElements(
                targetRect.x,
                targetRect.y,
                targetRect.width,
                targetRect.height,
                errorRatio,
                {
                    asyncIterations: 100, // Process 1000 elements per batch
                    onProgress: (progress) => {
                        updateProgressBar(progress);
                    }
                }
            ).then((matchingElements) => {
                displayResults(matchingElements);
                alert(`Search complete! Found ${matchingElements.length} matching elements.`);
                generateBtn.disabled = false;
                searchBtn.disabled = false;
            }).catch((error) => {
                console.error('Error during search:', error);
                alert('An error occurred during the search.');
                generateBtn.disabled = false;
                searchBtn.disabled = false;
            });
        });
    </script>

</body>
</html>
```

in generateElements, i want the elements generated to use inline style with absolute positioning.
the reason for this is for manual debugging when i move the rects's coordinates.
current the rects do not have positioning meaning their position is auto controlled by css.