diff --git a/nemo_skills/evaluation/evaluator/__init__.py b/nemo_skills/evaluation/evaluator/__init__.py index 269bff939c..907f94763a 100644 --- a/nemo_skills/evaluation/evaluator/__init__.py +++ b/nemo_skills/evaluation/evaluator/__init__.py @@ -27,7 +27,11 @@ eval_livebench_coding, eval_livecodebench_pro, ) -from nemo_skills.evaluation.evaluator.compute_eval import ComputeEvalEvaluator + +try: + from nemo_skills.evaluation.evaluator.compute_eval import ComputeEvalEvaluator +except ImportError: + ComputeEvalEvaluator = None from nemo_skills.evaluation.evaluator.icpc import ICPCEvaluator from nemo_skills.evaluation.evaluator.ifbench import eval_ifbench from nemo_skills.evaluation.evaluator.ifeval import eval_if @@ -40,7 +44,12 @@ from nemo_skills.evaluation.evaluator.mcq import eval_mcq from nemo_skills.evaluation.evaluator.mmau_pro import eval_mmau_pro from nemo_skills.evaluation.evaluator.mrcr import eval_mrcr -from nemo_skills.evaluation.evaluator.ruler import eval_ruler, eval_ruler2 + +try: + from nemo_skills.evaluation.evaluator.ruler import eval_ruler, eval_ruler2 +except ImportError: + eval_ruler = None + eval_ruler2 = None from nemo_skills.evaluation.evaluator.scicode import eval_scicode EVALUATOR_MAP = { @@ -50,8 +59,6 @@ "ifbench": eval_ifbench, "bfcl": eval_bfcl, "multichoice": eval_mcq, - "ruler": eval_ruler, - "ruler2": eval_ruler2, "livecodebench": eval_livecodebench, "livebench_coding": eval_livebench_coding, "livecodebench_pro": eval_livecodebench_pro, @@ -61,6 +68,11 @@ "human_eval_infilling": eval_human_eval_infilling, "mmau-pro": eval_mmau_pro, } +# Optional evaluators (require additional dependencies) +if eval_ruler is not None: + EVALUATOR_MAP["ruler"] = eval_ruler +if eval_ruler2 is not None: + EVALUATOR_MAP["ruler2"] = eval_ruler2 # Evaluator class mapping, other evaluators can be added here as they're converted to classes EVALUATOR_CLASS_MAP = { @@ -71,8 +83,10 @@ "icpc": ICPCEvaluator, "audio": AudioEvaluator, "bird": BirdEvaluator, - "compute-eval": ComputeEvalEvaluator, } +# Optional evaluators (require additional dependencies) +if ComputeEvalEvaluator is not None: + EVALUATOR_CLASS_MAP["compute-eval"] = ComputeEvalEvaluator # Validation: Ensure no overlap between class and function maps _class_types = set(EVALUATOR_CLASS_MAP.keys()) diff --git a/nemo_skills/inference/generate.py b/nemo_skills/inference/generate.py index e05a532793..16b0adff54 100644 --- a/nemo_skills/inference/generate.py +++ b/nemo_skills/inference/generate.py @@ -207,13 +207,15 @@ class GenerationTaskConfig: enable_litellm_cache: bool = False # List of content types to drop from messages (e.g., base64 audio) to keep output files smaller - drop_content_types: list[str] = field(default_factory=lambda: ["audio_url"]) + drop_content_types: list[str] = field(default_factory=lambda: ["input_audio"]) # Audio configuration - set by benchmarks that need audio processing (mmau-pro, audiobench, etc.) enable_audio: bool = False # Enable audio preprocessing (set by benchmark configs) enable_audio_chunking: bool = True audio_chunk_task_types: list[str] | None = None # If None, chunk all task types; if specified, only chunk these chunk_audio_threshold_sec: int = 30 # Duration in seconds for each audio chunk + # Audio format for API requests (input_audio only) + audio_format: str = "input_audio" # Evaluation setup if requested. If eval_type is set to None, evaluation is skipped eval_type: str | None = None # "lean4-proof", "math", etc. @@ -432,11 +434,11 @@ def setup_llm(self): # Build server config, potentially switching to vllm_multimodal for audio tasks server_config = dict(self.cfg.server) - if needs_audio and server_config.get("server_type") not in ["vllm", "vllm_multimodal"]: + if needs_audio and server_config.get("server_type") not in ["vllm", "vllm_multimodal", "api_multimodal"]: LOG.warning( - f"enable_audio is set but server_type is '{server_config.get('server_type')}'. " - "Audio processing is only supported for vllm_multimodal server types. " - "Audio will not be processed." + f"Audio enabled with server_type='{server_config.get('server_type')}'. " + "Advanced audio preprocessing (base64 encoding, chunking) is only available for " + "vllm_multimodal and api_multimodal. Server will receive raw audio paths." ) if needs_audio and server_config.get("server_type") in [ "vllm", @@ -453,6 +455,16 @@ def setup_llm(self): "chunk_audio_threshold_sec": self.cfg.chunk_audio_threshold_sec, } ) + if needs_audio and server_config.get("server_type") == "api_multimodal": + # Pass audio config to api_multimodal (has built-in audio processing) + server_config.update( + { + "enable_audio_chunking": self.cfg.enable_audio_chunking, + "audio_chunk_task_types": self.cfg.audio_chunk_task_types, + "chunk_audio_threshold_sec": self.cfg.chunk_audio_threshold_sec, + "audio_format": self.cfg.audio_format, + } + ) if self.cfg.code_execution: llm = get_code_execution_model( diff --git a/nemo_skills/inference/model/__init__.py b/nemo_skills/inference/model/__init__.py index 9c14a4b0d8..db5b8526a3 100644 --- a/nemo_skills/inference/model/__init__.py +++ b/nemo_skills/inference/model/__init__.py @@ -17,6 +17,8 @@ from nemo_skills.mcp.utils import locate from nemo_skills.utils import python_doc_to_cmd_help +from .api_multimodal import APIMultimodal + # NIM models (speech) from .asr_nim import ASRNIMModel @@ -65,6 +67,7 @@ "sglang": SGLangModel, "tts_nim": TTSNIMModel, "asr_nim": ASRNIMModel, + "api_multimodal": APIMultimodal, } diff --git a/nemo_skills/inference/model/api_multimodal.py b/nemo_skills/inference/model/api_multimodal.py new file mode 100644 index 0000000000..22f58c8338 --- /dev/null +++ b/nemo_skills/inference/model/api_multimodal.py @@ -0,0 +1,311 @@ +# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""API multimodal model with support for audio input. + +This module provides a multimodal model class that handles: +- Audio INPUT: encoding audio files to base64, chunking long audio +- Uses input_audio format (OpenAI native) +""" + +import copy +import logging +import os + +from nemo_skills.utils import get_logger_name + +from .audio_utils import ( + audio_file_to_base64, + chunk_audio, + load_audio_file, + make_audio_content_block, + save_audio_chunk_to_base64, +) +from .openai import OpenAIModel + +LOG = logging.getLogger(get_logger_name(__file__)) + + +class APIMultimodal(OpenAIModel): + """OpenAI-compatible client with NVIDIA Inference API defaults and audio support. + + Audio INPUT capabilities: + 1. Converts audio file paths to base64-encoded input_audio format + 2. Chunks long audio files for models with duration limits + 3. Aggregates results from chunked audio processing + + Example usage: + # Using NVIDIA API with Gemini + model = APIMultimodal(model="gcp/google/gemini-2.5-pro") + """ + + # Default base URL for NVIDIA Inference API + DEFAULT_BASE_URL = "https://inference-api.nvidia.com" + + def __init__( + self, + model: str | None = None, + base_url: str | None = None, + enable_audio_chunking: bool = True, + audio_chunk_task_types: list[str] | None = None, + chunk_audio_threshold_sec: int = 30, + audio_format: str = "input_audio", + **kwargs, + ): + """Initialize APIMultimodal with audio I/O support. + + Args: + model: Model name (e.g., "gcp/google/gemini-2.5-pro") + base_url: API base URL. Defaults to NVIDIA Inference API. + enable_audio_chunking: Master switch for audio chunking. + audio_chunk_task_types: If None, chunk all task types; if specified, only chunk these. + chunk_audio_threshold_sec: Audio duration threshold for chunking (in seconds). + audio_format: Format for audio content (must be "input_audio"). + **kwargs: Other parameters passed to OpenAIModel/BaseModel. + """ + # Use NVIDIA API as default if no base_url specified + if base_url is None: + base_url = os.getenv("NEMO_SKILLS_INFERENCE_API_URL", self.DEFAULT_BASE_URL) + + super().__init__( + model=model, + base_url=base_url, + **kwargs, + ) + + # Audio INPUT config + self.enable_audio_chunking = enable_audio_chunking + self.audio_chunk_task_types = audio_chunk_task_types + self.chunk_audio_threshold_sec = chunk_audio_threshold_sec + if audio_format != "input_audio": + raise ValueError(f"Unsupported audio_format '{audio_format}'. Only 'input_audio' is supported.") + self.audio_format = audio_format + + # ===================== + # Audio INPUT methods + # ===================== + + def content_text_to_list(self, message: dict) -> dict: + """Convert message content with audio to proper list format. + + Handles 'audio' or 'audios' keys in messages and converts them to + base64-encoded audio content items. + + CRITICAL: Audio must come BEFORE text for models to process correctly. + + Args: + message: Message dict that may contain 'audio' or 'audios' fields. + + Returns: + New message dict with content converted to list format including audio. + """ + if "audio" not in message and "audios" not in message: + return message + + result = copy.deepcopy(message) + + if "content" not in result: + raise KeyError("Missing required 'content' in message") + content = result["content"] + if isinstance(content, str): + result["content"] = [{"type": "text", "text": content}] + elif not isinstance(content, list): + raise TypeError(f"Unexpected content type: {type(content)}") + + audio_items = [] + + if "audio" in result: + audio = result.pop("audio") + audio_path = os.path.join(self.data_dir, audio["path"]) + base64_audio = audio_file_to_base64(audio_path) + audio_items.append(make_audio_content_block(base64_audio, self.audio_format)) + elif "audios" in result: + for audio in result.pop("audios"): + audio_path = os.path.join(self.data_dir, audio["path"]) + base64_audio = audio_file_to_base64(audio_path) + audio_items.append(make_audio_content_block(base64_audio, self.audio_format)) + + if audio_items: + result["content"] = audio_items + result["content"] + + return result + + def _needs_audio_chunking(self, messages: list[dict], task_type: str = None) -> tuple[bool, str, float]: + """Check if audio in messages needs chunking. + + Args: + messages: List of message dicts. + task_type: Optional task type for chunking filtering. + + Returns: + Tuple of (needs_chunking, audio_path, duration). + """ + if not self.enable_audio_chunking: + return False, None, 0.0 + + # Check if task type should be chunked (if filter is specified) + if self.audio_chunk_task_types is not None: + if task_type not in self.audio_chunk_task_types: + return False, None, 0.0 + + # Find audio in messages + for msg in messages: + if msg["role"] == "user": + if "audio" in msg: + audio_info = msg["audio"] + elif "audios" in msg: + audios = msg["audios"] + audio_info = audios[0] if audios else {} + else: + continue + if audio_info and "path" in audio_info: + audio_path = os.path.join(self.data_dir, audio_info["path"]) + + if not os.path.exists(audio_path): + return False, None, 0.0 + + # Load audio to check duration + audio_array, sampling_rate = load_audio_file(audio_path) + duration = len(audio_array) / sampling_rate + + if duration > self.chunk_audio_threshold_sec: + return True, audio_path, duration + + return False, None, 0.0 + + async def _generate_with_chunking( + self, + messages: list[dict], + audio_path: str, + duration: float, + tokens_to_generate: int | None = None, + **kwargs, + ) -> dict: + """Generate by chunking long audio and aggregating results. + + Args: + messages: Original messages containing audio reference. + audio_path: Path to the audio file to chunk. + duration: Duration of audio in seconds. + tokens_to_generate: Max tokens per chunk. + **kwargs: Additional generation parameters. + + Returns: + Aggregated result with combined generation from all chunks. + """ + audio_array, sampling_rate = load_audio_file(audio_path) + chunks = chunk_audio(audio_array, sampling_rate, self.chunk_audio_threshold_sec) + + LOG.info(f"Chunking audio ({duration:.1f}s) into {len(chunks)} chunks of {self.chunk_audio_threshold_sec}s") + + if not chunks: + raise RuntimeError("No audio chunks generated - audio may be too short or invalid") + + chunk_results = [] + result = None + + # Track cumulative statistics across chunks + total_input_tokens = 0 + total_generated_tokens = 0 + total_time = 0.0 + + for chunk_idx, audio_chunk in enumerate(chunks): + chunk_messages = [] + + for msg in messages: + msg_copy = copy.deepcopy(msg) + + if msg_copy["role"] == "user" and ("audio" in msg_copy or "audios" in msg_copy): + chunk_base64 = save_audio_chunk_to_base64(audio_chunk, sampling_rate) + + if "content" not in msg_copy: + raise KeyError("Missing required 'content' in message") + content = msg_copy["content"] + if isinstance(content, str): + text_content = [{"type": "text", "text": content}] + else: + text_content = content + + # Add audio chunk at the beginning (before text) + msg_copy["content"] = [make_audio_content_block(chunk_base64, self.audio_format)] + text_content + + # Remove original audio fields + msg_copy.pop("audio", None) + msg_copy.pop("audios", None) + + chunk_messages.append(msg_copy) + + # Generate for this chunk using parent's generate_async + result = await super().generate_async( + prompt=chunk_messages, tokens_to_generate=tokens_to_generate, **kwargs + ) + + # Sum statistics from each chunk + total_input_tokens += result.get("input_tokens", 0) + total_generated_tokens += result.get("generated_tokens", 0) + total_time += result.get("time_elapsed", 0.0) + + generation = result["generation"] + chunk_results.append(generation.strip()) + + # Aggregate results + aggregated_text = " ".join(chunk_results) + + if not result: + raise RuntimeError("Audio chunk generation returned no result") + + final_result = result.copy() + final_result["generation"] = aggregated_text + final_result["num_audio_chunks"] = len(chunks) + final_result["audio_duration"] = duration + # Update with summed statistics + final_result["input_tokens"] = total_input_tokens + final_result["generated_tokens"] = total_generated_tokens + final_result["time_elapsed"] = total_time + + return final_result + + async def generate_async( + self, + prompt: str | list[dict] | None = None, + tokens_to_generate: int | None = None, + task_type: str = None, + **kwargs, + ) -> dict: + """Generate with automatic audio chunking for long audio files. + + This override checks if the prompt (messages) contains long audio. + If so, it chunks the audio, processes each chunk separately, and aggregates results. + + Args: + prompt: Either a string (text completion) or list of messages (chat). + tokens_to_generate: Max tokens to generate. + task_type: Optional task type for chunking filtering. + **kwargs: Additional arguments passed to the underlying model. + + Returns: + Generation result dict with 'generation' key and optional metadata. + """ + if isinstance(prompt, list): + messages = prompt + needs_chunking, audio_path, duration = self._needs_audio_chunking(messages, task_type) + + if needs_chunking: + return await self._generate_with_chunking(messages, audio_path, duration, tokens_to_generate, **kwargs) + + # No chunking needed - convert audio fields to base64 format + prompt = [self.content_text_to_list(msg) for msg in messages] + + # Call parent's generate_async + return await super().generate_async(prompt=prompt, tokens_to_generate=tokens_to_generate, **kwargs) diff --git a/nemo_skills/inference/model/audio_utils.py b/nemo_skills/inference/model/audio_utils.py index 02c8eaf459..5f9d954bd1 100644 --- a/nemo_skills/inference/model/audio_utils.py +++ b/nemo_skills/inference/model/audio_utils.py @@ -130,21 +130,16 @@ def save_audio_chunk_to_base64(audio_chunk, sampling_rate) -> str: return encoded -def make_audio_content_block(base64_audio: str, audio_format: str = "audio_url") -> dict: - """Create an audio content block in the specified format. +def make_audio_content_block(base64_audio: str, audio_format: str = "input_audio") -> dict: + """Create an audio content block in the OpenAI native input_audio format. Args: base64_audio: Base64-encoded audio data. - audio_format: Format to use: - - "audio_url": Data URI format for vLLM/Qwen - - "input_audio": OpenAI native format for NVIDIA API/Gemini/Azure + audio_format: Must be "input_audio". Returns: Audio content block dict for API request. """ - if audio_format == "input_audio": - # OpenAI native format (works with NVIDIA API / Gemini / Azure) - return {"type": "input_audio", "input_audio": {"data": base64_audio, "format": "wav"}} - else: - # Data URI format (works with vLLM / Qwen) - return {"type": "audio_url", "audio_url": {"url": f"data:audio/wav;base64,{base64_audio}"}} + if audio_format != "input_audio": + raise ValueError(f"Unsupported audio_format '{audio_format}'. Only 'input_audio' is supported.") + return {"type": "input_audio", "input_audio": {"data": base64_audio, "format": "wav"}} diff --git a/nemo_skills/inference/model/vllm_multimodal.py b/nemo_skills/inference/model/vllm_multimodal.py index c2285b0ccc..5ce85e0baf 100644 --- a/nemo_skills/inference/model/vllm_multimodal.py +++ b/nemo_skills/inference/model/vllm_multimodal.py @@ -32,6 +32,7 @@ audio_file_to_base64, chunk_audio, load_audio_file, + make_audio_content_block, save_audio_chunk_to_base64, ) from .vllm import VLLMModel @@ -46,7 +47,7 @@ class VLLMMultimodalModel(VLLMModel): """VLLMModel with support for audio input and output. Audio INPUT capabilities: - 1. Converts audio file paths to base64-encoded audio_url format + 1. Converts audio file paths to base64-encoded input_audio format 2. Chunks long audio files for models with duration limits 3. Aggregates results from chunked audio processing @@ -158,7 +159,7 @@ def content_text_to_list(self, message: dict) -> dict: """Convert message content with audio to proper list format. Handles 'audio' or 'audios' keys in messages and converts them to - base64-encoded audio_url content items. + base64-encoded input_audio content items. CRITICAL: Audio must come BEFORE text for Qwen models to transcribe correctly. @@ -166,54 +167,38 @@ def content_text_to_list(self, message: dict) -> dict: message: Message dict that may contain 'audio' or 'audios' fields. Returns: - Message dict with content converted to list format including audio. + New message dict with content converted to list format including audio. """ if "audio" not in message and "audios" not in message: return message - content = message.get("content", "") + result = copy.deepcopy(message) + + if "content" not in result: + raise KeyError("Missing required 'content' in message") + content = result["content"] if isinstance(content, str): - message["content"] = [{"type": "text", "text": content}] - elif isinstance(content, list): - message["content"] = content - else: + result["content"] = [{"type": "text", "text": content}] + elif not isinstance(content, list): raise TypeError(f"Unexpected content type: {type(content)}") audio_items = [] - if "audio" in message: - audio = message["audio"] + if "audio" in result: + audio = result.pop("audio") audio_path = os.path.join(self.data_dir, audio["path"]) base64_audio = audio_file_to_base64(audio_path) - audio_message = {"type": "audio_url", "audio_url": {"url": f"data:audio/wav;base64,{base64_audio}"}} - audio_items.append(audio_message) - del message["audio"] # Remove original audio field after conversion - elif "audios" in message: - for audio in message["audios"]: + audio_items.append(make_audio_content_block(base64_audio, "input_audio")) + elif "audios" in result: + for audio in result.pop("audios"): audio_path = os.path.join(self.data_dir, audio["path"]) base64_audio = audio_file_to_base64(audio_path) - audio_message = {"type": "audio_url", "audio_url": {"url": f"data:audio/wav;base64,{base64_audio}"}} - audio_items.append(audio_message) - del message["audios"] # Remove original audios field after conversion + audio_items.append(make_audio_content_block(base64_audio, "input_audio")) - # Insert audio items at the BEGINNING of content list (before text) if audio_items: - message["content"] = audio_items + message["content"] - - return message - - def _preprocess_messages_for_model(self, messages: list[dict]) -> list[dict]: - """Preprocess messages - creates copies to avoid mutation. - - Note: /no_think suffix is passed through unchanged (handled by the model). - - Args: - messages: List of message dicts. + result["content"] = audio_items + result["content"] - Returns: - Copy of message dicts. - """ - return [copy.deepcopy(msg) for msg in messages] + return result def _needs_audio_chunking(self, messages: list[dict], task_type: str = None) -> tuple[bool, str, float]: """Check if audio in messages needs chunking. @@ -235,11 +220,14 @@ def _needs_audio_chunking(self, messages: list[dict], task_type: str = None) -> # Find audio in messages for msg in messages: - if msg.get("role") == "user": - audio_info = msg.get("audio") - if not audio_info: - audios = msg.get("audios", []) + if msg["role"] == "user": + if "audio" in msg: + audio_info = msg["audio"] + elif "audios" in msg: + audios = msg["audios"] audio_info = audios[0] if audios else {} + else: + continue if audio_info and "path" in audio_info: audio_path = os.path.join(self.data_dir, audio_info["path"]) @@ -300,16 +288,16 @@ async def _generate_with_chunking( if msg_copy["role"] == "user" and ("audio" in msg_copy or "audios" in msg_copy): chunk_base64 = save_audio_chunk_to_base64(audio_chunk, sampling_rate) - content = msg_copy.get("content", "") + if "content" not in msg_copy: + raise KeyError("Missing required 'content' in message") + content = msg_copy["content"] if isinstance(content, str): text_content = [{"type": "text", "text": content}] else: text_content = content # Add audio chunk at the beginning (before text) - msg_copy["content"] = [ - {"type": "audio_url", "audio_url": {"url": f"data:audio/wav;base64,{chunk_base64}"}} - ] + text_content + msg_copy["content"] = [make_audio_content_block(chunk_base64, "input_audio")] + text_content # Remove original audio fields msg_copy.pop("audio", None) @@ -317,9 +305,6 @@ async def _generate_with_chunking( chunk_messages.append(msg_copy) - # Preprocess messages (strip /no_think for Qwen) - chunk_messages = self._preprocess_messages_for_model(chunk_messages) - # Generate for this chunk using parent's generate_async result = await super().generate_async( prompt=chunk_messages, tokens_to_generate=tokens_to_generate, **kwargs @@ -379,28 +364,7 @@ async def generate_async( return await self._generate_with_chunking(messages, audio_path, duration, tokens_to_generate, **kwargs) # No chunking needed - convert audio fields to base64 format - messages = [self.content_text_to_list(copy.deepcopy(msg)) for msg in messages] - messages = self._preprocess_messages_for_model(messages) - prompt = messages + prompt = [self.content_text_to_list(msg) for msg in messages] # Call parent's generate_async (which handles audio OUTPUT via _parse_chat_completion_response) return await super().generate_async(prompt=prompt, tokens_to_generate=tokens_to_generate, **kwargs) - - def _build_chat_request_params( - self, - messages: list[dict], - **kwargs, - ) -> dict: - """Build chat request parameters with audio preprocessing. - - Args: - messages: List of message dicts. - **kwargs: Additional parameters for the request. - - Returns: - Request parameters dict. - """ - # content_text_to_list THEN preprocess - messages = [self.content_text_to_list(copy.deepcopy(msg)) for msg in messages] - messages = self._preprocess_messages_for_model(messages) - return super()._build_chat_request_params(messages=messages, **kwargs) diff --git a/nemo_skills/pipeline/utils/server.py b/nemo_skills/pipeline/utils/server.py index 87abca4a99..06b7e1b778 100644 --- a/nemo_skills/pipeline/utils/server.py +++ b/nemo_skills/pipeline/utils/server.py @@ -38,6 +38,7 @@ class SupportedServers(str, Enum): azureopenai = "azureopenai" gemini = "gemini" generic = "generic" + api_multimodal = "api_multimodal" # For connecting to external servers (NVIDIA API, etc.) def get_free_port(exclude: list[int] | None = None, strategy: int | str = 5000) -> int: diff --git a/tests/data/code-output.test b/tests/data/code-output.test index 48fdc87678..a4195a2405 100644 --- a/tests/data/code-output.test +++ b/tests/data/code-output.test @@ -1,10 +1,10 @@ -{"input": "Implement a function that merges two sorted integer arrays into one sorted array.\nnums1 = \\[1,3,5,7\\], nums2 = \\[2,4,6,8\\]\nImplement a function that merges two sorted integer arrays, nums1 and nums2, into one sorted array. Ensure the function operates in O(1) space complexity. Also, take into account the possibility of negative and duplicate integers in the input arrays.\nnums1 = \\[-3, -1, 3, 5\\], nums2 = \\[-2, 0, 2, 4, 6, 8\\]\nImplement a function that merges two sorted integer arrays into one sorted array. However, the function should work for two sorted lists of integers, in which each list may contain up to 10^6 integers. Ensure the function operates in O(1) space complexity. Also, take into account the possibility of negative, duplicate, and repeated integers in the input lists.\nnums1 = \\[-10000, -1000, -100, -10, 0, 10, 100, 1000, 10000\\], nums2 = \\[-9000, -900, -90, -9, 10000\\]", "output": "To merge two sorted arrays into one sorted array in O(1) space complexity, we can use an in-place merging approach. This involves overwriting one of the input arrays with the merged result. However, since both input arrays are sorted, we can accomplish this by iterating through both arrays simultaneously and merging the elements as we go.\n\nHere's the Python code that implements this approach:\n\n```python\ndef merge_sorted_arrays(nums1, nums2):\n n1 = len(nums1)\n n2 = len(nums2)\n i = j = n1 + n2 - 1\n\n # Iterate backwards through both arrays\n while i >= 0:\n # If nums1 is not empty and nums2 is empty, or nums1 element is greater than nums2 element\n if (n1 > 0 and nums1[n1-1] > nums2[n2-1]) or n2 == 0:\n nums1[i] = nums1[n1-1]\n n1 -= 1\n else:\n nums1[i] = nums2[n2-1]\n n2 -= 1\n i -= 1\n\n return nums1\n```\n\nThis code starts from the end of the merged array (`nums1`), compares the last elements of both arrays, and assigns the larger one to the corresponding position in `nums1`. It then decrements the index of the selected array and continues with the next position.\n\nThe function takes two sorted integer arrays, `nums1` and `nums2`, as input and returns a merged sorted array in `nums1`. It works in O(n) time complexity, where n is the total number of elements in both input arrays.\n\nNote: This solution assumes that `nums1` has enough space to accommodate the merged array. If `nums1` does not have enough space, the function will modify the existing elements in `nums1` and overflow outside the allocated memory. In that case, we need to allocate a new array with enough space before merging. However, allocating a new array would violate the O(1) space complexity requirement."} -{"input": "Given a matrix of `m` x `n` elements `matrix`, return all elements of the matrix in spiral order.\n`matrix = [[1,2,3],[4,5,6],[7,8,9]]`\nGiven a matrix of `m` x `n` elements `matrix`, return all elements of the matrix in spiral order. However, if an element is divisible by 3, replace it with 'X' before appending to the resultant list. Also, the matrix could be rectangular, not just square.\n`matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]`\n\nDo not provide any hints, solutions, or outputs. Only increase the difficulty of the instruction.", "output": "To solve this problem, we'll use a spiral order traversal algorithm for the matrix, which involves four different pointers/variables: top, bottom, left, and right. Each pointer represents the current limit to which we can travel in its respective direction while making the spiral.\n\nIf an element is divisible by 3, we will replace it with 'X' before appending it to the resultant list.\n\nHere is the Python code implementing this logic:\n\n```python\ndef spiral_matrix(matrix):\n ans = []\n while matrix:\n ans.extend(matrix.pop(0))\n if matrix and matrix[0]:\n for row in matrix:\n ans.append(row.pop() if row else 'X')\n if matrix:\n ans.extend(matrix.pop()[::-1])\n if matrix and matrix[0]:\n for row in matrix[::-1]:\n ans.append(row.pop(0) if row else 'X')\n return [el if el % 3 != 0 else 'X' for el in ans]\n```\n\nWhen testing this code with the given matrices, remember to call the function with the matrix as an argument and print the result to check its accuracy:\n\n```python\nmatrix = [[1,2,3],[4,5,6],[7,8,9]]\n\nprint(spiral_matrix(matrix))\n\nmatrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]\n\nprint(spiral_matrix(matrix))\n```\n\nMake sure to test this code with other matrices having different dimensions and numbers to ensure it works correctly for all scenarios.\n```python\ndef spiral_matrix(matrix):\n ans = []\n while matrix:\n ans.extend(matrix.pop(0))\n if matrix and matrix[0]:\n for row in matrix:\n ans.append(row.pop() if row else 'X')\n if matrix:\n ans.extend(matrix.pop()[::-1])\n if matrix and matrix[0]:\n for row in matrix[::-1]:\n ans.append(row.pop(0) if row else 'X')\n return [el if el % 3 != 0 else 'X' for el in ans]\n\nmatrix = [[1,2,3],[4,5,6],[7,8,9]]\nspiral_order = spiral_matrix(matrix)\noutput_template = \"The spiral order traversal of the matrix is: {}\\n\"\nprint(output_template.format(spiral_order))\n\nmatrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]\nspiral_order = spiral_matrix(matrix)\noutput_template = \"The spiral order traversal of the matrix is: {}\\n\"\nprint(output_template.format(spiral_order))\n```"} -{"input": "Given a **sorted** integer array `nums` and three integers `a`, `b`, and `c`, apply a quadratic function of the form `f(x) = ax2 + bx + c` to each element `nums[i]` in the array. Before returning the array, however, **remove** the **duplicates** and then return _the array in a sorted order_.\nGiven a **sorted** integer array `nums` and **four** integers `a`, `b`, `c`, and `d`. Apply a quadratic function of the form `f(x) = ax2 + bx + c` to each element `nums[i]` in the array. Before returning the array, however, **remove** the elements that have a **lower** value than `d`. Also, **remove** the **duplicates** and then return _the array in a sorted order_.\n\nOriginal Instruction:\n```python\ndef apply_function(nums: List[int], a: int, b: int, c: int) -> List[int]:\n # Apply quadratic function and remove duplicates, return sorted array\n```\n\nNew Instruction:\n```python\ndef apply_function(nums: List[int], a: int, b: int, c: int, d: int) -> List[int]:\n # Apply quadratic function, remove elements lower than d, remove duplicates, return sorted array\n```\n\nThe time complexity of your function should be O(n log n) and space complexity should be O(n), where n is the length of the input array `nums`.", "output": "Here is a Python solution with both original and new instructions implemented. The original instruction (`apply_function_original`) applies the quadratic function to each element, removes duplicates, and returns the sorted array. The new instruction (`apply_function`) applies the quadratic function, removes elements lower than `d`, removes duplicates, and returns the sorted array.\n\n```python\nfrom typing import List\n\ndef apply_function_original(nums: List[int], a: int, b: int, c: int) -> List[int]:\n # Apply quadratic function and remove duplicates, return sorted array\n return sorted(set(a * x**2 + b * x + c for x in nums))\n\ndef apply_function(nums: List[int], a: int, b: int, c: int, d: int) -> List[int]:\n # Apply quadratic function, remove elements lower than d, remove duplicates, return sorted array\n result = set(a * x**2 + b * x + c for x in nums if a * x**2 + b * x + c >= d)\n return sorted(result)\n```\n\nThese functions both achieve their desired properties:\n1. Time complexity: O(n log n)\n2. Space complexity: O(n)\n\nHere's why:\nThe first step in both functions is creating a set comprehension to apply the quadratic function. Since a set inherently stores only unique elements, this removes duplicates. This operation (creating the set) takes O(n) time. Then in the new instruction, the second part of the conditional statement checks if the applied value is greater than or equal to `d`. This also takes O(n) time, as it needs to process each element.\n\nNext, both functions sort the set. Since a set has no inherent order, it needs to be put into a reasonable order for output. Sorting a list or set is usually an O(n log n) operation, as it often involves a variation of merge sort, heap sort, or quicksort.\n\nAs a result, the total time complexity of both functions is O(n) for processing each element initially + O(n log n) for sorting, which results in O(n log n) as the larger term. The space complexity is also O(n), since we store the final, sorted result in a new list."} -{"input": "Write a program that takes a given string and two arrays of special characters. Your program should delete all occurrences of the special characters from the first array in the string, but only if they are consecutive and are not preceded or followed by an alphabet character. Then, your program should replace all occurrences of the special characters from the second array with a given replacement character, but only if they are consecutive and are not preceded or followed by a digit character. If any special characters from the first or second array are consecutive, your program should replace all such occurrences with a single replacement character.\n\n`Input: 'H@llo, W&rld!!', ['@', '&', '!'], ['l'], 'x'`\n\n`Output: 'Hllo, Wordx'`\n\nOriginal Instruction (with more reasoning steps):\n1. Write a program that takes a given string and two arrays of special characters.\n2. Delete all occurrences of the special characters from the first array in the string.\n a. Check if the current character is in the first array.\n b. Check if the previous character is not alphabet and the next character is not alphabet.\n c. Delete the current character if both conditions are true.\n3. Replace all occurrences of the special characters from the second array with a given replacement character.\n a. Check if the current character is in the second array.\n b. Check if the previous character is not a digit and the next character is not a digit.\n c. Replace the current character with the replacement character if both conditions are true.\n4. If any special characters from the first or second array are consecutive, replace all such occurrences with a single replacement character.\n a. Check if the current character is a special character from the first or second array.\n b. Check if the previous character is a special character from the same array and the next character is a special character from the same array.\n c. Replace the current character with the replacement character if both conditions are true.\n5. Output the modified string.", "output": "Here is the Python code to solve the problem:\n\n```python\nimport re\n\ndef modify_string(string, special_chars1, special_chars2, replacement):\n # Create regular expressions to match special characters from the first and second arrays\n regex1 = \"[\" + \"\".join(special_chars1) + \"]\"\n regex2 = \"[\" + \"\".join(special_chars2) + \"]\"\n\n # Replace consecutive special characters from the first array\n string = re.sub(r\"(? 1:\n return False\n\n # Check if the string is a valid number\n try:\n float(s)\n return True\n except ValueError:\n return False\n```\n\nExamples:\n\n```python\nprint(is_valid_number(\" 3.14E+7 \")) # True\nprint(is_valid_number(\" 1e-7 \")) # True\nprint(is_valid_number(\" 1E9 \")) # True\nprint(is_valid_number(\" 1e2e3 \")) # False\nprint(is_valid_number(\" ++++123 \")) # False\nprint(is_valid_number(\"1+2.3\")) # False\nprint(is_valid_number(\"-.1\")) # True\nprint(is_valid_number(\"1-2.3\")) # False\n```\n\nExplanation:\n\nTo solve the given problem, we first eliminate all the spaces in the input string `s`. Then we check if there are any multiple consecutive '+' or '-' signs in `s` and if there are we return False. After that, we check if there are any multiple 'e' or 'E' signs in `s` and if there are we return False. Finally, using Python's built-in function `float()`, we try to convert `s` to a float. If the conversion is successful, we return True indicating that `s` is a valid number."} -{"input": "Write a function that finds the smallest positive integer not included in a given array. Assume the given array is always sorted and contains at least one positive integer and one negative integer. Consider the case when the given array is empty.\narr = \\[-1, -3, 2, 7, 8\\]\nWrite a function that finds the smallest positive integer not included in a given array, while also handling the case when the array contains multiple occurrences of the same number. The function should return an error message if the given array is not sorted, assuming it always contains at least one positive integer and one negative integer.\narr = \\[-1, -3, 2, 2, 8, 8\\]", "output": "Here is a Python function that meets the specified requirements:\n\n```python\ndef find_smallest_missing_positive(arr):\n # Check if array is sorted\n if arr != sorted(arr):\n return \"Error: Array must be sorted.\"\n\n # Variables for storing the smallest positive integer and the current integer\n smallest_positive = 1\n current = arr[0]\n\n # Traverse the array\n for i in range(len(arr)):\n # If we find a negative integer, continue to the next iteration\n if current < 0:\n continue\n\n # If the current integer is greater than the smallest positive integer, return the smallest positive\n if current > smallest_positive:\n return smallest_positive\n\n # If the current integer is equal to the smallest positive integer, increment the smallest positive integer\n if current == smallest_positive:\n smallest_positive += 1\n\n # Update the current integer\n if i < len(arr) - 1:\n current = arr[i + 1]\n\n # If we have traversed the whole array, return the smallest positive integer\n return smallest_positive\n```\n\nTo use this function for arr = \\[-1, -3, 2, 2, 8, 8\\]:\n\n```python\narr = [-1, -3, 2, 2, 8, 8]\nprint(find_smallest_missing_positive(arr)) # output: 3\n```\n\nThe error handling for unsorted array can be tested as follows:\n\n```python\narr = [-1, 2, -3, 2, 8, 8]\nprint(find_smallest_missing_positive(arr)) # output: Error: Array must be sorted.\n```\n\nThe time complexity of this function is O(n). The space complexity is constant O(1) as no extra space is required."} -{"input": "Implement a function `fib(n)` that takes in a number `n` and returns the `n`th number in the Fibonacci sequence. This time, the function must return the result as a string and must handle negative integer input by raising a `ValueError` with the message \"Negative input not allowed\".\nn = 10\n\nReference Solution (Wrong):\n```\ndef fib(n):\n if n < 0:\n raise ValueError(\"Negative input not allowed\")\n a, b = 0, 1\n for _ in range(n):\n a, b = b, a + b\n return str(a)\n```\n\nWrite the correct solution for the instruction.", "output": "The provided Reference Solution is correct, except for the fact that the function does not actually return the `n`th number in the Fibonacci sequence. The loop should run for `n-1` times instead of `n`. Here is the corrected solution:\n\n```python\ndef fib(n):\n if n < 0:\n raise ValueError(\"Negative input not allowed\")\n a, b = 0, 1\n for _ in range(n-1):\n a, b = b, a + b\n return str(b)\n```\n\nTesting the function:\n```python\nprint(fib(10)) # Output: 55\nprint(fib(0)) # Output: 0\ntry:\n fib(-5)\nexcept ValueError as e:\n print(e) # Output: Negative input not allowed\n```"} -{"input": "Given a list of strings `words` and an integer `k`, return the `k` shortest words in `words` that contain only lowercase English alphabets, ordered by the length of the words and then by the reverse lexicographical order. If there are less than `k` such words, return all of them.\nNow, modify the previous problem by introducing an additional requirement. In addition to the previous constraints, the solution must also filter the words to only include those that do not contain any duplicate characters. For example, the word \"hello\" would be excluded as it has two \"l\" characters.\n\nDo not provide any hints, solutions, or outputs. The participant should write a complete solution by themselves. The solution should have a time complexity of O(n log n) and a space complexity of O(n).", "output": "```python\ndef k_shortest_no_duplicates(words, k):\n # Filter words with only lowercase alphabets, no duplicates, and sorted by length and reverse lexicographical order\n valid_words = sorted((word for word in words if len(set(word)) == len(word)), key=lambda word: (len(word), word), reverse=True)\n\n # Return the k shortest words\n return valid_words[:k]\n```\n\nExplanation:\n\nIn this solution, we first create a list comprehension that filters out words with any uppercase letters or non-alphabetic characters and checks if the word has any duplicate characters by comparing the length of the word with the length of its set. We then sort the words based on their length first and then in reverse lexicographical order. Finally, we slice the list to return the `k` shortest words, or all of them if there are less than `k` valid words.\n\nThis solution has a time complexity of O(n log n) because we sort the list of words, and a space complexity of O(n) due to the space taken by the valid_words list."} -{"input": "Given the `head` of a doubly linked list, where each node has a `prev`, `next`, and `value` property, and an integer `val`. Remove all the nodes of the doubly linked list that have `Node.value == val`, and return _the new head_.\n\nOriginal Instruction:\nIterate through the linked list, checking each node's value against `val`. If a node's value matches `val`, reassign the `next` pointer of the previous node to skip the current node. Update the `prev` pointer of the next node to point to the previous node, if applicable. Once you've iterated through the entire list, return the `head` node as the new head of the linked list.\n\nReasoning Steps:\n\n1. Create a dummy node with a null value and set its `next` pointer to the `head` of the linked list.\n2. Initialize a `previous` node to the dummy node.\n3. Iterate through the linked list, checking each node's `value` against `val`:\n a. If the current node's `value` matches `val`, reassign the `next` pointer of the `previous` node to skip the current node (`previous.next = current.next`).\n b. If `current.next` is not `null`, update the `prev` pointer of the next node to point to the `previous` node (`current.next.prev = previous`).\n4. Move the `previous` node to the current node (`previous = current`).\n5. Move the current node to the next node (`current = current.next`).\n6. Repeat steps 3-5 until the end of the linked list.\n7. Once you've iterated through the entire list, return the `next` node of the dummy node as the new head of the linked list.\nNow, complete the same task as described above but this time without using any additional space beyond the original linked list. You must iterate through the list only once, and any temporary or helper variables are not allowed.\n\nInstructions:\nBegin at the `head` of the linked list and iterate through the list, checking each node's `value` against `val`. As you iterate through the list, maintain two pointers: one pointing to the current node (`current`) and another pointing to the previous node (`previous`). If a node's value matches `val`, reassign the `next` pointer of the previous node to skip the current node and update the `prev` pointer of the next node to point to the previous node. Continue this process until you've iterated through the entire list. After you've finished iterating through the list, return the modified linked list starting at the `head` node.\n\nReasoning Steps:\n\n1. Initialize a `previous` node to `null`.\n2. Initialize a `current` node to the `head` of the linked list.\n3. Iterate through the linked list, checking each node's `value` against `val`:\n a. If the current node's `value` matches `val`, reassign the `next` pointer of the `previous` node to skip the current node (`previous.next = current.next`).\n b. If `current.next` is not `null`, update the `prev` pointer of the next node to point to the `previous` node (`current.next.prev = previous`).\n4. If the current node is not `null`, move the `previous` node to the current node (`previous = current`).\n5. Move the current node to the next node (`current = current.next`).\n6. Repeat steps 3-5 until the end of the linked list.\n7. Once you've iterated through the entire list, return the modified linked list starting at the `head` node.", "output": "```python\nclass Node:\n def __init__(self, prev, next, value):\n self.prev = prev\n self.next = next\n self.value = value\n\ndef remove_nodes(head, val):\n dummy = Node(prev=None, next=head, value=None)\n previous = dummy\n\n while head:\n if head.value == val:\n previous.next = head.next\n if head.next:\n head.next.prev = previous\n previous = head\n head = head.next\n\n return dummy.next\n```\n\nHere's the reasoning behind the solution:\n\n1. The function `remove_nodes` takes as input the `head` of the linked list and an integer `val`.\n2. Create a `dummy` variable to store a new Node with a null value and its `next` set to `head`.\n3. Create a `previous` variable that keeps track of the previous node for each node we are currently processing.\n4. We start iterating through the linked list using a `while` loop until `head` is `null`.\n5. Inside the loop, we check if the current node's `value` is equal to the target value. If they are equal, we can remove the node satisfying this condition by reassigning the `next` pointer of the `previous` node to the `next` of the current node, thus skipping the current node. If the current `next` is not null, we update the `prev` pointer of the next node.\n6. Finally, we adjust our `previous` and `head` nodes to prepare for the next iteration.\n7. The function returns the new `head` of the linked list.\n\nFor your particular request of performing the same task without using any additional space beyond the original linked list, it's possible but not required in Python to create the dummy node as we did in this Python implementation. This implementation efficiently iterates through the linked list and removes nodes with a specific `value`, as you requested. However, if you strictly need a solution without using any additional space apart from the original linked list, this additional constraint involves a more complicated solution, which is not usually necessary in a language like Python."} -{"input": "Given an array of integers with possible duplicate values and negative integers, write a program that returns the second largest unique integer in the array, taking into account the possibility of all array elements being identical. The program should handle scenarios where the array size is 1 or 2, where there might not be a second largest unique integer. For example, if the given array is \\[10, 10, 20, 30, 20, 40, 50, 20\\], then the second largest unique integer is `40`. If the array is \\[10, 10, 10, 10, 10\\], the program should return an appropriate message indicating there is no second largest unique integer.\n\nReference Solution (Wrong):\n```python\ndef second_largest_unique(numbers):\n unique_numbers = list(set(numbers))\n second_largest = sorted(unique_numbers)[-2] if len(unique_numbers) > 1 else \"No second largest unique integer\"\n return second_largest\n```\n\nWrite the Correct Solution:\nDo not provide the correct solution here. The user should write their own solution based on the new instruction.\nGiven an array of integers with possible duplicate values and negative integers, write a program that returns the second largest **even** integer in the array, taking into account the possibility of all array elements being identical, odd, or not having a second largest even integer. The program should handle scenarios where the array size is 1 or 2, where there might not be a second largest even integer. For example, if the given array is \\[10, 10, 20, 30, 20, 40, 50, 20\\], then the second largest even integer is `40`. If the array is \\[10, 10, 10, 10, 10\\], the program should return an appropriate message indicating there is no second largest even integer. If the array is \\[1, 3, 5, 7, 9\\], the program should also return an appropriate message indicating there is no second largest even integer.", "output": "```python\ndef second_largest_even(numbers):\n unique_even_numbers = list(set([num for num in numbers if num % 2 == 0]))\n second_largest_even = sorted(unique_even_numbers)[-2] if len(unique_even_numbers) > 1 else \"No second largest even integer\"\n return second_largest_even\n```\n\nIn this solution, first, a unique list of even numbers is created from the input array by using list comprehension and the `set` function. The list is then sorted in ascending order, and the last but one element is returned as the second largest even number, but only if there are at least two even numbers in the array. Otherwise, a message is returned indicating that there is no second largest even integer."} +{"question": "Implement a function that merges two sorted integer arrays into one sorted array.\nnums1 = \\[1,3,5,7\\], nums2 = \\[2,4,6,8\\]\nImplement a function that merges two sorted integer arrays, nums1 and nums2, into one sorted array. Ensure the function operates in O(1) space complexity. Also, take into account the possibility of negative and duplicate integers in the input arrays.\nnums1 = \\[-3, -1, 3, 5\\], nums2 = \\[-2, 0, 2, 4, 6, 8\\]\nImplement a function that merges two sorted integer arrays into one sorted array. However, the function should work for two sorted lists of integers, in which each list may contain up to 10^6 integers. Ensure the function operates in O(1) space complexity. Also, take into account the possibility of negative, duplicate, and repeated integers in the input lists.\nnums1 = \\[-10000, -1000, -100, -10, 0, 10, 100, 1000, 10000\\], nums2 = \\[-9000, -900, -90, -9, 10000\\]", "output": "To merge two sorted arrays into one sorted array in O(1) space complexity, we can use an in-place merging approach. This involves overwriting one of the input arrays with the merged result. However, since both input arrays are sorted, we can accomplish this by iterating through both arrays simultaneously and merging the elements as we go.\n\nHere's the Python code that implements this approach:\n\n```python\ndef merge_sorted_arrays(nums1, nums2):\n n1 = len(nums1)\n n2 = len(nums2)\n i = j = n1 + n2 - 1\n\n # Iterate backwards through both arrays\n while i >= 0:\n # If nums1 is not empty and nums2 is empty, or nums1 element is greater than nums2 element\n if (n1 > 0 and nums1[n1-1] > nums2[n2-1]) or n2 == 0:\n nums1[i] = nums1[n1-1]\n n1 -= 1\n else:\n nums1[i] = nums2[n2-1]\n n2 -= 1\n i -= 1\n\n return nums1\n```\n\nThis code starts from the end of the merged array (`nums1`), compares the last elements of both arrays, and assigns the larger one to the corresponding position in `nums1`. It then decrements the index of the selected array and continues with the next position.\n\nThe function takes two sorted integer arrays, `nums1` and `nums2`, as input and returns a merged sorted array in `nums1`. It works in O(n) time complexity, where n is the total number of elements in both input arrays.\n\nNote: This solution assumes that `nums1` has enough space to accommodate the merged array. If `nums1` does not have enough space, the function will modify the existing elements in `nums1` and overflow outside the allocated memory. In that case, we need to allocate a new array with enough space before merging. However, allocating a new array would violate the O(1) space complexity requirement."} +{"question": "Given a matrix of `m` x `n` elements `matrix`, return all elements of the matrix in spiral order.\n`matrix = [[1,2,3],[4,5,6],[7,8,9]]`\nGiven a matrix of `m` x `n` elements `matrix`, return all elements of the matrix in spiral order. However, if an element is divisible by 3, replace it with 'X' before appending to the resultant list. Also, the matrix could be rectangular, not just square.\n`matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]`\n\nDo not provide any hints, solutions, or outputs. Only increase the difficulty of the instruction.", "output": "To solve this problem, we'll use a spiral order traversal algorithm for the matrix, which involves four different pointers/variables: top, bottom, left, and right. Each pointer represents the current limit to which we can travel in its respective direction while making the spiral.\n\nIf an element is divisible by 3, we will replace it with 'X' before appending it to the resultant list.\n\nHere is the Python code implementing this logic:\n\n```python\ndef spiral_matrix(matrix):\n ans = []\n while matrix:\n ans.extend(matrix.pop(0))\n if matrix and matrix[0]:\n for row in matrix:\n ans.append(row.pop() if row else 'X')\n if matrix:\n ans.extend(matrix.pop()[::-1])\n if matrix and matrix[0]:\n for row in matrix[::-1]:\n ans.append(row.pop(0) if row else 'X')\n return [el if el % 3 != 0 else 'X' for el in ans]\n```\n\nWhen testing this code with the given matrices, remember to call the function with the matrix as an argument and print the result to check its accuracy:\n\n```python\nmatrix = [[1,2,3],[4,5,6],[7,8,9]]\n\nprint(spiral_matrix(matrix))\n\nmatrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]\n\nprint(spiral_matrix(matrix))\n```\n\nMake sure to test this code with other matrices having different dimensions and numbers to ensure it works correctly for all scenarios.\n```python\ndef spiral_matrix(matrix):\n ans = []\n while matrix:\n ans.extend(matrix.pop(0))\n if matrix and matrix[0]:\n for row in matrix:\n ans.append(row.pop() if row else 'X')\n if matrix:\n ans.extend(matrix.pop()[::-1])\n if matrix and matrix[0]:\n for row in matrix[::-1]:\n ans.append(row.pop(0) if row else 'X')\n return [el if el % 3 != 0 else 'X' for el in ans]\n\nmatrix = [[1,2,3],[4,5,6],[7,8,9]]\nspiral_order = spiral_matrix(matrix)\noutput_template = \"The spiral order traversal of the matrix is: {}\\n\"\nprint(output_template.format(spiral_order))\n\nmatrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]\nspiral_order = spiral_matrix(matrix)\noutput_template = \"The spiral order traversal of the matrix is: {}\\n\"\nprint(output_template.format(spiral_order))\n```"} +{"question": "Given a **sorted** integer array `nums` and three integers `a`, `b`, and `c`, apply a quadratic function of the form `f(x) = ax2 + bx + c` to each element `nums[i]` in the array. Before returning the array, however, **remove** the **duplicates** and then return _the array in a sorted order_.\nGiven a **sorted** integer array `nums` and **four** integers `a`, `b`, `c`, and `d`. Apply a quadratic function of the form `f(x) = ax2 + bx + c` to each element `nums[i]` in the array. Before returning the array, however, **remove** the elements that have a **lower** value than `d`. Also, **remove** the **duplicates** and then return _the array in a sorted order_.\n\nOriginal Instruction:\n```python\ndef apply_function(nums: List[int], a: int, b: int, c: int) -> List[int]:\n # Apply quadratic function and remove duplicates, return sorted array\n```\n\nNew Instruction:\n```python\ndef apply_function(nums: List[int], a: int, b: int, c: int, d: int) -> List[int]:\n # Apply quadratic function, remove elements lower than d, remove duplicates, return sorted array\n```\n\nThe time complexity of your function should be O(n log n) and space complexity should be O(n), where n is the length of the input array `nums`.", "output": "Here is a Python solution with both original and new instructions implemented. The original instruction (`apply_function_original`) applies the quadratic function to each element, removes duplicates, and returns the sorted array. The new instruction (`apply_function`) applies the quadratic function, removes elements lower than `d`, removes duplicates, and returns the sorted array.\n\n```python\nfrom typing import List\n\ndef apply_function_original(nums: List[int], a: int, b: int, c: int) -> List[int]:\n # Apply quadratic function and remove duplicates, return sorted array\n return sorted(set(a * x**2 + b * x + c for x in nums))\n\ndef apply_function(nums: List[int], a: int, b: int, c: int, d: int) -> List[int]:\n # Apply quadratic function, remove elements lower than d, remove duplicates, return sorted array\n result = set(a * x**2 + b * x + c for x in nums if a * x**2 + b * x + c >= d)\n return sorted(result)\n```\n\nThese functions both achieve their desired properties:\n1. Time complexity: O(n log n)\n2. Space complexity: O(n)\n\nHere's why:\nThe first step in both functions is creating a set comprehension to apply the quadratic function. Since a set inherently stores only unique elements, this removes duplicates. This operation (creating the set) takes O(n) time. Then in the new instruction, the second part of the conditional statement checks if the applied value is greater than or equal to `d`. This also takes O(n) time, as it needs to process each element.\n\nNext, both functions sort the set. Since a set has no inherent order, it needs to be put into a reasonable order for output. Sorting a list or set is usually an O(n log n) operation, as it often involves a variation of merge sort, heap sort, or quicksort.\n\nAs a result, the total time complexity of both functions is O(n) for processing each element initially + O(n log n) for sorting, which results in O(n log n) as the larger term. The space complexity is also O(n), since we store the final, sorted result in a new list."} +{"question": "Write a program that takes a given string and two arrays of special characters. Your program should delete all occurrences of the special characters from the first array in the string, but only if they are consecutive and are not preceded or followed by an alphabet character. Then, your program should replace all occurrences of the special characters from the second array with a given replacement character, but only if they are consecutive and are not preceded or followed by a digit character. If any special characters from the first or second array are consecutive, your program should replace all such occurrences with a single replacement character.\n\n`Input: 'H@llo, W&rld!!', ['@', '&', '!'], ['l'], 'x'`\n\n`Output: 'Hllo, Wordx'`\n\nOriginal Instruction (with more reasoning steps):\n1. Write a program that takes a given string and two arrays of special characters.\n2. Delete all occurrences of the special characters from the first array in the string.\n a. Check if the current character is in the first array.\n b. Check if the previous character is not alphabet and the next character is not alphabet.\n c. Delete the current character if both conditions are true.\n3. Replace all occurrences of the special characters from the second array with a given replacement character.\n a. Check if the current character is in the second array.\n b. Check if the previous character is not a digit and the next character is not a digit.\n c. Replace the current character with the replacement character if both conditions are true.\n4. If any special characters from the first or second array are consecutive, replace all such occurrences with a single replacement character.\n a. Check if the current character is a special character from the first or second array.\n b. Check if the previous character is a special character from the same array and the next character is a special character from the same array.\n c. Replace the current character with the replacement character if both conditions are true.\n5. Output the modified string.", "output": "Here is the Python code to solve the problem:\n\n```python\nimport re\n\ndef modify_string(string, special_chars1, special_chars2, replacement):\n # Create regular expressions to match special characters from the first and second arrays\n regex1 = \"[\" + \"\".join(special_chars1) + \"]\"\n regex2 = \"[\" + \"\".join(special_chars2) + \"]\"\n\n # Replace consecutive special characters from the first array\n string = re.sub(r\"(? 1:\n return False\n\n # Check if the string is a valid number\n try:\n float(s)\n return True\n except ValueError:\n return False\n```\n\nExamples:\n\n```python\nprint(is_valid_number(\" 3.14E+7 \")) # True\nprint(is_valid_number(\" 1e-7 \")) # True\nprint(is_valid_number(\" 1E9 \")) # True\nprint(is_valid_number(\" 1e2e3 \")) # False\nprint(is_valid_number(\" ++++123 \")) # False\nprint(is_valid_number(\"1+2.3\")) # False\nprint(is_valid_number(\"-.1\")) # True\nprint(is_valid_number(\"1-2.3\")) # False\n```\n\nExplanation:\n\nTo solve the given problem, we first eliminate all the spaces in the input string `s`. Then we check if there are any multiple consecutive '+' or '-' signs in `s` and if there are we return False. After that, we check if there are any multiple 'e' or 'E' signs in `s` and if there are we return False. Finally, using Python's built-in function `float()`, we try to convert `s` to a float. If the conversion is successful, we return True indicating that `s` is a valid number."} +{"question": "Write a function that finds the smallest positive integer not included in a given array. Assume the given array is always sorted and contains at least one positive integer and one negative integer. Consider the case when the given array is empty.\narr = \\[-1, -3, 2, 7, 8\\]\nWrite a function that finds the smallest positive integer not included in a given array, while also handling the case when the array contains multiple occurrences of the same number. The function should return an error message if the given array is not sorted, assuming it always contains at least one positive integer and one negative integer.\narr = \\[-1, -3, 2, 2, 8, 8\\]", "output": "Here is a Python function that meets the specified requirements:\n\n```python\ndef find_smallest_missing_positive(arr):\n # Check if array is sorted\n if arr != sorted(arr):\n return \"Error: Array must be sorted.\"\n\n # Variables for storing the smallest positive integer and the current integer\n smallest_positive = 1\n current = arr[0]\n\n # Traverse the array\n for i in range(len(arr)):\n # If we find a negative integer, continue to the next iteration\n if current < 0:\n continue\n\n # If the current integer is greater than the smallest positive integer, return the smallest positive\n if current > smallest_positive:\n return smallest_positive\n\n # If the current integer is equal to the smallest positive integer, increment the smallest positive integer\n if current == smallest_positive:\n smallest_positive += 1\n\n # Update the current integer\n if i < len(arr) - 1:\n current = arr[i + 1]\n\n # If we have traversed the whole array, return the smallest positive integer\n return smallest_positive\n```\n\nTo use this function for arr = \\[-1, -3, 2, 2, 8, 8\\]:\n\n```python\narr = [-1, -3, 2, 2, 8, 8]\nprint(find_smallest_missing_positive(arr)) # output: 3\n```\n\nThe error handling for unsorted array can be tested as follows:\n\n```python\narr = [-1, 2, -3, 2, 8, 8]\nprint(find_smallest_missing_positive(arr)) # output: Error: Array must be sorted.\n```\n\nThe time complexity of this function is O(n). The space complexity is constant O(1) as no extra space is required."} +{"question": "Implement a function `fib(n)` that takes in a number `n` and returns the `n`th number in the Fibonacci sequence. This time, the function must return the result as a string and must handle negative integer input by raising a `ValueError` with the message \"Negative input not allowed\".\nn = 10\n\nReference Solution (Wrong):\n```\ndef fib(n):\n if n < 0:\n raise ValueError(\"Negative input not allowed\")\n a, b = 0, 1\n for _ in range(n):\n a, b = b, a + b\n return str(a)\n```\n\nWrite the correct solution for the instruction.", "output": "The provided Reference Solution is correct, except for the fact that the function does not actually return the `n`th number in the Fibonacci sequence. The loop should run for `n-1` times instead of `n`. Here is the corrected solution:\n\n```python\ndef fib(n):\n if n < 0:\n raise ValueError(\"Negative input not allowed\")\n a, b = 0, 1\n for _ in range(n-1):\n a, b = b, a + b\n return str(b)\n```\n\nTesting the function:\n```python\nprint(fib(10)) # Output: 55\nprint(fib(0)) # Output: 0\ntry:\n fib(-5)\nexcept ValueError as e:\n print(e) # Output: Negative input not allowed\n```"} +{"question": "Given a list of strings `words` and an integer `k`, return the `k` shortest words in `words` that contain only lowercase English alphabets, ordered by the length of the words and then by the reverse lexicographical order. If there are less than `k` such words, return all of them.\nNow, modify the previous problem by introducing an additional requirement. In addition to the previous constraints, the solution must also filter the words to only include those that do not contain any duplicate characters. For example, the word \"hello\" would be excluded as it has two \"l\" characters.\n\nDo not provide any hints, solutions, or outputs. The participant should write a complete solution by themselves. The solution should have a time complexity of O(n log n) and a space complexity of O(n).", "output": "```python\ndef k_shortest_no_duplicates(words, k):\n # Filter words with only lowercase alphabets, no duplicates, and sorted by length and reverse lexicographical order\n valid_words = sorted((word for word in words if len(set(word)) == len(word)), key=lambda word: (len(word), word), reverse=True)\n\n # Return the k shortest words\n return valid_words[:k]\n```\n\nExplanation:\n\nIn this solution, we first create a list comprehension that filters out words with any uppercase letters or non-alphabetic characters and checks if the word has any duplicate characters by comparing the length of the word with the length of its set. We then sort the words based on their length first and then in reverse lexicographical order. Finally, we slice the list to return the `k` shortest words, or all of them if there are less than `k` valid words.\n\nThis solution has a time complexity of O(n log n) because we sort the list of words, and a space complexity of O(n) due to the space taken by the valid_words list."} +{"question": "Given the `head` of a doubly linked list, where each node has a `prev`, `next`, and `value` property, and an integer `val`. Remove all the nodes of the doubly linked list that have `Node.value == val`, and return _the new head_.\n\nOriginal Instruction:\nIterate through the linked list, checking each node's value against `val`. If a node's value matches `val`, reassign the `next` pointer of the previous node to skip the current node. Update the `prev` pointer of the next node to point to the previous node, if applicable. Once you've iterated through the entire list, return the `head` node as the new head of the linked list.\n\nReasoning Steps:\n\n1. Create a dummy node with a null value and set its `next` pointer to the `head` of the linked list.\n2. Initialize a `previous` node to the dummy node.\n3. Iterate through the linked list, checking each node's `value` against `val`:\n a. If the current node's `value` matches `val`, reassign the `next` pointer of the `previous` node to skip the current node (`previous.next = current.next`).\n b. If `current.next` is not `null`, update the `prev` pointer of the next node to point to the `previous` node (`current.next.prev = previous`).\n4. Move the `previous` node to the current node (`previous = current`).\n5. Move the current node to the next node (`current = current.next`).\n6. Repeat steps 3-5 until the end of the linked list.\n7. Once you've iterated through the entire list, return the `next` node of the dummy node as the new head of the linked list.\nNow, complete the same task as described above but this time without using any additional space beyond the original linked list. You must iterate through the list only once, and any temporary or helper variables are not allowed.\n\nInstructions:\nBegin at the `head` of the linked list and iterate through the list, checking each node's `value` against `val`. As you iterate through the list, maintain two pointers: one pointing to the current node (`current`) and another pointing to the previous node (`previous`). If a node's value matches `val`, reassign the `next` pointer of the previous node to skip the current node and update the `prev` pointer of the next node to point to the previous node. Continue this process until you've iterated through the entire list. After you've finished iterating through the list, return the modified linked list starting at the `head` node.\n\nReasoning Steps:\n\n1. Initialize a `previous` node to `null`.\n2. Initialize a `current` node to the `head` of the linked list.\n3. Iterate through the linked list, checking each node's `value` against `val`:\n a. If the current node's `value` matches `val`, reassign the `next` pointer of the `previous` node to skip the current node (`previous.next = current.next`).\n b. If `current.next` is not `null`, update the `prev` pointer of the next node to point to the `previous` node (`current.next.prev = previous`).\n4. If the current node is not `null`, move the `previous` node to the current node (`previous = current`).\n5. Move the current node to the next node (`current = current.next`).\n6. Repeat steps 3-5 until the end of the linked list.\n7. Once you've iterated through the entire list, return the modified linked list starting at the `head` node.", "output": "```python\nclass Node:\n def __init__(self, prev, next, value):\n self.prev = prev\n self.next = next\n self.value = value\n\ndef remove_nodes(head, val):\n dummy = Node(prev=None, next=head, value=None)\n previous = dummy\n\n while head:\n if head.value == val:\n previous.next = head.next\n if head.next:\n head.next.prev = previous\n previous = head\n head = head.next\n\n return dummy.next\n```\n\nHere's the reasoning behind the solution:\n\n1. The function `remove_nodes` takes as input the `head` of the linked list and an integer `val`.\n2. Create a `dummy` variable to store a new Node with a null value and its `next` set to `head`.\n3. Create a `previous` variable that keeps track of the previous node for each node we are currently processing.\n4. We start iterating through the linked list using a `while` loop until `head` is `null`.\n5. Inside the loop, we check if the current node's `value` is equal to the target value. If they are equal, we can remove the node satisfying this condition by reassigning the `next` pointer of the `previous` node to the `next` of the current node, thus skipping the current node. If the current `next` is not null, we update the `prev` pointer of the next node.\n6. Finally, we adjust our `previous` and `head` nodes to prepare for the next iteration.\n7. The function returns the new `head` of the linked list.\n\nFor your particular request of performing the same task without using any additional space beyond the original linked list, it's possible but not required in Python to create the dummy node as we did in this Python implementation. This implementation efficiently iterates through the linked list and removes nodes with a specific `value`, as you requested. However, if you strictly need a solution without using any additional space apart from the original linked list, this additional constraint involves a more complicated solution, which is not usually necessary in a language like Python."} +{"question": "Given an array of integers with possible duplicate values and negative integers, write a program that returns the second largest unique integer in the array, taking into account the possibility of all array elements being identical. The program should handle scenarios where the array size is 1 or 2, where there might not be a second largest unique integer. For example, if the given array is \\[10, 10, 20, 30, 20, 40, 50, 20\\], then the second largest unique integer is `40`. If the array is \\[10, 10, 10, 10, 10\\], the program should return an appropriate message indicating there is no second largest unique integer.\n\nReference Solution (Wrong):\n```python\ndef second_largest_unique(numbers):\n unique_numbers = list(set(numbers))\n second_largest = sorted(unique_numbers)[-2] if len(unique_numbers) > 1 else \"No second largest unique integer\"\n return second_largest\n```\n\nWrite the Correct Solution:\nDo not provide the correct solution here. The user should write their own solution based on the new instruction.\nGiven an array of integers with possible duplicate values and negative integers, write a program that returns the second largest **even** integer in the array, taking into account the possibility of all array elements being identical, odd, or not having a second largest even integer. The program should handle scenarios where the array size is 1 or 2, where there might not be a second largest even integer. For example, if the given array is \\[10, 10, 20, 30, 20, 40, 50, 20\\], then the second largest even integer is `40`. If the array is \\[10, 10, 10, 10, 10\\], the program should return an appropriate message indicating there is no second largest even integer. If the array is \\[1, 3, 5, 7, 9\\], the program should also return an appropriate message indicating there is no second largest even integer.", "output": "```python\ndef second_largest_even(numbers):\n unique_even_numbers = list(set([num for num in numbers if num % 2 == 0]))\n second_largest_even = sorted(unique_even_numbers)[-2] if len(unique_even_numbers) > 1 else \"No second largest even integer\"\n return second_largest_even\n```\n\nIn this solution, first, a unique list of even numbers is created from the input array by using list comprehension and the `set` function. The list is then sorted in ascending order, and the last but one element is returned as the second largest even number, but only if there are at least two even numbers in the array. Otherwise, a message is returned indicating that there is no second largest even integer."} diff --git a/tests/test_data_preparation.py b/tests/test_data_preparation.py index 472c1f45e9..d08f2893bb 100644 --- a/tests/test_data_preparation.py +++ b/tests/test_data_preparation.py @@ -109,6 +109,7 @@ def test_code_sft_data(): f" ++exclude_optional_keys=false " f" ++filters.drop_incorrect_code_blocks=false " f" ++do_shuffle=false " + f" ++input_key=question " ), ) diff --git a/tests/test_vllm_audio.py b/tests/test_vllm_audio.py index 0c8ca1b89a..fae6a27f90 100644 --- a/tests/test_vllm_audio.py +++ b/tests/test_vllm_audio.py @@ -72,8 +72,9 @@ def test_content_text_to_list_with_audio(mock_vllm_multimodal_model, tmp_path): assert isinstance(result["content"], list) assert len(result["content"]) == 2 - assert result["content"][0]["type"] == "audio_url" - assert result["content"][0]["audio_url"]["url"].startswith("data:audio/wav;base64,") + assert result["content"][0]["type"] == "input_audio" + assert result["content"][0]["input_audio"]["format"] == "wav" + assert isinstance(result["content"][0]["input_audio"]["data"], str) assert result["content"][1]["type"] == "text" @@ -99,9 +100,9 @@ def test_content_text_to_list_with_multiple_audios(mock_vllm_multimodal_model, t assert isinstance(result["content"], list) assert len(result["content"]) == 3 - # Audio MUST come before text for Qwen Audio - assert result["content"][0]["type"] == "audio_url" - assert result["content"][1]["type"] == "audio_url" + # Audio MUST come before text for models to process correctly. + assert result["content"][0]["type"] == "input_audio" + assert result["content"][1]["type"] == "input_audio" assert result["content"][2]["type"] == "text" @@ -114,20 +115,6 @@ def test_content_text_to_list_no_audio(mock_vllm_multimodal_model): assert "audio" not in result -def test_preprocess_messages_preserves_no_think(mock_vllm_multimodal_model): - """Test that /no_think is preserved in system messages.""" - messages = [ - {"role": "system", "content": "You are a helpful assistant. /no_think"}, - {"role": "user", "content": "Hello"}, - ] - - result = mock_vllm_multimodal_model._preprocess_messages_for_model(messages) - - # /no_think should be preserved, not stripped - assert result[0]["content"] == "You are a helpful assistant. /no_think" - assert result[1]["content"] == "Hello" - - def test_needs_audio_chunking_disabled(mock_vllm_multimodal_model): """Test that chunking is skipped when disabled.""" mock_vllm_multimodal_model.enable_audio_chunking = False