From 8de5ec95e1155f4a455c66d74c91afa6651af038 Mon Sep 17 00:00:00 2001 From: "Mario Sieg (Neo)" Date: Sat, 25 Jan 2025 11:37:20 +0100 Subject: [PATCH 01/24] Add layer init methods, refractor Python API --- cmake/compiler_config.cmake | 2 + python/examples/xor.py | 8 +- python/magnetron_framework/magnetron/core.py | 1002 +---------------- python/magnetron_framework/magnetron/layer.py | 107 ++ python/magnetron_framework/magnetron/model.py | 98 +- python/magnetron_framework/magnetron/optim.py | 26 + 6 files changed, 231 insertions(+), 1012 deletions(-) create mode 100644 python/magnetron_framework/magnetron/layer.py create mode 100644 python/magnetron_framework/magnetron/optim.py diff --git a/cmake/compiler_config.cmake b/cmake/compiler_config.cmake index a4e443d..c4a8792 100644 --- a/cmake/compiler_config.cmake +++ b/cmake/compiler_config.cmake @@ -18,6 +18,7 @@ set(MAG_CLANG_COMPILE_FLAGS -std=c99 -std=gnu99 -fvisibility=hidden + -fno-math-errno -Wall -Werror -Wno-gnu-zero-variadic-macro-arguments @@ -36,6 +37,7 @@ set(MAG_GCC_COMPILE_FLAGS -std=c99 -std=gnu99 -fvisibility=hidden + -fno-math-errno -Wall -Werror -Wno-gnu-zero-variadic-macro-arguments diff --git a/python/examples/xor.py b/python/examples/xor.py index ae537c0..9f70788 100644 --- a/python/examples/xor.py +++ b/python/examples/xor.py @@ -1,7 +1,8 @@ # (c) 2025 Mario "Neo" Sieg. from magnetron import Tensor -from magnetron.model import SequentialModel, DenseLayer +from magnetron.layer import DenseLayer +from magnetron.model import SequentialModel, HyperParams import matplotlib.pyplot as plt EPOCHS: int = 10000 @@ -23,13 +24,14 @@ [0.0] ]) -mlp = SequentialModel([ +params = HyperParams(lr=RATE, epochs=EPOCHS) +mlp = SequentialModel(params, [ DenseLayer(2, 8), DenseLayer(8, 1) ]) # Train -losses = mlp.train(inputs, targets, epochs=EPOCHS, rate=RATE) +losses = mlp.train(inputs, targets) # Inference test_points = [ diff --git a/python/magnetron_framework/magnetron/core.py b/python/magnetron_framework/magnetron/core.py index b1648a0..d316420 100644 --- a/python/magnetron_framework/magnetron/core.py +++ b/python/magnetron_framework/magnetron/core.py @@ -1,171 +1,79 @@ # (c) 2025 Mario "Neo" Sieg. -# Implements core functionality: Context, Tensors and Operations. - -# To debug Python to C FFI calls: -# $ cp examples/perceptron.py tmp.py && gdb -ex r --args python3 tmp.py -# See also https://wiki.python.org/moin/DebuggingWithGdb - import faulthandler import weakref from dataclasses import dataclass +from enum import Enum, auto, unique from os import getenv from os.path import isfile + from magnetron._lib_loader import load_native_module -from enum import Enum, auto -# Enable faulthandler for debugging faulthandler.enable() +ffi, C = load_native_module() -ffi, C = load_native_module() # Load the native magnetron shared library - -# Common constants MAX_DIMS: int = 6 MAX_ARG_TENSORS: int = 2 MAG_MAX_OP_PARAMS: int = 6 -DIM_MAX: int = ((1 << 64) - 1) >> 1 - - -def enable_log(enable: bool) -> None: - """ - Set logging mode for the magnetron backend. - - Parameters - ---------- - enable : bool - If True, enables logging of operations and internal states. - """ - C.mag_set_log_mode(enable) - - -def pack_color(r: int, g: int, b: int) -> int: - """ - Packs three 8-bit color components (R, G, B) into a single 24-bit integer. - - Parameters - ---------- - r : int - Red component [0-255]. - g : int - Green component [0-255]. - b : int - Blue component [0-255]. - - Returns - ------- - int - A single integer representing the packed RGB color. - """ - return C.mag_pack_color_u8(r, g, b) +DIM_MAX: int = (1 << 63) - 1 # INT64_MAX class ComputeDevice: - """ - Compute devices available for parallel computations. - """ class CPU: - """ - CPU device configuration. - """ def __init__(self, num_threads: int = 0): - """ - Initializes a new CPU device configuration. - - Parameters - ---------- - num_threads : int, optional - Number of threads to use, 0 for automatic, by default 0. - """ - self.num_threads = num_threads class CUDA: - """ - CUDA device configuration. - """ def __init__(self, device_id: int = 0): - """ - Initializes a new CUDA device configuration. - - Parameters - ---------- - device_id : int, optional - CUDA device ID, by default 0. - """ - self.device_id = device_id + +@unique class PRNGAlgorithm(Enum): - """ - Pseudorandom number generator algorithms. - """ - MERSENNE_TWISTER = 0 # Default - Mersenne Twister - PCG = auto() # Permuted Congruential Generator + MERSENNE_TWISTER = 0 + PCG = auto() +@unique class DType(Enum): - """ - Supported data types for tensors. - """ F32 = 0 +@unique class ColorChannels(Enum): - """ - Desired color channels when loading images. - """ - AUTO = 0 # Automatically determine the number of color channels - GRAY = auto() # Grayscale F32 - GRAY_A = auto() # Grayscale F32 + Alpha - RGB = auto() # R32G32B32 - RGBA = auto() # R32G32B32A32 - - @property - def name(self) -> str: - """Returns the operator name as a string.""" - return ffi.string(C.mag_op_get_name(self.value)).decode('utf-8') - - @property - def mnemonic(self) -> str: - """Returns a short mnemonic name for the operator.""" - return ffi.string(C.mag_op_get_mnemonic(self.value)).decode('utf-8') + AUTO = 0 + GRAY = auto() + GRAY_A = auto() + RGB = auto() + RGBA = auto() @property def argument_count(self) -> int: - """Returns the number of _ptr arguments required by this operator.""" return C.mag_op_get_argcount(self.value) @property def supports_inplace(self) -> bool: - """Checks if the operator supports in-place modifications.""" return C.mag_op_supports_inplace(self.value) @property def is_unary(self) -> bool: - """Checks if the operator is a unary operator.""" return self.argument_count == 1 @property def is_binary(self) -> bool: - """Checks if the operator is a binary operator.""" return self.argument_count == 2 +@unique class GraphEvalOrder(Enum): - """ - Order in which the computation graph should be evaluated. - Applies to deferred execution mode only. - """ - FORWARD = 0 # Evaluate graph left-to-right - REVERSE = 1 # Evaluate graph right-to-left + FORWARD = 0 + REVERSE = 1 +@unique class ExecutionMode(Enum): - """ - Execution modes for the magnetron context. - """ - EAGER = 0 # Execute operations immediately (dynamic graph) - DEFERRED = 1 # Build computation graph and execute later (static graph) + EAGER = 0 + DEFERRED = 1 @dataclass @@ -175,41 +83,17 @@ class GlobalConfig: class Context: - """ - Manages the magnetron context and _ptr lifecycles, including device selection, - memory allocation, and execution mode. - - A global active context is created automatically when needed. - """ - - _active: 'Context' = None # Global context + _active: 'Context' = None @staticmethod def active() -> 'Context': - """ - Returns the active global context, creating one if it does not exist. - - Returns - ------- - Context - The currently active context. - """ if Context._active is None: - enable_log(GlobalConfig.verbose) + C.mag_set_log_mode(GlobalConfig.verbose) Context._active = Context(GlobalConfig.compute_device) return Context._active - def __init__(self, device: ComputeDevice.CPU | ComputeDevice.CUDA, *, execution_mode: ExecutionMode = ExecutionMode.EAGER): - """ - Initializes a new magnetron context. - - Parameters - ---------- - device : ComputeDevice - The compute device (CPU or CUDA). - execution_mode : ExecutionMode, optional - The execution mode (eager or deferred), by default EAGER. - """ + def __init__(self, device: ComputeDevice.CPU | ComputeDevice.CUDA, *, + execution_mode: ExecutionMode = ExecutionMode.EAGER): descriptor: ffi.CData = ffi.new('mag_device_descriptor_t*') if isinstance(device, ComputeDevice.CPU): descriptor.type = 0 @@ -222,256 +106,92 @@ def __init__(self, device: ComputeDevice.CPU | ComputeDevice.CUDA, *, execution_ @property def compute_device_name(self) -> str: - """ - Returns the name of the active compute device. - - Returns - ------- - str - Name of the device, e.g. "CPU" or "NVIDIA GPU". - """ return ffi.string(C.mag_ctx_get_compute_device_name(self._ptr)).decode('utf-8') @property def execution_mode(self) -> ExecutionMode: - """ - Returns the execution mode of the context. - - Returns - ------- - ExecutionMode - The current execution mode (EAGER or DEFERRED). - """ return ExecutionMode(C.mag_ctx_get_exec_mode(self._ptr)) @execution_mode.setter def execution_mode(self, mode: ExecutionMode): - """ - Sets the execution mode of the context. - - Parameters - ---------- - mode : ExecutionMode - Desired mode (EAGER or DEFERRED). - """ C.mag_ctx_set_exec_mode(self._ptr, mode.value) @property def prng_algorithm(self) -> PRNGAlgorithm: - """ - Returns the PRNG algorithm currently used. - - Returns - ------- - PRNGAlgorithm - The PRNG algorithm in use. - """ return PRNGAlgorithm(C.mag_ctx_get_prng_algorithm(self._ptr)) @prng_algorithm.setter def prng_algorithm(self, algorithm: PRNGAlgorithm): - """ - Sets the PRNG algorithm and re-seeds it. - - Parameters - ---------- - algorithm : PRNGAlgorithm - The desired PRNG algorithm. - """ C.mag_ctx_set_prng_algorithm(self._ptr, algorithm.value, 0) @property def os_name(self) -> str: - """ - Returns the operating system name. - - Returns - ------- - str - The OS name (e.g., "Linux"). - """ return ffi.string(C.mag_ctx_get_os_name(self._ptr)).decode('utf-8') @property def cpu_name(self) -> str: - """ - Returns the CPU name/brand string. - - Returns - ------- - str - The CPU name. - """ return ffi.string(C.mag_ctx_get_cpu_name(self._ptr)).decode('utf-8') @property def cpu_virtual_cores(self) -> int: - """ - Returns the number of virtual CPU cores (logical processors). - - Returns - ------- - int - Count of virtual CPU cores. - """ return C.mag_ctx_get_cpu_virtual_cores(self._ptr) @property def cpu_physical_cores(self) -> int: - """ - Returns the number of physical CPU cores. - - Returns - ------- - int - Count of physical CPU cores. - """ return C.mag_ctx_get_cpu_physical_cores(self._ptr) @property def cpu_sockets(self) -> int: - """ - Returns the number of CPU sockets present on the system. - - Returns - ------- - int - CPU socket count. - """ return C.mag_ctx_get_cpu_sockets(self._ptr) @property def physical_memory_total(self) -> int: - """ - Returns the total physical memory (RAM) in bytes. - - Returns - ------- - int - Total system memory in bytes. - """ return C.mag_ctx_get_physical_memory_total(self._ptr) @property def physical_memory_free(self) -> int: - """ - Returns the amount of free physical memory (RAM) in bytes. - - Returns - ------- - int - Free memory in bytes. - """ return C.mag_ctx_get_physical_memory_free(self._ptr) @property def physical_memory_used(self) -> int: - """ - Returns the amount of used physical memory (RAM) in bytes. - - Returns - ------- - int - Used memory in bytes. - """ return abs(self.physical_memory_total - self.physical_memory_free) @property def is_numa_system(self) -> bool: - """ - Checks if the system uses Non-Uniform Memory Access (NUMA). - - Returns - ------- - bool - True if NUMA is supported, False otherwise. - """ return C.mag_ctx_is_numa_system(self._ptr) @property def total_allocated_pool_memory(self) -> int: - """ - Returns the total allocated memory (pool) by this context. - - Returns - ------- - int - Total allocated memory in bytes. - """ return C.mag_ctx_total_allocated_pool_memory(self._ptr) @property def total_tensors_created(self) -> int: - """ - Returns the total number of tensors created (including views). - - Returns - ------- - int - Count of all tensors created. - """ return C.mag_ctx_get_total_tensors_created(self._ptr) @property def total_tensors_allocated(self) -> int: - """ - Returns the total number of allocated tensors (not including views). - - Returns - ------- - int - Count of allocated tensors. - """ return C.mag_ctx_get_total_tensors_allocated(self._ptr) def start_profiler(self) -> None: - """ - Starts recording profiling information for operations. - Profiling must be stopped to produce a report. Slightly reduces performance. - """ C.mag_ctx_profile_start_recording(self._ptr) def stop_profiler(self, export_csv_file: str | None = None) -> None: - """ - Stops recording profiling information and generates a report. - - Parameters - ---------- - export_csv_file : str, optional - Path to export a CSV profiling report. If None, only an internal report is generated. - """ csv_file = ffi.NULL if export_csv_file is None else bytes(export_csv_file, 'utf-8') C.mag_ctx_profile_stop_recording(self._ptr, csv_file) def __del__(self): - """ - Destructor that releases context resources. - """ C.mag_ctx_destroy(self._ptr) self._ptr = ffi.NULL class Tensor: - """ - Represents a _ptr in the magnetron library. Supports various operations and transformations. - """ - def __init__(self, ptr: ffi.CData | None = None) -> None: - """ - Internal constructor. Do not call directly. Use static methods or operators. - - Parameters - ---------- - ptr : ffi.CData or None - Internal tensor instance (C data pointer). - """ if isinstance(ptr, ffi.CData): assert ptr != ffi.NULL, 'Invalid tensor pointer' self._ctx = None self._ptr = ptr def __del__(self) -> None: - """Releases _ptr resources upon object destruction.""" if hasattr(self, '_ptr') and isinstance(self._ptr, ffi.CData) and self._ptr != ffi.NULL: C.mag_tensor_decref(self._ptr) self._ptr = ffi.NULL @@ -488,20 +208,6 @@ def __del__(self) -> None: def _new(self, ctx: Context, *, shape: tuple[int, ...], dtype: DType = DType.F32, name: str | None = None) -> None: - """ - Internal helper to create a new _ptr instance. - - Parameters - ---------- - ctx : Context - The magnetron context where this _ptr belongs. - shape : tuple[int, ...] - Dimensions of the tensor. - dtype : DType, optional - Data type of the tensor, by default DType.F32. - name : str or None, optional - A friendly name for the tensor, by default None. - """ assert 0 < len(shape) <= MAX_DIMS, f'Invalid number of dimensions: {len(shape)}' assert all(0 < dim <= DIM_MAX for dim in shape), 'Invalid dimension size' self._ctx = weakref.ref(ctx) @@ -511,23 +217,6 @@ def _new(self, ctx: Context, *, shape: tuple[int, ...], dtype: DType = DType.F32 @classmethod def empty(cls, shape: tuple[int, ...], *, dtype: DType = DType.F32, name: str | None = None) -> 'Tensor': - """ - Creates an empty _ptr with uninitialized data. - - Parameters - ---------- - shape : tuple[int, ...] - The shape of the tensor. - dtype : DType, optional - Data type of the tensor, by default F32. - name : str or None, optional - A friendly name for the tensor, by default None. - - Returns - ------- - Tensor - The newly created empty tensor. - """ tensor = cls(None) tensor._new(Context.active(), shape=shape, dtype=dtype, name=name) return tensor @@ -535,25 +224,6 @@ def empty(cls, shape: tuple[int, ...], *, dtype: DType = DType.F32, name: str | @classmethod def full(cls, shape: tuple[int, ...], *, fill_value: float, dtype: DType = DType.F32, name: str | None = None) -> 'Tensor': - """ - Creates a _ptr filled with a given constant value. - - Parameters - ---------- - shape : tuple[int, ...] - The shape of the tensor. - fill_value : float - The constant value to fill. - dtype : DType, optional - Data type, by default F32. - name : str or None, optional - A friendly name for the tensor, by default None. - - Returns - ------- - Tensor - The filled tensor. - """ tensor = cls(None) tensor._new(Context.active(), shape=shape, dtype=dtype, name=name) C.mag_tensor_fill(tensor._ptr, fill_value) @@ -562,24 +232,6 @@ def full(cls, shape: tuple[int, ...], *, fill_value: float, dtype: DType = DType @classmethod def const(cls, data, *, dtype: DType = DType.F32, name: str | None = None) -> 'Tensor': - """ - Creates a _ptr from a nested Python list or a single scalar. - - Parameters - ---------- - data : scalar or nested list - Data to create the tensor from. - dtype : DType, optional - Data type, by default F32. - name : str or None, optional - A friendly name for the tensor, by default None. - - Returns - ------- - Tensor - The constructed tensor. - """ - def flatten_nested_lists(nested): if not isinstance(nested, list): return (), [nested] @@ -607,47 +259,11 @@ def flatten_nested_lists(nested): @classmethod def zeros(cls, shape: tuple[int, ...], *, dtype: DType = DType.F32, name: str | None = None) -> 'Tensor': - """ - Creates a _ptr filled with zeros. - - Parameters - ---------- - shape : tuple[int, ...] - The shape of the tensor. - dtype : DType, optional - Data type, by default F32. - name : str or None, optional - A friendly name for the tensor, by default None. - - Returns - ------- - Tensor - The zero-filled tensor. - """ return cls.full(shape, fill_value=0.0, dtype=dtype, name=name) @classmethod def uniform(cls, shape: tuple[int, ...], *, interval: (float, float) = (-1.0, 1.0), dtype: DType = DType.F32, name: str | None = None) -> 'Tensor': - """ - Creates a _ptr filled with random uniform values within a given interval. - - Parameters - ---------- - shape : tuple[int, ...] - The shape of the tensor. - interval : (float, float), optional - Min and max values for uniform distribution, by default (-1.0, 1.0). - dtype : DType, optional - Data type, by default F32. - name : str or None, optional - A friendly name for the tensor, by default None. - - Returns - ------- - Tensor - The _ptr filled with random values. - """ tensor = cls(None) tensor._new(Context.active(), shape=shape, dtype=dtype, name=name) if interval[1] < interval[0]: @@ -656,24 +272,7 @@ def uniform(cls, shape: tuple[int, ...], *, interval: (float, float) = (-1.0, 1. return tensor @classmethod - def normal(cls, shape: tuple[int, ...], *, mean: float=0.0, stddev: float=1.0) -> 'Tensor': - """ - Creates a _ptr filled with random values from a normal distribution. - - Parameters - ---------- - shape : tuple[int, ...] - The shape of the tensor. - mean : float - Mean of the normal distribution. - stddev : float - Standard deviation of the normal distribution. - - Returns - ------- - Tensor - The _ptr filled with normally distributed values. - """ + def normal(cls, shape: tuple[int, ...], *, mean: float = 0.0, stddev: float = 1.0) -> 'Tensor': tensor = cls(None) tensor._new(Context.active(), shape=shape, dtype=DType.F32) C.mag_tensor_fill_random_normal(tensor._ptr, mean, stddev) @@ -681,19 +280,6 @@ def normal(cls, shape: tuple[int, ...], *, mean: float=0.0, stddev: float=1.0) - @classmethod def load(cls, file_path: str) -> 'Tensor': - """ - Loads a _ptr from a binary magnetron file. - - Parameters - ---------- - file_path : str - Path to the .magnetron file. - - Returns - ------- - Tensor - The loaded tensor. - """ assert file_path.endswith('.magnetron'), 'File must be a magnetron file' instance = C.mag_tensor_load(Context.active()._ptr, bytes(file_path, 'utf-8')) return cls(ptr=instance) @@ -703,25 +289,6 @@ def load_image(cls, file_path: str, *, name: str | None = None, channels=ColorChannels.AUTO, resize_to: (int, int) = (0, 0)) -> 'Tensor': - """ - Loads an image from a file and creates a tensor. - - Parameters - ---------- - file_path : str - Path to the image file. - name : str or None, optional - A friendly name for the tensor, by default None. - channels : ColorChannels, optional - Desired color channels to load, by default AUTO. - resize_to : (int, int), optional - If not (0,0), resize the image to (width, height), by default (0,0). - - Returns - ------- - Tensor - The created image tensor. - """ assert isfile(file_path), f'File not found: {file_path}' instance = C.mag_tensor_load_image(Context.active()._ptr, bytes(file_path, 'utf-8'), channels.value, resize_to[0], resize_to[1]) @@ -731,350 +298,106 @@ def load_image(cls, file_path: str, *, return tensor def print(self, print_header: bool = False, print_data: bool = True) -> None: - """ - Prints the tensor metadata and optionally its data. - - Parameters - ---------- - print_header : bool, optional - If True, prints a header line, by default False. - print_data : bool, optional - If True, prints the tensor data, by default True. - """ C.mag_tensor_print(self._ptr, print_header, print_data) @property def name(self) -> str: - """ - Returns the name of the tensor. - - Returns - ------- - str - The _ptr's name. - """ return ffi.string(C.mag_tensor_get_name(self._ptr)).decode('utf-8') @name.setter def name(self, name: str) -> None: - """ - Sets a name for the tensor. - - Parameters - ---------- - name : str - The new name for the tensor. - """ C.mag_tensor_set_name(self._ptr, bytes(name, 'utf-8')) @property def rank(self) -> int: - """ - Returns the rank (number of dimensions) of the tensor. - - Returns - ------- - int - Number of dimensions. - """ return C.mag_tensor_rank(self._ptr) @property def shape(self) -> tuple[int, ...]: - """ - Returns the shape (dimensions) of the tensor. - - Returns - ------- - tuple[int, ...] - The dimensions of the tensor. - """ return tuple(ffi.unpack(C.mag_tensor_shape(self._ptr), self.rank)) @property def strides(self) -> tuple[int, ...]: - """ - Returns the strides of the tensor. - - Returns - ------- - tuple[int, ...] - The strides for each dimension. - """ return tuple(ffi.unpack(C.mag_tensor_strides(self._ptr), self.rank)) @property def dtype(self) -> DType: - """ - Returns the data type of the tensor. - - Returns - ------- - DType - The data type, e.g. DType.F32. - """ return DType(C.mag_tensor_dtype(self._ptr)) @property def data_ptr(self) -> int: - """ - Returns the pointer to the tensor's data buffer. - - Returns - ------- - int - Memory address of the tensor data. - """ return int(ffi.cast('uintptr_t', C.mag_tensor_data_ptr(self._ptr))) def tolist(self) -> list[float]: - """ - Returns the tensor data as a Python list of floats. - - Returns - ------- - list[float] - A flat list containing all _ptr elements. - """ assert self.dtype == DType.F32, 'Invalid data type' return ffi.unpack(ffi.cast('float*', C.mag_tensor_data_ptr(self._ptr)), self.numel) @property def data_size(self) -> int: - """ - Returns the size of the tensor buffer in bytes. - - Returns - ------- - int - Data size in bytes. - """ return C.mag_tensor_data_size(self._ptr) @property def numel(self) -> int: - """ - Returns the number of elements in the tensor. - - Returns - ------- - int - Number of elements. - """ return C.mag_tensor_numel(self._ptr) @property def num_rows(self) -> int: - """ - Returns the number of rows in a 2D _ptr (matrix). - - Returns - ------- - int - Number of rows. - """ return C.mag_tensor_num_rows(self._ptr) @property def num_cols(self) -> int: - """ - Returns the number of columns in a 2D _ptr (matrix). - - Returns - ------- - int - Number of columns. - """ return C.mag_tensor_num_cols(self._ptr) @property def is_scalar(self) -> bool: - """ - Checks if the tensor is a scalar (0D). - - Returns - ------- - bool - True if scalar, otherwise False. - """ return C.mag_tensor_is_scalar(self._ptr) @property def is_vector(self) -> bool: - """ - Checks if the tensor is a vector (1D). - - Returns - ------- - bool - True if vector, otherwise False. - """ return C.mag_tensor_is_vector(self._ptr) @property def is_matrix(self) -> bool: - """ - Checks if the tensor is a matrix (2D). - - Returns - ------- - bool - True if matrix, otherwise False. - """ return C.mag_tensor_is_matrix(self._ptr) @property def is_volume(self) -> bool: - """ - Checks if the tensor is a volume (3D or higher). - - Returns - ------- - bool - True if volume, otherwise False. - """ return C.mag_tensor_is_volume(self._ptr) @property def is_transposed(self) -> bool: - """ - Checks if the tensor is transposed. - - Returns - ------- - bool - True if transposed, otherwise False. - """ return C.mag_tensor_is_transposed(self._ptr) @property def is_permuted(self) -> bool: - """ - Checks if the tensor is permuted. - - Returns - ------- - bool - True if permuted, otherwise False. - """ return C.mag_tensor_is_permuted(self._ptr) def is_shape_eq(self, other: 'Tensor') -> bool: - """ - Checks if two tensors have the same shape. - - Parameters - ---------- - other : Tensor - Another _ptr to compare shape. - - Returns - ------- - bool - True if shapes match, otherwise False. - """ return C.mag_tensor_is_shape_eq(self._ptr, other._ptr) def are_strides_eq(self, other: 'Tensor') -> bool: - """ - Checks if two tensors have identical strides. - - Parameters - ---------- - other : Tensor - Another _ptr to compare strides. - - Returns - ------- - bool - True if strides match, otherwise False. - """ return C.mag_tensor_are_strides_eq(self._ptr, other._ptr) def can_broadcast(self, other: 'Tensor') -> bool: - """ - Checks if `other` _ptr can be broadcasted to the shape of this tensor. - - Parameters - ---------- - other : Tensor - Another tensor. - - Returns - ------- - bool - True if broadcastable, otherwise False. - """ return C.mag_tensor_can_broadcast(self._ptr, other._ptr) @property def width(self) -> int: - """ - Returns the width of an image _ptr (assumes layout: CxHxW). - - Returns - ------- - int - Width dimension. - """ return self.shape[2] @property def height(self) -> int: - """ - Returns the height of an image _ptr (assumes layout: CxHxW). - - Returns - ------- - int - Height dimension. - """ return self.shape[1] @property def channels(self) -> int: - """ - Returns the number of channels in an image _ptr (assumes layout: CxHxW). - - Returns - ------- - int - Number of channels. - """ return self.shape[0] @property def is_contiguous(self) -> bool: - """ - Checks if the tensor is contiguous in memory. - - Returns - ------- - bool - True if contiguous, otherwise False. - """ return C.mag_tensor_is_contiguous(self._ptr) def is_close(self, other: 'Tensor', eps: float = -1.0, print_eq_percent: bool = False) -> (bool, float): - """ - Checks if the tensor is close to another _ptr within a given epsilon. - - Parameters - ---------- - other : Tensor - Another _ptr to compare to. - eps : float, optional - Epsilon tolerance. If -1.0, uses a default internal value. - print_eq_percent : bool, optional - If True, prints the percentage of equal elements. - - Returns - ------- - (bool, float) - A tuple (close_status, eq_percent). - close_status is True if the tensors are close. - eq_percent is the percentage of approximately equal elements. - """ percent_eq = ffi.new('double[1]') is_eq = C.mag_tensor_is_close(self._ptr, other._ptr, eps, percent_eq) if print_eq_percent: @@ -1082,130 +405,36 @@ def is_close(self, other: 'Tensor', eps: float = -1.0, print_eq_percent: bool = return is_eq, percent_eq[0] def draw_box(self, p1: (int, int), p2: (int, int), width: int = 2, rgb: int = 0xffffff): - """ - Draws a rectangular box on an image tensor. - - Parameters - ---------- - p1 : (int, int) - Top-left corner coordinates (x, y). - p2 : (int, int) - Bottom-right corner coordinates (x, y). - width : int, optional - Line width of the box, by default 2. - rgb : int, optional - 24-bit RGB color (e.g. 0xFFFFFF for white), by default 0xffffff. - """ assert p2[0] > p1[0] and p2[1] > p1[1] and width > 0 C.mag_tensor_img_draw_box(self._ptr, p1[0], p1[1], p2[0], p2[1], width, rgb & 0xffffff) def draw_text(self, p: (int, int), size: int, txt: str, rgb: int = 0xffffff): - """ - Draws text on an image tensor. - - Parameters - ---------- - p : (int, int) - Position to draw text (x, y). - size : int - Font size. - txt : str - The text to draw. - rgb : int, optional - 24-bit RGB color, by default white (0xffffff). - """ C.mag_tensor_img_draw_text(self._ptr, p[0], p[1], size, rgb & 0xffffff, bytes(txt, 'utf-8')) def save(self, file_path: str) -> None: - """ - Saves the tensor to a binary magnetron file. - - Parameters - ---------- - file_path : str - File path to save the tensor. Appends '.magnetron' if not present. - """ if not file_path.endswith('.magnetron'): file_path += '.magnetron' C.mag_tensor_save(self._ptr, bytes(file_path, 'utf-8')) def save_image(self, file_path: str) -> None: - """ - Saves a 3D image _ptr as a JPG image file. - - Parameters - ---------- - file_path : str - File path to save the image. - - Raises - ------ - AssertionError - If _ptr is not 3D or channel count is not supported. - """ assert self.rank == 3, 'Tensor must be a 3D image _ptr' assert self.channels in (1, 3, 4), 'Invalid number of color channels' C.mag_tensor_save_image(self._ptr, bytes(file_path, 'utf-8')) def clone(self) -> 'Tensor': - """ - Creates a new _ptr with the same data as this one (deep copy). - - Returns - ------- - Tensor - A cloned tensor. - """ return Tensor(C.mag_clone(self._ptr)) def view(self) -> 'Tensor': - """ - Creates a view of the tensor that shares underlying data (shallow copy). - - Returns - ------- - Tensor - A view tensor. - """ return Tensor(C.mag_view(self._ptr)) def transpose(self) -> 'Tensor': - """ - Transposes the tensor (swaps the last two dimensions). Same as tensor.T. - - Returns - ------- - Tensor - A transposed tensor. - """ return Tensor(C.mag_transpose(self._ptr)) @property def T(self) -> 'Tensor': - """ - Transposes the tensor (swaps the last two dimensions). Same as tensor.transpose(). - - Returns - ------- - Tensor - A transposed tensor. - """ return Tensor(C.mag_transpose(self._ptr)) def permute(self, axes: tuple[int, ...]) -> 'Tensor': - """ - Permutes the dimensions of the tensor. - - Parameters - ---------- - axes : tuple[int, ...] - A tuple specifying the permutation of axes. - - Returns - ------- - Tensor - A tensor with permuted dimensions. - """ assert len(axes) == self.rank, f'Invalid number of axes, require {self.rank}, got {len(axes)}' if len(axes) != MAX_DIMS: axes = axes + tuple(range(self.rank, MAX_DIMS)) @@ -1217,288 +446,170 @@ def permute(self, axes: tuple[int, ...]) -> 'Tensor': return Tensor(C.mag_permute(self._ptr, *axes)) def mean(self) -> 'Tensor': - """Computes the mean of all elements in the tensor.""" return Tensor(C.mag_mean(self._ptr)) def min(self) -> 'Tensor': - """Computes the minimum value in the tensor.""" return Tensor(C.mag_min(self._ptr)) def max(self) -> 'Tensor': - """Computes the maximum value in the tensor.""" return Tensor(C.mag_max(self._ptr)) def sum(self) -> 'Tensor': - """Computes the sum of all elements in the tensor.""" return Tensor(C.mag_sum(self._ptr)) def abs(self) -> 'Tensor': - """Computes element-wise absolute value.""" return Tensor(C.mag_abs(self._ptr)) def abs_(self) -> 'Tensor': - """In-place element-wise absolute value.""" return Tensor(C.mag_abs_(self._ptr)) def neg(self) -> 'Tensor': - """Computes element-wise negation.""" return Tensor(C.mag_neg(self._ptr)) def neg_(self) -> 'Tensor': - """In-place element-wise negation.""" return Tensor(C.mag_neg_(self._ptr)) def __neg__(self) -> 'Tensor': - """Overloads unary negation: -X.""" return self.neg() def log(self) -> 'Tensor': - """Computes element-wise natural logarithm.""" return Tensor(C.mag_log(self._ptr)) def log_(self) -> 'Tensor': - """In-place element-wise natural logarithm.""" return Tensor(C.mag_log_(self._ptr)) def sqr(self) -> 'Tensor': - """Computes element-wise square of values.""" return Tensor(C.mag_sqr(self._ptr)) def sqr_(self) -> 'Tensor': - """In-place element-wise square of values.""" return Tensor(C.mag_sqr_(self._ptr)) def sqrt(self) -> 'Tensor': - """Computes element-wise square root.""" return Tensor(C.mag_sqrt(self._ptr)) def sqrt_(self) -> 'Tensor': - """In-place element-wise square root.""" return Tensor(C.mag_sqrt_(self._ptr)) def sin(self) -> 'Tensor': - """Computes element-wise sine.""" return Tensor(C.mag_sin(self._ptr)) def sin_(self) -> 'Tensor': - """In-place element-wise sine.""" return Tensor(C.mag_sin_(self._ptr)) def cos(self) -> 'Tensor': - """Computes element-wise cosine.""" return Tensor(C.mag_cos(self._ptr)) def cos_(self) -> 'Tensor': - """In-place element-wise cosine.""" return Tensor(C.mag_cos_(self._ptr)) def heaviside_step(self) -> 'Tensor': - """Computes element-wise Heaviside step function.""" return Tensor(C.mag_step(self._ptr)) def heaviside_step_(self) -> 'Tensor': - """In-place element-wise Heaviside step function.""" return Tensor(C.mag_step_(self._ptr)) def softmax(self, derivative: bool = False) -> 'Tensor': - """ - Applies softmax or its derivative on the tensor. - - Parameters - ---------- - derivative : bool, optional - If True, computes softmax derivative instead of softmax, by default False. - - Returns - ------- - Tensor - The transformed tensor. - """ return Tensor(C.mag_softmax_dv(self._ptr) if derivative else C.mag_softmax(self._ptr)) def softmax_(self, derivative: bool = False) -> 'Tensor': - """In-place softmax or softmax derivative.""" return Tensor(C.mag_softmax_dv_(self._ptr) if derivative else C.mag_softmax_(self._ptr)) def sigmoid(self, derivative: bool = False) -> 'Tensor': - """ - Applies sigmoid or its derivative on the tensor. - - Parameters - ---------- - derivative : bool, optional - If True, computes sigmoid derivative, by default False. - - Returns - ------- - Tensor - The transformed tensor. - """ return Tensor(C.mag_sigmoid_dv(self._ptr) if derivative else C.mag_sigmoid(self._ptr)) def sigmoid_(self, derivative: bool = False) -> 'Tensor': - """In-place sigmoid or sigmoid derivative.""" return Tensor(C.mag_sigmoid_dv_(self._ptr) if derivative else C.mag_sigmoid_(self._ptr)) def hard_sigmoid(self) -> 'Tensor': - """Applies hard sigmoid to the tensor.""" return Tensor(C.mag_hard_sigmoid(self._ptr)) def hard_sigmoid_(self) -> 'Tensor': - """In-place hard sigmoid.""" return Tensor(C.mag_hard_sigmoid_(self._ptr)) def silu(self, derivative: bool = False) -> 'Tensor': - """ - Applies SiLU (Sigmoid-Weighted Linear Unit) or its derivative. - - Parameters - ---------- - derivative : bool, optional - If True, applies SiLU derivative, by default False. - - Returns - ------- - Tensor - The transformed tensor. - """ return C.mag_silu_dv(self._ptr) if derivative else C.mag_silu(self._ptr) def silu_(self, derivative: bool = False) -> 'Tensor': - """In-place SiLU or SiLU derivative.""" return Tensor(C.mag_silu_dv_(self._ptr) if derivative else C.mag_silu_(self._ptr)) def tanh(self, derivative: bool = False) -> 'Tensor': - """ - Applies hyperbolic tangent or its derivative. - - Parameters - ---------- - derivative : bool, optional - If True, computes tanh derivative, by default False. - - Returns - ------- - Tensor - The transformed tensor. - """ return Tensor(C.mag_tanh_dv(self._ptr) if derivative else C.mag_tanh(self._ptr)) def tanh_(self, derivative: bool = False) -> 'Tensor': - """In-place tanh or tanh derivative.""" return Tensor(C.mag_tanh_dv_(self._ptr) if derivative else C.mag_tanh_(self._ptr)) def relu(self, derivative: bool = False) -> 'Tensor': - """ - Applies ReLU (Rectified Linear Unit) or its derivative. - - Parameters - ---------- - derivative : bool, optional - If True, computes ReLU derivative, by default False. - - Returns - ------- - Tensor - The transformed tensor. - """ return Tensor(C.mag_relu_dv(self._ptr) if derivative else C.mag_relu(self._ptr)) def relu_(self, derivative: bool = False) -> 'Tensor': - """In-place ReLU or ReLU derivative.""" return Tensor(C.mag_relu_dv_(self._ptr) if derivative else C.mag_relu_(self._ptr)) def gelu(self, derivative: bool = False) -> 'Tensor': - """ - Applies GELU (Gaussian Error Linear Unit) or its derivative. - - Parameters - ---------- - derivative : bool, optional - If True, computes GELU derivative, by default False. - - Returns - ------- - Tensor - The transformed tensor. - """ return Tensor(C.mag_gelu_dv(self._ptr) if derivative else C.mag_gelu(self._ptr)) def gelu_(self, derivative: bool = False) -> 'Tensor': - """In-place GELU or GELU derivative.""" return Tensor(C.mag_gelu_dv_(self._ptr) if derivative else C.mag_gelu_(self._ptr)) def __add__(self, other: object | int | float) -> 'Tensor': - """Element-wise addition with another _ptr or scalar.""" - return Tensor(C.mag_add(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_adds(self._ptr, - float(other))) + return Tensor( + C.mag_add(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_adds(self._ptr, float(other))) + + def __radd__(self, other: int | float) -> 'Tensor': + return Tensor( + C.mag_add(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_adds(self._ptr, float(other))) def __iadd__(self, other: object | int | float) -> 'Tensor': - """In-place element-wise addition.""" - return Tensor(C.mag_add_(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_adds_(self._ptr, - float(other))) + return Tensor( + C.mag_add_(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_adds_(self._ptr, float(other))) def __sub__(self, other: object | int | float) -> 'Tensor': - """Element-wise subtraction with another _ptr or scalar.""" - return Tensor(C.mag_sub(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_subs(self._ptr, - float(other))) + return Tensor( + C.mag_sub(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_subs(self._ptr, float(other))) + + def __rsub__(self, other: int | float) -> 'Tensor': + return Tensor.full(self.shape, fill_value=other) - self def __isub__(self, other: object | int | float) -> 'Tensor': - """In-place element-wise subtraction.""" - return Tensor(C.mag_sub_(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_subs_(self._ptr, - float(other))) + return Tensor( + C.mag_sub_(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_subs_(self._ptr, float(other))) def __mul__(self, other: object | int | float) -> 'Tensor': - """Element-wise multiplication with another _ptr or scalar.""" - return Tensor(C.mag_mul(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_muls(self._ptr, - float(other))) + return Tensor( + C.mag_mul(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_muls(self._ptr, float(other))) + + def __rmul__(self, other: int | float) -> 'Tensor': + return Tensor( + C.mag_mul(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_muls(self._ptr, float(other))) def __imul__(self, other: object | int | float) -> 'Tensor': - """In-place element-wise multiplication.""" - return Tensor(C.mag_mul_(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_muls_(self._ptr, - float(other))) + return Tensor( + C.mag_mul_(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_muls_(self._ptr, float(other))) def __truediv__(self, other: object | int | float) -> 'Tensor': - """Element-wise division with another _ptr or scalar.""" - return Tensor(C.mag_div(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_divs(self._ptr, - float(other))) + return Tensor( + C.mag_div(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_divs(self._ptr, float(other))) + + def __rtruediv__(self, other: int | float) -> 'Tensor': + return Tensor.full(self.shape, fill_value=other) / self def __itruediv__(self, other: object | int | float) -> 'Tensor': - """In-place element-wise division.""" - return Tensor(C.mag_div_(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_divs_(self._ptr, - float(other))) + return Tensor( + C.mag_div_(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_divs_(self._ptr, float(other))) def __matmul__(self, other: 'Tensor') -> 'Tensor': - """Matrix multiplication with another _ptr: A @ B.""" return Tensor(C.mag_matmul(self._ptr, other._ptr)) def __imatmul__(self, other: 'Tensor') -> 'Tensor': - """In-place matrix multiplication: A @= B.""" return Tensor(C.mag_matmul_(self._ptr, other._ptr)) def __eq__(self, other: 'Tensor') -> bool: - """ - Checks if two tensors have identical data and shape. - - Parameters - ---------- - other : Tensor - Another _ptr to compare. - - Returns - ------- - bool - True if equal, otherwise False. - """ return C.mag_tensor_eq(self._ptr, other._ptr) def __str__(self) -> str: - """String representation prints the tensor header and data.""" self.print(True, True) return '' def __getitem__(self, indices: int | tuple[int, ...]) -> float: - """Retrieve a value via virtual or physical indexing.""" if isinstance(indices, int): return C.mag_tensor_get_scalar_virtual_index(self._ptr, indices) elif isinstance(indices, tuple): @@ -1516,7 +627,6 @@ def __getitem__(self, indices: int | tuple[int, ...]) -> float: raise TypeError("Indices must be an int or a tuple of ints.") def __setitem__(self, indices: int | tuple[int, ...], value: float) -> None: - """Set a value via virtual or physical indexing.""" if isinstance(indices, int): C.mag_tensor_set_scalar_virtual_index(self._ptr, indices, float(value)) elif isinstance(indices, tuple): diff --git a/python/magnetron_framework/magnetron/layer.py b/python/magnetron_framework/magnetron/layer.py new file mode 100644 index 0000000..e5e84e6 --- /dev/null +++ b/python/magnetron_framework/magnetron/layer.py @@ -0,0 +1,107 @@ +# (c) 2025 Mario "Neo" Sieg. + +import math +from abc import ABC, abstractmethod +from dataclasses import dataclass +from enum import unique, Enum + +from magnetron import Tensor + + +class Layer(ABC): + """Abstract base class for all layers""" + @abstractmethod + def forward(self, inputs: Tensor) -> Tensor: + pass + + @abstractmethod + def backward(self, is_hidden_layer: bool, delta: Tensor, rate: float) -> Tensor: + pass + + +@dataclass +class LayerInit: + """Weight/bias initialization methods and parameters""" + + @unique + class Dist(Enum): + NORMAL = 0 + UNIFORM = 1 + + @unique + class Method(Enum): + RANDOM = 0 + XAVIER = 1 + HE = 2 + + method: Method + distrib: Dist + uniform_interval: (float, float) = (-1.0, 1.0) + mean: float = 0.0 + stddev: float = 1.0 + gain: float = 1.0 + + def __init__(self, method: Method, distrib: Dist, **kwargs): + self.method = method + self.distrib = distrib + for key, value in kwargs.items(): + setattr(self, key, value) + + def apply(self, shape: tuple[int, ...]) -> Tensor: + assert len(shape) >= 1 + + if self.method == self.Method.RANDOM: + if self.distrib == self.Dist.NORMAL: + return Tensor.normal(shape, mean=self.mean, stddev=self.stddev) + elif self.distrib == self.Dist.UNIFORM: + return Tensor.uniform(shape, interval=self.uniform_interval) + + fan_in: int = shape[0] + fan_out: int = shape[1] if self.method == self.Method.XAVIER else None + factor: float = 1.0 + bound: float = 1.0 + + if self.method == self.Method.XAVIER: + factor = 2.0 / (fan_in + fan_out) + bound = math.sqrt(6.0 / (fan_in + fan_out)) + elif self.method == self.Method.HE: + factor = 1.0 / fan_in + bound = math.sqrt(3.0 / fan_in) + + if self.distrib == self.Dist.NORMAL: + stddev = self.gain * math.sqrt(factor) + return Tensor.normal(shape, mean=0.0, stddev=stddev) + elif self.distrib == self.Dist.UNIFORM: + return Tensor.uniform(shape, interval=(-self.gain * bound, self.gain * bound)) + + +class DenseLayer(Layer): + """Fully connected layer""" + def __init__(self, in_features: int, out_features: int, + init: LayerInit = LayerInit(LayerInit.Method.RANDOM, LayerInit.Dist.UNIFORM)): + super().__init__() + self.weight = init.apply((out_features, in_features)) + self.bias = init.apply((out_features, 1)) + self._x = None + self._z = None + self._out = None + + def forward(self, x: Tensor) -> Tensor: + self._x = x + self._z = self.weight @ x + self.bias + self._out = self._z.sigmoid() + return self._out + + def backward(self, is_hidden_layer: bool, delta: Tensor, rate: float) -> Tensor: + self.weight -= (delta @ self._x.T.clone()) * rate + batch_size = delta.shape[1] + ones_vec = Tensor.const([[1.0] for _ in range(batch_size)]) + row_sums = delta @ ones_vec + row_means = row_sums * (1.0 / batch_size) + self.bias -= row_means * rate + if is_hidden_layer: + d_in = self.weight.T.clone() @ delta + d_in *= self._z.sigmoid(derivative=True) + return d_in + else: + return delta diff --git a/python/magnetron_framework/magnetron/model.py b/python/magnetron_framework/magnetron/model.py index 10393eb..12a56cd 100644 --- a/python/magnetron_framework/magnetron/model.py +++ b/python/magnetron_framework/magnetron/model.py @@ -1,81 +1,50 @@ # (c) 2025 Mario "Neo" Sieg. -# Implements high level model classes for neural networks based on the magnetron.core module. import time -from abc import ABC +from abc import ABC, abstractmethod +from dataclasses import dataclass from magnetron import Tensor +from magnetron.layer import Layer +from magnetron.optim import Optim -class Layer(ABC): - def forward(self, inputs: Tensor) -> Tensor: - pass - - def backward(self, is_hidden_layer: bool, delta: Tensor, rate: float) -> Tensor: - pass +@dataclass +class HyperParams: + lr: float = 0.01 + epochs: int = 10000 + epoch_step: int = 100 class Model(ABC): + """Abstract base class for all models""" + def __init__(self, hyper_params: HyperParams): + self.hyper_params = hyper_params + + @abstractmethod def forward(self, inputs: Tensor) -> Tensor: pass + @abstractmethod def backward(self, outputs: Tensor, targets: Tensor, rate: float): pass - def train(self, inputs: Tensor, targets: Tensor, epochs: int, learning_rate: float): + @abstractmethod + def train(self, inputs: Tensor, targets: Tensor): pass + @abstractmethod def summary(self): pass -class Optim: - @staticmethod - def mse(y: Tensor, y_hat: Tensor) -> float: - """Mean Squared Error""" - return (y - y_hat).sqr_().mean()[0] - - @staticmethod - def cross_entropy(y: Tensor, y_hat: Tensor) -> float: - """Cross Entropy Loss""" - return -(y * y_hat.log_()).sum()[0] - - -class DenseLayer(Layer): - def __init__(self, in_features: int, out_features: int): - super().__init__() - self.weight = Tensor.uniform(shape=(out_features, in_features)) - self.bias = Tensor.uniform(shape=(out_features, 1)) - self._x = None - self._z = None - self._out = None - - def forward(self, x: Tensor) -> Tensor: - self._x = x - self._z = self.weight @ x + self.bias - self._out = self._z.sigmoid() - return self._out - - def backward(self, is_hidden_layer: bool, delta: Tensor, rate: float) -> Tensor: - self.weight -= (delta @ self._x.transpose().clone()) * rate - batch_size = delta.shape[1] - ones_vec = Tensor.const([[1.0] for _ in range(batch_size)]) - row_sums = delta @ ones_vec - row_means = row_sums * (1.0 / batch_size) - self.bias -= row_means * rate - if is_hidden_layer: - d_in = self.weight.transpose().clone() @ delta - d_in *= self._z.sigmoid(derivative=True) - return d_in - else: - return delta - - class SequentialModel(Model): - def __init__(self, layers: list[DenseLayer]): - super().__init__() + """Feedforward neural network model (multi-layer perceptron)""" + + def __init__(self, hyper_params: HyperParams, layers: list[Layer]): + super().__init__(hyper_params) self.layers = layers - self.loss_epoch_step = 1000 + self.optim = Optim(hyper_params.lr) def forward(self, inputs: Tensor) -> Tensor: x = inputs @@ -90,23 +59,26 @@ def backward(self, outputs: Tensor, targets: Tensor, rate: float): is_hidden = (i > 0) delta = self.layers[i].backward(is_hidden, delta, rate) - def train(self, inputs: Tensor, targets: Tensor, epochs: int, rate: float): + def train(self, inputs: Tensor, targets: Tensor): + epochs: int = self.hyper_params.epochs + rate: float = self.hyper_params.lr + print(f'Training started for {epochs} epochs with learning rate {rate}') - import time start_time = time.time_ns() - - inputs = inputs.transpose().clone() - targets = targets.transpose().clone() - + inputs = inputs.T.clone() + targets = targets.T.clone() losses = [] for epoch in range(epochs): output = self.forward(inputs) self.backward(output, targets, rate) - loss = Optim.mse(output, targets) + loss = self.optim.mse(output, targets) losses.append(loss) - if epoch % self.loss_epoch_step == 0: + if epoch % self.hyper_params.epoch_step == 0: print(f'Epoch: {epoch}, Loss: {loss:.6f}') duration = (time.time_ns() - start_time) / 1e9 print(f'Training finished in {duration:.2f} seconds') - return losses \ No newline at end of file + return losses + + def summary(self): + pass diff --git a/python/magnetron_framework/magnetron/optim.py b/python/magnetron_framework/magnetron/optim.py new file mode 100644 index 0000000..2f14adb --- /dev/null +++ b/python/magnetron_framework/magnetron/optim.py @@ -0,0 +1,26 @@ +# (c) 2025 Mario "Neo" Sieg. + +from magnetron import Tensor + + +class PolyLR: + def __init__(self, initial_lr: float, max_iter: float): + self.initial_lr = initial_lr + self.max_iter = max_iter + + def step(self, iter: float) -> float: + y: float = iter / self.max_iter + return max(self.initial_lr * (1 - y) ** 2, 1.0e-7) + + +class Optim: + def __init__(self, lr: float): + self.lr = lr + + def mse(self, y: Tensor, y_hat: Tensor) -> float: + """Mean Squared Error""" + return (y - y_hat).sqr_().mean()[0] + + def cross_entropy(self, y: Tensor, y_hat: Tensor) -> float: + """Cross Entropy Loss""" + return -(y * y_hat.log_()).sum()[0] From 10b94829059c295cd428242829c5515876bfc381 Mon Sep 17 00:00:00 2001 From: "Mario Sieg (Neo)" Date: Sat, 25 Jan 2025 19:22:15 +0100 Subject: [PATCH 02/24] XOR network test --- magnetron/magnetron.c | 21 ++- magnetron/magnetron_cpu_blas.inl | 49 ++----- python/magnetron_framework/magnetron/core.py | 34 ++--- python/tests/tensor_ops2.py | 32 +++++ python/tests/xor_nn.py | 135 +++++++++++++++++++ 5 files changed, 207 insertions(+), 64 deletions(-) create mode 100644 python/tests/tensor_ops2.py create mode 100644 python/tests/xor_nn.py diff --git a/magnetron/magnetron.c b/magnetron/magnetron.c index 188c249..f184e4e 100644 --- a/magnetron/magnetron.c +++ b/magnetron/magnetron.c @@ -1130,9 +1130,24 @@ static mag_tensor_t* mag_result_constructor_routine_permuted(mag_tensor_t** inpu static mag_tensor_t* mag_result_constructor_routine_matmul(mag_tensor_t** inputs, const mag_op_param_t* params) { /* MxR = MxN * NxR */ (void)params; int64_t shape[MAG_MAX_DIMS]; - shape[0] = inputs[0]->shape[0]; /* M */ - shape[1] = inputs[1]->shape[1]; /* R */ - return mag_tensor_create(inputs[0]->ctx, MAG_DTYPE_F32, shape, 2, NULL, 0); + int64_t* rd0 = shape; + int64_t* rd1 = shape+1; + int64_t rank = 0; + if (inputs[0]->rank == 1 && inputs[1]->rank == 2) { /* (ℝⁿ)(ℝⁿˣʳ) → ℝʳ */ + *rd0 = 1; + *rd1 = inputs[1]->shape[1]; + rank = 2; + } else if (inputs[0]->rank == 2 && inputs[1]->rank == 1) { /* (ℝᵐˣⁿ)(ℝⁿ) → ℝᵐ */ + *rd0 = inputs[0]->shape[0]; + rank = 1; + } else if (inputs[0]->rank == 1 && inputs[1]->rank == 1) { /* (ℝⁿ)(ℝⁿ) → ℝ */ + rank = 1; + } else { /* (ℝᵐˣⁿ)(ℝⁿˣʳ) → ℝᵐˣʳ */ + *rd0 = inputs[0]->shape[0]; + *rd1 = inputs[1]->shape[1]; + rank = 2; + } + return mag_tensor_create(inputs[0]->ctx, MAG_DTYPE_F32, shape, rank, NULL, 0); } const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { diff --git a/magnetron/magnetron_cpu_blas.inl b/magnetron/magnetron_cpu_blas.inl index 182bd20..4056697 100644 --- a/magnetron/magnetron_cpu_blas.inl +++ b/magnetron/magnetron_cpu_blas.inl @@ -1382,42 +1382,21 @@ static void MAG_HOTPROC mag_blas_matmul_f32(const mag_compute_payload_t* payload xd1 ); #else - if (xd0==yd0 && xd1==yd1) { /* Square matrices */ - for (int64_t i = ra; i < rb; ++i) { /* Rows */ - for (int64_t j = 0; j < yd1; ++j) { - float* xo = br + rd1*i + j; - mag_bnd_chk(xo, br, mag_tensor_data_size(r)); - *xo = 0.0f; - } - for (int64_t k = 0; k < xd1; ++k) { /* Inner dim */ - const mag_f32_t* px = bx + xd1*i + k; - mag_bnd_chk(px, bx, mag_tensor_data_size(x)); - for (int64_t j = 0; j < yd1; ++j) { /* Columns */ - mag_f32_t* pr = br + rd1*i + j; - const mag_f32_t* py = by + yd1*k + j; - mag_bnd_chk(pr, br, mag_tensor_data_size(r)); - mag_bnd_chk(py, by, mag_tensor_data_size(y)); - *pr += (*px) * (*py); - } - } + for (int64_t i = ra; i < rb; ++i) { /* Rows */ + for (int64_t j = 0; j < yd1; ++j) { + float* xo = br + rd1*i + j; + mag_bnd_chk(xo, br, mag_tensor_data_size(r)); + *xo = 0.0f; } - } else { - for (int64_t i = ra; i < rb; ++i) { /* Rows */ - for (int64_t j = 0; j < yd1; ++j) { - float* xo = br + rd1*i + j; - mag_bnd_chk(xo, br, mag_tensor_data_size(r)); - *xo = 0.0f; - } - for (int64_t k = 0; k < xd1; ++k) { /* Inner dim */ - const mag_f32_t* px = bx + xd1*i + k; - mag_bnd_chk(px, bx, mag_tensor_data_size(x)); - for (int64_t j = 0; j < yd1; ++j) { /* Columns */ - mag_f32_t* pr = br + rd1*i + j; - const mag_f32_t* py = by + yd1*k + j; - mag_bnd_chk(pr, br, mag_tensor_data_size(r)); - mag_bnd_chk(py, by, mag_tensor_data_size(y)); - *pr += (*px) * (*py); - } + for (int64_t k = 0; k < xd1; ++k) { /* Inner dim */ + const mag_f32_t* px = bx + xd1*i + k; + mag_bnd_chk(px, bx, mag_tensor_data_size(x)); + for (int64_t j = 0; j < yd1; ++j) { /* Columns */ + mag_f32_t* pr = br + rd1*i + j; + const mag_f32_t* py = by + yd1*k + j; + mag_bnd_chk(pr, br, mag_tensor_data_size(r)); + mag_bnd_chk(py, by, mag_tensor_data_size(y)); + *pr += (*px) * (*py); } } } diff --git a/python/magnetron_framework/magnetron/core.py b/python/magnetron_framework/magnetron/core.py index d316420..efb3d71 100644 --- a/python/magnetron_framework/magnetron/core.py +++ b/python/magnetron_framework/magnetron/core.py @@ -1,6 +1,7 @@ # (c) 2025 Mario "Neo" Sieg. import faulthandler +import typing import weakref from dataclasses import dataclass from enum import Enum, auto, unique @@ -13,8 +14,6 @@ ffi, C = load_native_module() MAX_DIMS: int = 6 -MAX_ARG_TENSORS: int = 2 -MAG_MAX_OP_PARAMS: int = 6 DIM_MAX: int = (1 << 63) - 1 # INT64_MAX @@ -81,7 +80,7 @@ class GlobalConfig: verbose: bool = (getenv('MAG_VERBOSE', '0') == '1') compute_device: ComputeDevice.CPU | ComputeDevice.CUDA = ComputeDevice.CPU() - +@typing.final class Context: _active: 'Context' = None @@ -183,7 +182,7 @@ def __del__(self): C.mag_ctx_destroy(self._ptr) self._ptr = ffi.NULL - +@typing.final class Tensor: def __init__(self, ptr: ffi.CData | None = None) -> None: if isinstance(ptr, ffi.CData): @@ -567,7 +566,7 @@ def __sub__(self, other: object | int | float) -> 'Tensor': C.mag_sub(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_subs(self._ptr, float(other))) def __rsub__(self, other: int | float) -> 'Tensor': - return Tensor.full(self.shape, fill_value=other) - self + return Tensor.full(self.shape, fill_value=float(other)) - self def __isub__(self, other: object | int | float) -> 'Tensor': return Tensor( @@ -590,7 +589,7 @@ def __truediv__(self, other: object | int | float) -> 'Tensor': C.mag_div(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_divs(self._ptr, float(other))) def __rtruediv__(self, other: int | float) -> 'Tensor': - return Tensor.full(self.shape, fill_value=other) / self + return Tensor.full(self.shape, fill_value=float(other)) / self def __itruediv__(self, other: object | int | float) -> 'Tensor': return Tensor( @@ -606,7 +605,7 @@ def __eq__(self, other: 'Tensor') -> bool: return C.mag_tensor_eq(self._ptr, other._ptr) def __str__(self) -> str: - self.print(True, True) + self.print(False, True) return '' def __getitem__(self, indices: int | tuple[int, ...]) -> float: @@ -614,15 +613,7 @@ def __getitem__(self, indices: int | tuple[int, ...]) -> float: return C.mag_tensor_get_scalar_virtual_index(self._ptr, indices) elif isinstance(indices, tuple): idx = indices + (0,) * (6 - len(indices)) - return C.mag_tensor_get_scalar_physical_index( - self._ptr, - idx[0], - idx[1], - idx[2], - idx[3], - idx[4], - idx[5], - ) + return C.mag_tensor_get_scalar_physical_index(self._ptr, *idx) else: raise TypeError("Indices must be an int or a tuple of ints.") @@ -631,15 +622,6 @@ def __setitem__(self, indices: int | tuple[int, ...], value: float) -> None: C.mag_tensor_set_scalar_virtual_index(self._ptr, indices, float(value)) elif isinstance(indices, tuple): idx = indices + (0,) * (6 - len(indices)) - C.mag_tensor_set_scalar_physical_index( - self._ptr, - idx[0], - idx[1], - idx[2], - idx[3], - idx[4], - idx[5], - float(value), - ) + C.mag_tensor_set_scalar_physical_index(self._ptr, *idx, float(value)) else: raise TypeError("Indices must be an int or a tuple of ints.") diff --git a/python/tests/tensor_ops2.py b/python/tests/tensor_ops2.py new file mode 100644 index 0000000..12fb61e --- /dev/null +++ b/python/tests/tensor_ops2.py @@ -0,0 +1,32 @@ +# (c) 2025 Mario "Neo" Sieg. + +from magnetron import * +import numpy as np + +def tonumpy(t: Tensor): + return np.array(t.tolist(), dtype=np.float32).reshape(t.shape) + +def test_matmul_squared(): + shapes = [4, 8, 16, 32, 64, 128, 256, 512, 1024] + for shape in shapes: + mag_a = Tensor.uniform((shape, shape)) + mag_b = Tensor.uniform((shape, shape)) + np_a = tonumpy(mag_a) + np_b = tonumpy(mag_b) + mag_result = mag_a @ mag_b + np_result = np.matmul(np_a, np_b) + assert mag_result.shape == np_result.shape + assert np.allclose(tonumpy(mag_result), np_result) + +def test_matmul(): + shapes = [(4, 8), (8, 16), (16, 32), (32, 64), (64, 128), (128, 256), (256, 512), (512, 1024)] + for shape in shapes: + mag_a = Tensor.uniform(shape) + mag_b = Tensor.uniform((shape[1], shape[0])) + np_a = tonumpy(mag_a) + np_b = tonumpy(mag_b) + mag_result = mag_a @ mag_b + np_result = np.matmul(np_a, np_b) + assert mag_result.shape == np_result.shape + assert np.allclose(tonumpy(mag_result), np_result) + diff --git a/python/tests/xor_nn.py b/python/tests/xor_nn.py new file mode 100644 index 0000000..934c00e --- /dev/null +++ b/python/tests/xor_nn.py @@ -0,0 +1,135 @@ +# (c) 2025 Mario "Neo" Sieg. + +import numpy as np +from magnetron import Tensor + +learning_rate = 0.1 +epochs = 10000 + +def xor_nn_np(): + def sigmoid(x): + return 1 / (1 + np.exp(-x)) + + def sigmoid_derivative(x): + return x * (1 - x) + + X = np.array([[0, 0], + [0, 1], + [1, 0], + [1, 1]]) + + Y = np.array([[0], [1], [1], [0]]) + + W1 = np.random.randn(2, 4) + b1 = np.zeros((1, 4)) + W2 = np.random.randn(4, 1) + b2 = np.zeros((1, 1)) + + for epoch in range(epochs): + z1 = np.matmul(X, W1) + b1 + a1 = sigmoid(z1) + z2 = np.matmul(a1, W2) + b2 + a2 = sigmoid(z2) + + loss = np.mean((Y - a2) ** 2) + + if epoch % 1000 == 0: + print(f'Epoch: {epoch}, loss: {loss}') + + d_a2 = -(Y - a2) + d_z2 = d_a2 * sigmoid_derivative(a2) + d_W2 = np.matmul(a1.T, d_z2) + d_b2 = np.sum(d_z2) + + d_a1 = np.matmul(d_z2, W2.T) + d_z1 = d_a1 * sigmoid_derivative(a1) + d_W1 = np.matmul(X.T, d_z1) + d_b1 = np.sum(d_z1) + + W2 -= learning_rate * d_W2 + b2 -= learning_rate * d_b2 + W1 -= learning_rate * d_W1 + b1 -= learning_rate * d_b1 + + def predict(x): + z1 = np.matmul(x, W1) + b1 + a1 = sigmoid(z1) + z2 = np.matmul(a1, W2) + b2 + a2 = sigmoid(z2) + return a2 + + for input_ in X: + output = predict(input_) + print(output[0][0]) + +def tonumpy(t: Tensor): + return np.array(t.tolist(), dtype=np.float32).reshape(t.shape) + +def fromnumpy(a: np.ndarray): + return Tensor.const(a.tolist()) + +def xor_nn_mag(): + def sigmoid(x): + return 1 / (1 + np.exp(-x)) + + def sigmoid_derivative(x): + return x * (1 - x) + + X = np.array([[0, 0], + [0, 1], + [1, 0], + [1, 1]]) + + Y = np.array([[0], [1], [1], [0]]) + + W1 = np.random.randn(2, 4) + b1 = np.zeros((1, 4)) + W2 = np.random.randn(4, 1) + b2 = np.zeros((1, 1)) + + for epoch in range(epochs): + z1 = np.matmul(X, W1) + b1 + a1 = sigmoid(z1) + z2 = np.matmul(a1, W2) + b2 + a2 = sigmoid(z2) + + loss = np.mean((Y - a2) ** 2) + + if epoch % 1000 == 0: + print(f'Epoch: {epoch}, loss: {loss}') + + d_a2 = -(Y - a2) + d_z2 = d_a2 * sigmoid_derivative(a2) + d_W2 = np.matmul(a1.T, d_z2) + d_b2 = np.sum(d_z2) + + d_a1 = np.matmul(d_z2, W2.T) + d_z1 = d_a1 * sigmoid_derivative(a1) + d_W1 = np.matmul(X.T, d_z1) + d_b1 = np.sum(d_z1) + + W2 -= learning_rate * d_W2 + b2 -= learning_rate * d_b2 + W1 -= learning_rate * d_W1 + b1 -= learning_rate * d_b1 + + def predict(x): + z1 = np.matmul(tonumpy(x), W1) + b1 + mz1 = x @ fromnumpy(W1) + print(f'NP: {tonumpy(x).shape} @ {W1.shape} = {z1.shape}') + print(f'MAG: {x.shape} @ {W1.shape} = {mz1.shape}') + a1 = sigmoid(z1) + z2 = np.matmul(a1, W2) + b2 + a2 = sigmoid(z2) + return a2 + + XX = [[0, 0], + [0, 1], + [1, 0], + [1, 1]] + for input_ in XX: + output = predict(Tensor.const(input_))[0] + print(output) + +xor_nn_np() +xor_nn_mag() From 286db57af3c7540c17a20ec8e48c40cd3fcb5389 Mon Sep 17 00:00:00 2001 From: "Mario Sieg (Neo)" Date: Sun, 26 Jan 2025 22:20:50 +0100 Subject: [PATCH 03/24] More tests, add mlp unit test --- magnetron/magnetron.c | 4 +- python/examples/xor.py | 2 +- python/magnetron_framework/requirements.txt | 2 + python/requirements.txt | 5 +- python/tests/tensor_ops2.py | 105 +++++++++++++++++++- python/tests/xor_nn.py | 52 ++++++---- test/unit/compute_cpu.cpp | 46 +++++++++ 7 files changed, 188 insertions(+), 28 deletions(-) diff --git a/magnetron/magnetron.c b/magnetron/magnetron.c index f184e4e..e35b494 100644 --- a/magnetron/magnetron.c +++ b/magnetron/magnetron.c @@ -1078,8 +1078,6 @@ static bool mag_validate_op_scalar(mag_op_t op, mag_tensor_t* result, mag_tensor static bool mag_validate_op_matmul(mag_op_t op, mag_tensor_t* result, mag_tensor_t** inputs, const mag_op_param_t* params) { bool valid = true; valid = valid && mag_check_is_shape_matmulable(op, inputs[0], inputs[1]); - valid = valid && mag_check_is_contiguous(op, inputs[0]); - valid = valid && mag_check_is_contiguous(op, inputs[1]); return valid; } @@ -1129,7 +1127,7 @@ static mag_tensor_t* mag_result_constructor_routine_permuted(mag_tensor_t** inpu static mag_tensor_t* mag_result_constructor_routine_matmul(mag_tensor_t** inputs, const mag_op_param_t* params) { /* MxR = MxN * NxR */ (void)params; - int64_t shape[MAG_MAX_DIMS]; + int64_t shape[MAG_MAX_DIMS] = {0}; int64_t* rd0 = shape; int64_t* rd1 = shape+1; int64_t rank = 0; diff --git a/python/examples/xor.py b/python/examples/xor.py index 9f70788..27e402d 100644 --- a/python/examples/xor.py +++ b/python/examples/xor.py @@ -42,7 +42,7 @@ ] for (x_val, y_val) in test_points: - result = mlp.forward(Tensor.const([[x_val], [y_val]]))[0] + result = mlp.forward(Tensor.const([x_val, y_val]))[0] print(f"{x_val} XOR {y_val} => {result:.4f}") # Plot MSE loss diff --git a/python/magnetron_framework/requirements.txt b/python/magnetron_framework/requirements.txt index 2dc0a16..0cbc8dc 100644 --- a/python/magnetron_framework/requirements.txt +++ b/python/magnetron_framework/requirements.txt @@ -1 +1,3 @@ +# Magnetron requirements to use magnetron framework in production as Python library (no unit tests, benchmarks etc..) + cffi \ No newline at end of file diff --git a/python/requirements.txt b/python/requirements.txt index 8401701..4e74689 100644 --- a/python/requirements.txt +++ b/python/requirements.txt @@ -1,10 +1,13 @@ +# !! Requirements for full Magnetron development (Magnetron Core, Unit Tests, Benchmarks, Documentation) +# !! Magnetron itself requires *only* cffi. +# !! See magnetron_framework/requirements.txt for the production requirements. + cffi wheel setuptools pytest numpy matplotlib -cycler sphinx sphinx-autoapi sphinx-rtd-theme diff --git a/python/tests/tensor_ops2.py b/python/tests/tensor_ops2.py index 12fb61e..95ee531 100644 --- a/python/tests/tensor_ops2.py +++ b/python/tests/tensor_ops2.py @@ -6,6 +6,47 @@ def tonumpy(t: Tensor): return np.array(t.tolist(), dtype=np.float32).reshape(t.shape) +def sigmoid(x): + return 1 / (1 + np.exp(-x)) + +def test_simple_ff(): + truth_table = [ + [0, 0], + [0, 1], + [1, 0], + [1, 1] + ] + + W1 = Tensor.uniform((2, 4)) + b1 = Tensor.uniform((1, 4)) + W2 = Tensor.uniform((4, 1)) + b2 = Tensor.uniform((1, 1)) + + nW1 = tonumpy(W1) + nb1 = tonumpy(b1) + nW2 = tonumpy(W2) + nb2 = tonumpy(b2) + + np_data = [] + for x in truth_table: + z1 = x @ nW1 + nb1 + a1 = sigmoid(z1) + z2 = a1 @ nW2 + nb2 + a2 = sigmoid(z2) + np_data.append(a2) + + mag_data = [] + for x in truth_table: + x = Tensor.const([x]) + z1 = x @ W1 + b1 + a1 = z1.sigmoid() + z2 = a1 @ W2 + b2 + a2 = z2.sigmoid() + mag_data.append(a2) + + for mag, np_ in zip(mag_data, np_data): + np.testing.assert_allclose(tonumpy(mag), np_) + def test_matmul_squared(): shapes = [4, 8, 16, 32, 64, 128, 256, 512, 1024] for shape in shapes: @@ -16,7 +57,7 @@ def test_matmul_squared(): mag_result = mag_a @ mag_b np_result = np.matmul(np_a, np_b) assert mag_result.shape == np_result.shape - assert np.allclose(tonumpy(mag_result), np_result) + np.testing.assert_allclose(tonumpy(mag_result), np_result) def test_matmul(): shapes = [(4, 8), (8, 16), (16, 32), (32, 64), (64, 128), (128, 256), (256, 512), (512, 1024)] @@ -28,5 +69,65 @@ def test_matmul(): mag_result = mag_a @ mag_b np_result = np.matmul(np_a, np_b) assert mag_result.shape == np_result.shape - assert np.allclose(tonumpy(mag_result), np_result) + np.testing.assert_allclose(tonumpy(mag_result), np_result) + +def test_matmul_matrix_by_vector(): + shapes = [(4, 8), (8, 16), (16, 32), (32, 64), (64, 128), (128, 256), (256, 512), (512, 1024)] + for shape in shapes: + mag_a = Tensor.uniform(shape) + mag_b = Tensor.uniform((shape[1], 1)) + np_a = tonumpy(mag_a) + np_b = tonumpy(mag_b) + mag_result = mag_a @ mag_b + np_result = np.matmul(np_a, np_b) + assert mag_result.shape == np_result.shape + np.testing.assert_allclose(tonumpy(mag_result), np_result) + +def test_matmul_vector_by_matrix(): + shapes = [(4, 8), (8, 16), (16, 32), (32, 64), (64, 128), (128, 256), (256, 512), (512, 1024)] + for shape in shapes: + mag_a = Tensor.uniform((1, shape[0])) + mag_b = Tensor.uniform(shape) + np_a = tonumpy(mag_a) + np_b = tonumpy(mag_b) + mag_result = mag_a @ mag_b + np_result = np.matmul(np_a, np_b) + assert mag_result.shape == np_result.shape + np.testing.assert_allclose(tonumpy(mag_result), np_result) + +def test_matmul_scalar_by_matrix(): + shapes = [(4, 8), (8, 16), (16, 32), (32, 64), (64, 128), (128, 256), (256, 512), (512, 1024)] + for shape in shapes: + scalar = np.random.rand() + mag_b = Tensor.uniform(shape) + np_b = tonumpy(mag_b) + mag_result = scalar * mag_b + np_result = scalar * np_b + assert mag_result.shape == np_result.shape + np.testing.assert_allclose(tonumpy(mag_result), np_result) + +def test_matmul_x_transposed(): + shape_a = (4, 2) + shape_b = (4, 4) + mag_a = Tensor.uniform(shape_a) + mag_b = Tensor.uniform(shape_b) + np_a = tonumpy(mag_a) + np_b = tonumpy(mag_b) + mag_result = mag_a.T @ mag_b + np_result = np.matmul(np_a.T, np_b) + assert mag_result.shape == np_result.shape + assert mag_result.shape == (2, 4) + np.testing.assert_allclose(tonumpy(mag_result), np_result) +def test_matmul_y_transposed(): + shape_a = (4, 2) + shape_b = (4, 4) + mag_a = Tensor.uniform(shape_a) + mag_b = Tensor.uniform(shape_b) + np_a = tonumpy(mag_a) + np_b = tonumpy(mag_b) + mag_result = mag_a @ mag_b.T + np_result = np.matmul(np_a, np_b.T) + assert mag_result.shape == np_result.shape + assert mag_result.shape == (2, 4) + np.testing.assert_allclose(tonumpy(mag_result), np_result) diff --git a/python/tests/xor_nn.py b/python/tests/xor_nn.py index 934c00e..f3ac3c5 100644 --- a/python/tests/xor_nn.py +++ b/python/tests/xor_nn.py @@ -75,38 +75,51 @@ def sigmoid(x): def sigmoid_derivative(x): return x * (1 - x) - X = np.array([[0, 0], + X = Tensor.const([[0, 0], [0, 1], [1, 0], [1, 1]]) - Y = np.array([[0], [1], [1], [0]]) + Y = Tensor.const([[0], [1], [1], [0]]) W1 = np.random.randn(2, 4) b1 = np.zeros((1, 4)) W2 = np.random.randn(4, 1) b2 = np.zeros((1, 1)) + W1 = fromnumpy(W1) + b1 = fromnumpy(b1) + W2 = fromnumpy(W2) + b2 = fromnumpy(b2) + for epoch in range(epochs): - z1 = np.matmul(X, W1) + b1 - a1 = sigmoid(z1) - z2 = np.matmul(a1, W2) + b2 - a2 = sigmoid(z2) + a1 = (X @ W1 + b1).sigmoid() + a2 = (a1 @ W2 + b2).sigmoid() - loss = np.mean((Y - a2) ** 2) + loss = (Y - a2).sqr_().mean()[0] if epoch % 1000 == 0: print(f'Epoch: {epoch}, loss: {loss}') d_a2 = -(Y - a2) d_z2 = d_a2 * sigmoid_derivative(a2) - d_W2 = np.matmul(a1.T, d_z2) - d_b2 = np.sum(d_z2) + d_W2 = a1.T.clone() @ d_z2 + d_b2 = d_z2.sum() - d_a1 = np.matmul(d_z2, W2.T) - d_z1 = d_a1 * sigmoid_derivative(a1) - d_W1 = np.matmul(X.T, d_z1) - d_b1 = np.sum(d_z1) + d_z1 = (d_z2 @ W2.T.clone()) * sigmoid_derivative(a1) + + ld_W1 = X.T.clone() @ d_z1 + print(f'{X.T.clone().shape} @ {d_z1.shape} = {ld_W1.shape}') + + d_z1 = tonumpy(d_z1) + + d_W1 = tonumpy(X).T @ d_z1 + print(f'{tonumpy(X).T.shape} @ {d_z1.shape} = {d_W1.shape}') + + d_z1 = fromnumpy(d_z1) + d_W1 = fromnumpy(d_W1) + + d_b1 = d_z1.sum() W2 -= learning_rate * d_W2 b2 -= learning_rate * d_b2 @@ -114,13 +127,10 @@ def sigmoid_derivative(x): b1 -= learning_rate * d_b1 def predict(x): - z1 = np.matmul(tonumpy(x), W1) + b1 - mz1 = x @ fromnumpy(W1) - print(f'NP: {tonumpy(x).shape} @ {W1.shape} = {z1.shape}') - print(f'MAG: {x.shape} @ {W1.shape} = {mz1.shape}') - a1 = sigmoid(z1) - z2 = np.matmul(a1, W2) + b2 - a2 = sigmoid(z2) + z1 = x @ W1 + b1 + a1 = z1.sigmoid() + z2 = a1 @ W2 + b2 + a2 = z2.sigmoid() return a2 XX = [[0, 0], @@ -128,7 +138,7 @@ def predict(x): [1, 0], [1, 1]] for input_ in XX: - output = predict(Tensor.const(input_))[0] + output = predict(Tensor.const([input_]))[0] print(output) xor_nn_np() diff --git a/test/unit/compute_cpu.cpp b/test/unit/compute_cpu.cpp index 3a4b049..b70b8fd 100644 --- a/test/unit/compute_cpu.cpp +++ b/test/unit/compute_cpu.cpp @@ -172,6 +172,52 @@ impl_test_unary_op(gelu, 1e-3, gelu, [](float x) -> float { // return x <= 0.0f ? 0.0f : 1.0f; //}) +TEST(compute_cpu, clone_same_shape) { + mag_ctx_t* ctx = mag_ctx_create(MAG_COMPUTE_DEVICE_TYPE_CPU); + for (std::int64_t i0=1; i0 <= k_lim_same_shape; ++i0) + for (std::int64_t i1=1; i1 <= k_lim_same_shape; ++i1) + for (std::int64_t i2=1; i2 <= k_lim_same_shape; ++i2) + for (std::int64_t i3=1; i3 <= k_lim_same_shape; ++i3) + for (std::int64_t i4=1; i4 <= k_lim_same_shape; ++i4) + for (std::int64_t i5=1; i5 <= k_lim_same_shape; ++i5) { + mag_tensor_t* x = mag_tensor_create_6d(ctx, MAG_DTYPE_F32, i0, i1, i2, i3, i4, i5); + mag_tensor_fill_random_uniform(x, 0.0f, 1.0f); + mag_tensor_t* r = mag_clone(x); + const auto* b_x = static_cast(mag_tensor_data_ptr(x)); + const auto* b_r = static_cast(mag_tensor_data_ptr(r)); + ASSERT_EQ(mag_tensor_numel(x), mag_tensor_numel(r)); + for (std::int64_t i=0; i < mag_tensor_numel(x); ++i) { + ASSERT_EQ(b_r[i], b_x[i]); + } + mag_tensor_decref(x); + mag_tensor_decref(r); + } + mag_ctx_destroy(ctx); +} + +TEST(compute_cpu, clone_transpose) { + mag_ctx_t* ctx = mag_ctx_create(MAG_COMPUTE_DEVICE_TYPE_CPU); + for (std::int64_t i0=1; i0 <= k_lim_same_shape; ++i0) + for (std::int64_t i1=1; i1 <= k_lim_same_shape; ++i1) + for (std::int64_t i2=1; i2 <= k_lim_same_shape; ++i2) + for (std::int64_t i3=1; i3 <= k_lim_same_shape; ++i3) + for (std::int64_t i4=1; i4 <= k_lim_same_shape; ++i4) + for (std::int64_t i5=1; i5 <= k_lim_same_shape; ++i5) { + mag_tensor_t* x = mag_tensor_create_6d(ctx, MAG_DTYPE_F32, i0, i1, i2, i3, i4, i5); + mag_tensor_fill_random_uniform(x, 0.0f, 1.0f); + mag_tensor_t* r = mag_clone(mag_transpose(x)); + const auto* b_x = static_cast(mag_tensor_data_ptr(x)); + const auto* b_r = static_cast(mag_tensor_data_ptr(r)); + ASSERT_EQ(mag_tensor_numel(x), mag_tensor_numel(r)); + for (std::int64_t i=0; i < mag_tensor_numel(x); ++i) { + ASSERT_EQ(b_r[i], b_x[i]); + } + mag_tensor_decref(x); + mag_tensor_decref(r); + } + mag_ctx_destroy(ctx); +} + #undef impl_test_unary_op #define impl_test_binary_op(name, op, scalar_op) \ From 193c176786ae85a332fd56d7dee7ec941714e478 Mon Sep 17 00:00:00 2001 From: "Mario Sieg (Neo)" Date: Mon, 27 Jan 2025 00:43:18 +0100 Subject: [PATCH 04/24] Deterministic feed-forward check --- magnetron/magnetron.c | 4 +- python/magnetron_framework/magnetron/core.py | 3 + python/tests/xor_nn.py | 161 +++++++++---------- 3 files changed, 83 insertions(+), 85 deletions(-) diff --git a/magnetron/magnetron.c b/magnetron/magnetron.c index e35b494..20e972d 100644 --- a/magnetron/magnetron.c +++ b/magnetron/magnetron.c @@ -442,7 +442,7 @@ static void mag_prng_generate_n(mag_ctx_t* ctx, float* out_gen, int64_t out_n, f } static void mag_prng_init(mag_ctx_t* ctx, uint64_t seed) { - seed = seed ? seed : 0x853c49e6748fea9bull ^ (uintptr_t)ctx ^ (uintptr_t)&ctx; /* Default seed. */ + seed = seed ? seed : 0x853c49e6748fea9bull^ctx->tr_id^(uintptr_t)ctx^(uintptr_t)&ctx; switch (ctx->prng_algorithm) { case MAG_PRNG_MERSENNE_TWISTER: { uint32_t* state = ctx->prng.mersenne.state; @@ -554,7 +554,7 @@ mag_ctx_t* mag_ctx_create2(const mag_device_descriptor_t* device_info) { /* Initialize PRNG state. */ ctx->prng_algorithm = MAG_PRNG_MERSENNE_TWISTER; - mag_prng_init(ctx, ctx->tr_id^(uintptr_t)ctx^(uintptr_t)&ctx); /* Initialize PRNG state. */ + mag_prng_init(ctx, 0); /* Initialize PRNG state. */ /* Create selected compute device. */ ctx->exec_mode = MAG_EXEC_MODE_EAGER; diff --git a/python/magnetron_framework/magnetron/core.py b/python/magnetron_framework/magnetron/core.py index efb3d71..55e059f 100644 --- a/python/magnetron_framework/magnetron/core.py +++ b/python/magnetron_framework/magnetron/core.py @@ -123,6 +123,9 @@ def prng_algorithm(self) -> PRNGAlgorithm: def prng_algorithm(self, algorithm: PRNGAlgorithm): C.mag_ctx_set_prng_algorithm(self._ptr, algorithm.value, 0) + def seed(self, seed: int) -> None: + C.mag_ctx_set_prng_algorithm(self._ptr, self.prng_algorithm.value, seed) + @property def os_name(self) -> str: return ffi.string(C.mag_ctx_get_os_name(self._ptr)).decode('utf-8') diff --git a/python/tests/xor_nn.py b/python/tests/xor_nn.py index f3ac3c5..0202259 100644 --- a/python/tests/xor_nn.py +++ b/python/tests/xor_nn.py @@ -1,10 +1,19 @@ # (c) 2025 Mario "Neo" Sieg. import numpy as np -from magnetron import Tensor +from magnetron import Tensor, Context -learning_rate = 0.1 -epochs = 10000 +np.random.seed(932002) +Context.active().seed(932002) + +LR: float = 0.1 +EPOCHS: int = 10000 +INPUT: list[list[float]] = [[0, 0], + [0, 1], + [1, 0], + [1, 1]] +TARGET: list[list[float]] = [[0], [1], [1], [0]] +HIDDEN_DIM: int = 4 def xor_nn_np(): def sigmoid(x): @@ -13,133 +22,119 @@ def sigmoid(x): def sigmoid_derivative(x): return x * (1 - x) - X = np.array([[0, 0], - [0, 1], - [1, 0], - [1, 1]]) - - Y = np.array([[0], [1], [1], [0]]) + x = np.array(INPUT, dtype=np.float32) + y = np.array(TARGET, dtype=np.float32) - W1 = np.random.randn(2, 4) - b1 = np.zeros((1, 4)) - W2 = np.random.randn(4, 1) - b2 = np.zeros((1, 1)) + w1 = np.random.randn(2, HIDDEN_DIM).astype(np.float32) + b1 = np.zeros((1, HIDDEN_DIM), dtype=np.float32) + w2 = np.random.randn(HIDDEN_DIM, 1).astype(np.float32) + b2 = np.zeros((1, 1), dtype=np.float32) - for epoch in range(epochs): - z1 = np.matmul(X, W1) + b1 + for epoch in range(EPOCHS): + z1 = np.matmul(x, w1) + b1 a1 = sigmoid(z1) - z2 = np.matmul(a1, W2) + b2 + z2 = np.matmul(a1, w2) + b2 a2 = sigmoid(z2) - loss = np.mean((Y - a2) ** 2) + loss = np.mean((y - a2) ** 2) if epoch % 1000 == 0: print(f'Epoch: {epoch}, loss: {loss}') - d_a2 = -(Y - a2) + d_a2 = -(y - a2) d_z2 = d_a2 * sigmoid_derivative(a2) - d_W2 = np.matmul(a1.T, d_z2) + d_w2 = np.matmul(a1.T, d_z2) d_b2 = np.sum(d_z2) - d_a1 = np.matmul(d_z2, W2.T) + d_a1 = np.matmul(d_z2, w2.T) d_z1 = d_a1 * sigmoid_derivative(a1) - d_W1 = np.matmul(X.T, d_z1) + d_w1 = np.matmul(x.T, d_z1) d_b1 = np.sum(d_z1) - W2 -= learning_rate * d_W2 - b2 -= learning_rate * d_b2 - W1 -= learning_rate * d_W1 - b1 -= learning_rate * d_b1 + w2 -= LR * d_w2 + b2 -= LR * d_b2 + w1 -= LR * d_w1 + b1 -= LR * d_b1 def predict(x): - z1 = np.matmul(x, W1) + b1 + z1 = np.matmul(x, w1) + b1 a1 = sigmoid(z1) - z2 = np.matmul(a1, W2) + b2 + z2 = np.matmul(a1, w2) + b2 a2 = sigmoid(z2) return a2 - for input_ in X: - output = predict(input_) - print(output[0][0]) + return [float(predict(xr)[0][0]) for xr in x] + def tonumpy(t: Tensor): return np.array(t.tolist(), dtype=np.float32).reshape(t.shape) + def fromnumpy(a: np.ndarray): return Tensor.const(a.tolist()) -def xor_nn_mag(): - def sigmoid(x): - return 1 / (1 + np.exp(-x)) - - def sigmoid_derivative(x): - return x * (1 - x) - - X = Tensor.const([[0, 0], - [0, 1], - [1, 0], - [1, 1]]) - - Y = Tensor.const([[0], [1], [1], [0]]) - W1 = np.random.randn(2, 4) - b1 = np.zeros((1, 4)) - W2 = np.random.randn(4, 1) - b2 = np.zeros((1, 1)) +def xor_nn_mag(): + x = Tensor.const(INPUT) + y = Tensor.const(TARGET) - W1 = fromnumpy(W1) - b1 = fromnumpy(b1) - W2 = fromnumpy(W2) - b2 = fromnumpy(b2) + w1 = Tensor.uniform((2, HIDDEN_DIM)) + b1 = Tensor.zeros((1, HIDDEN_DIM)) + w2 = Tensor.uniform((HIDDEN_DIM, 1)) + b2 = Tensor.zeros((1, 1)) - for epoch in range(epochs): - a1 = (X @ W1 + b1).sigmoid() - a2 = (a1 @ W2 + b2).sigmoid() + for epoch in range(EPOCHS): + a1 = (x @ w1 + b1).sigmoid() + a2 = (a1 @ w2 + b2).sigmoid() - loss = (Y - a2).sqr_().mean()[0] + loss = (y - a2).sqr_().mean()[0] if epoch % 1000 == 0: print(f'Epoch: {epoch}, loss: {loss}') - d_a2 = -(Y - a2) - d_z2 = d_a2 * sigmoid_derivative(a2) - d_W2 = a1.T.clone() @ d_z2 + d_a2 = -(y - a2) + d_z2 = d_a2 * a2.sigmoid(True) + d_w2 = a1.T.clone() @ d_z2 d_b2 = d_z2.sum() - d_z1 = (d_z2 @ W2.T.clone()) * sigmoid_derivative(a1) + d_z1 = (d_z2 @ w2.T.clone()) * a1.sigmoid() - ld_W1 = X.T.clone() @ d_z1 - print(f'{X.T.clone().shape} @ {d_z1.shape} = {ld_W1.shape}') + # d_w1 = x.T @ d_z1 + # print(x.T.shape, d_z1.shape) d_z1 = tonumpy(d_z1) - - d_W1 = tonumpy(X).T @ d_z1 - print(f'{tonumpy(X).T.shape} @ {d_z1.shape} = {d_W1.shape}') - + d_w1 = tonumpy(x).T @ d_z1 d_z1 = fromnumpy(d_z1) - d_W1 = fromnumpy(d_W1) - + d_w1 = fromnumpy(d_w1) d_b1 = d_z1.sum() - W2 -= learning_rate * d_W2 - b2 -= learning_rate * d_b2 - W1 -= learning_rate * d_W1 - b1 -= learning_rate * d_b1 + w2 -= LR * d_w2 + b2 -= LR * d_b2 + w1 -= LR * d_w1 + b1 -= LR * d_b1 def predict(x): - z1 = x @ W1 + b1 + z1 = x @ w1 + b1 a1 = z1.sigmoid() - z2 = a1 @ W2 + b2 + z2 = a1 @ w2 + b2 a2 = z2.sigmoid() return a2 - XX = [[0, 0], - [0, 1], - [1, 0], - [1, 1]] - for input_ in XX: - output = predict(Tensor.const([input_]))[0] - print(output) - -xor_nn_np() -xor_nn_mag() + return [predict(Tensor.const([xr]))[0] for xr in INPUT] + +def test_xor_nn(): + np_out = xor_nn_np() + mag_out = xor_nn_mag() + assert [round(x) for x in np_out] == [0, 1, 1, 0] + assert [round(x) for x in mag_out] == [0, 1, 1, 0] + assert np_out[0] < 0.08 + assert mag_out[0] < 0.04 + assert np_out[1] > 0.95 + assert mag_out[1] > 0.95 + assert np_out[2] > 0.95 + assert mag_out[2] > 0.95 + assert np_out[3] < 0.05 + assert mag_out[3] < 0.04 + print(np_out) + print(mag_out) + assert np.allclose(np_out, mag_out, atol=0.1) \ No newline at end of file From b033fbb4c5551d87a163964866a90052e6fe808d Mon Sep 17 00:00:00 2001 From: Neo Date: Mon, 27 Jan 2025 19:52:05 +0100 Subject: [PATCH 05/24] Deterministic MLP test done --- magnetron/magnetron_cpu_blas.inl | 52 ++-- python/tests/tests.py | 1 - python/tests/xor_nn.py | 19 +- .../{compute_cpu.cpp => tensor_ops_1_cpu.cpp} | 107 ------- test/unit/tensor_ops_mm_cpu.cpp | 275 ++++++++++++++++++ 5 files changed, 293 insertions(+), 161 deletions(-) rename test/unit/{compute_cpu.cpp => tensor_ops_1_cpu.cpp} (87%) create mode 100644 test/unit/tensor_ops_mm_cpu.cpp diff --git a/magnetron/magnetron_cpu_blas.inl b/magnetron/magnetron_cpu_blas.inl index 4056697..079e07f 100644 --- a/magnetron/magnetron_cpu_blas.inl +++ b/magnetron/magnetron_cpu_blas.inl @@ -1364,43 +1364,25 @@ static void MAG_HOTPROC mag_blas_matmul_f32(const mag_compute_payload_t* payload int64_t chunk = (numel + tc - 1)/tc; int64_t ra = chunk*ti; int64_t rb = mag_xmin(ra+chunk, numel); - #ifdef MAG_ACCELERATE - int64_t vmel = rb - ra; - if (mag_unlikely(vmel <= 0)) return; - const mag_f32_t* px = bx + ra*xd1; - mag_f32_t* pr = br + ra*yd1; - memset(pr, 0, vmel*yd1*sizeof(float)); - vDSP_mmul( - px, - 1, - by, - 1, - pr, - 1, - vmel, - yd1, - xd1 - ); - #else - for (int64_t i = ra; i < rb; ++i) { /* Rows */ - for (int64_t j = 0; j < yd1; ++j) { - float* xo = br + rd1*i + j; - mag_bnd_chk(xo, br, mag_tensor_data_size(r)); - *xo = 0.0f; - } - for (int64_t k = 0; k < xd1; ++k) { /* Inner dim */ - const mag_f32_t* px = bx + xd1*i + k; - mag_bnd_chk(px, bx, mag_tensor_data_size(x)); - for (int64_t j = 0; j < yd1; ++j) { /* Columns */ - mag_f32_t* pr = br + rd1*i + j; - const mag_f32_t* py = by + yd1*k + j; - mag_bnd_chk(pr, br, mag_tensor_data_size(r)); - mag_bnd_chk(py, by, mag_tensor_data_size(y)); - *pr += (*px) * (*py); - } + bool tx = mag_tensor_is_transposed(x); + for (int64_t i = ra; i < rb; ++i) { /* Rows */ + for (int64_t j = 0; j < yd1; ++j) { + float* xo = br + rd1*i + j; + mag_bnd_chk(xo, br, mag_tensor_data_size(r)); + *xo = 0.0f; + } + for (int64_t k = 0; k < xd1; ++k) { /* Inner dim */ + const mag_f32_t* px = bx + (tx ? k*xd0 + i : xd1*i + k); + mag_bnd_chk(px, bx, mag_tensor_data_size(x)); + for (int64_t j = 0; j < yd1; ++j) { /* Columns */ + mag_f32_t* pr = br + rd1*i + j; + const mag_f32_t* py = by + yd1*k + j; + mag_bnd_chk(pr, br, mag_tensor_data_size(r)); + mag_bnd_chk(py, by, mag_tensor_data_size(y)); + *pr += (*px) * (*py); } } - #endif + } } #ifndef MAG_BLAS_SPECIALIZATION diff --git a/python/tests/tests.py b/python/tests/tests.py index a39f602..ff9da82 100644 --- a/python/tests/tests.py +++ b/python/tests/tests.py @@ -4,7 +4,6 @@ def test_import_magnetron(): import magnetron assert magnetron.__version__ is not None - def test_simple_exec(): import magnetron as mag a = mag.Tensor.const([1, 4, 1]) diff --git a/python/tests/xor_nn.py b/python/tests/xor_nn.py index 0202259..6b9df80 100644 --- a/python/tests/xor_nn.py +++ b/python/tests/xor_nn.py @@ -65,15 +65,6 @@ def predict(x): return [float(predict(xr)[0][0]) for xr in x] - -def tonumpy(t: Tensor): - return np.array(t.tolist(), dtype=np.float32).reshape(t.shape) - - -def fromnumpy(a: np.ndarray): - return Tensor.const(a.tolist()) - - def xor_nn_mag(): x = Tensor.const(INPUT) y = Tensor.const(TARGET) @@ -96,16 +87,8 @@ def xor_nn_mag(): d_z2 = d_a2 * a2.sigmoid(True) d_w2 = a1.T.clone() @ d_z2 d_b2 = d_z2.sum() - d_z1 = (d_z2 @ w2.T.clone()) * a1.sigmoid() - - # d_w1 = x.T @ d_z1 - # print(x.T.shape, d_z1.shape) - - d_z1 = tonumpy(d_z1) - d_w1 = tonumpy(x).T @ d_z1 - d_z1 = fromnumpy(d_z1) - d_w1 = fromnumpy(d_w1) + d_w1 = x.T @ d_z1 d_b1 = d_z1.sum() w2 -= LR * d_w2 diff --git a/test/unit/compute_cpu.cpp b/test/unit/tensor_ops_1_cpu.cpp similarity index 87% rename from test/unit/compute_cpu.cpp rename to test/unit/tensor_ops_1_cpu.cpp index b70b8fd..2628937 100644 --- a/test/unit/compute_cpu.cpp +++ b/test/unit/tensor_ops_1_cpu.cpp @@ -392,113 +392,6 @@ impl_test_binary_op(div_f32, div, /) #undef impl_test_binary_op -static void mag__inner_matmul_naive( - const float* A, - const float* B, - float* C, - const int64_t M, - const int64_t N, - const int64_t K -) { - for (int64_t i = 0; i < M * N; ++i) C[i] = 0.0f; - for (int64_t i = 0; i < M; ++i) { // Rows of A and C - for (int64_t k = 0; k < K; ++k) { // Columns of A, Rows of B - float a_ik = A[i * K + k]; // Access A[i][k] - for (int64_t j = 0; j < N; ++j) { // Columns of B and C - C[i * N + j] += a_ik * B[k * N + j]; // C[i][j] += A[i][k] * B[k][j] - } - } - } -} - -TEST(compute_cpu, matmul_inner_naive) { - static constexpr float A[6] = { - 1.0f, 2.0f, - 3.0f, 4.0f, - 5.0f, 6.0f - }; - static constexpr float B[2] = {0.5f, -1.0f}; - float C[3]; - mag__inner_matmul_naive(A, B, C, 3, 1, 2); - ASSERT_FLOAT_EQ(C[0], -1.5f); - ASSERT_FLOAT_EQ(C[1], -2.5f); - ASSERT_FLOAT_EQ(C[2], -3.5f); -} - -TEST(compute_cpu, matmul_f32_same_shape_2x2) { - mag_ctx_t* ctx = mag_ctx_create(MAG_COMPUTE_DEVICE_TYPE_CPU); - - static constexpr float A_values[2][2] = { - {1.6354027, -1.3607267}, - {1.8556793, 1.1689897} - }; - static constexpr float B_values[2][2] = { - {-0.6105532, 0.10695228}, - {-1.0069681, -0.40955952} - }; - - // Manually set known values for A and B - mag_tensor_t* A = mag_tensor_create_2d(ctx, MAG_DTYPE_F32, 2, 2); - mag_tensor_copy_buffer_from(A, A_values, sizeof(A_values)); - - mag_tensor_t* B = mag_tensor_create_2d(ctx, MAG_DTYPE_F32, 2, 2); - mag_tensor_copy_buffer_from(B, B_values, sizeof(B_values)); - - // Create result tensor R for matrix multiplication - mag_tensor_t* R = mag_matmul(A, B); - mag_tensor_print(R, true, true); - auto* buf = static_cast(mag_tensor_data_ptr(R)); - - static constexpr float expected[2*2] = { - 0.3717081, 0.7322086, - -2.3101263, -0.28030172 - }; - - for (int i = 0; i < 2*2; ++i) { - ASSERT_FLOAT_EQ(buf[i], expected[i]); - } - - mag_tensor_decref(A); - mag_tensor_decref(B); - mag_tensor_decref(R); - - mag_ctx_destroy(ctx); -} - -TEST(compute_cpu, matmul_f32_different_shape_2x2) { - mag_ctx_t* ctx = mag_ctx_create(MAG_COMPUTE_DEVICE_TYPE_CPU); - - static constexpr float AV[3*2] = { - 1.0f, 2.0f, - 3.0f, 4.0f, - 5.0f, 6.0f - }; - static constexpr float BV[2] = {0.5f, -1.0f}; - - // Manually set known values for A and B - mag_tensor_t* A = mag_tensor_create_2d(ctx, MAG_DTYPE_F32, 3, 2); - mag_tensor_copy_buffer_from(A, AV, sizeof(AV)); - - mag_tensor_t* B = mag_tensor_create_1d(ctx, MAG_DTYPE_F32, 2); - mag_tensor_copy_buffer_from(B, BV, sizeof(BV)); - - // Create result tensor R for matrix multiplication - mag_tensor_t* R = mag_matmul(A, B); - //ASSERT_EQ(mag_tensor_rank(R), 1); - ASSERT_EQ(mag_tensor_shape(R)[0], 3); - const auto* C = static_cast(mag_tensor_data_ptr(R)); - //mag__inner_matmul_naive(A, B, C, 3, 1, 2); - ASSERT_FLOAT_EQ(C[0], -1.5f); - ASSERT_FLOAT_EQ(C[1], -2.5f); - ASSERT_FLOAT_EQ(C[2], -3.5f); - - mag_tensor_decref(A); - mag_tensor_decref(B); - mag_tensor_decref(R); - - mag_ctx_destroy(ctx); -} - TEST(compute_cpu, arithmetic_mean) { mag_ctx_t* ctx = mag_ctx_create(MAG_COMPUTE_DEVICE_TYPE_CPU); mag_tensor_t* A = mag_tensor_create_4d(ctx, MAG_DTYPE_F32, 4, 1, 3, 2); diff --git a/test/unit/tensor_ops_mm_cpu.cpp b/test/unit/tensor_ops_mm_cpu.cpp new file mode 100644 index 0000000..0bb8a55 --- /dev/null +++ b/test/unit/tensor_ops_mm_cpu.cpp @@ -0,0 +1,275 @@ +// (c) 2025 Mario "Neo" Sieg. + +// For some tests, we use a larger absolute error than machine epsilon, +// because the BLAS uses SIMD for certain functions which have higher accuracy than the scalar high-precision lambdas. + +#include "prelude.hpp" + +static auto mm_naive( + const float* A, + const float* B, + float* C, + const std::int64_t M, + const std::int64_t N, + const std::int64_t K +) -> void { + for (std::int64_t i = 0; i < M * N; ++i) + C[i] = 0.0f; + for (std::int64_t i = 0; i < M; ++i) { + for (std::int64_t k = 0; k < K; ++k) { + float a_ik = A[i*K + k]; + for (std::int64_t j = 0; j < N; ++j) { + C[i*N + j] += a_ik * B[k*N + j]; + } + } + } +} + +TEST(compute_cpu, mm_naive_verify) { + static constexpr float A[6] = { + 1.0f, 2.0f, + 3.0f, 4.0f, + 5.0f, 6.0f + }; + static constexpr float B[2] = {0.5f, -1.0f}; + float C[3]; + mm_naive(A, B, C, 3, 1, 2); + ASSERT_FLOAT_EQ(C[0], -1.5f); + ASSERT_FLOAT_EQ(C[1], -2.5f); + ASSERT_FLOAT_EQ(C[2], -3.5f); +} + +TEST(compute_cpu, mm_square_2x2) { + mag_ctx_t* ctx = mag_ctx_create(MAG_COMPUTE_DEVICE_TYPE_CPU); + + constexpr std::size_t M = 2, N = 2; + static constexpr float A_data[M][N] = { + {1.6354027, -1.3607267}, + {1.8556793, 1.1689897} + }; + static constexpr float B_data[M][N] = { + {-0.6105532, 0.10695228}, + {-1.0069681, -0.40955952} + }; + + mag_tensor_t* A = mag_tensor_create_2d(ctx, MAG_DTYPE_F32, M, N); + mag_tensor_copy_buffer_from(A, A_data, sizeof(A_data)); + mag_tensor_t* B = mag_tensor_create_2d(ctx, MAG_DTYPE_F32, M, N); + mag_tensor_copy_buffer_from(B, B_data, sizeof(B_data)); + mag_tensor_t* R = mag_matmul(A, B); + ASSERT_EQ(R->rank, 2); + ASSERT_EQ(R->shape[0], 2); + ASSERT_EQ(R->shape[1], 2); + + static constexpr float expected[M][N] = { + 0.3717081, 0.7322086, + -2.3101263, -0.28030172 + }; + auto* buf = static_cast(mag_tensor_data_ptr(R)); + for (std::int64_t i=0; i < M; ++i) + for (std::int64_t j=0; j < N; ++j) + ASSERT_FLOAT_EQ(buf[i*M + j], expected[i][j]); + + mag_tensor_decref(A); + mag_tensor_decref(B); + mag_tensor_decref(R); + + mag_ctx_destroy(ctx); +} + + +TEST(compute_cpu, mm_square_2x2_transpose_x) { + mag_ctx_t* ctx = mag_ctx_create(MAG_COMPUTE_DEVICE_TYPE_CPU); + + constexpr std::size_t M = 2, N = 2; + static constexpr float A_data[M][N] = { + {1.6354027, -1.3607267}, + {1.8556793, 1.1689897} + }; + static constexpr float B_data[M][N] = { + {-0.6105532, 0.10695228}, + {-1.0069681, -0.40955952} + }; + + mag_tensor_t* A = mag_tensor_create_2d(ctx, MAG_DTYPE_F32, M, N); + mag_tensor_copy_buffer_from(A, A_data, sizeof(A_data)); + mag_tensor_t* B = mag_tensor_create_2d(ctx, MAG_DTYPE_F32, M, N); + mag_tensor_copy_buffer_from(B, B_data, sizeof(B_data)); + mag_tensor_t* R = mag_matmul(mag_transpose(A), B); + ASSERT_EQ(R->rank, 2); + ASSERT_EQ(R->shape[0], 2); + ASSERT_EQ(R->shape[1], 2); + + static constexpr float expected[M*N] = { + -2.8671103f, -0.58510107f, + -0.3463393f, -0.6243037f + }; + auto* buf = static_cast(mag_tensor_data_ptr(R)); + for (std::int64_t i=0; i < M*N; ++i) + ASSERT_FLOAT_EQ(buf[i], expected[i]); + + mag_tensor_decref(A); + mag_tensor_decref(B); + mag_tensor_decref(R); + + mag_ctx_destroy(ctx); +} + +TEST(compute_cpu, mm_square_2x2_transpose_y) { + mag_ctx_t* ctx = mag_ctx_create(MAG_COMPUTE_DEVICE_TYPE_CPU); + + constexpr std::size_t M = 2, N = 2; + static constexpr float A_data[M][N] = { + {1.6354027, -1.3607267}, + {1.8556793, 1.1689897} + }; + static constexpr float B_data[M][N] = { + {-0.6105532, 0.10695228}, + {-1.0069681, -0.40955952} + }; + + mag_tensor_t* A = mag_tensor_create_2d(ctx, MAG_DTYPE_F32, M, N); + mag_tensor_copy_buffer_from(A, A_data, sizeof(A_data)); + mag_tensor_t* B = mag_tensor_create_2d(ctx, MAG_DTYPE_F32, M, N); + mag_tensor_copy_buffer_from(B, B_data, sizeof(B_data)); + mag_tensor_t* R = mag_matmul(mag_transpose(A), B); + ASSERT_EQ(R->rank, 2); + ASSERT_EQ(R->shape[0], 2); + ASSERT_EQ(R->shape[1], 2); + + static constexpr float expected[M*N] = { + -1.1440332f, -1.0894998f, + -1.0079648f, -2.3473806f + }; + auto* buf = static_cast(mag_tensor_data_ptr(R)); + for (std::int64_t i=0; i < M*N; ++i) + ASSERT_FLOAT_EQ(buf[i], expected[i]); + + mag_tensor_decref(A); + mag_tensor_decref(B); + mag_tensor_decref(R); + + mag_ctx_destroy(ctx); +} + +TEST(compute_cpu, mm_rect_2x3_3x4) { + mag_ctx_t* ctx = mag_ctx_create(MAG_COMPUTE_DEVICE_TYPE_CPU); + constexpr std::size_t M = 2, K = 3, N = 4; + + static constexpr float A_data[M][K] = { + {1.0f, 2.0f, 3.0f}, + {4.0f, 5.0f, 6.0f} + }; + + static constexpr float B_data[K][N] = { + {1.0f, 2.0f, 3.0f, 4.0f}, + {5.0f, 6.0f, 7.0f, 8.0f}, + {9.0f, 10.0f, 11.0f, 12.0f} + }; + + static constexpr float expected[M][N] = { + { 38.0f, 44.0f, 50.0f, 56.0f}, + { 83.0f, 98.0f, 113.0f, 128.0f} + }; + + mag_tensor_t* A = mag_tensor_create_2d(ctx, MAG_DTYPE_F32, M, K); + mag_tensor_t* B = mag_tensor_create_2d(ctx, MAG_DTYPE_F32, K, N); + mag_tensor_copy_buffer_from(A, A_data, sizeof(A_data)); + mag_tensor_copy_buffer_from(B, B_data, sizeof(B_data)); + + mag_tensor_t* R = mag_matmul(A, B); + + ASSERT_EQ(R->rank, 2); + ASSERT_EQ(R->shape[0], (int64_t)M); + ASSERT_EQ(R->shape[1], (int64_t)N); + + const float* buf = static_cast(mag_tensor_data_ptr(R)); + for (std::int64_t i = 0; i < (std::int64_t)M; ++i) { + for (std::int64_t j = 0; j < (std::int64_t)N; ++j) { + ASSERT_FLOAT_EQ(buf[i * N + j], expected[i][j]); + } + } + + mag_tensor_decref(A); + mag_tensor_decref(B); + mag_tensor_decref(R); + + mag_ctx_destroy(ctx); +} + +TEST(compute_cpu, mm_matrix_vector_2x3) { + mag_ctx_t* ctx = mag_ctx_create(MAG_COMPUTE_DEVICE_TYPE_CPU); + + constexpr std::size_t M = 2, K = 3; + + static constexpr float A_data[M][K] = { + {1.0f, 2.0f, 3.0f}, + {4.0f, 5.0f, 6.0f} + }; + + static constexpr float v_data[K] = {7.0f, 8.0f, 9.0f}; + static constexpr float expected[M] = {50.0f, 122.0f}; + + mag_tensor_t* A = mag_tensor_create_2d(ctx, MAG_DTYPE_F32, M, K); + mag_tensor_t* v = mag_tensor_create_1d(ctx, MAG_DTYPE_F32, K); + mag_tensor_copy_buffer_from(A, A_data, sizeof(A_data)); + mag_tensor_copy_buffer_from(v, v_data, sizeof(v_data)); + + mag_tensor_t* R = mag_matmul(A, v); + + ASSERT_EQ(R->rank, 1); + ASSERT_EQ(R->shape[0], (int64_t)M); + + const float* buf = static_cast(mag_tensor_data_ptr(R)); + for (std::int64_t i = 0; i < (std::int64_t)M; ++i) { + ASSERT_FLOAT_EQ(buf[i], expected[i]); + } + + mag_tensor_decref(A); + mag_tensor_decref(v); + mag_tensor_decref(R); + + mag_ctx_destroy(ctx); +} + +TEST(compute_cpu, mm_vector_matrix_3x2) { + mag_ctx_t* ctx = mag_ctx_create(MAG_COMPUTE_DEVICE_TYPE_CPU); + + constexpr std::size_t K = 3, N = 2; + + static constexpr float v_data[1][K] = { + {1.0f, 2.0f, 3.0f} + }; + + static constexpr float B_data[K][N] = { + {4.0f, 5.0f}, + {6.0f, 7.0f}, + {8.0f, 9.0f} + }; + + static constexpr float expected[1][N] = { + {40.0f, 46.0f} + }; + + mag_tensor_t* v = mag_tensor_create_2d(ctx, MAG_DTYPE_F32, 1, K); + mag_tensor_t* B = mag_tensor_create_2d(ctx, MAG_DTYPE_F32, K, N); + mag_tensor_copy_buffer_from(v, v_data, sizeof(v_data)); + mag_tensor_copy_buffer_from(B, B_data, sizeof(B_data)); + + mag_tensor_t* R = mag_matmul(v, B); + + ASSERT_EQ(R->rank, 2); + ASSERT_EQ(R->shape[0], 1); + ASSERT_EQ(R->shape[1], (int64_t)N); + + const float* buf = static_cast(mag_tensor_data_ptr(R)); + for (std::int64_t j = 0; j < (std::int64_t)N; ++j) { + ASSERT_FLOAT_EQ(buf[j], expected[0][j]); + } + + mag_tensor_decref(v); + mag_tensor_decref(B); + mag_tensor_decref(R); + + mag_ctx_destroy(ctx); +} From 1ecfe1f8a3e4547285ca505595f8a9ade97e7172 Mon Sep 17 00:00:00 2001 From: Neo Date: Mon, 27 Jan 2025 20:46:18 +0100 Subject: [PATCH 06/24] Refractor model API --- python/examples/xor.py | 30 +++++++++---------- python/magnetron_framework/magnetron/layer.py | 26 ++++++++-------- python/magnetron_framework/magnetron/model.py | 7 ++--- python/tests/xor_nn.py | 6 ++-- 4 files changed, 32 insertions(+), 37 deletions(-) diff --git a/python/examples/xor.py b/python/examples/xor.py index 27e402d..c492230 100644 --- a/python/examples/xor.py +++ b/python/examples/xor.py @@ -10,24 +10,24 @@ # Inputs: shape (4, 2) inputs = Tensor.const([ - [0.0, 0.0], - [0.0, 1.0], - [1.0, 0.0], - [1.0, 1.0] + [0, 0], + [0, 1], + [1, 0], + [1, 1] ]) # Targets: shape (4, 1) targets = Tensor.const([ - [0.0], - [1.0], - [1.0], - [0.0] + [0], + [1], + [1], + [0] ]) params = HyperParams(lr=RATE, epochs=EPOCHS) mlp = SequentialModel(params, [ - DenseLayer(2, 8), - DenseLayer(8, 1) + DenseLayer(2, 4), + DenseLayer(4, 1) ]) # Train @@ -35,14 +35,14 @@ # Inference test_points = [ - (0.0, 0.0), - (0.0, 1.0), - (1.0, 0.0), - (1.0, 1.0), + (0, 0), + (0, 1), + (1, 0), + (1, 1), ] for (x_val, y_val) in test_points: - result = mlp.forward(Tensor.const([x_val, y_val]))[0] + result = mlp.forward(Tensor.const([[x_val, y_val]]))[0] print(f"{x_val} XOR {y_val} => {result:.4f}") # Plot MSE loss diff --git a/python/magnetron_framework/magnetron/layer.py b/python/magnetron_framework/magnetron/layer.py index e5e84e6..c2ae2d3 100644 --- a/python/magnetron_framework/magnetron/layer.py +++ b/python/magnetron_framework/magnetron/layer.py @@ -80,28 +80,26 @@ class DenseLayer(Layer): def __init__(self, in_features: int, out_features: int, init: LayerInit = LayerInit(LayerInit.Method.RANDOM, LayerInit.Dist.UNIFORM)): super().__init__() - self.weight = init.apply((out_features, in_features)) - self.bias = init.apply((out_features, 1)) + self.weight = init.apply((in_features, out_features)) + self.bias = init.apply((1, out_features)) self._x = None self._z = None self._out = None def forward(self, x: Tensor) -> Tensor: self._x = x - self._z = self.weight @ x + self.bias + self._z = x @ self.weight + self.bias self._out = self._z.sigmoid() return self._out def backward(self, is_hidden_layer: bool, delta: Tensor, rate: float) -> Tensor: - self.weight -= (delta @ self._x.T.clone()) * rate - batch_size = delta.shape[1] - ones_vec = Tensor.const([[1.0] for _ in range(batch_size)]) - row_sums = delta @ ones_vec - row_means = row_sums * (1.0 / batch_size) - self.bias -= row_means * rate + dW = self._x.T.clone() @ delta + ones_batch = Tensor.full((delta.shape[0], 1), fill_value=1.0) + dB = (delta.T.clone() @ ones_batch).T.clone() + self.weight -= dW * rate + self.bias -= dB * rate + + next_delta = delta @ self.weight.T.clone() if is_hidden_layer: - d_in = self.weight.T.clone() @ delta - d_in *= self._z.sigmoid(derivative=True) - return d_in - else: - return delta + next_delta *= self._z.sigmoid(derivative=True) + return next_delta diff --git a/python/magnetron_framework/magnetron/model.py b/python/magnetron_framework/magnetron/model.py index 12a56cd..251d89b 100644 --- a/python/magnetron_framework/magnetron/model.py +++ b/python/magnetron_framework/magnetron/model.py @@ -13,7 +13,7 @@ class HyperParams: lr: float = 0.01 epochs: int = 10000 - epoch_step: int = 100 + epoch_step: int = 1000 class Model(ABC): @@ -53,8 +53,7 @@ def forward(self, inputs: Tensor) -> Tensor: return x def backward(self, outputs: Tensor, targets: Tensor, rate: float): - error = outputs - targets - delta = error * outputs.sigmoid(derivative=True) + delta = (outputs - targets) * outputs.sigmoid(derivative=True) for i in reversed(range(len(self.layers))): is_hidden = (i > 0) delta = self.layers[i].backward(is_hidden, delta, rate) @@ -65,8 +64,6 @@ def train(self, inputs: Tensor, targets: Tensor): print(f'Training started for {epochs} epochs with learning rate {rate}') start_time = time.time_ns() - inputs = inputs.T.clone() - targets = targets.T.clone() losses = [] for epoch in range(epochs): output = self.forward(inputs) diff --git a/python/tests/xor_nn.py b/python/tests/xor_nn.py index 6b9df80..dd139ca 100644 --- a/python/tests/xor_nn.py +++ b/python/tests/xor_nn.py @@ -87,7 +87,7 @@ def xor_nn_mag(): d_z2 = d_a2 * a2.sigmoid(True) d_w2 = a1.T.clone() @ d_z2 d_b2 = d_z2.sum() - d_z1 = (d_z2 @ w2.T.clone()) * a1.sigmoid() + d_z1 = (d_z2 @ w2.T.clone()) * a1.sigmoid(True) d_w1 = x.T @ d_z1 d_b1 = d_z1.sum() @@ -111,9 +111,9 @@ def test_xor_nn(): assert [round(x) for x in np_out] == [0, 1, 1, 0] assert [round(x) for x in mag_out] == [0, 1, 1, 0] assert np_out[0] < 0.08 - assert mag_out[0] < 0.04 + assert mag_out[0] < 0.06 assert np_out[1] > 0.95 - assert mag_out[1] > 0.95 + assert mag_out[1] > 0.94 assert np_out[2] > 0.95 assert mag_out[2] > 0.95 assert np_out[3] < 0.05 From 4307ebdfa9e80d3a08681278a4454e81a6d6efbd Mon Sep 17 00:00:00 2001 From: "Mario Sieg (Neo)" Date: Tue, 28 Jan 2025 03:20:14 +0100 Subject: [PATCH 07/24] Add back old adam optimizer --- python/magnetron_framework/magnetron/core.py | 42 ++++++++++++ python/magnetron_framework/magnetron/layer.py | 10 ++- python/magnetron_framework/magnetron/model.py | 6 +- python/magnetron_framework/magnetron/optim.py | 66 ++++++++++++++++--- 4 files changed, 110 insertions(+), 14 deletions(-) diff --git a/python/magnetron_framework/magnetron/core.py b/python/magnetron_framework/magnetron/core.py index 55e059f..6cb3ce3 100644 --- a/python/magnetron_framework/magnetron/core.py +++ b/python/magnetron_framework/magnetron/core.py @@ -80,6 +80,7 @@ class GlobalConfig: verbose: bool = (getenv('MAG_VERBOSE', '0') == '1') compute_device: ComputeDevice.CPU | ComputeDevice.CUDA = ComputeDevice.CPU() + @typing.final class Context: _active: 'Context' = None @@ -103,6 +104,14 @@ def __init__(self, device: ComputeDevice.CPU | ComputeDevice.CUDA, *, self._ptr = C.mag_ctx_create2(descriptor) self.execution_mode = execution_mode + @property + def enable_grad_recorder(self) -> bool: + return C.mag_ctx_enable_grad_recorder(self._ptr) + + @enable_grad_recorder.setter + def enable_grad_recorder(self, enable: bool): + C.mag_ctx_set_enable_grad_recorder(self._ptr, enable) + @property def compute_device_name(self) -> str: return ffi.string(C.mag_ctx_get_compute_device_name(self._ptr)).decode('utf-8') @@ -185,6 +194,27 @@ def __del__(self): C.mag_ctx_destroy(self._ptr) self._ptr = ffi.NULL + +def no_grad(): + """Temporary disable gradient computation""" + + class Scope: + def __call__(self, func): + def f(*args, **kwargs): + with Scope(): + return func(*args, **kwargs) + + return f + + def __enter__(self): + Context.active().enable_grad_recorder = False + + def __exit__(self, exc_type, exc_value, traceback): + Context.active().enable_grad_recorder = True + + return Scope() + + @typing.final class Tensor: def __init__(self, ptr: ffi.CData | None = None) -> None: @@ -399,6 +429,18 @@ def channels(self) -> int: def is_contiguous(self) -> bool: return C.mag_tensor_is_contiguous(self._ptr) + @property + def grad(self): + ptr: ffi.CData = C.mag_tensor_grad(self._ptr) + return Tensor(ptr) if ptr != ffi.NULL else None + + @grad.setter + def grad(self, grad: 'Tensor') -> None: + C.mag_tensor_set_grad(self._ptr, grad._ptr) + + def zero_grad(self) -> None: + C.mag_tensor_zero_grad(self._ptr) + def is_close(self, other: 'Tensor', eps: float = -1.0, print_eq_percent: bool = False) -> (bool, float): percent_eq = ffi.new('double[1]') is_eq = C.mag_tensor_is_close(self._ptr, other._ptr, eps, percent_eq) diff --git a/python/magnetron_framework/magnetron/layer.py b/python/magnetron_framework/magnetron/layer.py index c2ae2d3..e549383 100644 --- a/python/magnetron_framework/magnetron/layer.py +++ b/python/magnetron_framework/magnetron/layer.py @@ -77,18 +77,22 @@ def apply(self, shape: tuple[int, ...]) -> Tensor: class DenseLayer(Layer): """Fully connected layer""" - def __init__(self, in_features: int, out_features: int, + def __init__(self, in_features: int, out_features: int, bias: bool, init: LayerInit = LayerInit(LayerInit.Method.RANDOM, LayerInit.Dist.UNIFORM)): super().__init__() + self.in_features = in_features + self.out_features = out_features self.weight = init.apply((in_features, out_features)) - self.bias = init.apply((1, out_features)) + self.bias = init.apply((1, out_features)) if bias else None self._x = None self._z = None self._out = None def forward(self, x: Tensor) -> Tensor: self._x = x - self._z = x @ self.weight + self.bias + self._z = x @ self.weight + if self.bias is not None: + self._z += self.bias self._out = self._z.sigmoid() return self._out diff --git a/python/magnetron_framework/magnetron/model.py b/python/magnetron_framework/magnetron/model.py index 251d89b..811ce0d 100644 --- a/python/magnetron_framework/magnetron/model.py +++ b/python/magnetron_framework/magnetron/model.py @@ -6,7 +6,7 @@ from magnetron import Tensor from magnetron.layer import Layer -from magnetron.optim import Optim +from magnetron.optim import Optimizer @dataclass @@ -18,6 +18,7 @@ class HyperParams: class Model(ABC): """Abstract base class for all models""" + def __init__(self, hyper_params: HyperParams): self.hyper_params = hyper_params @@ -44,7 +45,6 @@ class SequentialModel(Model): def __init__(self, hyper_params: HyperParams, layers: list[Layer]): super().__init__(hyper_params) self.layers = layers - self.optim = Optim(hyper_params.lr) def forward(self, inputs: Tensor) -> Tensor: x = inputs @@ -68,7 +68,7 @@ def train(self, inputs: Tensor, targets: Tensor): for epoch in range(epochs): output = self.forward(inputs) self.backward(output, targets, rate) - loss = self.optim.mse(output, targets) + loss = Optimizer.mse(output, targets) losses.append(loss) if epoch % self.hyper_params.epoch_step == 0: print(f'Epoch: {epoch}, Loss: {loss:.6f}') diff --git a/python/magnetron_framework/magnetron/optim.py b/python/magnetron_framework/magnetron/optim.py index 2f14adb..151c6df 100644 --- a/python/magnetron_framework/magnetron/optim.py +++ b/python/magnetron_framework/magnetron/optim.py @@ -1,10 +1,13 @@ # (c) 2025 Mario "Neo" Sieg. +from abc import ABC, abstractmethod from magnetron import Tensor -class PolyLR: - def __init__(self, initial_lr: float, max_iter: float): +class PolynomialDecayLRScheduler: + """Polynomial Decay Learning Rate Scheduler""" + + def __init__(self, initial_lr: float, max_iter: float) -> None: self.initial_lr = initial_lr self.max_iter = max_iter @@ -13,14 +16,61 @@ def step(self, iter: float) -> float: return max(self.initial_lr * (1 - y) ** 2, 1.0e-7) -class Optim: - def __init__(self, lr: float): +class Optimizer(ABC): + """Base class for all optimizers.""" + + def __init__(self, params: list[Tensor], lr: float) -> None: self.lr = lr + self.params = params + + @abstractmethod + def step(self) -> None: + pass - def mse(self, y: Tensor, y_hat: Tensor) -> float: - """Mean Squared Error""" + def zero_grad(self) -> None: + for param in self.params: + param.zero_grad() + + @staticmethod + def mse(y: Tensor, y_hat: Tensor) -> float: return (y - y_hat).sqr_().mean()[0] - def cross_entropy(self, y: Tensor, y_hat: Tensor) -> float: - """Cross Entropy Loss""" + @staticmethod + def cross_entropy(y: Tensor, y_hat: Tensor) -> float: return -(y * y_hat.log_()).sum()[0] + + +class SGD(Optimizer): + """Stochastic Gradient Descent""" + + def __init__(self, params: list[Tensor], lr: float) -> None: + super().__init__(params, lr) + + def step(self) -> None: + for param in self.params: + param -= param.grad * self.lr + + +class Adam(Optimizer): + """Adaptive Moment Estimation""" + + def __init__(self, params: list[Tensor], lr: float = 0.001, betas: tuple[float, float] = (0.9, 0.999), + eps: float = 1e-8): + super().__init__(params, lr) + self.betas = betas + self.eps = eps + self.t = 0 + self.m = [Tensor.zeros(p.shape) for p in self.params] + self.v = [Tensor.zeros(p.shape) for p in self.params] + + def step(self) -> None: + self.t += 1 + for i, p in enumerate(self.params): + grad = p.grad + if grad is None: + continue + self.m[i] = self.betas[0] * self.m[i] + (1.0 - self.betas[0]) * grad + self.v[i] = self.betas[1] * self.v[i] + (1.0 - self.betas[1]) * grad.sqr_() + m_hat: Tensor = self.m[i] / (1.0 - self.betas[0] ** self.t) + v_hat: Tensor = self.v[i] / (1.0 - self.betas[1] ** self.t) + p -= self.lr * m_hat / (v_hat.sqrt_() + self.eps) From a1ff118c07500da3429cbb7f8222b4ccc9dbeccf Mon Sep 17 00:00:00 2001 From: Neo Date: Wed, 29 Jan 2025 15:36:39 +0100 Subject: [PATCH 08/24] Add exp() and pows() operations --- magnetron/magnetron.c | 56 +++-- magnetron/magnetron.h | 5 +- magnetron/magnetron_cpu_blas.inl | 43 ++++ magnetron/magnetron_internal.h | 2 + python/magnetron_framework/bing_gen.py | 21 +- .../magnetron/_ffi_cdecl_generated.py | 202 +-------------- python/magnetron_framework/magnetron/core.py | 20 +- python/tests/tensor_ops2.py | 235 ++++++++---------- python/tests/tensor_ops3.py | 133 ++++++++++ test/unit/tensor_ops_1_cpu.cpp | 27 ++ test/unit/tensor_ops_mm_cpu.cpp | 2 + 11 files changed, 379 insertions(+), 367 deletions(-) create mode 100644 python/tests/tensor_ops3.py diff --git a/magnetron/magnetron.c b/magnetron/magnetron.c index 20e972d..ce9d009 100644 --- a/magnetron/magnetron.c +++ b/magnetron/magnetron.c @@ -1,31 +1,5 @@ /* (c) 2025 Mario "Neo" Sieg. */ -/* -** -** -** ### To add a new operation: -** 1. Add the operation to the mag_op_def macro, which defines all operations, with all information needed. -** 2. Write a validation routine, or use an existing one (e.g. 'mag_validate_op_binary'). -** 3. Add the validation routine to the 'routines' table in 'mag_op_get_validator_routine', at the op index. -** 4. Write a result tensor constructor routine or use an existing one (e.g. 'mag_result_constructor_routine_isomorph'). -** 5. Add the result tensor constructor routine to the 'routines' table in 'mag_op_get_result_constructor_routine', at the op index. -** 6. Write a BLAS computation routine. -** 7. Add the BLAS computation routine to the 'dispatch_lut' table in 'mag_blas_compute_dispatch_table_default', at the op index. -*/ - -/* -** Here ⊕ denotes a binary or unary operator. -** Note that an operators can or cannot support any of those forms. This must be specified in mag_op_def. -** -** Operators can have two forms: -** -** 1. R = A ⊕ B -** Result is a new tensor of shape of A and contains element-wise result of A ⊕ B, where A and B are tensors. -** -** 2. R = A ⊕= B -** Result is a view tensor of shape of A and contains element-wise result of A ⊕= B, where A and B are tensors. (Safes 1 allocation) -*/ - #include "magnetron.h" #include "magnetron_internal.h" @@ -1310,6 +1284,15 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, + [MAG_OP_EXP] = { + .mnemonic = "exp", + .argcount = 1, + .paramcount = 0, + .param_types = {MAG_OP_TPARAM_NONE}, + .inplace = true, + .r_alloc = &mag_result_constructor_routine_isomorph, + .validator = &mag_validate_op_unary + }, [MAG_OP_SOFTMAX] = { .mnemonic = "softmax", .argcount = 1, @@ -1499,6 +1482,15 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, + [MAG_OP_POWS] = { + .mnemonic = "pows", + .argcount = 1, + .paramcount = 1, + .param_types = {MAG_OP_TPARAM_F32}, + .inplace = true, + .r_alloc = &mag_result_constructor_routine_isomorph, + .validator = &mag_validate_op_unary + }, [MAG_OP_MATMUL] = { .mnemonic = "matmul", .argcount = 2, @@ -1770,6 +1762,8 @@ mag_tensor_t* mag_cos(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_ mag_tensor_t* mag_cos_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_COS, true, &x, 1, NULL, 0); } mag_tensor_t* mag_step(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_STEP, false, &x, 1, NULL, 0); } mag_tensor_t* mag_step_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_STEP, true, &x, 1, NULL, 0); } +mag_tensor_t* mag_exp(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_EXP, false, &x, 1, NULL, 0); } +mag_tensor_t* mag_exp_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_EXP, true, &x, 1, NULL, 0); } mag_tensor_t* mag_softmax(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SOFTMAX, false, &x, 1, NULL, 0); } mag_tensor_t* mag_softmax_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SOFTMAX, true, &x, 1, NULL, 0); } mag_tensor_t* mag_softmax_dv(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SOFTMAX_DV, false, &x, 1, NULL, 0); } @@ -1845,6 +1839,16 @@ mag_tensor_t* mag_divs_(mag_tensor_t* x, float xi) { return mag_tensor_operator(x->ctx, MAG_OP_DIVS, true, &x, 1, ¶m, 1); } +mag_tensor_t* mag_pows(mag_tensor_t* x, float xi) { + mag_op_param_t param = {.type=MAG_OP_TPARAM_F32, .x.f32=xi}; + return mag_tensor_operator(x->ctx, MAG_OP_POWS, false, &x, 1, ¶m, 1); +} + +mag_tensor_t* mag_pows_(mag_tensor_t* x, float xi) { + mag_op_param_t param = {.type=MAG_OP_TPARAM_F32, .x.f32=xi}; + return mag_tensor_operator(x->ctx, MAG_OP_POWS, true, &x, 1, ¶m, 1); +} + mag_tensor_t* mag_matmul(mag_tensor_t* x, mag_tensor_t* y) { return mag_tensor_operator(x->ctx, MAG_OP_MATMUL, false, (mag_tensor_t*[]){x, y}, 2, NULL, 0); } diff --git a/magnetron/magnetron.h b/magnetron/magnetron.h index d350b8c..262f05a 100644 --- a/magnetron/magnetron.h +++ b/magnetron/magnetron.h @@ -249,6 +249,8 @@ extern MAG_EXPORT mag_tensor_t* mag_cos(mag_tensor_t* x); extern MAG_EXPORT mag_tensor_t* mag_cos_(mag_tensor_t* x); extern MAG_EXPORT mag_tensor_t* mag_step(mag_tensor_t* x); extern MAG_EXPORT mag_tensor_t* mag_step_(mag_tensor_t* x); +extern MAG_EXPORT mag_tensor_t* mag_exp(mag_tensor_t* x); +extern MAG_EXPORT mag_tensor_t* mag_exp_(mag_tensor_t* x); extern MAG_EXPORT mag_tensor_t* mag_softmax(mag_tensor_t* x); extern MAG_EXPORT mag_tensor_t* mag_softmax_(mag_tensor_t* x); extern MAG_EXPORT mag_tensor_t* mag_softmax_dv(mag_tensor_t* x); @@ -275,7 +277,6 @@ extern MAG_EXPORT mag_tensor_t* mag_gelu(mag_tensor_t* x); extern MAG_EXPORT mag_tensor_t* mag_gelu_(mag_tensor_t* x); extern MAG_EXPORT mag_tensor_t* mag_gelu_dv(mag_tensor_t* x); extern MAG_EXPORT mag_tensor_t* mag_gelu_dv_(mag_tensor_t* x); - extern MAG_EXPORT mag_tensor_t* mag_add(mag_tensor_t* x, mag_tensor_t* y); extern MAG_EXPORT mag_tensor_t* mag_add_(mag_tensor_t* x, mag_tensor_t* y); extern MAG_EXPORT mag_tensor_t* mag_sub( mag_tensor_t* x, mag_tensor_t* y); @@ -292,6 +293,8 @@ extern MAG_EXPORT mag_tensor_t* mag_muls(mag_tensor_t* x, float xi); extern MAG_EXPORT mag_tensor_t* mag_muls_(mag_tensor_t* x, float xi); extern MAG_EXPORT mag_tensor_t* mag_divs(mag_tensor_t* x, float xi); extern MAG_EXPORT mag_tensor_t* mag_divs_(mag_tensor_t* x, float xi); +extern MAG_EXPORT mag_tensor_t* mag_pows(mag_tensor_t* x, float xi); +extern MAG_EXPORT mag_tensor_t* mag_pows_(mag_tensor_t* x, float xi); extern MAG_EXPORT mag_tensor_t* mag_matmul(mag_tensor_t* a, mag_tensor_t* b); /** diff --git a/magnetron/magnetron_cpu_blas.inl b/magnetron/magnetron_cpu_blas.inl index 079e07f..db0dbe7 100644 --- a/magnetron/magnetron_cpu_blas.inl +++ b/magnetron/magnetron_cpu_blas.inl @@ -396,6 +396,17 @@ static void MAG_HOTPROC mag_vdivs_f32( } } +static void MAG_HOTPROC mag_vpows_f32( + int64_t numel, + mag_f32_t* o, + const mag_f32_t* x, + const mag_f32_t y +) { + for (int64_t i=0; i < numel; ++i) { + o[i] = powf(x[i], y); + } +} + static mag_f32_t MAG_UNUSED MAG_HOTPROC mag_vdot_f32( int64_t numel, const mag_f32_t* x, @@ -761,6 +772,32 @@ static void MAG_HOTPROC mag_vstep_f32( /* Heaviside step function. */ } } +static void MAG_HOTPROC mag_vexp_f32( /* e^x */ + int64_t numel, + mag_f32_t* o, + const mag_f32_t* x +) { + int64_t i=0; +#if MAG_APPROXMATH && (defined(__aarch64__) && defined(__ARM_NEON)) || defined(_M_ARM64) + for (; i+3 < numel; i += 4) { + vst1q_f32(o+i, mag_simd_expf(vld1q_f32(x+i))); + } +#elif MAG_APPROXMATH && defined(__AVX512F__) && defined(__AVX512DQ__) + for (; i+15 < numel; i += 16) { + _mm512_storeu_ps(o+i, mag_simd_expf(_mm512_loadu_ps(x+i))); + } +#elif MAG_APPROXMATH && defined(__AVX2__) && defined(__FMA__) + for (; i+7 < numel; i += 8) { + _mm256_storeu_ps(o+i, mag_simd_expf(_mm256_loadu_ps(x+i))); + } +#elif MAG_APPROXMATH && defined(__SSE2__) + for (; i+3 < numel; i += 4) { + _mm_storeu_ps(o+i, mag_simd_expf(_mm_loadu_ps(x+i))); + } +#endif + for (; i < numel; ++i) o[i] = expf(x[i]); /* Process leftovers scalar-wise */ +} + static void MAG_HOTPROC mag_vsoftmax_f32( /* softmax : ℝ -> (0, ∞), x |-> e^x */ int64_t numel, mag_f32_t* o, @@ -1201,6 +1238,7 @@ mag_cpu_blas_impl_unary(f32, sqrt) mag_cpu_blas_impl_unary(f32, sin) mag_cpu_blas_impl_unary(f32, cos) mag_cpu_blas_impl_unary(f32, step) +mag_cpu_blas_impl_unary(f32, exp) mag_cpu_blas_impl_unary(f32, softmax) mag_cpu_blas_impl_unary(f32, softmax_dv) mag_cpu_blas_impl_unary(f32, sigmoid) @@ -1244,6 +1282,7 @@ mag_cpu_blas_impl_unary_scalar(f32, add) mag_cpu_blas_impl_unary_scalar(f32, sub) mag_cpu_blas_impl_unary_scalar(f32, mul) mag_cpu_blas_impl_unary_scalar(f32, div) +mag_cpu_blas_impl_unary_scalar(f32, pow) #undef mag_cpu_blas_impl_unary_scalar @@ -1566,6 +1605,7 @@ static void (*const forward_kernels[MAG_OP__NUM])(const mag_compute_payload_t*) [MAG_OP_SIN] = &mag_blas_sin_f32, [MAG_OP_COS] = &mag_blas_cos_f32, [MAG_OP_STEP] = &mag_blas_step_f32, + [MAG_OP_EXP] = &mag_blas_exp_f32, [MAG_OP_SOFTMAX] = &mag_blas_softmax_f32, [MAG_OP_SOFTMAX_DV] = &mag_blas_softmax_dv_f32, [MAG_OP_SIGMOID] = &mag_blas_sigmoid_f32, @@ -1587,6 +1627,7 @@ static void (*const forward_kernels[MAG_OP__NUM])(const mag_compute_payload_t*) [MAG_OP_SUBS] = &mag_blas_subs_f32, [MAG_OP_MULS] = &mag_blas_muls_f32, [MAG_OP_DIVS] = &mag_blas_divs_f32, + [MAG_OP_POWS] = &mag_blas_pows_f32, [MAG_OP_MATMUL] = &mag_blas_matmul_f32, }; @@ -1608,6 +1649,7 @@ static void (*const backward_kernels[MAG_OP__NUM])(const mag_compute_payload_t*) [MAG_OP_SIN] = &mag_blas_sin_f32, [MAG_OP_COS] = &mag_blas_cos_f32, [MAG_OP_STEP] = &mag_blas_step_f32, + [MAG_OP_EXP] = &mag_blas_exp_f32, [MAG_OP_SOFTMAX] = &mag_blas_softmax_f32, [MAG_OP_SOFTMAX_DV] = &mag_blas_softmax_dv_f32, [MAG_OP_SIGMOID] = &mag_blas_sigmoid_f32, @@ -1629,6 +1671,7 @@ static void (*const backward_kernels[MAG_OP__NUM])(const mag_compute_payload_t*) [MAG_OP_SUBS] = &mag_blas_subs_f32, [MAG_OP_MULS] = &mag_blas_muls_f32, [MAG_OP_DIVS] = &mag_blas_divs_f32, + [MAG_OP_POWS] = &mag_blas_pows_f32, [MAG_OP_MATMUL] = &mag_blas_matmul_f32, }; diff --git a/magnetron/magnetron_internal.h b/magnetron/magnetron_internal.h index d1defb6..181a5d5 100644 --- a/magnetron/magnetron_internal.h +++ b/magnetron/magnetron_internal.h @@ -431,6 +431,7 @@ typedef enum mag_op_t { MAG_OP_SIN, MAG_OP_COS, MAG_OP_STEP, + MAG_OP_EXP, MAG_OP_SOFTMAX, MAG_OP_SOFTMAX_DV, MAG_OP_SIGMOID, @@ -452,6 +453,7 @@ typedef enum mag_op_t { MAG_OP_SUBS, MAG_OP_MULS, MAG_OP_DIVS, + MAG_OP_POWS, MAG_OP_MATMUL, MAG_OP__NUM } mag_op_t; diff --git a/python/magnetron_framework/bing_gen.py b/python/magnetron_framework/bing_gen.py index 9e7c2b9..feb7bca 100644 --- a/python/magnetron_framework/bing_gen.py +++ b/python/magnetron_framework/bing_gen.py @@ -18,9 +18,7 @@ def comment_replacer(match): macro_substitutions: dict[str, str] = { - 'MAG_EXPORT': ' ', - 'MAG_MAX_DIMS': str(6), # SYNC with magnetron.h - 'MAG_MAX_OP_PARAMS': str(6) # SYNC with magnetron.h + 'MAG_EXPORT': ' ' } def keep_line(line: str) -> bool: @@ -32,7 +30,6 @@ def keep_line(line: str) -> bool: return False return True - c_input: list[str] = [] with open(C_HDR_FILE, 'rt') as f: full_src: str = f.read() @@ -46,15 +43,15 @@ def keep_line(line: str) -> bool: c_input = [line.strip() for line in full_src.splitlines()] # remove empty lines c_input = [line for line in c_input if keep_line(line)] # remove empty lines -out = f'# Autogenered by {__file__} {datetime.datetime.now()}, do NOT edit!\n\n' -out += "__MAG_CDECLS: str = '''\n" +out = f'# Autogenered by {__file__} {datetime.datetime.now()}, do NOT edit!\n' +decls = '' for line in c_input: - out += f'{line}\n' -out = out.rstrip() -if out.endswith('}'): - out = out[:-1] -out += "'''\n" - + decls += f'{line}\n' +decls = decls.rstrip() +if decls.endswith('}'): + decls = decls[:-1] +bin_decls: str = 'b\'' + ''.join(f'\\x{b:02x}' for b in decls.encode('utf-8')) + '\'' +out += f"__MAG_CDECLS: str = {bin_decls}.decode(\'utf-8\')\n" with open(OUTPUT_FILE, 'wt') as f: f.write(out) diff --git a/python/magnetron_framework/magnetron/_ffi_cdecl_generated.py b/python/magnetron_framework/magnetron/_ffi_cdecl_generated.py index 7b0fa65..372528c 100644 --- a/python/magnetron_framework/magnetron/_ffi_cdecl_generated.py +++ b/python/magnetron_framework/magnetron/_ffi_cdecl_generated.py @@ -1,200 +1,2 @@ -# Autogenered by /Users/mariosieg/Documents/projects/magnetron/python/magnetron_framework/bing_gen.py 2025-01-20 11:24:33.732169, do NOT edit! - -__MAG_CDECLS: str = ''' -typedef enum mag_compute_device_type_t { -MAG_COMPUTE_DEVICE_TYPE_CPU = 0, -MAG_COMPUTE_DEVICE_TYPE_GPU_CUDA = 1, -MAG_COMPUTE_DEVICE_TYPE__NUM -} mag_compute_device_type_t; -extern const char* mag_device_type_get_name(mag_compute_device_type_t op); -typedef enum mag_exec_mode_t { -MAG_EXEC_MODE_EAGER = 0, -MAG_EXEC_MODE_DEFERRED = 1, -MAG_EXEC_MODE__NUM -} mag_exec_mode_t; -typedef enum mag_prng_algorithm_t { -MAG_PRNG_MERSENNE_TWISTER = 0, -MAG_PRNG_PCG = 1, -MAG_PRNG__NUM -} mag_prng_algorithm_t; -typedef enum mag_thread_sched_prio_t { -MAG_THREAD_SCHED_PRIO_NORMAL = 0, -MAG_THREAD_SCHED_PRIO_MEDIUM = 1, -MAG_THREAD_SCHED_PRIO_HIGH = 2, -MAG_THREAD_SCHED_PRIO_REALTIME = 3, -} mag_thread_sched_prio_t; -typedef enum mag_color_channels_t { -MAG_COLOR_CHANNELS_AUTO, -MAG_COLOR_CHANNELS_GRAY, -MAG_COLOR_CHANNELS_GRAY_A, -MAG_COLOR_CHANNELS_RGB, -MAG_COLOR_CHANNELS_RGBA, -MAG_COLOR_CHANNELS__NUM -} mag_color_channels_t; -extern void* (*mag_get_alloc_fn(void))(void* blk, size_t size); -extern void mag_set_alloc_fn(void* (*alloc)(void* blk, size_t size)); -extern void mag_set_log_mode(bool enabled); -typedef uint32_t mag_char32_t; -typedef struct mag_ctx_t mag_ctx_t; -typedef struct mag_device_descriptor_t { -mag_compute_device_type_t type; -uint32_t thread_count; -uint32_t cuda_device_id; -} mag_device_descriptor_t; -extern mag_ctx_t* mag_ctx_create(mag_compute_device_type_t device); -extern mag_ctx_t* mag_ctx_create2(const mag_device_descriptor_t* device_info); -extern mag_exec_mode_t mag_ctx_get_exec_mode(const mag_ctx_t* _ptr); -extern void mag_ctx_set_exec_mode(mag_ctx_t* _ptr, mag_exec_mode_t mode); -extern mag_prng_algorithm_t mag_ctx_get_prng_algorithm(const mag_ctx_t* _ptr); -extern void mag_ctx_set_prng_algorithm(mag_ctx_t* _ptr, mag_prng_algorithm_t algorithm, uint64_t seed); -extern mag_compute_device_type_t mag_ctx_get_compute_device_type(const mag_ctx_t* _ptr); -extern const char* mag_ctx_get_compute_device_name(const mag_ctx_t* _ptr); -extern const char* mag_ctx_get_os_name(const mag_ctx_t* _ptr); -extern const char* mag_ctx_get_cpu_name(const mag_ctx_t* _ptr); -extern uint32_t mag_ctx_get_cpu_virtual_cores(const mag_ctx_t* _ptr); -extern uint32_t mag_ctx_get_cpu_physical_cores(const mag_ctx_t* _ptr); -extern uint32_t mag_ctx_get_cpu_sockets(const mag_ctx_t* _ptr); -extern uint64_t mag_ctx_get_physical_memory_total(const mag_ctx_t* _ptr); -extern uint64_t mag_ctx_get_physical_memory_free(const mag_ctx_t* _ptr); -extern bool mag_ctx_is_numa_system(const mag_ctx_t* _ptr); -extern size_t mag_ctx_get_total_tensors_created(const mag_ctx_t* _ptr); -extern void mag_ctx_profile_start_recording(mag_ctx_t* _ptr); -extern void mag_ctx_profile_stop_recording(mag_ctx_t* _ptr, const char* export_csv_file); -extern void mag_ctx_destroy(mag_ctx_t* _ptr); -typedef struct mag_tensor_t mag_tensor_t; -typedef enum mag_dtype_t { -MAG_DTYPE_F32, -MAG_DTYPE__NUM -} mag_dtype_t; -typedef struct mag_dtype_meta_t { -int64_t size; -const char* name; -} mag_dtype_meta_t; -extern const mag_dtype_meta_t* mag_dtype_meta_of(mag_dtype_t type); -extern uint32_t mag_pack_color_u8(uint8_t r, uint8_t g, uint8_t b); -extern uint32_t mag_pack_color_f32(float r, float g, float b); -typedef enum mag_graph_eval_order_t { -MAG_GRAPH_EVAL_ORDER_FORWARD = 0, -MAG_GRAPH_EVAL_ORDER_REVERSE = 1 -} mag_graph_eval_order_t; -extern mag_tensor_t* mag_tensor_create_1d(mag_ctx_t* _ptr, mag_dtype_t type, int64_t d1); -extern mag_tensor_t* mag_tensor_create_2d(mag_ctx_t* _ptr, mag_dtype_t type, int64_t d1, int64_t d2); -extern mag_tensor_t* mag_tensor_create_3d(mag_ctx_t* _ptr, mag_dtype_t type, int64_t d1, int64_t d2, int64_t d3); -extern mag_tensor_t* mag_tensor_create_4d(mag_ctx_t* _ptr, mag_dtype_t type, int64_t d1, int64_t d2, int64_t d3, int64_t d4); -extern mag_tensor_t* mag_tensor_create_5d(mag_ctx_t* _ptr, mag_dtype_t type, int64_t d1, int64_t d2, int64_t d3, int64_t d4, int64_t d5); -extern mag_tensor_t* mag_tensor_create_6d(mag_ctx_t* _ptr, mag_dtype_t type, int64_t d1, int64_t d2, int64_t d3, int64_t d4, int64_t d5, int64_t d6); -extern mag_tensor_t* mag_clone(mag_tensor_t* x); -extern mag_tensor_t* mag_view(mag_tensor_t* x); -extern mag_tensor_t* mag_transpose(mag_tensor_t* x); -extern mag_tensor_t* mag_permute(mag_tensor_t* x, uint32_t d0, uint32_t d1, uint32_t d2, uint32_t d3, uint32_t d4, uint32_t d5); -extern mag_tensor_t* mag_mean(mag_tensor_t* x); -extern mag_tensor_t* mag_min(mag_tensor_t* x); -extern mag_tensor_t* mag_max(mag_tensor_t* x); -extern mag_tensor_t* mag_sum(mag_tensor_t* x); -extern mag_tensor_t* mag_abs(mag_tensor_t* x); -extern mag_tensor_t* mag_abs_(mag_tensor_t* x); -extern mag_tensor_t* mag_neg(mag_tensor_t* x); -extern mag_tensor_t* mag_neg_(mag_tensor_t* x); -extern mag_tensor_t* mag_log(mag_tensor_t* x); -extern mag_tensor_t* mag_log_(mag_tensor_t* x); -extern mag_tensor_t* mag_sqr(mag_tensor_t* x); -extern mag_tensor_t* mag_sqr_(mag_tensor_t* x); -extern mag_tensor_t* mag_sqrt(mag_tensor_t* x); -extern mag_tensor_t* mag_sqrt_(mag_tensor_t* x); -extern mag_tensor_t* mag_sin(mag_tensor_t* x); -extern mag_tensor_t* mag_sin_(mag_tensor_t* x); -extern mag_tensor_t* mag_cos(mag_tensor_t* x); -extern mag_tensor_t* mag_cos_(mag_tensor_t* x); -extern mag_tensor_t* mag_step(mag_tensor_t* x); -extern mag_tensor_t* mag_step_(mag_tensor_t* x); -extern mag_tensor_t* mag_softmax(mag_tensor_t* x); -extern mag_tensor_t* mag_softmax_(mag_tensor_t* x); -extern mag_tensor_t* mag_softmax_dv(mag_tensor_t* x); -extern mag_tensor_t* mag_softmax_dv_(mag_tensor_t* x); -extern mag_tensor_t* mag_sigmoid(mag_tensor_t* x); -extern mag_tensor_t* mag_sigmoid_(mag_tensor_t* x); -extern mag_tensor_t* mag_sigmoid_dv(mag_tensor_t* x); -extern mag_tensor_t* mag_sigmoid_dv_(mag_tensor_t* x); -extern mag_tensor_t* mag_hard_sigmoid(mag_tensor_t* x); -extern mag_tensor_t* mag_hard_sigmoid_(mag_tensor_t* x); -extern mag_tensor_t* mag_silu(mag_tensor_t* x); -extern mag_tensor_t* mag_silu_(mag_tensor_t* x); -extern mag_tensor_t* mag_silu_dv(mag_tensor_t* x); -extern mag_tensor_t* mag_silu_dv_(mag_tensor_t* x); -extern mag_tensor_t* mag_tanh(mag_tensor_t* x); -extern mag_tensor_t* mag_tanh_(mag_tensor_t* x); -extern mag_tensor_t* mag_tanh_dv(mag_tensor_t* x); -extern mag_tensor_t* mag_tanh_dv_(mag_tensor_t* x); -extern mag_tensor_t* mag_relu(mag_tensor_t* x); -extern mag_tensor_t* mag_relu_(mag_tensor_t* x); -extern mag_tensor_t* mag_relu_dv(mag_tensor_t* x); -extern mag_tensor_t* mag_relu_dv_(mag_tensor_t* x); -extern mag_tensor_t* mag_gelu(mag_tensor_t* x); -extern mag_tensor_t* mag_gelu_(mag_tensor_t* x); -extern mag_tensor_t* mag_gelu_dv(mag_tensor_t* x); -extern mag_tensor_t* mag_gelu_dv_(mag_tensor_t* x); -extern mag_tensor_t* mag_add(mag_tensor_t* x, mag_tensor_t* y); -extern mag_tensor_t* mag_add_(mag_tensor_t* x, mag_tensor_t* y); -extern mag_tensor_t* mag_sub( mag_tensor_t* x, mag_tensor_t* y); -extern mag_tensor_t* mag_sub_(mag_tensor_t* x, mag_tensor_t* y); -extern mag_tensor_t* mag_mul( mag_tensor_t* x, mag_tensor_t* y); -extern mag_tensor_t* mag_mul_(mag_tensor_t* x, mag_tensor_t* y); -extern mag_tensor_t* mag_div(mag_tensor_t* x, mag_tensor_t* y); -extern mag_tensor_t* mag_div_(mag_tensor_t* x, mag_tensor_t* y); -extern mag_tensor_t* mag_adds(mag_tensor_t* x, float xi); -extern mag_tensor_t* mag_adds_(mag_tensor_t* x, float xi); -extern mag_tensor_t* mag_subs( mag_tensor_t* x, float xi); -extern mag_tensor_t* mag_subs_(mag_tensor_t* x, float xi); -extern mag_tensor_t* mag_muls(mag_tensor_t* x, float xi); -extern mag_tensor_t* mag_muls_(mag_tensor_t* x, float xi); -extern mag_tensor_t* mag_divs(mag_tensor_t* x, float xi); -extern mag_tensor_t* mag_divs_(mag_tensor_t* x, float xi); -extern mag_tensor_t* mag_matmul(mag_tensor_t* a, mag_tensor_t* b); -extern void mag_tensor_incref(mag_tensor_t* t); -extern bool mag_tensor_decref(mag_tensor_t* t); -extern void mag_tensor_copy_buffer_from(mag_tensor_t* t, const void* data, size_t size); -extern void mag_tensor_fill(mag_tensor_t* t, float x); -extern void mag_tensor_fill_random_uniform(mag_tensor_t* t, float min, float max); -extern void mag_tensor_fill_random_normal(mag_tensor_t* t, float mean, float stddev); -extern uint64_t mag_tensor_get_packed_refcounts(const mag_tensor_t* t); -extern void mag_tensor_retain(mag_tensor_t* t); -extern size_t mag_tensor_get_memory_usage(const mag_tensor_t* t); -extern void mag_tensor_print(const mag_tensor_t* t, bool with_header, bool with_data); -extern void mag_tensor_set_name(mag_tensor_t* t, const char* name); -extern void mag_tensor_fmt_name(mag_tensor_t* t, const char* fmt, ...); -extern const char* mag_tensor_get_name(const mag_tensor_t* t); -extern int64_t mag_tensor_rank(const mag_tensor_t* t); -extern const int64_t* mag_tensor_shape(const mag_tensor_t* t); -extern const int64_t* mag_tensor_strides(const mag_tensor_t* t); -extern mag_dtype_t mag_tensor_dtype(const mag_tensor_t* t); -extern void* mag_tensor_data_ptr(const mag_tensor_t* t); -extern int64_t mag_tensor_data_size(const mag_tensor_t* t); -extern int64_t mag_tensor_numel(const mag_tensor_t* t); -extern int64_t mag_tensor_num_rows(const mag_tensor_t* t); -extern int64_t mag_tensor_num_cols(const mag_tensor_t* t); -extern bool mag_tensor_is_scalar(const mag_tensor_t* t); -extern bool mag_tensor_is_vector(const mag_tensor_t* t); -extern bool mag_tensor_is_matrix(const mag_tensor_t* t); -extern bool mag_tensor_is_volume(const mag_tensor_t* t); -extern bool mag_tensor_is_shape_eq(const mag_tensor_t* a, const mag_tensor_t* b); -extern bool mag_tensor_are_strides_eq(const mag_tensor_t* a, const mag_tensor_t* b); -extern bool mag_tensor_can_broadcast(const mag_tensor_t* a, const mag_tensor_t* b); -extern bool mag_tensor_is_transposed(const mag_tensor_t* t); -extern bool mag_tensor_is_permuted(const mag_tensor_t* t); -extern bool mag_tensor_is_contiguous(const mag_tensor_t* t); -extern float mag_tensor_get_scalar_physical_index(mag_tensor_t* t, int64_t d0, int64_t d1, int64_t d2, int64_t d3, int64_t d4, int64_t d5); -extern void mag_tensor_set_scalar_physical_index(mag_tensor_t* t, int64_t d0, int64_t d1, int64_t d2, int64_t d3, int64_t d4, int64_t d5, float x); -extern float mag_tensor_get_scalar_virtual_index(mag_tensor_t* t, int64_t v_idx); -extern void mag_tensor_set_scalar_virtual_index(mag_tensor_t* t, int64_t v_idx, float x); -extern bool mag_tensor_eq(const mag_tensor_t* a, const mag_tensor_t* b); -extern bool mag_tensor_is_close(const mag_tensor_t* a, const mag_tensor_t* b, float eps, double* percent_eq); -extern void mag_tensor_img_draw_box(mag_tensor_t* t, int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t wi, uint32_t rgb); -extern void mag_tensor_img_draw_text(mag_tensor_t* t, int32_t x, int32_t y, int32_t size, uint32_t rgb, const char* txt); -extern mag_ctx_t* mag_tensor_get_ctx(const mag_tensor_t* t); -extern void* mag_tensor_get_user_data(const mag_tensor_t* t); -extern void mag_tensor_set_user_data(mag_tensor_t* t, void* ud); -extern void mag_tensor_save(const mag_tensor_t* t, const char* file); -extern mag_tensor_t* mag_tensor_load(mag_ctx_t* _ptr, const char* file); -extern mag_tensor_t* mag_tensor_load_image(mag_ctx_t* _ptr, const char* file, mag_color_channels_t channels, uint32_t resize_w, uint32_t resize_h); -extern void mag_tensor_save_image(const mag_tensor_t* t, const char* file); -''' +# Autogenered by /home/mario/Documents/remote_projects/magnetron/python/magnetron_framework/bing_gen.py 2025-01-29 14:41:12.407408, do NOT edit! +__MAG_CDECLS: str = b'\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x43\x4f\x4d\x50\x55\x54\x45\x5f\x44\x45\x56\x49\x43\x45\x5f\x54\x59\x50\x45\x5f\x43\x50\x55\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4d\x50\x55\x54\x45\x5f\x44\x45\x56\x49\x43\x45\x5f\x54\x59\x50\x45\x5f\x47\x50\x55\x5f\x43\x55\x44\x41\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4d\x50\x55\x54\x45\x5f\x44\x45\x56\x49\x43\x45\x5f\x54\x59\x50\x45\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x67\x65\x74\x5f\x6e\x61\x6d\x65\x28\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x6f\x70\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x45\x58\x45\x43\x5f\x4d\x4f\x44\x45\x5f\x45\x41\x47\x45\x52\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x45\x58\x45\x43\x5f\x4d\x4f\x44\x45\x5f\x44\x45\x46\x45\x52\x52\x45\x44\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x45\x58\x45\x43\x5f\x4d\x4f\x44\x45\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x50\x52\x4e\x47\x5f\x4d\x45\x52\x53\x45\x4e\x4e\x45\x5f\x54\x57\x49\x53\x54\x45\x52\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x50\x52\x4e\x47\x5f\x50\x43\x47\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x50\x52\x4e\x47\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x74\x68\x72\x65\x61\x64\x5f\x73\x63\x68\x65\x64\x5f\x70\x72\x69\x6f\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x4e\x4f\x52\x4d\x41\x4c\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x4d\x45\x44\x49\x55\x4d\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x48\x49\x47\x48\x20\x3d\x20\x32\x2c\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x52\x45\x41\x4c\x54\x49\x4d\x45\x20\x3d\x20\x33\x2c\x0a\x7d\x20\x6d\x61\x67\x5f\x74\x68\x72\x65\x61\x64\x5f\x73\x63\x68\x65\x64\x5f\x70\x72\x69\x6f\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x63\x6f\x6c\x6f\x72\x5f\x63\x68\x61\x6e\x6e\x65\x6c\x73\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x41\x55\x54\x4f\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x47\x52\x41\x59\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x47\x52\x41\x59\x5f\x41\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x52\x47\x42\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x52\x47\x42\x41\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x63\x6f\x6c\x6f\x72\x5f\x63\x68\x61\x6e\x6e\x65\x6c\x73\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x2a\x20\x28\x2a\x6d\x61\x67\x5f\x67\x65\x74\x5f\x61\x6c\x6c\x6f\x63\x5f\x66\x6e\x28\x76\x6f\x69\x64\x29\x29\x28\x76\x6f\x69\x64\x2a\x20\x62\x6c\x6b\x2c\x20\x73\x69\x7a\x65\x5f\x74\x20\x73\x69\x7a\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x73\x65\x74\x5f\x61\x6c\x6c\x6f\x63\x5f\x66\x6e\x28\x76\x6f\x69\x64\x2a\x20\x28\x2a\x61\x6c\x6c\x6f\x63\x29\x28\x76\x6f\x69\x64\x2a\x20\x62\x6c\x6b\x2c\x20\x73\x69\x7a\x65\x5f\x74\x20\x73\x69\x7a\x65\x29\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x73\x65\x74\x5f\x6c\x6f\x67\x5f\x6d\x6f\x64\x65\x28\x62\x6f\x6f\x6c\x20\x65\x6e\x61\x62\x6c\x65\x64\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x68\x61\x72\x33\x32\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x64\x65\x73\x63\x72\x69\x70\x74\x6f\x72\x5f\x74\x20\x7b\x0a\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x3b\x0a\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x74\x68\x72\x65\x61\x64\x5f\x63\x6f\x75\x6e\x74\x3b\x0a\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x63\x75\x64\x61\x5f\x64\x65\x76\x69\x63\x65\x5f\x69\x64\x3b\x0a\x7d\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x64\x65\x73\x63\x72\x69\x70\x74\x6f\x72\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x63\x72\x65\x61\x74\x65\x28\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x64\x65\x76\x69\x63\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x63\x72\x65\x61\x74\x65\x32\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x64\x65\x73\x63\x72\x69\x70\x74\x6f\x72\x5f\x74\x2a\x20\x64\x65\x76\x69\x63\x65\x5f\x69\x6e\x66\x6f\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x73\x65\x74\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x20\x6d\x6f\x64\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x73\x65\x74\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x2c\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x73\x65\x65\x64\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x6f\x73\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x76\x69\x72\x74\x75\x61\x6c\x5f\x63\x6f\x72\x65\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x63\x6f\x72\x65\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x73\x6f\x63\x6b\x65\x74\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x6d\x65\x6d\x6f\x72\x79\x5f\x74\x6f\x74\x61\x6c\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x6d\x65\x6d\x6f\x72\x79\x5f\x66\x72\x65\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x69\x73\x5f\x6e\x75\x6d\x61\x5f\x73\x79\x73\x74\x65\x6d\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x73\x69\x7a\x65\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x74\x6f\x74\x61\x6c\x5f\x74\x65\x6e\x73\x6f\x72\x73\x5f\x63\x72\x65\x61\x74\x65\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x70\x72\x6f\x66\x69\x6c\x65\x5f\x73\x74\x61\x72\x74\x5f\x72\x65\x63\x6f\x72\x64\x69\x6e\x67\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x70\x72\x6f\x66\x69\x6c\x65\x5f\x73\x74\x6f\x70\x5f\x72\x65\x63\x6f\x72\x64\x69\x6e\x67\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x65\x78\x70\x6f\x72\x74\x5f\x63\x73\x76\x5f\x66\x69\x6c\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x64\x65\x73\x74\x72\x6f\x79\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x44\x54\x59\x50\x45\x5f\x46\x33\x32\x2c\x0a\x4d\x41\x47\x5f\x44\x54\x59\x50\x45\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x74\x20\x7b\x0a\x69\x6e\x74\x36\x34\x5f\x74\x20\x73\x69\x7a\x65\x3b\x0a\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6e\x61\x6d\x65\x3b\x0a\x7d\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x6f\x66\x28\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x70\x61\x63\x6b\x5f\x63\x6f\x6c\x6f\x72\x5f\x75\x38\x28\x75\x69\x6e\x74\x38\x5f\x74\x20\x72\x2c\x20\x75\x69\x6e\x74\x38\x5f\x74\x20\x67\x2c\x20\x75\x69\x6e\x74\x38\x5f\x74\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x70\x61\x63\x6b\x5f\x63\x6f\x6c\x6f\x72\x5f\x66\x33\x32\x28\x66\x6c\x6f\x61\x74\x20\x72\x2c\x20\x66\x6c\x6f\x61\x74\x20\x67\x2c\x20\x66\x6c\x6f\x61\x74\x20\x62\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x67\x72\x61\x70\x68\x5f\x65\x76\x61\x6c\x5f\x6f\x72\x64\x65\x72\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x47\x52\x41\x50\x48\x5f\x45\x56\x41\x4c\x5f\x4f\x52\x44\x45\x52\x5f\x46\x4f\x52\x57\x41\x52\x44\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x47\x52\x41\x50\x48\x5f\x45\x56\x41\x4c\x5f\x4f\x52\x44\x45\x52\x5f\x52\x45\x56\x45\x52\x53\x45\x20\x3d\x20\x31\x0a\x7d\x20\x6d\x61\x67\x5f\x67\x72\x61\x70\x68\x5f\x65\x76\x61\x6c\x5f\x6f\x72\x64\x65\x72\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x31\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x32\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x33\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x34\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x35\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x36\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x36\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x6c\x6f\x6e\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x76\x69\x65\x77\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x72\x61\x6e\x73\x70\x6f\x73\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x70\x65\x72\x6d\x75\x74\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x30\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x31\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x32\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x33\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x34\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x35\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x65\x61\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x69\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x61\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x6d\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x62\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x62\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6e\x65\x67\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6e\x65\x67\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6c\x6f\x67\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6c\x6f\x67\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x74\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x74\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6e\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x6f\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x6f\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x74\x65\x70\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x74\x65\x70\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x65\x78\x70\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x65\x78\x70\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x68\x61\x72\x64\x5f\x73\x69\x67\x6d\x6f\x69\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x68\x61\x72\x64\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x28\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x28\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x73\x28\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x70\x6f\x77\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x70\x6f\x77\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x61\x74\x6d\x75\x6c\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x6e\x63\x72\x65\x66\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x65\x63\x72\x65\x66\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x6f\x70\x79\x5f\x62\x75\x66\x66\x65\x72\x5f\x66\x72\x6f\x6d\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x76\x6f\x69\x64\x2a\x20\x64\x61\x74\x61\x2c\x20\x73\x69\x7a\x65\x5f\x74\x20\x73\x69\x7a\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x69\x6c\x6c\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x69\x6c\x6c\x5f\x72\x61\x6e\x64\x6f\x6d\x5f\x75\x6e\x69\x66\x6f\x72\x6d\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x66\x6c\x6f\x61\x74\x20\x6d\x69\x6e\x2c\x20\x66\x6c\x6f\x61\x74\x20\x6d\x61\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x69\x6c\x6c\x5f\x72\x61\x6e\x64\x6f\x6d\x5f\x6e\x6f\x72\x6d\x61\x6c\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x66\x6c\x6f\x61\x74\x20\x6d\x65\x61\x6e\x2c\x20\x66\x6c\x6f\x61\x74\x20\x73\x74\x64\x64\x65\x76\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x70\x61\x63\x6b\x65\x64\x5f\x72\x65\x66\x63\x6f\x75\x6e\x74\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x72\x65\x74\x61\x69\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x73\x69\x7a\x65\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x6d\x65\x6d\x6f\x72\x79\x5f\x75\x73\x61\x67\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x70\x72\x69\x6e\x74\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x62\x6f\x6f\x6c\x20\x77\x69\x74\x68\x5f\x68\x65\x61\x64\x65\x72\x2c\x20\x62\x6f\x6f\x6c\x20\x77\x69\x74\x68\x5f\x64\x61\x74\x61\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x6e\x61\x6d\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6e\x61\x6d\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x6d\x74\x5f\x6e\x61\x6d\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x6d\x74\x2c\x20\x2e\x2e\x2e\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x72\x61\x6e\x6b\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x69\x6e\x74\x36\x34\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x68\x61\x70\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x69\x6e\x74\x36\x34\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x74\x72\x69\x64\x65\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x74\x79\x70\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x61\x74\x61\x5f\x70\x74\x72\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x61\x74\x61\x5f\x73\x69\x7a\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6e\x75\x6d\x65\x6c\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6e\x75\x6d\x5f\x72\x6f\x77\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6e\x75\x6d\x5f\x63\x6f\x6c\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x73\x63\x61\x6c\x61\x72\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x76\x65\x63\x74\x6f\x72\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x6d\x61\x74\x72\x69\x78\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x76\x6f\x6c\x75\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x73\x68\x61\x70\x65\x5f\x65\x71\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x61\x72\x65\x5f\x73\x74\x72\x69\x64\x65\x73\x5f\x65\x71\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x61\x6e\x5f\x62\x72\x6f\x61\x64\x63\x61\x73\x74\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x74\x72\x61\x6e\x73\x70\x6f\x73\x65\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x70\x65\x72\x6d\x75\x74\x65\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x63\x6f\x6e\x74\x69\x67\x75\x6f\x75\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x66\x6c\x6f\x61\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x30\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x30\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x66\x6c\x6f\x61\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x76\x69\x72\x74\x75\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x76\x5f\x69\x64\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x76\x69\x72\x74\x75\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x76\x5f\x69\x64\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x65\x71\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x63\x6c\x6f\x73\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x2c\x20\x66\x6c\x6f\x61\x74\x20\x65\x70\x73\x2c\x20\x64\x6f\x75\x62\x6c\x65\x2a\x20\x70\x65\x72\x63\x65\x6e\x74\x5f\x65\x71\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x6d\x67\x5f\x64\x72\x61\x77\x5f\x62\x6f\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x78\x31\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x79\x31\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x78\x32\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x79\x32\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x77\x69\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x67\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x6d\x67\x5f\x64\x72\x61\x77\x5f\x74\x65\x78\x74\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x78\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x79\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x73\x69\x7a\x65\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x67\x62\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x74\x78\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x63\x74\x78\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x75\x73\x65\x72\x5f\x64\x61\x74\x61\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x75\x73\x65\x72\x5f\x64\x61\x74\x61\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x76\x6f\x69\x64\x2a\x20\x75\x64\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x61\x76\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6c\x6f\x61\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6c\x6f\x61\x64\x5f\x69\x6d\x61\x67\x65\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x2c\x20\x6d\x61\x67\x5f\x63\x6f\x6c\x6f\x72\x5f\x63\x68\x61\x6e\x6e\x65\x6c\x73\x5f\x74\x20\x63\x68\x61\x6e\x6e\x65\x6c\x73\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x65\x73\x69\x7a\x65\x5f\x77\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x65\x73\x69\x7a\x65\x5f\x68\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x61\x76\x65\x5f\x69\x6d\x61\x67\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x29\x3b\x0a'.decode('utf-8') diff --git a/python/magnetron_framework/magnetron/core.py b/python/magnetron_framework/magnetron/core.py index 6cb3ce3..c6b5e95 100644 --- a/python/magnetron_framework/magnetron/core.py +++ b/python/magnetron_framework/magnetron/core.py @@ -217,6 +217,8 @@ def __exit__(self, exc_type, exc_value, traceback): @typing.final class Tensor: + __slots__ = ('_ctx', '_ptr') + def __init__(self, ptr: ffi.CData | None = None) -> None: if isinstance(ptr, ffi.CData): assert ptr != ffi.NULL, 'Invalid tensor pointer' @@ -546,12 +548,18 @@ def cos(self) -> 'Tensor': def cos_(self) -> 'Tensor': return Tensor(C.mag_cos_(self._ptr)) - def heaviside_step(self) -> 'Tensor': + def step(self) -> 'Tensor': return Tensor(C.mag_step(self._ptr)) - def heaviside_step_(self) -> 'Tensor': + def step_(self) -> 'Tensor': return Tensor(C.mag_step_(self._ptr)) + def exp(self) -> 'Tensor': + return Tensor(C.mag_exp(self._ptr)) + + def exp_(self) -> 'Tensor': + return Tensor(C.mag_exp_(self._ptr)) + def softmax(self, derivative: bool = False) -> 'Tensor': return Tensor(C.mag_softmax_dv(self._ptr) if derivative else C.mag_softmax(self._ptr)) @@ -640,6 +648,14 @@ def __itruediv__(self, other: object | int | float) -> 'Tensor': return Tensor( C.mag_div_(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_divs_(self._ptr, float(other))) + def __pow__(self, exponent: int | float, modulo=None) -> 'Tensor': + assert modulo is None + return Tensor(C.mag_pows(self._ptr, float(exponent))) + + def __ipow__(self, exponent: int | float, modulo=None) -> 'Tensor': + assert modulo is None + return Tensor(C.mag_pows_(self._ptr, float(exponent))) + def __matmul__(self, other: 'Tensor') -> 'Tensor': return Tensor(C.mag_matmul(self._ptr, other._ptr)) diff --git a/python/tests/tensor_ops2.py b/python/tests/tensor_ops2.py index 95ee531..7477ca1 100644 --- a/python/tests/tensor_ops2.py +++ b/python/tests/tensor_ops2.py @@ -1,133 +1,116 @@ # (c) 2025 Mario "Neo" Sieg. +import random -from magnetron import * +from magnetron import Tensor import numpy as np def tonumpy(t: Tensor): return np.array(t.tolist(), dtype=np.float32).reshape(t.shape) -def sigmoid(x): - return 1 / (1 + np.exp(-x)) - -def test_simple_ff(): - truth_table = [ - [0, 0], - [0, 1], - [1, 0], - [1, 1] - ] - - W1 = Tensor.uniform((2, 4)) - b1 = Tensor.uniform((1, 4)) - W2 = Tensor.uniform((4, 1)) - b2 = Tensor.uniform((1, 1)) - - nW1 = tonumpy(W1) - nb1 = tonumpy(b1) - nW2 = tonumpy(W2) - nb2 = tonumpy(b2) - - np_data = [] - for x in truth_table: - z1 = x @ nW1 + nb1 - a1 = sigmoid(z1) - z2 = a1 @ nW2 + nb2 - a2 = sigmoid(z2) - np_data.append(a2) - - mag_data = [] - for x in truth_table: - x = Tensor.const([x]) - z1 = x @ W1 + b1 - a1 = z1.sigmoid() - z2 = a1 @ W2 + b2 - a2 = z2.sigmoid() - mag_data.append(a2) - - for mag, np_ in zip(mag_data, np_data): - np.testing.assert_allclose(tonumpy(mag), np_) - -def test_matmul_squared(): - shapes = [4, 8, 16, 32, 64, 128, 256, 512, 1024] - for shape in shapes: - mag_a = Tensor.uniform((shape, shape)) - mag_b = Tensor.uniform((shape, shape)) - np_a = tonumpy(mag_a) - np_b = tonumpy(mag_b) - mag_result = mag_a @ mag_b - np_result = np.matmul(np_a, np_b) - assert mag_result.shape == np_result.shape - np.testing.assert_allclose(tonumpy(mag_result), np_result) - -def test_matmul(): - shapes = [(4, 8), (8, 16), (16, 32), (32, 64), (64, 128), (128, 256), (256, 512), (512, 1024)] - for shape in shapes: - mag_a = Tensor.uniform(shape) - mag_b = Tensor.uniform((shape[1], shape[0])) - np_a = tonumpy(mag_a) - np_b = tonumpy(mag_b) - mag_result = mag_a @ mag_b - np_result = np.matmul(np_a, np_b) - assert mag_result.shape == np_result.shape - np.testing.assert_allclose(tonumpy(mag_result), np_result) - -def test_matmul_matrix_by_vector(): - shapes = [(4, 8), (8, 16), (16, 32), (32, 64), (64, 128), (128, 256), (256, 512), (512, 1024)] - for shape in shapes: - mag_a = Tensor.uniform(shape) - mag_b = Tensor.uniform((shape[1], 1)) - np_a = tonumpy(mag_a) - np_b = tonumpy(mag_b) - mag_result = mag_a @ mag_b - np_result = np.matmul(np_a, np_b) - assert mag_result.shape == np_result.shape - np.testing.assert_allclose(tonumpy(mag_result), np_result) - -def test_matmul_vector_by_matrix(): - shapes = [(4, 8), (8, 16), (16, 32), (32, 64), (64, 128), (128, 256), (256, 512), (512, 1024)] - for shape in shapes: - mag_a = Tensor.uniform((1, shape[0])) - mag_b = Tensor.uniform(shape) - np_a = tonumpy(mag_a) - np_b = tonumpy(mag_b) - mag_result = mag_a @ mag_b - np_result = np.matmul(np_a, np_b) - assert mag_result.shape == np_result.shape - np.testing.assert_allclose(tonumpy(mag_result), np_result) - -def test_matmul_scalar_by_matrix(): - shapes = [(4, 8), (8, 16), (16, 32), (32, 64), (64, 128), (128, 256), (256, 512), (512, 1024)] - for shape in shapes: - scalar = np.random.rand() - mag_b = Tensor.uniform(shape) - np_b = tonumpy(mag_b) - mag_result = scalar * mag_b - np_result = scalar * np_b - assert mag_result.shape == np_result.shape - np.testing.assert_allclose(tonumpy(mag_result), np_result) - -def test_matmul_x_transposed(): - shape_a = (4, 2) - shape_b = (4, 4) - mag_a = Tensor.uniform(shape_a) - mag_b = Tensor.uniform(shape_b) - np_a = tonumpy(mag_a) - np_b = tonumpy(mag_b) - mag_result = mag_a.T @ mag_b - np_result = np.matmul(np_a.T, np_b) - assert mag_result.shape == np_result.shape - assert mag_result.shape == (2, 4) - np.testing.assert_allclose(tonumpy(mag_result), np_result) - -def test_matmul_y_transposed(): - shape_a = (4, 2) - shape_b = (4, 4) - mag_a = Tensor.uniform(shape_a) - mag_b = Tensor.uniform(shape_b) - np_a = tonumpy(mag_a) - np_b = tonumpy(mag_b) - mag_result = mag_a @ mag_b.T - np_result = np.matmul(np_a, np_b.T) - assert mag_result.shape == np_result.shape - assert mag_result.shape == (2, 4) - np.testing.assert_allclose(tonumpy(mag_result), np_result) +def square_shape_permutations(f: callable, lim: int) -> None: + lim += 1 + for i0 in range(1, lim): + for i1 in range(1, lim): + for i2 in range(1, lim): + for i3 in range(1, lim): + for i4 in range(1, lim): + for i5 in range(1, lim): + f((i0, i1, i2, i3, i4, i5)) + +def binary_op_square(f: callable, lim: int = 4) -> None: + def compute(shape: tuple[int, ...]) -> None: + x = Tensor.uniform(shape) + y = Tensor.uniform(shape) + r = f(x, y) + np.testing.assert_allclose(tonumpy(r), f(tonumpy(x), tonumpy(y))) + square_shape_permutations(compute, lim) + +def unary_op(magf: callable, npf: callable, lim: int = 4) -> None: + def compute(shape: tuple[int, ...]) -> None: + x = Tensor.uniform(shape) + r = magf(x) + np.testing.assert_allclose(tonumpy(r), npf(tonumpy(x)), atol=1e-6) + + square_shape_permutations(compute, lim) + +def scalar_op(f: callable, rhs: bool = True, lim: int = 4) -> None: + def compute(shape: tuple[int, ...]) -> None: # x op scalar + xi: float = random.uniform(-10.0, 10.0) + x = Tensor.uniform(shape) + r = f(x, xi) + np.testing.assert_allclose(tonumpy(r), f(tonumpy(x), xi)) + square_shape_permutations(compute, lim) + + if not rhs: + return + def compute(shape: tuple[int, ...]) -> None: # scalar op x + xi: float = random.uniform(-10.0, 10.0) + x = Tensor.uniform(shape) + r = f(xi, x) + np.testing.assert_allclose(tonumpy(r), f(xi, tonumpy(x))) + square_shape_permutations(compute, lim) + +def test_unary_op_abs() -> None: + unary_op(lambda x: x.abs(), lambda x: np.abs(x)) + unary_op(lambda x: x.abs_(), lambda x: np.abs(x)) + +def test_unary_op_neg() -> None: + unary_op(lambda x: -x, lambda x: -x) + +def test_unary_op_log() -> None: + unary_op(lambda x: x.log(), lambda x: np.log(x)) + unary_op(lambda x: x.log_(), lambda x: np.log(x)) + +def test_unary_op_sqr() -> None: + unary_op(lambda x: x.sqr(), lambda x: x * x) + unary_op(lambda x: x.sqr_(), lambda x: x * x) + +def test_unary_op_sqrt() -> None: + unary_op(lambda x: x.sqrt(), lambda x: np.sqrt(x)) + unary_op(lambda x: x.sqrt_(), lambda x: np.sqrt(x)) + +def test_unary_op_sin() -> None: + unary_op(lambda x: x.sin(), lambda x: np.sin(x)) + unary_op(lambda x: x.sin_(), lambda x: np.sin(x)) + +def test_unary_op_cos() -> None: + unary_op(lambda x: x.cos(), lambda x: np.cos(x)) + unary_op(lambda x: x.cos_(), lambda x: np.cos(x)) + +""" +def test_unary_op_step() -> None: + unary_op(lambda x: x.cos(), lambda x: np.cos(x)) + unary_op(lambda x: x.cos_(), lambda x: np.cos(x)) +""" + +def test_unary_op_exp() -> None: + unary_op(lambda x: x.exp(), lambda x: np.exp(x)) + unary_op(lambda x: x.exp_(), lambda x: np.exp(x)) + +def test_binary_op_add() -> None: + binary_op_square(lambda x, y: x + y) + +def test_binary_op_sub() -> None: + binary_op_square(lambda x, y: x + y) + +def test_binary_op_mul() -> None: + binary_op_square(lambda x, y: x * y) + +def test_binary_op_div() -> None: + binary_op_square(lambda x, y: x / y) + +def test_scalar_op_add() -> None: + scalar_op(lambda x, xi: x + xi) + +def test_scalar_op_sub() -> None: + scalar_op(lambda x, xi: x + xi) + +def test_scalar_op_mul() -> None: + scalar_op(lambda x, xi: x * xi) + +def test_scalar_op_div() -> None: + scalar_op(lambda x, xi: x / xi) + +def test_scalar_op_pow() -> None: + scalar_op(lambda x, xi: x ** xi, rhs=False) + diff --git a/python/tests/tensor_ops3.py b/python/tests/tensor_ops3.py new file mode 100644 index 0000000..95ee531 --- /dev/null +++ b/python/tests/tensor_ops3.py @@ -0,0 +1,133 @@ +# (c) 2025 Mario "Neo" Sieg. + +from magnetron import * +import numpy as np + +def tonumpy(t: Tensor): + return np.array(t.tolist(), dtype=np.float32).reshape(t.shape) + +def sigmoid(x): + return 1 / (1 + np.exp(-x)) + +def test_simple_ff(): + truth_table = [ + [0, 0], + [0, 1], + [1, 0], + [1, 1] + ] + + W1 = Tensor.uniform((2, 4)) + b1 = Tensor.uniform((1, 4)) + W2 = Tensor.uniform((4, 1)) + b2 = Tensor.uniform((1, 1)) + + nW1 = tonumpy(W1) + nb1 = tonumpy(b1) + nW2 = tonumpy(W2) + nb2 = tonumpy(b2) + + np_data = [] + for x in truth_table: + z1 = x @ nW1 + nb1 + a1 = sigmoid(z1) + z2 = a1 @ nW2 + nb2 + a2 = sigmoid(z2) + np_data.append(a2) + + mag_data = [] + for x in truth_table: + x = Tensor.const([x]) + z1 = x @ W1 + b1 + a1 = z1.sigmoid() + z2 = a1 @ W2 + b2 + a2 = z2.sigmoid() + mag_data.append(a2) + + for mag, np_ in zip(mag_data, np_data): + np.testing.assert_allclose(tonumpy(mag), np_) + +def test_matmul_squared(): + shapes = [4, 8, 16, 32, 64, 128, 256, 512, 1024] + for shape in shapes: + mag_a = Tensor.uniform((shape, shape)) + mag_b = Tensor.uniform((shape, shape)) + np_a = tonumpy(mag_a) + np_b = tonumpy(mag_b) + mag_result = mag_a @ mag_b + np_result = np.matmul(np_a, np_b) + assert mag_result.shape == np_result.shape + np.testing.assert_allclose(tonumpy(mag_result), np_result) + +def test_matmul(): + shapes = [(4, 8), (8, 16), (16, 32), (32, 64), (64, 128), (128, 256), (256, 512), (512, 1024)] + for shape in shapes: + mag_a = Tensor.uniform(shape) + mag_b = Tensor.uniform((shape[1], shape[0])) + np_a = tonumpy(mag_a) + np_b = tonumpy(mag_b) + mag_result = mag_a @ mag_b + np_result = np.matmul(np_a, np_b) + assert mag_result.shape == np_result.shape + np.testing.assert_allclose(tonumpy(mag_result), np_result) + +def test_matmul_matrix_by_vector(): + shapes = [(4, 8), (8, 16), (16, 32), (32, 64), (64, 128), (128, 256), (256, 512), (512, 1024)] + for shape in shapes: + mag_a = Tensor.uniform(shape) + mag_b = Tensor.uniform((shape[1], 1)) + np_a = tonumpy(mag_a) + np_b = tonumpy(mag_b) + mag_result = mag_a @ mag_b + np_result = np.matmul(np_a, np_b) + assert mag_result.shape == np_result.shape + np.testing.assert_allclose(tonumpy(mag_result), np_result) + +def test_matmul_vector_by_matrix(): + shapes = [(4, 8), (8, 16), (16, 32), (32, 64), (64, 128), (128, 256), (256, 512), (512, 1024)] + for shape in shapes: + mag_a = Tensor.uniform((1, shape[0])) + mag_b = Tensor.uniform(shape) + np_a = tonumpy(mag_a) + np_b = tonumpy(mag_b) + mag_result = mag_a @ mag_b + np_result = np.matmul(np_a, np_b) + assert mag_result.shape == np_result.shape + np.testing.assert_allclose(tonumpy(mag_result), np_result) + +def test_matmul_scalar_by_matrix(): + shapes = [(4, 8), (8, 16), (16, 32), (32, 64), (64, 128), (128, 256), (256, 512), (512, 1024)] + for shape in shapes: + scalar = np.random.rand() + mag_b = Tensor.uniform(shape) + np_b = tonumpy(mag_b) + mag_result = scalar * mag_b + np_result = scalar * np_b + assert mag_result.shape == np_result.shape + np.testing.assert_allclose(tonumpy(mag_result), np_result) + +def test_matmul_x_transposed(): + shape_a = (4, 2) + shape_b = (4, 4) + mag_a = Tensor.uniform(shape_a) + mag_b = Tensor.uniform(shape_b) + np_a = tonumpy(mag_a) + np_b = tonumpy(mag_b) + mag_result = mag_a.T @ mag_b + np_result = np.matmul(np_a.T, np_b) + assert mag_result.shape == np_result.shape + assert mag_result.shape == (2, 4) + np.testing.assert_allclose(tonumpy(mag_result), np_result) + +def test_matmul_y_transposed(): + shape_a = (4, 2) + shape_b = (4, 4) + mag_a = Tensor.uniform(shape_a) + mag_b = Tensor.uniform(shape_b) + np_a = tonumpy(mag_a) + np_b = tonumpy(mag_b) + mag_result = mag_a @ mag_b.T + np_result = np.matmul(np_a, np_b.T) + assert mag_result.shape == np_result.shape + assert mag_result.shape == (2, 4) + np.testing.assert_allclose(tonumpy(mag_result), np_result) diff --git a/test/unit/tensor_ops_1_cpu.cpp b/test/unit/tensor_ops_1_cpu.cpp index 2628937..da65696 100644 --- a/test/unit/tensor_ops_1_cpu.cpp +++ b/test/unit/tensor_ops_1_cpu.cpp @@ -123,6 +123,10 @@ impl_test_unary_op(step, 1e-9, step, [](float x) -> float { return x >= 0.0f ? 1.0f : 0.0f; }) +impl_test_unary_op(exp, 1e-6, exp, [](float x) -> float { + return std::exp(x); +}) + impl_test_unary_op(softmax, 1e-6, softmax, [](float x) -> float { return std::exp(x); }) @@ -392,6 +396,29 @@ impl_test_binary_op(div_f32, div, /) #undef impl_test_binary_op +TEST(compute_cpu, pows) { + mag_ctx_t* ctx = mag_ctx_create(MAG_COMPUTE_DEVICE_TYPE_CPU); + for (std::int64_t i0=1; i0 <= k_lim_same_shape; ++i0) + for (std::int64_t i1=1; i1 <= k_lim_same_shape; ++i1) + for (std::int64_t i2=1; i2 <= k_lim_same_shape; ++i2) + for (std::int64_t i3=1; i3 <= k_lim_same_shape; ++i3) + for (std::int64_t i4=1; i4 <= k_lim_same_shape; ++i4) + for (std::int64_t i5=1; i5 <= k_lim_same_shape; ++i5) { + mag_tensor_t* x = mag_tensor_create_6d(ctx, MAG_DTYPE_F32, i0, i1, i2, i3, i4, i5); + mag_tensor_fill_random_uniform(x, 0.0f, 1.0f); + mag_tensor_t* r = mag_pows(x, static_cast(i0+i1+i2+i3+i4+i5)*0.221f); + const auto* b_x = static_cast(mag_tensor_data_ptr(x)); + const auto* b_r = static_cast(mag_tensor_data_ptr(r)); + ASSERT_NE(mag_tensor_data_ptr(r), mag_tensor_data_ptr(x)); + for (std::int64_t i=0; i < mag_tensor_numel(x); ++i) { + ASSERT_FLOAT_EQ(b_r[i], std::pow(b_x[i], (static_cast(i0+i1+i2+i3+i4+i5)*0.221f))); + } + mag_tensor_decref(r); + mag_tensor_decref(x); + } + mag_ctx_destroy(ctx); +} + TEST(compute_cpu, arithmetic_mean) { mag_ctx_t* ctx = mag_ctx_create(MAG_COMPUTE_DEVICE_TYPE_CPU); mag_tensor_t* A = mag_tensor_create_4d(ctx, MAG_DTYPE_F32, 4, 1, 3, 2); diff --git a/test/unit/tensor_ops_mm_cpu.cpp b/test/unit/tensor_ops_mm_cpu.cpp index 0bb8a55..04a5b7f 100644 --- a/test/unit/tensor_ops_mm_cpu.cpp +++ b/test/unit/tensor_ops_mm_cpu.cpp @@ -115,6 +115,7 @@ TEST(compute_cpu, mm_square_2x2_transpose_x) { mag_ctx_destroy(ctx); } +#if 0 TEST(compute_cpu, mm_square_2x2_transpose_y) { mag_ctx_t* ctx = mag_ctx_create(MAG_COMPUTE_DEVICE_TYPE_CPU); @@ -151,6 +152,7 @@ TEST(compute_cpu, mm_square_2x2_transpose_y) { mag_ctx_destroy(ctx); } +#endif TEST(compute_cpu, mm_rect_2x3_3x4) { mag_ctx_t* ctx = mag_ctx_create(MAG_COMPUTE_DEVICE_TYPE_CPU); From 966311d64d78de461b942f0f61b2e36ddfe0336e Mon Sep 17 00:00:00 2001 From: "Mario Sieg (Neo)" Date: Thu, 30 Jan 2025 15:54:26 +0100 Subject: [PATCH 09/24] base autograd --- magnetron/magnetron.c | 299 +++++++++++++------ magnetron/magnetron.h | 7 + magnetron/magnetron_cpu.c | 30 +- magnetron/magnetron_internal.h | 8 +- python/magnetron_framework/magnetron/core.py | 4 +- test/unit/autograd.cpp | 91 ++++++ 6 files changed, 328 insertions(+), 111 deletions(-) create mode 100644 test/unit/autograd.cpp diff --git a/magnetron/magnetron.c b/magnetron/magnetron.c index ce9d009..4a90828 100644 --- a/magnetron/magnetron.c +++ b/magnetron/magnetron.c @@ -516,6 +516,7 @@ mag_ctx_t* mag_ctx_create2(const mag_device_descriptor_t* device_info) { mag_fixed_intrusive_pool_init(&ctx->tensor_pool, sizeof(mag_tensor_t), __alignof(mag_tensor_t), 4096); ctx->tr_id = mag_thread_id(); /* Get thread ID. */ + ctx->is_grad_recording = true; /* Enable gradient recording by default. */ /* Query and print host system information. */ mag_system_host_info_query(ctx); @@ -572,6 +573,14 @@ void mag_ctx_destroy(mag_ctx_t* ctx) { mag_log_info("magnetron context destroyed."); } +bool mag_ctx_is_grad_recorder_enabled(const mag_ctx_t* ctx) { + return ctx->is_grad_recording; +} + +void mag_ctx_enable_grad_recorder(mag_ctx_t* ctx, bool enabled) { + ctx->is_grad_recording = enabled; +} + mag_exec_mode_t mag_ctx_get_exec_mode(const mag_ctx_t* ctx) { return ctx->exec_mode; } void mag_ctx_set_exec_mode(mag_ctx_t* ctx, mag_exec_mode_t mode) { @@ -738,9 +747,9 @@ MAG_COLDPROC void mag_fixed_intrusive_pool_print_info(mag_fixed_intrusive_pool* } void mag_ctx_profile_start_recording(mag_ctx_t* ctx) { - if (ctx->profiler_enabled) return; + if (ctx->is_profiling) return; memset(ctx->op_perf_mons_total, 0, sizeof(ctx->op_perf_mons_total)); - ctx->profiler_enabled = true; + ctx->is_profiling = true; } typedef struct mag_op_perf_record_t { @@ -757,8 +766,8 @@ static int mag_cmp_perf_info(const void* x, const void* y) { } void mag_ctx_profile_stop_recording(mag_ctx_t* ctx, const char* export_csv_file) { - mag_assert(ctx->profiler_enabled, "Profiler must be enabled to generate report"); - ctx->profiler_enabled = false; + mag_assert(ctx->is_profiling, "Profiler must be enabled to generate report"); + ctx->is_profiling = false; bool csv = export_csv_file && *export_csv_file; if (!csv) { mag_print_separator(stdout); @@ -1130,6 +1139,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = false, + .backward = false, .r_alloc = NULL, .validator = NULL }, @@ -1139,6 +1149,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = false, + .backward = false, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1148,6 +1159,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = false, + .backward = false, .r_alloc = &mag_result_constructor_routine_view, .validator = &mag_validate_op_unary }, @@ -1157,6 +1169,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = false, + .backward = false, .r_alloc = &mag_result_constructor_routine_transposed, .validator = &mag_validate_op_transpose }, @@ -1173,6 +1186,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { MAG_OP_TPARAM_U32, }, .inplace = false, + .backward = true, .r_alloc = &mag_result_constructor_routine_permuted, .validator = &mag_validate_op_transpose }, @@ -1182,6 +1196,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = false, + .backward = true, .r_alloc = &mag_result_constructor_routine_scalar, .validator = &mag_validate_op_scalar }, @@ -1191,6 +1206,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = false, + .backward = true, .r_alloc = &mag_result_constructor_routine_scalar, .validator = &mag_validate_op_scalar }, @@ -1200,6 +1216,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = false, + .backward = true, .r_alloc = &mag_result_constructor_routine_scalar, .validator = &mag_validate_op_scalar }, @@ -1209,6 +1226,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = false, + .backward = true, .r_alloc = &mag_result_constructor_routine_scalar, .validator = &mag_validate_op_scalar }, @@ -1218,6 +1236,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1227,6 +1246,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1236,6 +1256,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1245,6 +1266,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1254,6 +1276,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1263,6 +1286,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1272,6 +1296,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1281,6 +1306,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1290,6 +1316,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1299,6 +1326,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1308,6 +1336,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1317,6 +1346,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1326,6 +1356,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1344,6 +1375,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1353,6 +1385,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1362,6 +1395,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1371,6 +1405,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1380,6 +1415,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1389,6 +1425,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1398,6 +1435,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1407,6 +1445,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1416,6 +1455,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_binary }, @@ -1425,6 +1465,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_binary }, @@ -1434,6 +1475,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_binary }, @@ -1443,6 +1485,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {MAG_OP_TPARAM_NONE}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_binary }, @@ -1452,6 +1495,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 1, .param_types = {MAG_OP_TPARAM_F32}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1461,6 +1505,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 1, .param_types = {MAG_OP_TPARAM_F32}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1470,6 +1515,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 1, .param_types = {MAG_OP_TPARAM_F32}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1479,6 +1525,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 1, .param_types = {MAG_OP_TPARAM_F32}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1488,6 +1535,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 1, .param_types = {MAG_OP_TPARAM_F32}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_isomorph, .validator = &mag_validate_op_unary }, @@ -1497,6 +1545,7 @@ const mag_op_meta_t* mag_op_meta_of(mag_op_t type) { .paramcount = 0, .param_types = {}, .inplace = true, + .backward = true, .r_alloc = &mag_result_constructor_routine_matmul, .validator = &mag_validate_op_matmul } @@ -1556,7 +1605,7 @@ static mag_tensor_t* mag_tensor_create(mag_ctx_t* ctx, mag_dtype_t type, const i .dtype = type, .storage = {0}, .numel = numel, - .flags = view ? MAG_TFLAG_VIEW : MAG_TFLAG_OWNER, + .flags = MAG_TFLAG_REQUIRES_GRAD | (view ? MAG_TFLAG_VIEW : MAG_TFLAG_OWNER), .op = MAG_OP_NOP, .op_inputs = {0}, .op_params = {{0}}, @@ -1629,10 +1678,9 @@ void mag_tensor_incref(mag_tensor_t* t) { } bool mag_tensor_decref(mag_tensor_t* t) { - if (t->view_uplink) { /* If tensor is a view, decrement base RC and free tensor chain */ - mag_tensor_decref(t->view_uplink); - } - if (!--t->rcb.rc_strong) { /* Strong RC reaches zero, destroy. */ + if (t->view_uplink) mag_tensor_decref(t->view_uplink); /* If tensor is a view, decrement base RC and free tensor chain */ + if (t->grad) mag_tensor_decref(t->grad); /* Decrement gradient tensor RC */ + if (!--t->rcb.rc_strong) { /* Strong RC reaches zero, destroy. */ mag_tensor_destroy(t); return true; } @@ -1667,10 +1715,10 @@ static void MAG_HOTPROC mag_op_exec(mag_tensor_t* R, mag_compute_device_t* dvc, mag_perf_mon_t* pmon = &R->pmon; mag_op_perf_info_t (*pmon_ops)[MAG_OP__NUM] = &R->ctx->op_perf_mons_total; mag_op_perf_info_t* pmon_op = (*pmon_ops)+R->op; - uint64_t start = R->ctx->profiler_enabled ? mag_hpc_clock_ns() : 0; /* Profiling monitoring */ + uint64_t start = R->ctx->is_profiling ? mag_hpc_clock_ns() : 0; /* Profiling monitoring */ void (*exec)(mag_compute_device_t*, mag_tensor_t*) = ord == MAG_GRAPH_EVAL_ORDER_FORWARD ? dvc->eager_exec_fwd : dvc->eager_exec_bwd; (*exec)(dvc, R); /* Dispatch to backend. */ - if (!R->ctx->profiler_enabled) return; /* Profiling disabled. */ + if (!R->ctx->is_profiling) return; /* Profiling disabled. */ pmon->elapsed_ns = mag_hpc_clock_elapsed_ns(start); pmon->elapsed_ns_acc += pmon->elapsed_ns; pmon_op->elapsed_ns_acc += pmon->elapsed_ns; @@ -1685,31 +1733,25 @@ static mag_tensor_t* MAG_HOTPROC mag_tensor_operator( mag_tensor_t** inputs, uint32_t numin, const mag_op_param_t* params, - uint32_t numparams + uint32_t numparams, + mag_graph_eval_order_t gra ) { /* Validate inputs and params first */ mag_assert2(op != MAG_OP_NOP); mag_assert(inputs && mag_check_are_inputs_valid(op, inputs, numin), "Invalid input tensors for operation %s.", mag_op_meta_of(op)->mnemonic); mag_assert(mag_check_are_op_params_valid(op, params, numparams), "Invalid parameters for operation %s.", mag_op_meta_of(op)->mnemonic); - const mag_op_meta_t* meta = mag_op_meta_of(op); - mag_graph_eval_order_t gra = MAG_GRA_FWD; /* TODO */ mag_tensor_t* (*r_alloc)(mag_tensor_t**, const mag_op_param_t*) = meta->r_alloc; bool (*validate_op)(mag_op_t, mag_tensor_t*, mag_tensor_t**, const mag_op_param_t*) = meta->validator; mag_tensor_t* R = (inplace && numin && meta->inplace) /* Inplace requested? */ ? mag_tensor_create(ctx, (*inputs)->dtype, (*inputs)->shape, (*inputs)->rank, *inputs, 0) /* View R <- X for inplace aliasing op. */ : (*r_alloc)(inputs, params); /* Construct new result tensor. */ if (mag_unlikely(!(*validate_op)(op, R, inputs, params))) return NULL; /* Validation failed. */ - mag_tensor_t* grad = NULL; /* ∇ᵦL = ∂L/∂B - Upper gradient tensor. */ /* TODO */ - if (gra == MAG_GRA_BWD && grad) { - R->grad = R->grad /* ∇ₐL = ∑ᵢ (∂L/∂Bᵢ) ⋅ (∂Bᵢ/∂A) - Chain rule accumulate. */ - ? mag_add(R->grad, grad) /* ∇ₐL <- ∇ₐL + ∇ᵦL */ - : mag_clone(grad); /* ∇ₐL <- ∂L/∂B */ - } - mag_assert2(R->op == MAG_OP_NOP); R->op = op; /* Set operation for deferred execution mode. */ for (uint32_t i=0; i < numin; ++i) { /* Set input tensors and flags. */ - R->op_inputs[i] = inputs[i]; + mag_tensor_t* input = inputs[i]; + R->op_inputs[i] = input; + R->flags |= input->flags & MAG_TFLAG_REQUIRES_GRAD; } if (params) memcpy(R->op_params, params, sizeof(*params)); /* Copy operation parameters */ if (ctx->exec_mode == MAG_EXEC_MODE_EAGER) { /* In eager execution mode, we execute immediately. */ @@ -1719,15 +1761,15 @@ static mag_tensor_t* MAG_HOTPROC mag_tensor_operator( } mag_tensor_t* mag_clone(mag_tensor_t* x) { - return mag_tensor_operator(x->ctx, MAG_OP_CLONE, false, &x, 1, NULL, 0); + return mag_tensor_operator(x->ctx, MAG_OP_CLONE, false, &x, 1, NULL, 0, MAG_GRA_FWD); } mag_tensor_t* mag_view(mag_tensor_t* x) { - return mag_tensor_operator(x->ctx, MAG_OP_VIEW, false, &x, 1, NULL, 0); + return mag_tensor_operator(x->ctx, MAG_OP_VIEW, false, &x, 1, NULL, 0, MAG_GRA_FWD); } mag_tensor_t* mag_transpose(mag_tensor_t* x) { - return mag_tensor_operator(x->ctx, MAG_OP_TRANSPOSE, false, &x, 1, NULL, 0); + return mag_tensor_operator(x->ctx, MAG_OP_TRANSPOSE, false, &x, 1, NULL, 0, MAG_GRA_FWD); } mag_tensor_t* mag_permute(mag_tensor_t* x, uint32_t d0, uint32_t d1, uint32_t d2, uint32_t d3, uint32_t d4, uint32_t d5) { @@ -1739,118 +1781,118 @@ mag_tensor_t* mag_permute(mag_tensor_t* x, uint32_t d0, uint32_t d1, uint32_t d2 {.type=MAG_OP_TPARAM_U32, .x.u32=d4}, {.type=MAG_OP_TPARAM_U32, .x.u32=d5} }; - return mag_tensor_operator(x->ctx, MAG_OP_PERMUTE, false, &x, 1, params, sizeof(params)/sizeof(*params)); -} - -mag_tensor_t* mag_mean(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_MEAN, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_min(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_MIN, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_max(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_MAX, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_sum(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SUM, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_abs(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_ABS, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_abs_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_ABS, true, &x, 1, NULL, 0); } -mag_tensor_t* mag_neg(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_NEG, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_neg_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_NEG, true, &x, 1, NULL, 0); } -mag_tensor_t* mag_log(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_LOG, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_log_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_LOG, true, &x, 1, NULL, 0); } -mag_tensor_t* mag_sqr(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SQR, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_sqr_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SQR, true, &x, 1, NULL, 0); } -mag_tensor_t* mag_sqrt(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SQRT, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_sqrt_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SQRT, true, &x, 1, NULL, 0); } -mag_tensor_t* mag_sin(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SIN, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_sin_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SIN, true, &x, 1, NULL, 0); } -mag_tensor_t* mag_cos(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_COS, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_cos_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_COS, true, &x, 1, NULL, 0); } -mag_tensor_t* mag_step(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_STEP, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_step_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_STEP, true, &x, 1, NULL, 0); } -mag_tensor_t* mag_exp(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_EXP, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_exp_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_EXP, true, &x, 1, NULL, 0); } -mag_tensor_t* mag_softmax(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SOFTMAX, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_softmax_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SOFTMAX, true, &x, 1, NULL, 0); } -mag_tensor_t* mag_softmax_dv(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SOFTMAX_DV, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_softmax_dv_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SOFTMAX_DV, true, &x, 1, NULL, 0); } -mag_tensor_t* mag_sigmoid(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SIGMOID, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_sigmoid_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SIGMOID, true, &x, 1, NULL, 0); } -mag_tensor_t* mag_sigmoid_dv(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SIGMOID_DV, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_sigmoid_dv_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SIGMOID_DV, true, &x, 1, NULL, 0); } -mag_tensor_t* mag_hard_sigmoid(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_HARD_SIGMOID, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_hard_sigmoid_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_HARD_SIGMOID, true, &x, 1, NULL, 0); } -mag_tensor_t* mag_silu(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SILU, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_silu_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SILU, true, &x, 1, NULL, 0); } -mag_tensor_t* mag_silu_dv(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SILU_DV, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_silu_dv_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SILU_DV, true, &x, 1, NULL, 0); } -mag_tensor_t* mag_tanh(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_TANH, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_tanh_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_TANH, true, &x, 1, NULL, 0); } -mag_tensor_t* mag_tanh_dv(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_TANH_DV, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_tanh_dv_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_TANH_DV, true, &x, 1, NULL, 0); } -mag_tensor_t* mag_relu(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_RELU, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_relu_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_RELU, true, &x, 1, NULL, 0); } -mag_tensor_t* mag_relu_dv(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_RELU_DV, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_relu_dv_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_RELU_DV, true, &x, 1, NULL, 0); } -mag_tensor_t* mag_gelu(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_GELU, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_gelu_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_GELU, true, &x, 1, NULL, 0); } -mag_tensor_t* mag_gelu_dv(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_GELU_DV, false, &x, 1, NULL, 0); } -mag_tensor_t* mag_gelu_dv_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_GELU_DV, true, &x, 1, NULL, 0); } -mag_tensor_t* mag_add(mag_tensor_t* x, mag_tensor_t* y) { return mag_tensor_operator(x->ctx, MAG_OP_ADD, false, (mag_tensor_t*[]){x, y}, 2, NULL, 0); } -mag_tensor_t* mag_add_(mag_tensor_t* x, mag_tensor_t* y) { return mag_tensor_operator(x->ctx, MAG_OP_ADD, true, (mag_tensor_t*[]){x, y}, 2, NULL, 0); } -mag_tensor_t* mag_sub(mag_tensor_t* x, mag_tensor_t* y) { return mag_tensor_operator(x->ctx, MAG_OP_SUB, false, (mag_tensor_t*[]){x, y}, 2, NULL, 0); } -mag_tensor_t* mag_sub_(mag_tensor_t* x, mag_tensor_t* y) { return mag_tensor_operator(x->ctx, MAG_OP_SUB, true, (mag_tensor_t*[]){x, y}, 2, NULL, 0); } -mag_tensor_t* mag_mul(mag_tensor_t* x, mag_tensor_t* y) { return mag_tensor_operator(x->ctx, MAG_OP_MUL, false, (mag_tensor_t*[]){x, y}, 2, NULL, 0); } -mag_tensor_t* mag_mul_(mag_tensor_t* x, mag_tensor_t* y) { return mag_tensor_operator(x->ctx, MAG_OP_MUL, true, (mag_tensor_t*[]){x, y}, 2, NULL, 0); } -mag_tensor_t* mag_div(mag_tensor_t* x, mag_tensor_t* y) { return mag_tensor_operator(x->ctx, MAG_OP_DIV, false, (mag_tensor_t*[]){x, y}, 2, NULL, 0); } -mag_tensor_t* mag_div_(mag_tensor_t* x, mag_tensor_t* y) { return mag_tensor_operator(x->ctx, MAG_OP_DIV, true, (mag_tensor_t*[]){x, y}, 2, NULL, 0); } + return mag_tensor_operator(x->ctx, MAG_OP_PERMUTE, false, &x, 1, params, sizeof(params)/sizeof(*params), MAG_GRA_FWD); +} + +mag_tensor_t* mag_mean(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_MEAN, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_min(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_MIN, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_max(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_MAX, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_sum(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SUM, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_abs(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_ABS, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_abs_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_ABS, true, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_neg(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_NEG, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_neg_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_NEG, true, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_log(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_LOG, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_log_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_LOG, true, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_sqr(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SQR, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_sqr_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SQR, true, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_sqrt(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SQRT, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_sqrt_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SQRT, true, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_sin(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SIN, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_sin_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SIN, true, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_cos(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_COS, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_cos_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_COS, true, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_step(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_STEP, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_step_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_STEP, true, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_exp(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_EXP, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_exp_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_EXP, true, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_softmax(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SOFTMAX, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_softmax_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SOFTMAX, true, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_softmax_dv(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SOFTMAX_DV, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_softmax_dv_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SOFTMAX_DV, true, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_sigmoid(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SIGMOID, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_sigmoid_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SIGMOID, true, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_sigmoid_dv(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SIGMOID_DV, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_sigmoid_dv_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SIGMOID_DV, true, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_hard_sigmoid(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_HARD_SIGMOID, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_hard_sigmoid_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_HARD_SIGMOID, true, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_silu(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SILU, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_silu_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SILU, true, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_silu_dv(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SILU_DV, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_silu_dv_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_SILU_DV, true, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_tanh(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_TANH, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_tanh_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_TANH, true, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_tanh_dv(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_TANH_DV, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_tanh_dv_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_TANH_DV, true, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_relu(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_RELU, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_relu_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_RELU, true, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_relu_dv(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_RELU_DV, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_relu_dv_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_RELU_DV, true, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_gelu(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_GELU, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_gelu_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_GELU, true, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_gelu_dv(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_GELU_DV, false, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_gelu_dv_(mag_tensor_t* x) { return mag_tensor_operator(x->ctx, MAG_OP_GELU_DV, true, &x, 1, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_add(mag_tensor_t* x, mag_tensor_t* y) { return mag_tensor_operator(x->ctx, MAG_OP_ADD, false, (mag_tensor_t*[]){x, y}, 2, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_add_(mag_tensor_t* x, mag_tensor_t* y) { return mag_tensor_operator(x->ctx, MAG_OP_ADD, true, (mag_tensor_t*[]){x, y}, 2, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_sub(mag_tensor_t* x, mag_tensor_t* y) { return mag_tensor_operator(x->ctx, MAG_OP_SUB, false, (mag_tensor_t*[]){x, y}, 2, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_sub_(mag_tensor_t* x, mag_tensor_t* y) { return mag_tensor_operator(x->ctx, MAG_OP_SUB, true, (mag_tensor_t*[]){x, y}, 2, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_mul(mag_tensor_t* x, mag_tensor_t* y) { return mag_tensor_operator(x->ctx, MAG_OP_MUL, false, (mag_tensor_t*[]){x, y}, 2, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_mul_(mag_tensor_t* x, mag_tensor_t* y) { return mag_tensor_operator(x->ctx, MAG_OP_MUL, true, (mag_tensor_t*[]){x, y}, 2, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_div(mag_tensor_t* x, mag_tensor_t* y) { return mag_tensor_operator(x->ctx, MAG_OP_DIV, false, (mag_tensor_t*[]){x, y}, 2, NULL, 0, MAG_GRA_FWD); } +mag_tensor_t* mag_div_(mag_tensor_t* x, mag_tensor_t* y) { return mag_tensor_operator(x->ctx, MAG_OP_DIV, true, (mag_tensor_t*[]){x, y}, 2, NULL, 0, MAG_GRA_FWD); } mag_tensor_t* mag_adds(mag_tensor_t* x, float xi) { mag_op_param_t param = {.type=MAG_OP_TPARAM_F32, .x.f32=xi}; - return mag_tensor_operator(x->ctx, MAG_OP_ADDS, false, &x, 1, ¶m, 1); + return mag_tensor_operator(x->ctx, MAG_OP_ADDS, false, &x, 1, ¶m, 1, MAG_GRA_FWD); } mag_tensor_t* mag_adds_(mag_tensor_t* x, float xi) { mag_op_param_t param = {.type=MAG_OP_TPARAM_F32, .x.f32=xi}; - return mag_tensor_operator(x->ctx, MAG_OP_ADDS, true, &x, 1, ¶m, 1); + return mag_tensor_operator(x->ctx, MAG_OP_ADDS, true, &x, 1, ¶m, 1, MAG_GRA_FWD); } mag_tensor_t* mag_subs(mag_tensor_t* x, float xi) { mag_op_param_t param = {.type=MAG_OP_TPARAM_F32, .x.f32=xi}; - return mag_tensor_operator(x->ctx, MAG_OP_SUBS, false, &x, 1, ¶m, 1); + return mag_tensor_operator(x->ctx, MAG_OP_SUBS, false, &x, 1, ¶m, 1, MAG_GRA_FWD); } mag_tensor_t* mag_subs_(mag_tensor_t* x, float xi) { mag_op_param_t param = {.type=MAG_OP_TPARAM_F32, .x.f32=xi}; - return mag_tensor_operator(x->ctx, MAG_OP_SUBS, true, &x, 1, ¶m, 1); + return mag_tensor_operator(x->ctx, MAG_OP_SUBS, true, &x, 1, ¶m, 1, MAG_GRA_FWD); } mag_tensor_t* mag_muls(mag_tensor_t* x, float xi) { mag_op_param_t param = {.type=MAG_OP_TPARAM_F32, .x.f32=xi}; - return mag_tensor_operator(x->ctx, MAG_OP_MULS, false, &x, 1, ¶m, 1); + return mag_tensor_operator(x->ctx, MAG_OP_MULS, false, &x, 1, ¶m, 1, MAG_GRA_FWD); } mag_tensor_t* mag_muls_(mag_tensor_t* x, float xi) { mag_op_param_t param = {.type=MAG_OP_TPARAM_F32, .x.f32=xi}; - return mag_tensor_operator(x->ctx, MAG_OP_MULS, true, &x, 1, ¶m, 1); + return mag_tensor_operator(x->ctx, MAG_OP_MULS, true, &x, 1, ¶m, 1, MAG_GRA_FWD); } mag_tensor_t* mag_divs(mag_tensor_t* x, float xi) { mag_op_param_t param = {.type=MAG_OP_TPARAM_F32, .x.f32=xi}; - return mag_tensor_operator(x->ctx, MAG_OP_DIVS, false, &x, 1, ¶m, 1); + return mag_tensor_operator(x->ctx, MAG_OP_DIVS, false, &x, 1, ¶m, 1, MAG_GRA_FWD); } mag_tensor_t* mag_divs_(mag_tensor_t* x, float xi) { mag_op_param_t param = {.type=MAG_OP_TPARAM_F32, .x.f32=xi}; - return mag_tensor_operator(x->ctx, MAG_OP_DIVS, true, &x, 1, ¶m, 1); + return mag_tensor_operator(x->ctx, MAG_OP_DIVS, true, &x, 1, ¶m, 1, MAG_GRA_FWD); } mag_tensor_t* mag_pows(mag_tensor_t* x, float xi) { mag_op_param_t param = {.type=MAG_OP_TPARAM_F32, .x.f32=xi}; - return mag_tensor_operator(x->ctx, MAG_OP_POWS, false, &x, 1, ¶m, 1); + return mag_tensor_operator(x->ctx, MAG_OP_POWS, false, &x, 1, ¶m, 1, MAG_GRA_FWD); } mag_tensor_t* mag_pows_(mag_tensor_t* x, float xi) { mag_op_param_t param = {.type=MAG_OP_TPARAM_F32, .x.f32=xi}; - return mag_tensor_operator(x->ctx, MAG_OP_POWS, true, &x, 1, ¶m, 1); + return mag_tensor_operator(x->ctx, MAG_OP_POWS, true, &x, 1, ¶m, 1, MAG_GRA_FWD); } mag_tensor_t* mag_matmul(mag_tensor_t* x, mag_tensor_t* y) { - return mag_tensor_operator(x->ctx, MAG_OP_MATMUL, false, (mag_tensor_t*[]){x, y}, 2, NULL, 0); + return mag_tensor_operator(x->ctx, MAG_OP_MATMUL, false, (mag_tensor_t*[]){x, y}, 2, NULL, 0, MAG_GRA_FWD); } static MAG_AINLINE void mag_tensor_virtual_to_physical_index(const mag_tensor_t* t, int64_t v_idx, int64_t(*p_idx)[MAG_MAX_DIMS]) { @@ -2115,6 +2157,73 @@ bool mag_tensor_is_contiguous(const mag_tensor_t* t) { return *t->strides == 1; } +mag_tensor_t* mag_tensor_grad(const mag_tensor_t* t) { + return t->grad; +} + +void mag_tensor_required_grad(mag_tensor_t* t, bool requires_grad) { + if (requires_grad) t->flags |= MAG_TFLAG_REQUIRES_GRAD; + else t->flags &= ~MAG_TFLAG_REQUIRES_GRAD; +} + +void mag_tensor_backward(mag_tensor_t* t) { + if (!(t->flags & MAG_TFLAG_REQUIRES_GRAD)) return; + if (!t->grad) { /* Lazily allocate gradient and set ∂L/∂L = 1.0 */ + mag_tensor_t* grad = mag_tensor_create(t->ctx, t->dtype, t->shape, t->rank, NULL, 0); + grad->flags |= MAG_TFLAG_IS_GRAD; + mag_tensor_fill(grad, 1.0f); + t->grad = grad; + } + if (mag_unlikely(t->op == MAG_OP_NOP)) return; + const mag_op_meta_t* meta = mag_op_meta_of(t->op); + + mag_tensor_t* x = t->op_inputs[0]; + mag_tensor_t* y = t->op_inputs[1]; + mag_tensor_t* gx = NULL; + mag_tensor_t* gy = NULL; + mag_tensor_t* gout = t->grad; + + switch (t->op) { + case MAG_OP_RELU: { + mag_tensor_t* mask = mag_step(x); + gx = mag_mul(gout, mask); + mag_tensor_decref(mask); + } break; + case MAG_OP_ADD: + gx = mag_clone(gout); + gy = mag_clone(gout); + break; + case MAG_OP_SUB: + gx = mag_clone(gout); + gy = mag_neg(gout); + break; + case MAG_OP_MUL: + gx = mag_mul(gout, y); + gy = mag_mul(gout, x); + break; + case MAG_OP_DIV: + gx = mag_div(gout, y); + gy = mag_neg(mag_div(mag_mul(gout, x), mag_mul(y, y))); + break; + default: mag_panic("fuck"); + } + for (uint32_t i = 0; i < meta->argcount; ++i) { + mag_tensor_t* input = t->op_inputs[i]; + if (!(input->flags & MAG_TFLAG_REQUIRES_GRAD)) continue; + mag_tensor_t* grad_input = !i ? gx : gy; + mag_assert2(grad_input); + if (!input->grad) { + input->grad = grad_input; + } else { + mag_tensor_t* acc = mag_add(input->grad, grad_input); + mag_tensor_decref(input->grad); + input->grad = acc; + mag_tensor_decref(grad_input); + } + mag_tensor_backward(input); + } +} + float mag_tensor_get_scalar_physical_index(mag_tensor_t* t, int64_t d0, int64_t d1, int64_t d2, int64_t d3, int64_t d4, int64_t d5) { mag_static_assert(MAG_MAX_DIMS == 6); mag_load_local_storage_group(t, s, strides); diff --git a/magnetron/magnetron.h b/magnetron/magnetron.h index 262f05a..b7fd606 100644 --- a/magnetron/magnetron.h +++ b/magnetron/magnetron.h @@ -96,6 +96,8 @@ typedef struct mag_device_descriptor_t { extern MAG_EXPORT mag_ctx_t* mag_ctx_create(mag_compute_device_type_t device); /* Create context with default config, and only specify device type. */ extern MAG_EXPORT mag_ctx_t* mag_ctx_create2(const mag_device_descriptor_t* device_info); /* Create context with customized device config, and only specify device type. */ +extern MAG_EXPORT bool mag_ctx_is_grad_recorder_enabled(const mag_ctx_t* ctx); /* Check if gradient recording is enabled */ +extern MAG_EXPORT void mag_ctx_enable_grad_recorder(mag_ctx_t* ctx, bool enabled); /* Enable/disable gradient recording */ extern MAG_EXPORT mag_exec_mode_t mag_ctx_get_exec_mode(const mag_ctx_t* ctx); /* Get execution mode */ extern MAG_EXPORT void mag_ctx_set_exec_mode(mag_ctx_t* ctx, mag_exec_mode_t mode); /* Set execution mode */ extern MAG_EXPORT mag_prng_algorithm_t mag_ctx_get_prng_algorithm(const mag_ctx_t* ctx); /* Get PRNG algorithm */ @@ -349,6 +351,11 @@ extern MAG_EXPORT bool mag_tensor_can_broadcast(const mag_tensor_t* a, const mag extern MAG_EXPORT bool mag_tensor_is_transposed(const mag_tensor_t* t); /* Check if the tensor is transposed */ extern MAG_EXPORT bool mag_tensor_is_permuted(const mag_tensor_t* t); /* Check if the tensor is permuted */ extern MAG_EXPORT bool mag_tensor_is_contiguous(const mag_tensor_t* t); /* Check if the tensor memory is contiguous */ + +extern MAG_EXPORT mag_tensor_t* mag_tensor_grad(const mag_tensor_t* t); /* Get the gradient tensor of the tensor */ +extern MAG_EXPORT void mag_tensor_required_grad(mag_tensor_t* t, bool requires_grad); /* Set if the tensor requires gradient computation */ +extern MAG_EXPORT void mag_tensor_backward(mag_tensor_t* t); /* Compute the gradient of the tensor */ + extern MAG_EXPORT float mag_tensor_get_scalar_physical_index(mag_tensor_t* t, int64_t d0, int64_t d1, int64_t d2, int64_t d3, int64_t d4, int64_t d5); /* Get scalar value at physical index */ extern MAG_EXPORT void mag_tensor_set_scalar_physical_index(mag_tensor_t* t, int64_t d0, int64_t d1, int64_t d2, int64_t d3, int64_t d4, int64_t d5, float x); /* Set scalar value at physical index */ extern MAG_EXPORT float mag_tensor_get_scalar_virtual_index(mag_tensor_t* t, int64_t v_idx); /* Get scalar value at virtual index */ diff --git a/magnetron/magnetron_cpu.c b/magnetron/magnetron_cpu.c index ac16892..55afc7e 100644 --- a/magnetron/magnetron_cpu.c +++ b/magnetron/magnetron_cpu.c @@ -208,7 +208,9 @@ static bool mag_worker_await_work(mag_worker_t* worker, mag_threadpool_t* pool) /* Execute the operation on the current thread */ static void mag_worker_exec_thread_local(const mag_kernel_registry_t* kernels, mag_compute_payload_t* payload) { if (mag_likely(payload->node)) { /* Do the work 🦾 */ - (*kernels->fwd[payload->node->op])(payload); + mag_op_t op = payload->node->op; + void (*kernel)(const mag_compute_payload_t*) = payload->is_fwd ? kernels->fwd[op] : kernels->bwd[op]; + (*kernel)(payload); payload->node = NULL; } } @@ -262,7 +264,7 @@ static mag_threadpool_t* mag_threadpool_create(uint32_t num_workers, const mag_k for (uint32_t ti=0; ti < num_workers; ++ti) { /* Initialize workers */ workers[ti] = (mag_worker_t){ .phase = 0, - .payload = (mag_compute_payload_t){.thread_num = num_workers, .thread_idx = ti, .node = NULL, }, + .payload = (mag_compute_payload_t){.thread_num = num_workers, .thread_idx = ti, .node = NULL, .is_fwd = true }, .pool = pool, .is_async = ti != 0 /* Main thread is worker but without thread */ }; @@ -293,13 +295,14 @@ static void mag_threadpool_destroy(mag_threadpool_t* pool) { } /* Submits work payload and awakens all threads */ -static void mag_threadpool_kickoff(mag_threadpool_t* pool, mag_tensor_t* node, uint32_t num_active_workers) { +static void mag_threadpool_kickoff(mag_threadpool_t* pool, mag_tensor_t* node, bool is_fwd, uint32_t num_active_workers) { mag_mutex_lock(&pool->mtx); pool->num_active_workers = num_active_workers; for (uint32_t i=0; i < pool->num_allocated_workers; ++i) { /* Set up payload */ mag_compute_payload_t* payload = &pool->workers[i].payload; - payload->node = node; payload->thread_num = num_active_workers; + payload->node = node; + payload->is_fwd = is_fwd; } ++pool->phase; pool->num_completed = 0; /* Reset completion counter */ @@ -319,17 +322,17 @@ static void mag_threadpool_barrier(mag_threadpool_t* pool) { } /* Execute an operator tensor on the CPU */ -static MAG_HOTPROC void mag_threadpool_parallel_compute(mag_threadpool_t* pool, mag_tensor_t* node, uint32_t num_active_workers) { +static MAG_HOTPROC void mag_threadpool_parallel_compute(mag_threadpool_t* pool, mag_tensor_t* node, bool is_fwd, uint32_t num_active_workers) { mag_assert2(pool != NULL); - mag_threadpool_kickoff(pool, node, num_active_workers); /* Kick off workers */ - mag_cv_broadcast(&pool->cv); /* Wake up all workers */ + mag_threadpool_kickoff(pool, node, is_fwd, num_active_workers); /* Kick off workers */ + mag_cv_broadcast(&pool->cv); /* Wake up all workers */ mag_worker_exec_and_broadcast(pool, pool->kernels, &pool->workers->payload); /* Main thread does work too */ mag_threadpool_barrier(pool); /* Wait for all workers to finish */ } static uint32_t mag_cpu_dynamic_work_scaling(mag_cpu_device_t* dvc, mag_op_t op, int64_t numel); -static MAG_HOTPROC void mag_cpu_exec_fwd(mag_compute_device_t* dvc, mag_tensor_t* node) { +static MAG_HOTPROC void mag_cpu_exec(mag_compute_device_t* dvc, bool is_fwd, mag_tensor_t* node) { mag_cpu_device_t* cpu_dvc = dvc->impl; uint32_t intraop_workers = mag_cpu_dynamic_work_scaling(cpu_dvc, node->op, node->numel); if (intraop_workers <= 1) { /* Main thread does the work (single threaded mode). */ @@ -341,12 +344,15 @@ static MAG_HOTPROC void mag_cpu_exec_fwd(mag_compute_device_t* dvc, mag_tensor_t mag_worker_exec_thread_local(&cpu_dvc->kernels, &payload); return; /* Done */ } - mag_threadpool_parallel_compute(cpu_dvc->pool, node, intraop_workers); /* Multithreaded mode. */ + mag_threadpool_parallel_compute(cpu_dvc->pool, node, is_fwd, intraop_workers); /* Multithreaded mode. */ +} + +static void mag_cpu_exec_fwd(mag_compute_device_t* dvc, mag_tensor_t* node) { + mag_cpu_exec(dvc, true, node); } -static MAG_HOTPROC void mag_cpu_exec_bwd(mag_compute_device_t* dvc, mag_tensor_t* root) { - (void)dvc, (void)root; - mag_panic("NYI"); +static void mag_cpu_exec_bwd(mag_compute_device_t* dvc, mag_tensor_t* node) { + mag_cpu_exec(dvc, false, node); } static void mag_cpu_buf_set(mag_storage_buffer_t* sto, size_t offs, uint8_t x) { diff --git a/magnetron/magnetron_internal.h b/magnetron/magnetron_internal.h index 181a5d5..ce523f4 100644 --- a/magnetron/magnetron_internal.h +++ b/magnetron/magnetron_internal.h @@ -485,6 +485,7 @@ typedef struct mag_op_meta_t { uint8_t paramcount; /* Number of parameters */ mag_op_param_type_t param_types[MAG_MAX_OP_PARAMS]; /* Parameter types */ bool inplace; /* Supports inplace execution */ + bool backward; /* Supports backward execution */ mag_tensor_t* (*r_alloc)(mag_tensor_t**, const mag_op_param_t*); bool (*validator)(mag_op_t, mag_tensor_t*, mag_tensor_t**, const mag_op_param_t*); } mag_op_meta_t; @@ -686,7 +687,8 @@ struct mag_ctx_t { #endif mag_fixed_intrusive_pool tensor_pool; /* Fixed-size memory pool for tensors. */ mag_exec_mode_t exec_mode; - bool profiler_enabled; + bool is_profiling; + bool is_grad_recording; mag_op_perf_info_t op_perf_mons_total[MAG_OP__NUM]; union { struct { @@ -715,8 +717,9 @@ typedef enum mag_tensor_flags_t { MAG_TFLAG_NONE = 0, MAG_TFLAG_OWNER = 1<<0, /* Tensor is the owner of the buffer. */ MAG_TFLAG_VIEW = 1<<1, /* Tensor is a view. */ - MAG_FLAG_GRAD = 1<<2, /* Tensor is a gradient. */ + MAG_TFLAG_IS_GRAD = 1<<2, /* Tensor is a gradient. */ MAG_TFLAG_EXEC_EAGER = 1<<3, /* Tensor is executed eagerly. */ + MAG_TFLAG_REQUIRES_GRAD = 1<<4, /* Tensor requires gradient. */ MAG_TFLAG_LEN = 4 } mag_tensor_flags_t; @@ -770,6 +773,7 @@ typedef struct mag_compute_payload_t { int64_t thread_num; int64_t thread_idx; mag_tensor_t* node; + bool is_fwd; } mag_compute_payload_t; typedef struct mag_kernel_registry_t { diff --git a/python/magnetron_framework/magnetron/core.py b/python/magnetron_framework/magnetron/core.py index c6b5e95..fca9bb7 100644 --- a/python/magnetron_framework/magnetron/core.py +++ b/python/magnetron_framework/magnetron/core.py @@ -106,11 +106,11 @@ def __init__(self, device: ComputeDevice.CPU | ComputeDevice.CUDA, *, @property def enable_grad_recorder(self) -> bool: - return C.mag_ctx_enable_grad_recorder(self._ptr) + return C.mag_ctx_is_grad_recorder_enabled(self._ptr) @enable_grad_recorder.setter def enable_grad_recorder(self, enable: bool): - C.mag_ctx_set_enable_grad_recorder(self._ptr, enable) + C.mag_ctx_enable_grad_recorder(self._ptr, enable) @property def compute_device_name(self) -> str: diff --git a/test/unit/autograd.cpp b/test/unit/autograd.cpp new file mode 100644 index 0000000..25574aa --- /dev/null +++ b/test/unit/autograd.cpp @@ -0,0 +1,91 @@ +/* (c) 2025 Mario "Neo" Sieg. */ + +#include "prelude.hpp" + +TEST(autograd, bin_ops1) { + /* + * x = 3.0 + * y = 2.0 + * z = (x + y) * (x - y) + * z.backward() + */ + + mag_ctx_t* ctx = mag_ctx_create(MAG_COMPUTE_DEVICE_TYPE_CPU); + + auto* x = mag_tensor_create_1d(ctx, MAG_DTYPE_F32, 1); + mag_tensor_fill(x, 3.0f); + + auto* y = mag_tensor_create_1d(ctx, MAG_DTYPE_F32, 1); + mag_tensor_fill(y, 2.0f); + + auto* k = mag_tensor_create_1d(ctx, MAG_DTYPE_F32, 1); + mag_tensor_fill(k, 10.0f); + + auto* z = mag_div(mag_mul(mag_add(x, y), mag_sub(x, y)), k); + mag_tensor_backward(z); + + // check forward pass + float vx = mag_tensor_get_scalar_virtual_index(x, 0); + float vy = mag_tensor_get_scalar_virtual_index(y, 0); + float vz = mag_tensor_get_scalar_virtual_index(z, 0); + ASSERT_FLOAT_EQ(vx, 3.0f); + ASSERT_FLOAT_EQ(vy, 2.0f); + ASSERT_EQ(vz, 0.5f); + + // check backward pass + float gx = mag_tensor_get_scalar_virtual_index(x->grad, 0); + float gy = mag_tensor_get_scalar_virtual_index(y->grad, 0); + float gz = mag_tensor_get_scalar_virtual_index(z->grad, 0); + + ASSERT_FLOAT_EQ(gx, 0.6f); // ∂z/∂x = 0.6 + ASSERT_FLOAT_EQ(gy, -0.4f); // ∂z/∂y = -0.4 + ASSERT_FLOAT_EQ(gz, 1.0f); // ∂z/∂z = 1.0 + + mag_tensor_decref(x); + mag_tensor_decref(y); + mag_tensor_decref(z); + mag_ctx_destroy(ctx); +} + +TEST(autograd, bin_ops2) { + /* + * x = -4.0 + * z = 2 * x + 2 + x + * q = z.relu() + z * x + * h = (z * z).relu() + * y = h + q + q * x + * y.backward() + */ + + mag_ctx_t* ctx = mag_ctx_create(MAG_COMPUTE_DEVICE_TYPE_CPU); + + auto* two = mag_tensor_create_1d(ctx, MAG_DTYPE_F32, 1); + mag_tensor_fill(two, 2.0f); + + auto* x = mag_tensor_create_1d(ctx, MAG_DTYPE_F32, 1); + mag_tensor_fill(x, -4.0f); + + auto* z = mag_add(mag_add(mag_mul(two, x), two), x); + auto* q = mag_add(mag_relu(z), mag_mul(z, x)); + auto* h = mag_relu(mag_mul(z, z)); + auto* y = mag_add(q, mag_add(mag_mul(q, x), h)); + mag_tensor_backward(y); + + // check forward pass + float vx = mag_tensor_get_scalar_virtual_index(x, 0); + float vy = mag_tensor_get_scalar_virtual_index(y, 0); + ASSERT_FLOAT_EQ(vx, -4.0f); + ASSERT_FLOAT_EQ(vy, -20.0f); + + // check backward pass + float gx = mag_tensor_get_scalar_virtual_index(x->grad, 0); + float gy = mag_tensor_get_scalar_virtual_index(y->grad, 0); + + ASSERT_FLOAT_EQ(gx, 46.0f); // ∂z/∂x = 46.0 + ASSERT_FLOAT_EQ(gy, 1.0f); // ∂z/∂y = 1.0 + + mag_tensor_decref(x); + mag_tensor_decref(y); + mag_tensor_decref(z); + mag_ctx_destroy(ctx); +} From 7040215b5620673bb5155b12bba13f5579d5ec7b Mon Sep 17 00:00:00 2001 From: "Mario Sieg (Neo)" Date: Thu, 30 Jan 2025 17:08:28 +0100 Subject: [PATCH 10/24] mag autograd dfs --- magnetron/magnetron.c | 217 +++++++++++++++++++++++++++++++---------- test/unit/autograd.cpp | 5 + 2 files changed, 169 insertions(+), 53 deletions(-) diff --git a/magnetron/magnetron.c b/magnetron/magnetron.c index 4a90828..331f3fb 100644 --- a/magnetron/magnetron.c +++ b/magnetron/magnetron.c @@ -2166,62 +2166,173 @@ void mag_tensor_required_grad(mag_tensor_t* t, bool requires_grad) { else t->flags &= ~MAG_TFLAG_REQUIRES_GRAD; } -void mag_tensor_backward(mag_tensor_t* t) { - if (!(t->flags & MAG_TFLAG_REQUIRES_GRAD)) return; - if (!t->grad) { /* Lazily allocate gradient and set ∂L/∂L = 1.0 */ - mag_tensor_t* grad = mag_tensor_create(t->ctx, t->dtype, t->shape, t->rank, NULL, 0); - grad->flags |= MAG_TFLAG_IS_GRAD; - mag_tensor_fill(grad, 1.0f); - t->grad = grad; - } - if (mag_unlikely(t->op == MAG_OP_NOP)) return; - const mag_op_meta_t* meta = mag_op_meta_of(t->op); - - mag_tensor_t* x = t->op_inputs[0]; - mag_tensor_t* y = t->op_inputs[1]; - mag_tensor_t* gx = NULL; - mag_tensor_t* gy = NULL; - mag_tensor_t* gout = t->grad; - - switch (t->op) { - case MAG_OP_RELU: { - mag_tensor_t* mask = mag_step(x); - gx = mag_mul(gout, mask); - mag_tensor_decref(mask); - } break; - case MAG_OP_ADD: - gx = mag_clone(gout); - gy = mag_clone(gout); - break; - case MAG_OP_SUB: - gx = mag_clone(gout); - gy = mag_neg(gout); - break; - case MAG_OP_MUL: - gx = mag_mul(gout, y); - gy = mag_mul(gout, x); - break; - case MAG_OP_DIV: - gx = mag_div(gout, y); - gy = mag_neg(mag_div(mag_mul(gout, x), mag_mul(y, y))); - break; - default: mag_panic("fuck"); - } - for (uint32_t i = 0; i < meta->argcount; ++i) { - mag_tensor_t* input = t->op_inputs[i]; - if (!(input->flags & MAG_TFLAG_REQUIRES_GRAD)) continue; - mag_tensor_t* grad_input = !i ? gx : gy; - mag_assert2(grad_input); - if (!input->grad) { - input->grad = grad_input; +typedef struct mag_topo_stack_record_t { + mag_tensor_t* tensor; + uint32_t next_child_idx; +} mag_topo_stack_record_t; + +typedef struct mag_tensor_array_t { + mag_tensor_t** data; + size_t size; + size_t capacity; +} mag_tensor_array_t; + +static void mag_tensor_array_init(mag_tensor_array_t* arr) { + arr->data = NULL; + arr->size = 0; + arr->capacity = 0; +} + +static void mag_tensor_array_free(mag_tensor_array_t* arr) { + (*mag_alloc)(arr->data, 0); + arr->size = 0; + arr->capacity = 0; +} + +static void mag_tensor_array_push(mag_tensor_array_t* arr, mag_tensor_t* t) { + if (arr->size == arr->capacity) { + size_t cap = !arr->capacity ? 16 : arr->capacity<<1; + arr->data = (*mag_alloc)(arr->data, cap*sizeof(*arr->data)); + arr->capacity = cap; + } + arr->data[arr->size++] = t; +} + +static bool mag_was_visited(mag_tensor_t** visited, size_t len, mag_tensor_t* t) { + for (size_t i = 0; i < len; ++i) if (visited[i] == t) return 1; + return 0; +} + +static void mag_mark_visited(mag_tensor_t*** visited, size_t* len, size_t* cap, mag_tensor_t* t) { + if (*len == *cap) { + size_t nc = !*cap ? 16 : (*cap<<=1); + *visited = (*mag_alloc)(*visited, nc*sizeof(**visited)); + *cap = nc; + } + (*visited)[(*len)++] = t; +} + +static void mag_collect_topo_iterative(mag_tensor_t* root, mag_tensor_array_t* out_array) { + size_t sta_len = 0, sta_cap = 0; + size_t vi_len = 0, vi_cap = 0; + mag_topo_stack_record_t* stack = NULL; + mag_tensor_t** visited = NULL; + + #define mag_sta_push(_t) do { \ + if (sta_len == sta_cap) { \ + size_t nc = !sta_cap ? 16 : (sta_cap<<1); \ + stack = (*mag_alloc)(stack, nc*sizeof(*stack)); \ + sta_cap = nc; \ + } \ + stack[sta_len].tensor = (_t); \ + stack[sta_len].next_child_idx = 0; \ + sta_len++; \ + } while(0) + #define mag_sta_pop() (stack[--sta_len]) + + if (!(root->flags & MAG_TFLAG_REQUIRES_GRAD)) return; + mag_mark_visited(&visited, &vi_len, &vi_cap, root); + mag_sta_push(root); + while (sta_len) { /* Iterative DFS */ + mag_topo_stack_record_t* top = &stack[sta_len - 1]; + mag_tensor_t* cur_tensor = top->tensor; + if (top->next_child_idx < mag_op_meta_of(cur_tensor->op)->argcount) { + mag_tensor_t* child = cur_tensor->op_inputs[top->next_child_idx++]; + if (child && (child->flags & MAG_TFLAG_REQUIRES_GRAD)) { + if (!mag_was_visited(visited, vi_len, child)) { + mag_mark_visited(&visited, &vi_len, &vi_cap, child); + mag_sta_push(child); + } + } } else { - mag_tensor_t* acc = mag_add(input->grad, grad_input); - mag_tensor_decref(input->grad); - input->grad = acc; - mag_tensor_decref(grad_input); + (void)mag_sta_pop(); + mag_tensor_array_push(out_array, cur_tensor); + } + } + + #undef mag_sta_push + #undef mag_sta_pop + + (*mag_alloc)(stack, 0); + (*mag_alloc)(visited, 0); +} + +void mag_tensor_backward(mag_tensor_t* t) { + mag_tensor_array_t post_order; + mag_tensor_array_init(&post_order); + mag_collect_topo_iterative(t, &post_order); + if (mag_unlikely(!post_order.size)) return; + for (size_t i = 0, j = post_order.size-1; i < j; ++i, --j) + mag_swap(mag_tensor_t*, post_order.data[i], post_order.data[j]); + for (size_t id = 0; id < post_order.size; ++id) { + t = post_order.data[id]; + if (id == 0 && !t->grad) { + mag_tensor_t* grad = mag_tensor_create(t->ctx, t->dtype, t->shape, t->rank, NULL, 0); + grad->flags |= MAG_TFLAG_IS_GRAD; + mag_tensor_fill(grad, 1.0f); + t->grad = grad; + } + if (mag_unlikely(t->op == MAG_OP_NOP)) continue; + mag_tensor_t* grads[MAG_MAX_OP_PARAMS] = {0}; + mag_tensor_t* gout = t->grad; + switch (t->op) { + case MAG_OP_RELU: { + mag_tensor_t* x = t->op_inputs[0]; + mag_tensor_t* mask = mag_step(x); + grads[0] = mag_mul(gout, mask); + mag_tensor_decref(mask); + break; + } + case MAG_OP_ADD: { + grads[0] = mag_clone(gout); + grads[1] = mag_clone(gout); + break; + } + case MAG_OP_SUB: { + grads[0] = mag_clone(gout); + grads[1] = mag_neg(gout); + break; + } + case MAG_OP_MUL: { + mag_tensor_t* x = t->op_inputs[0]; + mag_tensor_t* y = t->op_inputs[1]; + grads[0] = mag_mul(gout, y); + grads[1] = mag_mul(gout, x); + break; + } + case MAG_OP_DIV: { + mag_tensor_t* x = t->op_inputs[0]; + mag_tensor_t* y = t->op_inputs[1]; + grads[0] = mag_div(gout, y); + mag_tensor_t* tmp = mag_mul(gout, x); + mag_tensor_t* y2 = mag_mul(y, y); + mag_tensor_t* tmp2= mag_div(tmp, y2); + grads[1] = mag_neg(tmp2); + mag_tensor_decref(tmp); + mag_tensor_decref(y2); + mag_tensor_decref(tmp2); + break; + } + default: mag_panic("Unsupported op in backward!"); + } + uint32_t numin = mag_op_meta_of(t->op)->argcount; + for (uint32_t i = 0; i < numin; ++i) { + mag_tensor_t* input = t->op_inputs[i]; + mag_assert2(input); + if (!(input->flags & MAG_TFLAG_REQUIRES_GRAD)) continue; + mag_tensor_t* gri = grads[i]; + if (!gri) continue; + if (!input->grad) { + input->grad = gri; + } else { + mag_tensor_t* acc = mag_add(input->grad, gri); + mag_tensor_decref(input->grad); + input->grad = acc; + mag_tensor_decref(gri); + } } - mag_tensor_backward(input); } + mag_tensor_array_free(&post_order); } float mag_tensor_get_scalar_physical_index(mag_tensor_t* t, int64_t d0, int64_t d1, int64_t d2, int64_t d3, int64_t d4, int64_t d5) { diff --git a/test/unit/autograd.cpp b/test/unit/autograd.cpp index 25574aa..0f966fe 100644 --- a/test/unit/autograd.cpp +++ b/test/unit/autograd.cpp @@ -24,6 +24,11 @@ TEST(autograd, bin_ops1) { auto* z = mag_div(mag_mul(mag_add(x, y), mag_sub(x, y)), k); mag_tensor_backward(z); + ASSERT_NE(x->grad, nullptr); + ASSERT_NE(y->grad, nullptr); + ASSERT_NE(k->grad, nullptr); + ASSERT_NE(z->grad, nullptr); + // check forward pass float vx = mag_tensor_get_scalar_virtual_index(x, 0); float vy = mag_tensor_get_scalar_virtual_index(y, 0); From 9ad2c436674f8f1ef44b76060bdd816f89a1c37d Mon Sep 17 00:00:00 2001 From: Neo Date: Thu, 30 Jan 2025 21:09:49 +0100 Subject: [PATCH 11/24] Type hint updates, ruff fixes --- python/magnetron_framework/bing_gen.py | 14 +- .../magnetron_framework/magnetron/__init__.py | 12 +- .../magnetron/_ffi_cdecl_generated.py | 4 +- .../magnetron/_lib_loader.py | 15 +- python/magnetron_framework/magnetron/core.py | 263 +++++++++++++----- python/magnetron_framework/magnetron/layer.py | 37 ++- python/magnetron_framework/magnetron/model.py | 18 +- python/magnetron_framework/magnetron/optim.py | 9 +- python/magnetron_framework/ruff.toml | 9 + python/magnetron_framework/setup.py | 36 ++- python/magnetron_viewer/icons/folder.png | Bin 2364 -> 0 bytes python/magnetron_viewer/icons/icon.png | Bin 4032 -> 0 bytes .../magnetron_viewer/icons/icons_credits.txt | 1 - python/magnetron_viewer/icons/metadata.png | Bin 1265 -> 0 bytes python/magnetron_viewer/icons/tensor.png | Bin 4296 -> 0 bytes python/magnetron_viewer/logo.png | Bin 131703 -> 0 bytes python/magnetron_viewer/main.py | 158 ----------- python/magnetron_viewer/requirements.txt | 1 - python/optional-requirements.txt | 13 + python/requirements.txt | 13 - 20 files changed, 304 insertions(+), 299 deletions(-) create mode 100644 python/magnetron_framework/ruff.toml delete mode 100644 python/magnetron_viewer/icons/folder.png delete mode 100644 python/magnetron_viewer/icons/icon.png delete mode 100644 python/magnetron_viewer/icons/icons_credits.txt delete mode 100644 python/magnetron_viewer/icons/metadata.png delete mode 100644 python/magnetron_viewer/icons/tensor.png delete mode 100644 python/magnetron_viewer/logo.png delete mode 100644 python/magnetron_viewer/main.py delete mode 100644 python/magnetron_viewer/requirements.txt create mode 100644 python/optional-requirements.txt delete mode 100644 python/requirements.txt diff --git a/python/magnetron_framework/bing_gen.py b/python/magnetron_framework/bing_gen.py index feb7bca..f685576 100644 --- a/python/magnetron_framework/bing_gen.py +++ b/python/magnetron_framework/bing_gen.py @@ -9,7 +9,7 @@ print(f'Generating {OUTPUT_FILE} from {C_HDR_FILE}...') -def comment_replacer(match): +def comment_replacer(match: re.Match[str]) -> str: s = match.group(0) if s.startswith('/'): return ' ' @@ -17,9 +17,8 @@ def comment_replacer(match): return s -macro_substitutions: dict[str, str] = { - 'MAG_EXPORT': ' ' -} +macro_substitutions: dict[str, str] = {'MAG_EXPORT': ' '} + def keep_line(line: str) -> bool: if line == '' or line.startswith('#'): @@ -30,12 +29,13 @@ def keep_line(line: str) -> bool: return False return True + c_input: list[str] = [] with open(C_HDR_FILE, 'rt') as f: full_src: str = f.read() pattern = re.compile( r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"', - re.DOTALL | re.MULTILINE + re.DOTALL | re.MULTILINE, ) full_src = re.sub(pattern, comment_replacer, full_src) # remove comments for macro, replacement in macro_substitutions.items(): @@ -50,8 +50,8 @@ def keep_line(line: str) -> bool: decls = decls.rstrip() if decls.endswith('}'): decls = decls[:-1] -bin_decls: str = 'b\'' + ''.join(f'\\x{b:02x}' for b in decls.encode('utf-8')) + '\'' -out += f"__MAG_CDECLS: str = {bin_decls}.decode(\'utf-8\')\n" +bin_decls: str = "b'" + ''.join(f'\\x{b:02x}' for b in decls.encode('utf-8')) + "'" +out += f"__MAG_CDECLS: str = {bin_decls}.decode('utf-8')\n" with open(OUTPUT_FILE, 'wt') as f: f.write(out) diff --git a/python/magnetron_framework/magnetron/__init__.py b/python/magnetron_framework/magnetron/__init__.py index 0fa8c7b..d1e19df 100644 --- a/python/magnetron_framework/magnetron/__init__.py +++ b/python/magnetron_framework/magnetron/__init__.py @@ -1,10 +1,10 @@ # (c) 2025 Mario "Neo" Sieg. -__version__ = "0.1.0" -__author__ = "Mario Sieg" -__email__ = "mario.sieg.64@gmail.com" -__author_email__ = "mario.sieg.64@gmail.com" -__license__ = "Apache 2.0" -__url__ = "https://github.com/MarioSieg/magnetron" +__version__ = '0.1.0' +__author__ = 'Mario Sieg' +__email__ = 'mario.sieg.64@gmail.com' +__author_email__ = 'mario.sieg.64@gmail.com' +__license__ = 'Apache 2.0' +__url__ = 'https://github.com/MarioSieg/magnetron' from .core import * diff --git a/python/magnetron_framework/magnetron/_ffi_cdecl_generated.py b/python/magnetron_framework/magnetron/_ffi_cdecl_generated.py index 372528c..41c09ed 100644 --- a/python/magnetron_framework/magnetron/_ffi_cdecl_generated.py +++ b/python/magnetron_framework/magnetron/_ffi_cdecl_generated.py @@ -1,2 +1,4 @@ # Autogenered by /home/mario/Documents/remote_projects/magnetron/python/magnetron_framework/bing_gen.py 2025-01-29 14:41:12.407408, do NOT edit! -__MAG_CDECLS: str = b'\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x43\x4f\x4d\x50\x55\x54\x45\x5f\x44\x45\x56\x49\x43\x45\x5f\x54\x59\x50\x45\x5f\x43\x50\x55\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4d\x50\x55\x54\x45\x5f\x44\x45\x56\x49\x43\x45\x5f\x54\x59\x50\x45\x5f\x47\x50\x55\x5f\x43\x55\x44\x41\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4d\x50\x55\x54\x45\x5f\x44\x45\x56\x49\x43\x45\x5f\x54\x59\x50\x45\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x67\x65\x74\x5f\x6e\x61\x6d\x65\x28\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x6f\x70\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x45\x58\x45\x43\x5f\x4d\x4f\x44\x45\x5f\x45\x41\x47\x45\x52\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x45\x58\x45\x43\x5f\x4d\x4f\x44\x45\x5f\x44\x45\x46\x45\x52\x52\x45\x44\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x45\x58\x45\x43\x5f\x4d\x4f\x44\x45\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x50\x52\x4e\x47\x5f\x4d\x45\x52\x53\x45\x4e\x4e\x45\x5f\x54\x57\x49\x53\x54\x45\x52\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x50\x52\x4e\x47\x5f\x50\x43\x47\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x50\x52\x4e\x47\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x74\x68\x72\x65\x61\x64\x5f\x73\x63\x68\x65\x64\x5f\x70\x72\x69\x6f\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x4e\x4f\x52\x4d\x41\x4c\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x4d\x45\x44\x49\x55\x4d\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x48\x49\x47\x48\x20\x3d\x20\x32\x2c\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x52\x45\x41\x4c\x54\x49\x4d\x45\x20\x3d\x20\x33\x2c\x0a\x7d\x20\x6d\x61\x67\x5f\x74\x68\x72\x65\x61\x64\x5f\x73\x63\x68\x65\x64\x5f\x70\x72\x69\x6f\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x63\x6f\x6c\x6f\x72\x5f\x63\x68\x61\x6e\x6e\x65\x6c\x73\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x41\x55\x54\x4f\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x47\x52\x41\x59\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x47\x52\x41\x59\x5f\x41\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x52\x47\x42\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x52\x47\x42\x41\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x63\x6f\x6c\x6f\x72\x5f\x63\x68\x61\x6e\x6e\x65\x6c\x73\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x2a\x20\x28\x2a\x6d\x61\x67\x5f\x67\x65\x74\x5f\x61\x6c\x6c\x6f\x63\x5f\x66\x6e\x28\x76\x6f\x69\x64\x29\x29\x28\x76\x6f\x69\x64\x2a\x20\x62\x6c\x6b\x2c\x20\x73\x69\x7a\x65\x5f\x74\x20\x73\x69\x7a\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x73\x65\x74\x5f\x61\x6c\x6c\x6f\x63\x5f\x66\x6e\x28\x76\x6f\x69\x64\x2a\x20\x28\x2a\x61\x6c\x6c\x6f\x63\x29\x28\x76\x6f\x69\x64\x2a\x20\x62\x6c\x6b\x2c\x20\x73\x69\x7a\x65\x5f\x74\x20\x73\x69\x7a\x65\x29\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x73\x65\x74\x5f\x6c\x6f\x67\x5f\x6d\x6f\x64\x65\x28\x62\x6f\x6f\x6c\x20\x65\x6e\x61\x62\x6c\x65\x64\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x68\x61\x72\x33\x32\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x64\x65\x73\x63\x72\x69\x70\x74\x6f\x72\x5f\x74\x20\x7b\x0a\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x3b\x0a\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x74\x68\x72\x65\x61\x64\x5f\x63\x6f\x75\x6e\x74\x3b\x0a\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x63\x75\x64\x61\x5f\x64\x65\x76\x69\x63\x65\x5f\x69\x64\x3b\x0a\x7d\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x64\x65\x73\x63\x72\x69\x70\x74\x6f\x72\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x63\x72\x65\x61\x74\x65\x28\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x64\x65\x76\x69\x63\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x63\x72\x65\x61\x74\x65\x32\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x64\x65\x73\x63\x72\x69\x70\x74\x6f\x72\x5f\x74\x2a\x20\x64\x65\x76\x69\x63\x65\x5f\x69\x6e\x66\x6f\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x73\x65\x74\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x20\x6d\x6f\x64\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x73\x65\x74\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x2c\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x73\x65\x65\x64\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x6f\x73\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x76\x69\x72\x74\x75\x61\x6c\x5f\x63\x6f\x72\x65\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x63\x6f\x72\x65\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x73\x6f\x63\x6b\x65\x74\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x6d\x65\x6d\x6f\x72\x79\x5f\x74\x6f\x74\x61\x6c\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x6d\x65\x6d\x6f\x72\x79\x5f\x66\x72\x65\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x69\x73\x5f\x6e\x75\x6d\x61\x5f\x73\x79\x73\x74\x65\x6d\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x73\x69\x7a\x65\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x74\x6f\x74\x61\x6c\x5f\x74\x65\x6e\x73\x6f\x72\x73\x5f\x63\x72\x65\x61\x74\x65\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x70\x72\x6f\x66\x69\x6c\x65\x5f\x73\x74\x61\x72\x74\x5f\x72\x65\x63\x6f\x72\x64\x69\x6e\x67\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x70\x72\x6f\x66\x69\x6c\x65\x5f\x73\x74\x6f\x70\x5f\x72\x65\x63\x6f\x72\x64\x69\x6e\x67\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x65\x78\x70\x6f\x72\x74\x5f\x63\x73\x76\x5f\x66\x69\x6c\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x64\x65\x73\x74\x72\x6f\x79\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x44\x54\x59\x50\x45\x5f\x46\x33\x32\x2c\x0a\x4d\x41\x47\x5f\x44\x54\x59\x50\x45\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x74\x20\x7b\x0a\x69\x6e\x74\x36\x34\x5f\x74\x20\x73\x69\x7a\x65\x3b\x0a\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6e\x61\x6d\x65\x3b\x0a\x7d\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x6f\x66\x28\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x70\x61\x63\x6b\x5f\x63\x6f\x6c\x6f\x72\x5f\x75\x38\x28\x75\x69\x6e\x74\x38\x5f\x74\x20\x72\x2c\x20\x75\x69\x6e\x74\x38\x5f\x74\x20\x67\x2c\x20\x75\x69\x6e\x74\x38\x5f\x74\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x70\x61\x63\x6b\x5f\x63\x6f\x6c\x6f\x72\x5f\x66\x33\x32\x28\x66\x6c\x6f\x61\x74\x20\x72\x2c\x20\x66\x6c\x6f\x61\x74\x20\x67\x2c\x20\x66\x6c\x6f\x61\x74\x20\x62\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x67\x72\x61\x70\x68\x5f\x65\x76\x61\x6c\x5f\x6f\x72\x64\x65\x72\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x47\x52\x41\x50\x48\x5f\x45\x56\x41\x4c\x5f\x4f\x52\x44\x45\x52\x5f\x46\x4f\x52\x57\x41\x52\x44\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x47\x52\x41\x50\x48\x5f\x45\x56\x41\x4c\x5f\x4f\x52\x44\x45\x52\x5f\x52\x45\x56\x45\x52\x53\x45\x20\x3d\x20\x31\x0a\x7d\x20\x6d\x61\x67\x5f\x67\x72\x61\x70\x68\x5f\x65\x76\x61\x6c\x5f\x6f\x72\x64\x65\x72\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x31\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x32\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x33\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x34\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x35\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x36\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x36\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x6c\x6f\x6e\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x76\x69\x65\x77\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x72\x61\x6e\x73\x70\x6f\x73\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x70\x65\x72\x6d\x75\x74\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x30\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x31\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x32\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x33\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x34\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x35\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x65\x61\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x69\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x61\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x6d\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x62\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x62\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6e\x65\x67\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6e\x65\x67\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6c\x6f\x67\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6c\x6f\x67\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x74\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x74\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6e\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x6f\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x6f\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x74\x65\x70\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x74\x65\x70\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x65\x78\x70\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x65\x78\x70\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x68\x61\x72\x64\x5f\x73\x69\x67\x6d\x6f\x69\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x68\x61\x72\x64\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x28\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x28\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x73\x28\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x70\x6f\x77\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x70\x6f\x77\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x61\x74\x6d\x75\x6c\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x6e\x63\x72\x65\x66\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x65\x63\x72\x65\x66\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x6f\x70\x79\x5f\x62\x75\x66\x66\x65\x72\x5f\x66\x72\x6f\x6d\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x76\x6f\x69\x64\x2a\x20\x64\x61\x74\x61\x2c\x20\x73\x69\x7a\x65\x5f\x74\x20\x73\x69\x7a\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x69\x6c\x6c\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x69\x6c\x6c\x5f\x72\x61\x6e\x64\x6f\x6d\x5f\x75\x6e\x69\x66\x6f\x72\x6d\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x66\x6c\x6f\x61\x74\x20\x6d\x69\x6e\x2c\x20\x66\x6c\x6f\x61\x74\x20\x6d\x61\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x69\x6c\x6c\x5f\x72\x61\x6e\x64\x6f\x6d\x5f\x6e\x6f\x72\x6d\x61\x6c\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x66\x6c\x6f\x61\x74\x20\x6d\x65\x61\x6e\x2c\x20\x66\x6c\x6f\x61\x74\x20\x73\x74\x64\x64\x65\x76\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x70\x61\x63\x6b\x65\x64\x5f\x72\x65\x66\x63\x6f\x75\x6e\x74\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x72\x65\x74\x61\x69\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x73\x69\x7a\x65\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x6d\x65\x6d\x6f\x72\x79\x5f\x75\x73\x61\x67\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x70\x72\x69\x6e\x74\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x62\x6f\x6f\x6c\x20\x77\x69\x74\x68\x5f\x68\x65\x61\x64\x65\x72\x2c\x20\x62\x6f\x6f\x6c\x20\x77\x69\x74\x68\x5f\x64\x61\x74\x61\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x6e\x61\x6d\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6e\x61\x6d\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x6d\x74\x5f\x6e\x61\x6d\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x6d\x74\x2c\x20\x2e\x2e\x2e\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x72\x61\x6e\x6b\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x69\x6e\x74\x36\x34\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x68\x61\x70\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x69\x6e\x74\x36\x34\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x74\x72\x69\x64\x65\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x74\x79\x70\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x61\x74\x61\x5f\x70\x74\x72\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x61\x74\x61\x5f\x73\x69\x7a\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6e\x75\x6d\x65\x6c\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6e\x75\x6d\x5f\x72\x6f\x77\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6e\x75\x6d\x5f\x63\x6f\x6c\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x73\x63\x61\x6c\x61\x72\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x76\x65\x63\x74\x6f\x72\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x6d\x61\x74\x72\x69\x78\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x76\x6f\x6c\x75\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x73\x68\x61\x70\x65\x5f\x65\x71\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x61\x72\x65\x5f\x73\x74\x72\x69\x64\x65\x73\x5f\x65\x71\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x61\x6e\x5f\x62\x72\x6f\x61\x64\x63\x61\x73\x74\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x74\x72\x61\x6e\x73\x70\x6f\x73\x65\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x70\x65\x72\x6d\x75\x74\x65\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x63\x6f\x6e\x74\x69\x67\x75\x6f\x75\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x66\x6c\x6f\x61\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x30\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x30\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x66\x6c\x6f\x61\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x76\x69\x72\x74\x75\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x76\x5f\x69\x64\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x76\x69\x72\x74\x75\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x76\x5f\x69\x64\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x65\x71\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x63\x6c\x6f\x73\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x2c\x20\x66\x6c\x6f\x61\x74\x20\x65\x70\x73\x2c\x20\x64\x6f\x75\x62\x6c\x65\x2a\x20\x70\x65\x72\x63\x65\x6e\x74\x5f\x65\x71\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x6d\x67\x5f\x64\x72\x61\x77\x5f\x62\x6f\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x78\x31\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x79\x31\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x78\x32\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x79\x32\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x77\x69\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x67\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x6d\x67\x5f\x64\x72\x61\x77\x5f\x74\x65\x78\x74\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x78\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x79\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x73\x69\x7a\x65\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x67\x62\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x74\x78\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x63\x74\x78\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x75\x73\x65\x72\x5f\x64\x61\x74\x61\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x75\x73\x65\x72\x5f\x64\x61\x74\x61\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x76\x6f\x69\x64\x2a\x20\x75\x64\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x61\x76\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6c\x6f\x61\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6c\x6f\x61\x64\x5f\x69\x6d\x61\x67\x65\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x2c\x20\x6d\x61\x67\x5f\x63\x6f\x6c\x6f\x72\x5f\x63\x68\x61\x6e\x6e\x65\x6c\x73\x5f\x74\x20\x63\x68\x61\x6e\x6e\x65\x6c\x73\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x65\x73\x69\x7a\x65\x5f\x77\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x65\x73\x69\x7a\x65\x5f\x68\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x61\x76\x65\x5f\x69\x6d\x61\x67\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x29\x3b\x0a'.decode('utf-8') +__MAG_CDECLS: str = b'\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x43\x4f\x4d\x50\x55\x54\x45\x5f\x44\x45\x56\x49\x43\x45\x5f\x54\x59\x50\x45\x5f\x43\x50\x55\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4d\x50\x55\x54\x45\x5f\x44\x45\x56\x49\x43\x45\x5f\x54\x59\x50\x45\x5f\x47\x50\x55\x5f\x43\x55\x44\x41\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4d\x50\x55\x54\x45\x5f\x44\x45\x56\x49\x43\x45\x5f\x54\x59\x50\x45\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x67\x65\x74\x5f\x6e\x61\x6d\x65\x28\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x6f\x70\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x45\x58\x45\x43\x5f\x4d\x4f\x44\x45\x5f\x45\x41\x47\x45\x52\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x45\x58\x45\x43\x5f\x4d\x4f\x44\x45\x5f\x44\x45\x46\x45\x52\x52\x45\x44\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x45\x58\x45\x43\x5f\x4d\x4f\x44\x45\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x50\x52\x4e\x47\x5f\x4d\x45\x52\x53\x45\x4e\x4e\x45\x5f\x54\x57\x49\x53\x54\x45\x52\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x50\x52\x4e\x47\x5f\x50\x43\x47\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x50\x52\x4e\x47\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x74\x68\x72\x65\x61\x64\x5f\x73\x63\x68\x65\x64\x5f\x70\x72\x69\x6f\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x4e\x4f\x52\x4d\x41\x4c\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x4d\x45\x44\x49\x55\x4d\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x48\x49\x47\x48\x20\x3d\x20\x32\x2c\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x52\x45\x41\x4c\x54\x49\x4d\x45\x20\x3d\x20\x33\x2c\x0a\x7d\x20\x6d\x61\x67\x5f\x74\x68\x72\x65\x61\x64\x5f\x73\x63\x68\x65\x64\x5f\x70\x72\x69\x6f\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x63\x6f\x6c\x6f\x72\x5f\x63\x68\x61\x6e\x6e\x65\x6c\x73\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x41\x55\x54\x4f\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x47\x52\x41\x59\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x47\x52\x41\x59\x5f\x41\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x52\x47\x42\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x52\x47\x42\x41\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x63\x6f\x6c\x6f\x72\x5f\x63\x68\x61\x6e\x6e\x65\x6c\x73\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x2a\x20\x28\x2a\x6d\x61\x67\x5f\x67\x65\x74\x5f\x61\x6c\x6c\x6f\x63\x5f\x66\x6e\x28\x76\x6f\x69\x64\x29\x29\x28\x76\x6f\x69\x64\x2a\x20\x62\x6c\x6b\x2c\x20\x73\x69\x7a\x65\x5f\x74\x20\x73\x69\x7a\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x73\x65\x74\x5f\x61\x6c\x6c\x6f\x63\x5f\x66\x6e\x28\x76\x6f\x69\x64\x2a\x20\x28\x2a\x61\x6c\x6c\x6f\x63\x29\x28\x76\x6f\x69\x64\x2a\x20\x62\x6c\x6b\x2c\x20\x73\x69\x7a\x65\x5f\x74\x20\x73\x69\x7a\x65\x29\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x73\x65\x74\x5f\x6c\x6f\x67\x5f\x6d\x6f\x64\x65\x28\x62\x6f\x6f\x6c\x20\x65\x6e\x61\x62\x6c\x65\x64\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x68\x61\x72\x33\x32\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x64\x65\x73\x63\x72\x69\x70\x74\x6f\x72\x5f\x74\x20\x7b\x0a\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x3b\x0a\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x74\x68\x72\x65\x61\x64\x5f\x63\x6f\x75\x6e\x74\x3b\x0a\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x63\x75\x64\x61\x5f\x64\x65\x76\x69\x63\x65\x5f\x69\x64\x3b\x0a\x7d\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x64\x65\x73\x63\x72\x69\x70\x74\x6f\x72\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x63\x72\x65\x61\x74\x65\x28\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x64\x65\x76\x69\x63\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x63\x72\x65\x61\x74\x65\x32\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x64\x65\x73\x63\x72\x69\x70\x74\x6f\x72\x5f\x74\x2a\x20\x64\x65\x76\x69\x63\x65\x5f\x69\x6e\x66\x6f\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x73\x65\x74\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x20\x6d\x6f\x64\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x73\x65\x74\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x2c\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x73\x65\x65\x64\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x6f\x73\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x76\x69\x72\x74\x75\x61\x6c\x5f\x63\x6f\x72\x65\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x63\x6f\x72\x65\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x73\x6f\x63\x6b\x65\x74\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x6d\x65\x6d\x6f\x72\x79\x5f\x74\x6f\x74\x61\x6c\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x6d\x65\x6d\x6f\x72\x79\x5f\x66\x72\x65\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x69\x73\x5f\x6e\x75\x6d\x61\x5f\x73\x79\x73\x74\x65\x6d\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x73\x69\x7a\x65\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x74\x6f\x74\x61\x6c\x5f\x74\x65\x6e\x73\x6f\x72\x73\x5f\x63\x72\x65\x61\x74\x65\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x70\x72\x6f\x66\x69\x6c\x65\x5f\x73\x74\x61\x72\x74\x5f\x72\x65\x63\x6f\x72\x64\x69\x6e\x67\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x70\x72\x6f\x66\x69\x6c\x65\x5f\x73\x74\x6f\x70\x5f\x72\x65\x63\x6f\x72\x64\x69\x6e\x67\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x65\x78\x70\x6f\x72\x74\x5f\x63\x73\x76\x5f\x66\x69\x6c\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x64\x65\x73\x74\x72\x6f\x79\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x44\x54\x59\x50\x45\x5f\x46\x33\x32\x2c\x0a\x4d\x41\x47\x5f\x44\x54\x59\x50\x45\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x74\x20\x7b\x0a\x69\x6e\x74\x36\x34\x5f\x74\x20\x73\x69\x7a\x65\x3b\x0a\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6e\x61\x6d\x65\x3b\x0a\x7d\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x6f\x66\x28\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x70\x61\x63\x6b\x5f\x63\x6f\x6c\x6f\x72\x5f\x75\x38\x28\x75\x69\x6e\x74\x38\x5f\x74\x20\x72\x2c\x20\x75\x69\x6e\x74\x38\x5f\x74\x20\x67\x2c\x20\x75\x69\x6e\x74\x38\x5f\x74\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x70\x61\x63\x6b\x5f\x63\x6f\x6c\x6f\x72\x5f\x66\x33\x32\x28\x66\x6c\x6f\x61\x74\x20\x72\x2c\x20\x66\x6c\x6f\x61\x74\x20\x67\x2c\x20\x66\x6c\x6f\x61\x74\x20\x62\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x67\x72\x61\x70\x68\x5f\x65\x76\x61\x6c\x5f\x6f\x72\x64\x65\x72\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x47\x52\x41\x50\x48\x5f\x45\x56\x41\x4c\x5f\x4f\x52\x44\x45\x52\x5f\x46\x4f\x52\x57\x41\x52\x44\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x47\x52\x41\x50\x48\x5f\x45\x56\x41\x4c\x5f\x4f\x52\x44\x45\x52\x5f\x52\x45\x56\x45\x52\x53\x45\x20\x3d\x20\x31\x0a\x7d\x20\x6d\x61\x67\x5f\x67\x72\x61\x70\x68\x5f\x65\x76\x61\x6c\x5f\x6f\x72\x64\x65\x72\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x31\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x32\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x33\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x34\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x35\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x36\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x36\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x6c\x6f\x6e\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x76\x69\x65\x77\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x72\x61\x6e\x73\x70\x6f\x73\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x70\x65\x72\x6d\x75\x74\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x30\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x31\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x32\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x33\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x34\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x35\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x65\x61\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x69\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x61\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x6d\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x62\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x62\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6e\x65\x67\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6e\x65\x67\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6c\x6f\x67\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6c\x6f\x67\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x74\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x74\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6e\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x6f\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x6f\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x74\x65\x70\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x74\x65\x70\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x65\x78\x70\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x65\x78\x70\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x68\x61\x72\x64\x5f\x73\x69\x67\x6d\x6f\x69\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x68\x61\x72\x64\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x28\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x28\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x73\x28\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x70\x6f\x77\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x70\x6f\x77\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x61\x74\x6d\x75\x6c\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x6e\x63\x72\x65\x66\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x65\x63\x72\x65\x66\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x6f\x70\x79\x5f\x62\x75\x66\x66\x65\x72\x5f\x66\x72\x6f\x6d\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x76\x6f\x69\x64\x2a\x20\x64\x61\x74\x61\x2c\x20\x73\x69\x7a\x65\x5f\x74\x20\x73\x69\x7a\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x69\x6c\x6c\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x69\x6c\x6c\x5f\x72\x61\x6e\x64\x6f\x6d\x5f\x75\x6e\x69\x66\x6f\x72\x6d\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x66\x6c\x6f\x61\x74\x20\x6d\x69\x6e\x2c\x20\x66\x6c\x6f\x61\x74\x20\x6d\x61\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x69\x6c\x6c\x5f\x72\x61\x6e\x64\x6f\x6d\x5f\x6e\x6f\x72\x6d\x61\x6c\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x66\x6c\x6f\x61\x74\x20\x6d\x65\x61\x6e\x2c\x20\x66\x6c\x6f\x61\x74\x20\x73\x74\x64\x64\x65\x76\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x70\x61\x63\x6b\x65\x64\x5f\x72\x65\x66\x63\x6f\x75\x6e\x74\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x72\x65\x74\x61\x69\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x73\x69\x7a\x65\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x6d\x65\x6d\x6f\x72\x79\x5f\x75\x73\x61\x67\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x70\x72\x69\x6e\x74\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x62\x6f\x6f\x6c\x20\x77\x69\x74\x68\x5f\x68\x65\x61\x64\x65\x72\x2c\x20\x62\x6f\x6f\x6c\x20\x77\x69\x74\x68\x5f\x64\x61\x74\x61\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x6e\x61\x6d\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6e\x61\x6d\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x6d\x74\x5f\x6e\x61\x6d\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x6d\x74\x2c\x20\x2e\x2e\x2e\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x72\x61\x6e\x6b\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x69\x6e\x74\x36\x34\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x68\x61\x70\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x69\x6e\x74\x36\x34\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x74\x72\x69\x64\x65\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x74\x79\x70\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x61\x74\x61\x5f\x70\x74\x72\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x61\x74\x61\x5f\x73\x69\x7a\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6e\x75\x6d\x65\x6c\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6e\x75\x6d\x5f\x72\x6f\x77\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6e\x75\x6d\x5f\x63\x6f\x6c\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x73\x63\x61\x6c\x61\x72\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x76\x65\x63\x74\x6f\x72\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x6d\x61\x74\x72\x69\x78\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x76\x6f\x6c\x75\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x73\x68\x61\x70\x65\x5f\x65\x71\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x61\x72\x65\x5f\x73\x74\x72\x69\x64\x65\x73\x5f\x65\x71\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x61\x6e\x5f\x62\x72\x6f\x61\x64\x63\x61\x73\x74\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x74\x72\x61\x6e\x73\x70\x6f\x73\x65\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x70\x65\x72\x6d\x75\x74\x65\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x63\x6f\x6e\x74\x69\x67\x75\x6f\x75\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x66\x6c\x6f\x61\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x30\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x30\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x66\x6c\x6f\x61\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x76\x69\x72\x74\x75\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x76\x5f\x69\x64\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x76\x69\x72\x74\x75\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x76\x5f\x69\x64\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x65\x71\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x63\x6c\x6f\x73\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x2c\x20\x66\x6c\x6f\x61\x74\x20\x65\x70\x73\x2c\x20\x64\x6f\x75\x62\x6c\x65\x2a\x20\x70\x65\x72\x63\x65\x6e\x74\x5f\x65\x71\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x6d\x67\x5f\x64\x72\x61\x77\x5f\x62\x6f\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x78\x31\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x79\x31\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x78\x32\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x79\x32\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x77\x69\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x67\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x6d\x67\x5f\x64\x72\x61\x77\x5f\x74\x65\x78\x74\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x78\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x79\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x73\x69\x7a\x65\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x67\x62\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x74\x78\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x63\x74\x78\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x75\x73\x65\x72\x5f\x64\x61\x74\x61\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x75\x73\x65\x72\x5f\x64\x61\x74\x61\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x76\x6f\x69\x64\x2a\x20\x75\x64\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x61\x76\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6c\x6f\x61\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6c\x6f\x61\x64\x5f\x69\x6d\x61\x67\x65\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x2c\x20\x6d\x61\x67\x5f\x63\x6f\x6c\x6f\x72\x5f\x63\x68\x61\x6e\x6e\x65\x6c\x73\x5f\x74\x20\x63\x68\x61\x6e\x6e\x65\x6c\x73\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x65\x73\x69\x7a\x65\x5f\x77\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x65\x73\x69\x7a\x65\x5f\x68\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x61\x76\x65\x5f\x69\x6d\x61\x67\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x29\x3b\x0a'.decode( + 'utf-8' +) diff --git a/python/magnetron_framework/magnetron/_lib_loader.py b/python/magnetron_framework/magnetron/_lib_loader.py index a4f5102..22d679f 100644 --- a/python/magnetron_framework/magnetron/_lib_loader.py +++ b/python/magnetron_framework/magnetron/_lib_loader.py @@ -1,26 +1,27 @@ from pathlib import Path from magnetron._ffi_cdecl_generated import __MAG_CDECLS +from cffi import FFI + import sys -MAG_LIBS = [ +MAG_LIBS: list[tuple[str, str]] = [ ('win32', 'magnetron.dll'), ('linux', 'libmagnetron.so'), ('darwin', 'libmagnetron.dylib'), ] -def load_native_module(): + +def load_native_module() -> tuple[FFI, object]: platform = sys.platform lib_name = next((lib for os, lib in MAG_LIBS if platform.startswith(os)), None) - assert lib_name, f"Unsupported platform: {platform}" + assert lib_name, f'Unsupported platform: {platform}' # Locate the library in the package directory pkg_path = Path(__file__).parent lib_path = pkg_path / lib_name - assert lib_path.exists(), f"magnetron shared library not found: {lib_path}" + assert lib_path.exists(), f'magnetron shared library not found: {lib_path}' - # Load the library using cffi - from cffi import FFI ffi = FFI() ffi.cdef(__MAG_CDECLS) # Define the C declarations lib = ffi.dlopen(str(lib_path)) # Load the shared library - return ffi, lib \ No newline at end of file + return ffi, lib diff --git a/python/magnetron_framework/magnetron/core.py b/python/magnetron_framework/magnetron/core.py index fca9bb7..4e18213 100644 --- a/python/magnetron_framework/magnetron/core.py +++ b/python/magnetron_framework/magnetron/core.py @@ -19,11 +19,11 @@ class ComputeDevice: class CPU: - def __init__(self, num_threads: int = 0): + def __init__(self, num_threads: int = 0) -> None: self.num_threads = num_threads class CUDA: - def __init__(self, device_id: int = 0): + def __init__(self, device_id: int = 0) -> None: self.device_id = device_id @@ -77,7 +77,7 @@ class ExecutionMode(Enum): @dataclass class GlobalConfig: - verbose: bool = (getenv('MAG_VERBOSE', '0') == '1') + verbose: bool = getenv('MAG_VERBOSE', '0') == '1' compute_device: ComputeDevice.CPU | ComputeDevice.CUDA = ComputeDevice.CPU() @@ -92,8 +92,12 @@ def active() -> 'Context': Context._active = Context(GlobalConfig.compute_device) return Context._active - def __init__(self, device: ComputeDevice.CPU | ComputeDevice.CUDA, *, - execution_mode: ExecutionMode = ExecutionMode.EAGER): + def __init__( + self, + device: ComputeDevice.CPU | ComputeDevice.CUDA, + *, + execution_mode: ExecutionMode = ExecutionMode.EAGER, + ) -> None: descriptor: ffi.CData = ffi.new('mag_device_descriptor_t*') if isinstance(device, ComputeDevice.CPU): descriptor.type = 0 @@ -109,7 +113,7 @@ def enable_grad_recorder(self) -> bool: return C.mag_ctx_is_grad_recorder_enabled(self._ptr) @enable_grad_recorder.setter - def enable_grad_recorder(self, enable: bool): + def enable_grad_recorder(self, enable: bool) -> None: C.mag_ctx_enable_grad_recorder(self._ptr, enable) @property @@ -121,7 +125,7 @@ def execution_mode(self) -> ExecutionMode: return ExecutionMode(C.mag_ctx_get_exec_mode(self._ptr)) @execution_mode.setter - def execution_mode(self, mode: ExecutionMode): + def execution_mode(self, mode: ExecutionMode) -> None: C.mag_ctx_set_exec_mode(self._ptr, mode.value) @property @@ -129,7 +133,7 @@ def prng_algorithm(self) -> PRNGAlgorithm: return PRNGAlgorithm(C.mag_ctx_get_prng_algorithm(self._ptr)) @prng_algorithm.setter - def prng_algorithm(self, algorithm: PRNGAlgorithm): + def prng_algorithm(self, algorithm: PRNGAlgorithm) -> None: C.mag_ctx_set_prng_algorithm(self._ptr, algorithm.value, 0) def seed(self, seed: int) -> None: @@ -187,29 +191,33 @@ def start_profiler(self) -> None: C.mag_ctx_profile_start_recording(self._ptr) def stop_profiler(self, export_csv_file: str | None = None) -> None: - csv_file = ffi.NULL if export_csv_file is None else bytes(export_csv_file, 'utf-8') + csv_file = ( + ffi.NULL if export_csv_file is None else bytes(export_csv_file, 'utf-8') + ) C.mag_ctx_profile_stop_recording(self._ptr, csv_file) - def __del__(self): + def __del__(self) -> None: C.mag_ctx_destroy(self._ptr) self._ptr = ffi.NULL -def no_grad(): +def no_grad() -> 'no_grad.Scope': """Temporary disable gradient computation""" class Scope: - def __call__(self, func): - def f(*args, **kwargs): + def __call__(self, func: callable) -> None: + def f(*args: tuple[object, ...], **kwargs: dict[str, object]) -> None: with Scope(): return func(*args, **kwargs) return f - def __enter__(self): + def __enter__(self) -> None: Context.active().enable_grad_recorder = False - def __exit__(self, exc_type, exc_value, traceback): + def __exit__( + self, exc_type: object, exc_value: object, traceback: object + ) -> None: Context.active().enable_grad_recorder = True return Scope() @@ -226,22 +234,32 @@ def __init__(self, ptr: ffi.CData | None = None) -> None: self._ptr = ptr def __del__(self) -> None: - if hasattr(self, '_ptr') and isinstance(self._ptr, ffi.CData) and self._ptr != ffi.NULL: + if ( + hasattr(self, '_ptr') + and isinstance(self._ptr, ffi.CData) + and self._ptr != ffi.NULL + ): C.mag_tensor_decref(self._ptr) self._ptr = ffi.NULL - _DISPATCH = { + _DISPATCH: list[int, ffi.CData] = { 1: C.mag_tensor_create_1d, 2: C.mag_tensor_create_2d, 3: C.mag_tensor_create_3d, 4: C.mag_tensor_create_4d, 5: C.mag_tensor_create_5d, - 6: C.mag_tensor_create_6d + 6: C.mag_tensor_create_6d, } assert len(_DISPATCH) == MAX_DIMS - def _new(self, ctx: Context, *, shape: tuple[int, ...], dtype: DType = DType.F32, - name: str | None = None) -> None: + def _new( + self, + ctx: Context, + *, + shape: tuple[int, ...], + dtype: DType = DType.F32, + name: str | None = None, + ) -> None: assert 0 < len(shape) <= MAX_DIMS, f'Invalid number of dimensions: {len(shape)}' assert all(0 < dim <= DIM_MAX for dim in shape), 'Invalid dimension size' self._ctx = weakref.ref(ctx) @@ -250,23 +268,40 @@ def _new(self, ctx: Context, *, shape: tuple[int, ...], dtype: DType = DType.F32 self.name = name @classmethod - def empty(cls, shape: tuple[int, ...], *, dtype: DType = DType.F32, name: str | None = None) -> 'Tensor': + def empty( + cls, + shape: tuple[int, ...], + *, + dtype: DType = DType.F32, + name: str | None = None, + ) -> 'Tensor': tensor = cls(None) tensor._new(Context.active(), shape=shape, dtype=dtype, name=name) return tensor @classmethod - def full(cls, shape: tuple[int, ...], *, fill_value: float, dtype: DType = DType.F32, - name: str | None = None) -> 'Tensor': + def full( + cls, + shape: tuple[int, ...], + *, + fill_value: float, + dtype: DType = DType.F32, + name: str | None = None, + ) -> 'Tensor': tensor = cls(None) tensor._new(Context.active(), shape=shape, dtype=dtype, name=name) C.mag_tensor_fill(tensor._ptr, fill_value) return tensor @classmethod - def const(cls, data, *, dtype: DType = DType.F32, - name: str | None = None) -> 'Tensor': - def flatten_nested_lists(nested): + def const( + cls, + data: list[float, ...], + *, + dtype: DType = DType.F32, + name: str | None = None, + ) -> 'Tensor': + def flatten_nested_lists(nested: object) -> tuple[tuple[int, ...], list[float]]: if not isinstance(nested, list): return (), [nested] elif len(nested) == 0: @@ -285,19 +320,32 @@ def flatten_nested_lists(nested): shape, flattened_data = flatten_nested_lists(data) tensor = cls(None) - tensor._new(Context.active(), shape=tuple(shape), dtype=dtype, name=name) + tensor._new(Context.active(), shape=shape, dtype=dtype, name=name) size: int = len(flattened_data) * ffi.sizeof('float') - C.mag_tensor_copy_buffer_from(tensor._ptr, ffi.new(f'float[{len(flattened_data)}]', flattened_data), size) + C.mag_tensor_copy_buffer_from( + tensor._ptr, ffi.new(f'float[{len(flattened_data)}]', flattened_data), size + ) return tensor @classmethod - def zeros(cls, shape: tuple[int, ...], *, dtype: DType = DType.F32, - name: str | None = None) -> 'Tensor': + def zeros( + cls, + shape: tuple[int, ...], + *, + dtype: DType = DType.F32, + name: str | None = None, + ) -> 'Tensor': return cls.full(shape, fill_value=0.0, dtype=dtype, name=name) @classmethod - def uniform(cls, shape: tuple[int, ...], *, interval: (float, float) = (-1.0, 1.0), dtype: DType = DType.F32, - name: str | None = None) -> 'Tensor': + def uniform( + cls, + shape: tuple[int, ...], + *, + interval: (float, float) = (-1.0, 1.0), + dtype: DType = DType.F32, + name: str | None = None, + ) -> 'Tensor': tensor = cls(None) tensor._new(Context.active(), shape=shape, dtype=dtype, name=name) if interval[1] < interval[0]: @@ -306,7 +354,9 @@ def uniform(cls, shape: tuple[int, ...], *, interval: (float, float) = (-1.0, 1. return tensor @classmethod - def normal(cls, shape: tuple[int, ...], *, mean: float = 0.0, stddev: float = 1.0) -> 'Tensor': + def normal( + cls, shape: tuple[int, ...], *, mean: float = 0.0, stddev: float = 1.0 + ) -> 'Tensor': tensor = cls(None) tensor._new(Context.active(), shape=shape, dtype=DType.F32) C.mag_tensor_fill_random_normal(tensor._ptr, mean, stddev) @@ -319,13 +369,22 @@ def load(cls, file_path: str) -> 'Tensor': return cls(ptr=instance) @classmethod - def load_image(cls, file_path: str, *, - name: str | None = None, - channels=ColorChannels.AUTO, - resize_to: (int, int) = (0, 0)) -> 'Tensor': + def load_image( + cls, + file_path: str, + *, + name: str | None = None, + channels: ColorChannels = ColorChannels.AUTO, + resize_to: (int, int) = (0, 0), + ) -> 'Tensor': assert isfile(file_path), f'File not found: {file_path}' - instance = C.mag_tensor_load_image(Context.active()._ptr, bytes(file_path, 'utf-8'), channels.value, - resize_to[0], resize_to[1]) + instance = C.mag_tensor_load_image( + Context.active()._ptr, + bytes(file_path, 'utf-8'), + channels.value, + resize_to[0], + resize_to[1], + ) tensor = cls(instance) if name is not None: tensor.name = name @@ -364,7 +423,9 @@ def data_ptr(self) -> int: def tolist(self) -> list[float]: assert self.dtype == DType.F32, 'Invalid data type' - return ffi.unpack(ffi.cast('float*', C.mag_tensor_data_ptr(self._ptr)), self.numel) + return ffi.unpack( + ffi.cast('float*', C.mag_tensor_data_ptr(self._ptr)), self.numel + ) @property def data_size(self) -> int: @@ -432,7 +493,7 @@ def is_contiguous(self) -> bool: return C.mag_tensor_is_contiguous(self._ptr) @property - def grad(self): + def grad(self) -> object | None: # -> Tensor | None - Forward Reference ('Tensor') + None is a bug in Python, will be fixed in Python 3.5.3, so long we use object. ptr: ffi.CData = C.mag_tensor_grad(self._ptr) return Tensor(ptr) if ptr != ffi.NULL else None @@ -443,19 +504,29 @@ def grad(self, grad: 'Tensor') -> None: def zero_grad(self) -> None: C.mag_tensor_zero_grad(self._ptr) - def is_close(self, other: 'Tensor', eps: float = -1.0, print_eq_percent: bool = False) -> (bool, float): + def is_close( + self, other: 'Tensor', eps: float = -1.0, print_eq_percent: bool = False + ) -> (bool, float): percent_eq = ffi.new('double[1]') is_eq = C.mag_tensor_is_close(self._ptr, other._ptr, eps, percent_eq) if print_eq_percent: print(f'Tensors are close: {is_eq}, Percent equal: {percent_eq[0]:.2f}%') return is_eq, percent_eq[0] - def draw_box(self, p1: (int, int), p2: (int, int), width: int = 2, rgb: int = 0xffffff): + def draw_box( + self, p1: (int, int), p2: (int, int), width: int = 2, rgb: int = 0xFFFFFF + ) -> None: assert p2[0] > p1[0] and p2[1] > p1[1] and width > 0 - C.mag_tensor_img_draw_box(self._ptr, p1[0], p1[1], p2[0], p2[1], width, rgb & 0xffffff) + C.mag_tensor_img_draw_box( + self._ptr, p1[0], p1[1], p2[0], p2[1], width, rgb & 0xFFFFFF + ) - def draw_text(self, p: (int, int), size: int, txt: str, rgb: int = 0xffffff): - C.mag_tensor_img_draw_text(self._ptr, p[0], p[1], size, rgb & 0xffffff, bytes(txt, 'utf-8')) + def draw_text( + self, p: (int, int), size: int, txt: str, rgb: int = 0xFFFFFF + ) -> None: + C.mag_tensor_img_draw_text( + self._ptr, p[0], p[1], size, rgb & 0xFFFFFF, bytes(txt, 'utf-8') + ) def save(self, file_path: str) -> None: if not file_path.endswith('.magnetron'): @@ -481,7 +552,9 @@ def T(self) -> 'Tensor': return Tensor(C.mag_transpose(self._ptr)) def permute(self, axes: tuple[int, ...]) -> 'Tensor': - assert len(axes) == self.rank, f'Invalid number of axes, require {self.rank}, got {len(axes)}' + assert len(axes) == self.rank, ( + f'Invalid number of axes, require {self.rank}, got {len(axes)}' + ) if len(axes) != MAX_DIMS: axes = axes + tuple(range(self.rank, MAX_DIMS)) assert len(axes) == MAX_DIMS @@ -561,16 +634,24 @@ def exp_(self) -> 'Tensor': return Tensor(C.mag_exp_(self._ptr)) def softmax(self, derivative: bool = False) -> 'Tensor': - return Tensor(C.mag_softmax_dv(self._ptr) if derivative else C.mag_softmax(self._ptr)) + return Tensor( + C.mag_softmax_dv(self._ptr) if derivative else C.mag_softmax(self._ptr) + ) def softmax_(self, derivative: bool = False) -> 'Tensor': - return Tensor(C.mag_softmax_dv_(self._ptr) if derivative else C.mag_softmax_(self._ptr)) + return Tensor( + C.mag_softmax_dv_(self._ptr) if derivative else C.mag_softmax_(self._ptr) + ) def sigmoid(self, derivative: bool = False) -> 'Tensor': - return Tensor(C.mag_sigmoid_dv(self._ptr) if derivative else C.mag_sigmoid(self._ptr)) + return Tensor( + C.mag_sigmoid_dv(self._ptr) if derivative else C.mag_sigmoid(self._ptr) + ) def sigmoid_(self, derivative: bool = False) -> 'Tensor': - return Tensor(C.mag_sigmoid_dv_(self._ptr) if derivative else C.mag_sigmoid_(self._ptr)) + return Tensor( + C.mag_sigmoid_dv_(self._ptr) if derivative else C.mag_sigmoid_(self._ptr) + ) def hard_sigmoid(self) -> 'Tensor': return Tensor(C.mag_hard_sigmoid(self._ptr)) @@ -582,78 +663,114 @@ def silu(self, derivative: bool = False) -> 'Tensor': return C.mag_silu_dv(self._ptr) if derivative else C.mag_silu(self._ptr) def silu_(self, derivative: bool = False) -> 'Tensor': - return Tensor(C.mag_silu_dv_(self._ptr) if derivative else C.mag_silu_(self._ptr)) + return Tensor( + C.mag_silu_dv_(self._ptr) if derivative else C.mag_silu_(self._ptr) + ) def tanh(self, derivative: bool = False) -> 'Tensor': return Tensor(C.mag_tanh_dv(self._ptr) if derivative else C.mag_tanh(self._ptr)) def tanh_(self, derivative: bool = False) -> 'Tensor': - return Tensor(C.mag_tanh_dv_(self._ptr) if derivative else C.mag_tanh_(self._ptr)) + return Tensor( + C.mag_tanh_dv_(self._ptr) if derivative else C.mag_tanh_(self._ptr) + ) def relu(self, derivative: bool = False) -> 'Tensor': return Tensor(C.mag_relu_dv(self._ptr) if derivative else C.mag_relu(self._ptr)) def relu_(self, derivative: bool = False) -> 'Tensor': - return Tensor(C.mag_relu_dv_(self._ptr) if derivative else C.mag_relu_(self._ptr)) + return Tensor( + C.mag_relu_dv_(self._ptr) if derivative else C.mag_relu_(self._ptr) + ) def gelu(self, derivative: bool = False) -> 'Tensor': return Tensor(C.mag_gelu_dv(self._ptr) if derivative else C.mag_gelu(self._ptr)) def gelu_(self, derivative: bool = False) -> 'Tensor': - return Tensor(C.mag_gelu_dv_(self._ptr) if derivative else C.mag_gelu_(self._ptr)) + return Tensor( + C.mag_gelu_dv_(self._ptr) if derivative else C.mag_gelu_(self._ptr) + ) def __add__(self, other: object | int | float) -> 'Tensor': return Tensor( - C.mag_add(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_adds(self._ptr, float(other))) + C.mag_add(self._ptr, other._ptr) + if isinstance(other, Tensor) + else C.mag_adds(self._ptr, float(other)) + ) def __radd__(self, other: int | float) -> 'Tensor': return Tensor( - C.mag_add(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_adds(self._ptr, float(other))) + C.mag_add(self._ptr, other._ptr) + if isinstance(other, Tensor) + else C.mag_adds(self._ptr, float(other)) + ) def __iadd__(self, other: object | int | float) -> 'Tensor': return Tensor( - C.mag_add_(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_adds_(self._ptr, float(other))) + C.mag_add_(self._ptr, other._ptr) + if isinstance(other, Tensor) + else C.mag_adds_(self._ptr, float(other)) + ) def __sub__(self, other: object | int | float) -> 'Tensor': return Tensor( - C.mag_sub(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_subs(self._ptr, float(other))) + C.mag_sub(self._ptr, other._ptr) + if isinstance(other, Tensor) + else C.mag_subs(self._ptr, float(other)) + ) def __rsub__(self, other: int | float) -> 'Tensor': return Tensor.full(self.shape, fill_value=float(other)) - self def __isub__(self, other: object | int | float) -> 'Tensor': return Tensor( - C.mag_sub_(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_subs_(self._ptr, float(other))) + C.mag_sub_(self._ptr, other._ptr) + if isinstance(other, Tensor) + else C.mag_subs_(self._ptr, float(other)) + ) def __mul__(self, other: object | int | float) -> 'Tensor': return Tensor( - C.mag_mul(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_muls(self._ptr, float(other))) + C.mag_mul(self._ptr, other._ptr) + if isinstance(other, Tensor) + else C.mag_muls(self._ptr, float(other)) + ) def __rmul__(self, other: int | float) -> 'Tensor': return Tensor( - C.mag_mul(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_muls(self._ptr, float(other))) + C.mag_mul(self._ptr, other._ptr) + if isinstance(other, Tensor) + else C.mag_muls(self._ptr, float(other)) + ) def __imul__(self, other: object | int | float) -> 'Tensor': return Tensor( - C.mag_mul_(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_muls_(self._ptr, float(other))) + C.mag_mul_(self._ptr, other._ptr) + if isinstance(other, Tensor) + else C.mag_muls_(self._ptr, float(other)) + ) def __truediv__(self, other: object | int | float) -> 'Tensor': return Tensor( - C.mag_div(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_divs(self._ptr, float(other))) + C.mag_div(self._ptr, other._ptr) + if isinstance(other, Tensor) + else C.mag_divs(self._ptr, float(other)) + ) def __rtruediv__(self, other: int | float) -> 'Tensor': return Tensor.full(self.shape, fill_value=float(other)) / self def __itruediv__(self, other: object | int | float) -> 'Tensor': return Tensor( - C.mag_div_(self._ptr, other._ptr) if isinstance(other, Tensor) else C.mag_divs_(self._ptr, float(other))) + C.mag_div_(self._ptr, other._ptr) + if isinstance(other, Tensor) + else C.mag_divs_(self._ptr, float(other)) + ) - def __pow__(self, exponent: int | float, modulo=None) -> 'Tensor': - assert modulo is None + def __pow__(self, exponent: int | float) -> 'Tensor': return Tensor(C.mag_pows(self._ptr, float(exponent))) - def __ipow__(self, exponent: int | float, modulo=None) -> 'Tensor': - assert modulo is None + def __ipow__(self, exponent: int | float) -> 'Tensor': return Tensor(C.mag_pows_(self._ptr, float(exponent))) def __matmul__(self, other: 'Tensor') -> 'Tensor': @@ -673,16 +790,16 @@ def __getitem__(self, indices: int | tuple[int, ...]) -> float: if isinstance(indices, int): return C.mag_tensor_get_scalar_virtual_index(self._ptr, indices) elif isinstance(indices, tuple): - idx = indices + (0,) * (6 - len(indices)) + idx = indices + (0,) * (MAX_DIMS - len(indices)) return C.mag_tensor_get_scalar_physical_index(self._ptr, *idx) else: - raise TypeError("Indices must be an int or a tuple of ints.") + raise TypeError('Indices must be an int or a tuple of ints.') def __setitem__(self, indices: int | tuple[int, ...], value: float) -> None: if isinstance(indices, int): C.mag_tensor_set_scalar_virtual_index(self._ptr, indices, float(value)) elif isinstance(indices, tuple): - idx = indices + (0,) * (6 - len(indices)) + idx = indices + (0,) * (MAX_DIMS - len(indices)) C.mag_tensor_set_scalar_physical_index(self._ptr, *idx, float(value)) else: - raise TypeError("Indices must be an int or a tuple of ints.") + raise TypeError('Indices must be an int or a tuple of ints.') diff --git a/python/magnetron_framework/magnetron/layer.py b/python/magnetron_framework/magnetron/layer.py index e549383..3978af9 100644 --- a/python/magnetron_framework/magnetron/layer.py +++ b/python/magnetron_framework/magnetron/layer.py @@ -10,6 +10,7 @@ class Layer(ABC): """Abstract base class for all layers""" + @abstractmethod def forward(self, inputs: Tensor) -> Tensor: pass @@ -24,7 +25,7 @@ class LayerInit: """Weight/bias initialization methods and parameters""" @unique - class Dist(Enum): + class Distribution(Enum): NORMAL = 0 UNIFORM = 1 @@ -35,15 +36,17 @@ class Method(Enum): HE = 2 method: Method - distrib: Dist + dist: Distribution uniform_interval: (float, float) = (-1.0, 1.0) mean: float = 0.0 stddev: float = 1.0 gain: float = 1.0 - def __init__(self, method: Method, distrib: Dist, **kwargs): + def __init__( + self, method: Method, dist: Distribution, **kwargs: dict[str, object] + ) -> None: self.method = method - self.distrib = distrib + self.dist = dist for key, value in kwargs.items(): setattr(self, key, value) @@ -51,9 +54,9 @@ def apply(self, shape: tuple[int, ...]) -> Tensor: assert len(shape) >= 1 if self.method == self.Method.RANDOM: - if self.distrib == self.Dist.NORMAL: + if self.dist == self.Distribution.NORMAL: return Tensor.normal(shape, mean=self.mean, stddev=self.stddev) - elif self.distrib == self.Dist.UNIFORM: + elif self.dist == self.Distribution.UNIFORM: return Tensor.uniform(shape, interval=self.uniform_interval) fan_in: int = shape[0] @@ -68,17 +71,29 @@ def apply(self, shape: tuple[int, ...]) -> Tensor: factor = 1.0 / fan_in bound = math.sqrt(3.0 / fan_in) - if self.distrib == self.Dist.NORMAL: + if self.dist == self.Distribution.NORMAL: stddev = self.gain * math.sqrt(factor) return Tensor.normal(shape, mean=0.0, stddev=stddev) - elif self.distrib == self.Dist.UNIFORM: - return Tensor.uniform(shape, interval=(-self.gain * bound, self.gain * bound)) + elif self.dist == self.Distribution.UNIFORM: + return Tensor.uniform( + shape, interval=(-self.gain * bound, self.gain * bound) + ) + else: + raise ValueError('Invalid weight/bias initialization method') class DenseLayer(Layer): """Fully connected layer""" - def __init__(self, in_features: int, out_features: int, bias: bool, - init: LayerInit = LayerInit(LayerInit.Method.RANDOM, LayerInit.Dist.UNIFORM)): + + def __init__( + self, + in_features: int, + out_features: int, + bias: bool, + init: LayerInit = LayerInit( + LayerInit.Method.RANDOM, LayerInit.Distribution.UNIFORM + ), + ) -> None: super().__init__() self.in_features = in_features self.out_features = out_features diff --git a/python/magnetron_framework/magnetron/model.py b/python/magnetron_framework/magnetron/model.py index 811ce0d..829408c 100644 --- a/python/magnetron_framework/magnetron/model.py +++ b/python/magnetron_framework/magnetron/model.py @@ -19,7 +19,7 @@ class HyperParams: class Model(ABC): """Abstract base class for all models""" - def __init__(self, hyper_params: HyperParams): + def __init__(self, hyper_params: HyperParams) -> None: self.hyper_params = hyper_params @abstractmethod @@ -27,22 +27,22 @@ def forward(self, inputs: Tensor) -> Tensor: pass @abstractmethod - def backward(self, outputs: Tensor, targets: Tensor, rate: float): + def backward(self, outputs: Tensor, targets: Tensor, rate: float) -> None: pass @abstractmethod - def train(self, inputs: Tensor, targets: Tensor): + def train(self, inputs: Tensor, targets: Tensor) -> None: pass @abstractmethod - def summary(self): + def summary(self) -> None: pass class SequentialModel(Model): """Feedforward neural network model (multi-layer perceptron)""" - def __init__(self, hyper_params: HyperParams, layers: list[Layer]): + def __init__(self, hyper_params: HyperParams, layers: list[Layer]) -> None: super().__init__(hyper_params) self.layers = layers @@ -52,13 +52,13 @@ def forward(self, inputs: Tensor) -> Tensor: x = layer.forward(x) return x - def backward(self, outputs: Tensor, targets: Tensor, rate: float): + def backward(self, outputs: Tensor, targets: Tensor, rate: float) -> None: delta = (outputs - targets) * outputs.sigmoid(derivative=True) for i in reversed(range(len(self.layers))): - is_hidden = (i > 0) + is_hidden = i > 0 delta = self.layers[i].backward(is_hidden, delta, rate) - def train(self, inputs: Tensor, targets: Tensor): + def train(self, inputs: Tensor, targets: Tensor) -> None: epochs: int = self.hyper_params.epochs rate: float = self.hyper_params.lr @@ -77,5 +77,5 @@ def train(self, inputs: Tensor, targets: Tensor): print(f'Training finished in {duration:.2f} seconds') return losses - def summary(self): + def summary(self) -> None: pass diff --git a/python/magnetron_framework/magnetron/optim.py b/python/magnetron_framework/magnetron/optim.py index 151c6df..a5176a7 100644 --- a/python/magnetron_framework/magnetron/optim.py +++ b/python/magnetron_framework/magnetron/optim.py @@ -54,8 +54,13 @@ def step(self) -> None: class Adam(Optimizer): """Adaptive Moment Estimation""" - def __init__(self, params: list[Tensor], lr: float = 0.001, betas: tuple[float, float] = (0.9, 0.999), - eps: float = 1e-8): + def __init__( + self, + params: list[Tensor], + lr: float = 0.001, + betas: tuple[float, float] = (0.9, 0.999), + eps: float = 1e-8, + ) -> None: super().__init__(params, lr) self.betas = betas self.eps = eps diff --git a/python/magnetron_framework/ruff.toml b/python/magnetron_framework/ruff.toml new file mode 100644 index 0000000..39bec92 --- /dev/null +++ b/python/magnetron_framework/ruff.toml @@ -0,0 +1,9 @@ +[lint] +ignore = ["F403"] +select = ["ANN"] + +[format] +quote-style = "single" + +[lint.per-file-ignores] +"setup.py" = ["ANN"] diff --git a/python/magnetron_framework/setup.py b/python/magnetron_framework/setup.py index 0de4554..6f0354b 100644 --- a/python/magnetron_framework/setup.py +++ b/python/magnetron_framework/setup.py @@ -7,19 +7,24 @@ from setuptools import setup, Extension from setuptools.command.build_ext import build_ext -CMAKE_ROOT: str = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')) # Root directory of the CMake project -NUM_JOBS: int = max(multiprocessing.cpu_count() - 1, 1) # Use all but one core +CMAKE_ROOT: str = os.path.abspath( + os.path.join(os.path.dirname(__file__), '..', '..') +) # Root directory of the CMake project +NUM_JOBS: int = max(multiprocessing.cpu_count() - 1, 1) # Use all but one core + class BuildException(Exception): def __init__(self, message: str): self.message = message super().__init__(self.message) + class CMakeBuildExtension(Extension): - def __init__(self, name, root_dir: str=''): + def __init__(self, name, root_dir: str = ''): super().__init__(name, sources=[]) self.root_dir = os.path.abspath(root_dir) + class CMakeBuildExecutor(build_ext): def initialize_options(self): super().initialize_options() @@ -28,7 +33,9 @@ def run(self): try: print(subprocess.check_output(['cmake', '--version'])) except OSError: - raise BuildException('CMake must be installed to build the magnetron binaries from source. Please install CMake and try again.') + raise BuildException( + 'CMake must be installed to build the magnetron binaries from source. Please install CMake and try again.' + ) super().run() for ext in self.extensions: self.build_extension(ext) @@ -37,17 +44,26 @@ def build_extension(self, ext): if not os.path.exists(self.build_temp): os.makedirs(self.build_temp) cmake_args = [ - '-DMAGNETRON_ENABLE_CUDA=OFF', # TODO: Fix cuda compilation + '-DMAGNETRON_ENABLE_CUDA=OFF', # TODO: Fix cuda compilation f'-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={os.path.abspath(os.path.join(self.build_lib, "magnetron"))}', '-DCMAKE_BUILD_TYPE=Release', ] build_args = [ - '--target magnetron', # Only build the magnetron library + '--target magnetron', # Only build the magnetron library f'-j{NUM_JOBS}', - '-v' + '-v', ] - print(subprocess.check_call(['cmake', ext.root_dir] + cmake_args, cwd=self.build_temp)) - print(subprocess.check_call(['cmake', '--build', '.'] + build_args, cwd=self.build_temp)) + print( + subprocess.check_call( + ['cmake', ext.root_dir] + cmake_args, cwd=self.build_temp + ) + ) + print( + subprocess.check_call( + ['cmake', '--build', '.'] + build_args, cwd=self.build_temp + ) + ) + # Setup dependencies from requirements.txt lib_folder = os.path.dirname(os.path.realpath(__file__)) @@ -75,5 +91,5 @@ def build_extension(self, ext): 'build_ext': CMakeBuildExecutor, }, zip_safe=False, - install_requires=install_requires + install_requires=install_requires, ) diff --git a/python/magnetron_viewer/icons/folder.png b/python/magnetron_viewer/icons/folder.png deleted file mode 100644 index 2ce1ce71a4d9cdf956caf70a8623d6a0efc3407a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2364 zcmV-C3B&e@P)cK`qY8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H12)Ri_K~#90?Va0i99JF3zrQm(vp0La>)5elr%h6bQfNb+)M*9e zp_LFqydhA85E8t=6TI*@@WcaB`3q=5h@wD)5;<|(gcK!82u*q+5NMj#Nt?#_65H$b zExR*wcpxUOZ@W8l&YaoZ-{;BRJ?A^envc(!IXmYpK>?+y+utX`F%?;+nHm4rSjBa} zwd=Lj5HPFlwumCmEtP-#Qbz zE&&UmfJT-eVe;U<^f6nUhU-?eovO)U~e$x*oFvX*>dB~U1I!EusW@;9SVh6AGlB$H`u?|;f<6SUF@67X%H zX5u~^umBMdX9-wHESPLSD-#C^_%6UOrX?^s69@oO0VmL6?14#IQ z)OqqB`@6#9Lut#Fv3F3rT?B@#AFeqv2)iRh!{VV zVsv?UGoO3_aJ>p1mX}O7V^fSS4{!DlfrMotX-B^@xgLxT0sZ|u@!-K2T-P<(3@r#t zptH~o5y8$RVK6FyZ9C}geZpiDTAuh`oiLHW0<<_RK#Riyv^Xq4i^Br6I4nSm6Tv)y zKPoE|Bhp){eR)ocLkVXdcJ^Xjb%Zl9bN#?iYbtE{cRFshou@-5JgoxwOLs5&W4}2d z{mM3-$7x0q9bc-uj)eSfO$B|z%opY_j9hr>+u8(>(*4*Q{q@%Y$-_(>A-tUL$o@pz z$?-~;?(xZwJMu@mPLL~$=Xv9)__JG!0KPl@4&34v9odXhZs`%Acs`ni|4dkpXb?co zqIBgs^|_2(GqHtrVo4_#XRMV$b%f1Og5#pr|%6FuIUSBU@;KD^mCpH-2?=*6$&M4yJN3Jo00Ph`Ir+gL4PqjtoTwyER4Y`s0K#d%0it;C zf64&B>D-GaA5<$;w*Zvc&MSQz0jl2p19b`@iT+NB3{U;WpQ(JC9ImBcT>@CCuF+ua zVIqZaY@?~))zl+Eh0=ZX?^?h_j__*h{(*V~5cX9P_LG5H#6$?)&OLwTZ?%@LSpbpV z7TFgXGhy%#Z#MP2nwkWVz>G+D^VDzrk>rV^)r&o*CIN(%eH);Bur@Ifaxs5k=;L}S z)Fwc-uPs=6m?)td@BLR&eU~tz5pp_s>Nn;{UwQ2%LWc&HnuMt>h(MK(8DgcpnO;~B(?k)aI6y(_JK`L%8K&pGqLINh19HYVO zl^_*26JSYXd%qH}K}>{D4G(^7#D)Tp218(V*?-w zzYSA=BLOC?WY-H}f|*E=1pio=fb|8i+pdAAJxnkY0TOQ&_6^?&6S1xUR<@rX`ECpX zU;Hhzb}E?g0VUeG`;R#iSb{9S7B**14?xMz4`KK_g_-II;AH3ExuSxc&*6(q|5IT@ z8U6T>p}T)zwE$#YA(8yDieN@77eOFc3{{^rzeZ1FC13*%~sxt490P<5fg|Dg% zX0+gYo)>EBcQpb4$jQA6B7X2Q{``5%3vYa?GEe}3NN?xO9vcF67k|qL33`w0EKmK$ zp9OcpKB+oXB-(ae0$`~QW)wqst^44qlImDNsm_C{LmBl5pMIw$7XUB$jz6Qhk22?U z1PcO2bObZ%kdTMz&*Qb#bOjB(HRU(ppVI`^GDDoOqjz)b*JEMo#7QG&#f iC+Vf`!Pm8I>Qo6D%(fhwq?w$P9? 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 m6S0r~d;a-@rS5*>?(qN0pi5fRX(rPE00004nJ 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 diff --git a/python/magnetron_viewer/logo.png b/python/magnetron_viewer/logo.png deleted file mode 100644 index d05670b767ce765bf18b48180f1f462b4a94b6d4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 131703 zcmYg&c|25a`~ML|NlZy9OKGEQl{9H=AtA|D*^BJk$U1hZ$l4;xT7()Qdv;|j6Jtr1 z$(9(h8#}}AI{G}{*YA(d^SoZ;KIgvgYkgnu>pDHYeodKe$AKLPg0QJvQPe_^tyJ_s zmTmA$UEG&i_`mJ=D|*fd!nzy%kD;~4cMU-fAu5XUIv(-kJrhm`^ok_=3pBQ*s&Bu}GxES|%$))jTGmz_SDA zT-Vz8yZ60yENs;sqjf;Jrk{CzByzpD@CPPmIY73^A=aX2Mh`*y1&$5i5F}>O@zPvI zkMdeQRiN;*=g-eM5k1<9A2x<*?d;yyM%a<6+C5E-2%bwzHL!ErkXV|duisw3oj=9S zU6;KPIa9B`{I)N3 zm>wLjT=V7at6nRyj^%$p#enz+Zf<}VO3mnW$gO1SHo5N+aM8%4lU~y6Q^q#;a0x+p z;_-Zl{|&mrG`x~{?UC6+Ys$v%UzOXI)Z28*6Lui}c}L@!k^c0u?R^`QW}Vef`=1y~ zPG=NV?tmSZ^u7=fWc{~#$ErkyV^v06z{Ubc-}%W7F@E*7533hJ74=uJ>npK?B8A-j*NT}B!#l=<7TDyCL5G}2!ii^ zKtmAAesz|O-8GeKy+axJZoWbF+x(Q~ioY`;<$Oi-^`=^?Z5&^!Q@sv$Q}US+tUMgc z|46~ugUS&5B^frK=_)_3uqlHD-4XP!`kQ~9wm7(c>`Brbfv@{C&#uA0fB(vE{JJe3 zJmc|;9^ghKuDp?5oStr;kjF+*JGU+9cILvP2hc|uJ=@r1!>8oSNP3Y~cW3K66zT?@ zc5Vxl^>&a-SRWeO#ruIjw32nHW6bqK$Cw}emqp&=`uchY_WDTA;Hd&^K@}g;{}H_y z?{eHBUmhE4sd+bTI{D3VQuRV5Yxjk+MvFbJbm=`=V^WfFjeYBVcC-8s(1+yEheVdO z&oyH0YKlxH4%FTjUsC8?Us$|(^QNp0Z#98XTD&p4T6AaKvpga_J)Q5GTXZiRzD;7x zHxg`z4}HPZ6Rnbvse?0Z95oyHm8?A`gwt%$Wty%GPb?D&Qq50e1N;0X;F-_=vXw}KDzd#-ottH zgA@Z{nnO9{C6&mTH4DgVfaU~SP2|4c{? zS~|(!S+kPx%uK<2sWxABw(^rNU-;sQ^nYNXGWb8IrL!8QJJlu3=ta{H`}@65W$+lY zmu^zarf+rWmQ|`bSrIfJhpJ-Q8Qru+O^EIHs>Pawz$2f;^i$OX)91XCuBB)9Oe0hC zk{>GlMON!OAI^g>N+)ea`enDn9x3@_;eOlyD3|l4F{ZSPoD25bKFF}^PPuXAqXkR8 zPQS+!H|VNccOm#NbO+5VLPoWV;_D^kynK5sPldx5*53hUE(`&5H4{3?qI?Gr^4c9P z_xik0YK*uqm!+2ObMr1+j_lZNSaZh={NECpqFt*&OME{%eE7E^pRS^UxZUJ*5oEP? zIJmN;XQHc%la`+Mc`MmTWJWv>gHomD(=M+6DH=2pweu<6&yN2F=FI

sbxDSPYxDC3;(Yxz@$$FC-Z3M- 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%0Dc0&)qi;R_v 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 - -import time -import sys -import os -from magnetron.core import Tensor -from PyQt5.QtWidgets import * -from PyQt5.QtGui import * -from PyQt5.QtCore import * - -FONT_SIZE: int = 14 - - -def process_events_idle(): - for _ in range(0, 5): # Sleep for a bit to allow the loading box to show up - QApplication.processEvents() - time.sleep(0.1) - - -class MAGNETRONViewer(QMainWindow): - def __init__(self): - super().__init__() - self.window_icon = QIcon('logo.png') - self.tensor_icon = QIcon('icons/_ptr.png') - self.folder_icon = QIcon('icons/folder.png') - self.metadata_icon = QIcon('icons/metadata.png') - self.setWindowTitle('magnetron File Viewer') - self.setWindowIcon(self.window_icon) - self.resize(1920, 1080) - - main_widget = QWidget() - main_layout = QVBoxLayout(main_widget) - splitter = QSplitter(Qt.Horizontal) - - self.tensor_tree = QTreeWidget() - self.tensor_tree.setHeaderHidden(True) - self.tensor_tree.setStyleSheet(f'font-size: {FONT_SIZE}px;') - self.tensor_tree.itemClicked.connect(self.show_tensor_data) - - self.data_view_container = QWidget() - data_view_layout = QVBoxLayout(self.data_view_container) - - self.data_view = QPlainTextEdit() - self.data_view.setReadOnly(True) - self.data_view.setStyleSheet(f'font-size: {FONT_SIZE}px;') - - data_view_layout.addWidget(self.data_view) - data_view_layout.setContentsMargins(0, 0, 0, 0) - - self.info_panel = QTextEdit() - self.info_panel.setReadOnly(True) - self.info_panel.setStyleSheet(f'font-size: {FONT_SIZE}px;') - - splitter.addWidget(self.tensor_tree) - splitter.addWidget(self.data_view_container) - splitter.addWidget(self.info_panel) - splitter.setStretchFactor(0, 1) - splitter.setStretchFactor(1, 8) - splitter.setStretchFactor(2, 1) - - main_layout.addWidget(splitter) - self.setCentralWidget(main_widget) - - menu_bar = self.menuBar() - file_menu = menu_bar.addMenu('File') - open_action = QAction('Open', self) - open_action.triggered.connect(self.open_file) - file_menu.addAction(open_action) - - self.tensors = {} - self.metadata = {} - - def open_file(self): - file_name, _ = QFileDialog.getOpenFileName(self, 'Open magnetron File', os.getcwd(), - 'magnetron Files (*.magnetron);;All Files (*)') - if not file_name: - print('No file selected') - return - - self.setWindowTitle(f'magnetron File Viewer - {os.path.basename(file_name)}') - - self.tensor_tree.clear() - self.tensors.clear() - self.metadata.clear() - - tensor_file = Tensor.load(file_name) - tensors_item = QTreeWidgetItem(self.tensor_tree) - tensors_item.setText(0, 'Tensors') - tensors_item.setIcon(0, self.folder_icon) - for tensor in [tensor_file]: - tensor.name = tensor.name or f'Tensor {len(self.tensors) + 1}' - self.tensors[tensor.name] = tensor - tensor_item = QTreeWidgetItem(tensors_item) - tensor_item.setText(0, tensor.name) - tensor_item.setIcon(0, self.tensor_icon) - - metadata_item = QTreeWidgetItem(self.tensor_tree) - metadata_item.setText(0, 'Metadata') - metadata_item.setIcon(0, self.folder_icon) - metadata = { - 'File Name': os.path.basename(file_name), - 'File Size': os.path.getsize(file_name), - 'File Path': file_name - } - for key, value in metadata.items(): - self.metadata[key] = value - metadata_entry = QTreeWidgetItem(metadata_item) - metadata_entry.setText(0, f'{key}: {value}') - metadata_entry.setIcon(0, self.metadata_icon) - - self.tensor_tree.expandAll() - - def show_tensor_data(self, item): - parent = item.parent() - if parent is None or parent.text(0) != 'Tensors': - return - - tensor_name = item.text(0) - if tensor_name not in self.tensors: - return - tensor = self.tensors[tensor_name] - tensor_data = tensor.tolist() - - rows = [] - elements_per_row = 16 - for i in range(0, len(tensor_data), elements_per_row): - row = ' '.join(f'{value:10.5f}' for value in tensor_data[i:i + elements_per_row]) - rows.append(row) - data_str = '\n'.join(rows) - self.data_view.setPlainText(data_str) - - extra_info = [ - f'Name: {tensor.name}', - f'Dimensions: {tensor.shape}', - f'Strides: {tensor.strides}', - f'DType: {tensor.dtype}', - f'Rank: {tensor.rank}', - f'Total Elements: {tensor.numel}', - f'Total Bytes: {tensor.data_size}', - f'Min: {min(tensor_data)}', - f'Max: {max(tensor_data)}', - f'Mean: {sum(tensor_data) / len(tensor_data)}', - f'Transposed: {tensor.is_transposed}', - f'Permuted: {tensor.is_permuted}', - f'Contiguous: {tensor.is_contiguous}', - ] - self.info_panel.setText('\n'.join(extra_info)) - - -def main(): - app = QApplication(sys.argv) - viewer = MAGNETRONViewer() - viewer.show() - sys.exit(app.exec_()) - - -if __name__ == '__main__': - main() diff --git a/python/magnetron_viewer/requirements.txt b/python/magnetron_viewer/requirements.txt deleted file mode 100644 index 067539d..0000000 --- a/python/magnetron_viewer/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -pyqt5 \ No newline at end of file diff --git a/python/optional-requirements.txt b/python/optional-requirements.txt new file mode 100644 index 0000000..d4313a0 --- /dev/null +++ b/python/optional-requirements.txt @@ -0,0 +1,13 @@ +# Dependencies for developing Magnetron (including testing, benchmarks, and docs). +# Runtime usage requires *only* CFFI. + +cffi +wheel +setuptools +pytest +numpy +matplotlib +sphinx +sphinx-autoapi +sphinx-rtd-theme +ruff diff --git a/python/requirements.txt b/python/requirements.txt deleted file mode 100644 index 4e74689..0000000 --- a/python/requirements.txt +++ /dev/null @@ -1,13 +0,0 @@ -# !! Requirements for full Magnetron development (Magnetron Core, Unit Tests, Benchmarks, Documentation) -# !! Magnetron itself requires *only* cffi. -# !! See magnetron_framework/requirements.txt for the production requirements. - -cffi -wheel -setuptools -pytest -numpy -matplotlib -sphinx -sphinx-autoapi -sphinx-rtd-theme From 71f8cad27bf569c78517c78b5bbbd2d5d7a8215e Mon Sep 17 00:00:00 2001 From: Neo Date: Fri, 31 Jan 2025 03:47:03 +0100 Subject: [PATCH 12/24] Invariand multiplication proofs and tests --- benchmark/benchmarks.cpp | 54 ++++++++++++++++++++++++++++++++++ magnetron/magnetron_internal.h | 22 +++++++++++++- test/unit/ivdiv_proof.cpp | 27 +++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 test/unit/ivdiv_proof.cpp diff --git a/benchmark/benchmarks.cpp b/benchmark/benchmarks.cpp index c4a1570..8222170 100644 --- a/benchmark/benchmarks.cpp +++ b/benchmark/benchmarks.cpp @@ -50,6 +50,60 @@ static auto bench_cpu_compute(std::int64_t numel_per_dim) -> void { } auto main() -> int { + std::vector data {}; + std::uniform_int_distribution dist(1, UINT32_MAX); + std::mt19937 gen(std::random_device{}()); + int N = 10000; + data.reserve(N); + for (int i=0; i setups {}; + setups.reserve(N); + for (int i=0; i 1 ? 1 : l&0xff; + uint32_t s2 = !l ? 0 : (l-1)&0xff; + return ((((1ull<>32)>>32; + return (t + ((x - t)>>((di>>8)&0xff)))>>(di&0xff); +} +static inline uint32_t mag_ivrem32(uint32_t x, uint32_t y, uint64_t ctx) { /* r = x % y. Fast remainder using invariant multiplication */ + return x - y*mag_ivdiv32(x, y, ctx); +} + #ifdef _WIN32 typedef DWORD mag_thread_ret_t; diff --git a/test/unit/ivdiv_proof.cpp b/test/unit/ivdiv_proof.cpp new file mode 100644 index 0000000..399fa28 --- /dev/null +++ b/test/unit/ivdiv_proof.cpp @@ -0,0 +1,27 @@ +// (c) 2025 Mario "Neo" Sieg. + +#include "prelude.hpp" + +// Some bruteforce exhaustive "inductive" proofs that the invariant division impl is correct for all x and y in {0, ..., (2^32)-1} \ {0} for y + +TEST(ivdiv_proof, proof_div32) { + for (std::uint32_t x {}; x < std::numeric_limits::max(); ++x) { + for (std::uint32_t y {1}; y < std::numeric_limits::max(); ++y) { + if ((x / y) != mag_ivdiv32(x, y, mag_ivdiv_mkdi(y))) [[unlikely]] { + std::cout << x << " / " << y << std::endl; + ASSERT_EQ((x / y), mag_ivdiv32(x, y, mag_ivdiv_mkdi(y))); + } + } + } +} + +TEST(ivdiv_proof, proof_rem32) { + for (std::uint32_t x {}; x < std::numeric_limits::max(); ++x) { + for (std::uint32_t y {1}; y < std::numeric_limits::max(); ++y) { + if ((x % y) != mag_ivrem32(x, y, mag_ivdiv_mkdi(y))) [[unlikely]] { + std::cout << x << " / " << y << std::endl; + ASSERT_EQ((x % y), mag_ivrem32(x, y, mag_ivdiv_mkdi(y))); + } + } + } +} From ba10b88448bddc2aa70ab1e416eacbc66af948bd Mon Sep 17 00:00:00 2001 From: "Mario Sieg (Neo)" Date: Fri, 31 Jan 2025 04:00:50 +0100 Subject: [PATCH 13/24] parallel invariant proof --- test/CMakeLists.txt | 4 ++++ test/invariant_proof.cpp | 40 ++++++++++++++++++++++++++++++++++++++ test/unit/ivdiv_proof.cpp | 27 ------------------------- test_data/test.magnetron | Bin 0 -> 6536 bytes 4 files changed, 44 insertions(+), 27 deletions(-) create mode 100644 test/invariant_proof.cpp delete mode 100644 test/unit/ivdiv_proof.cpp create mode 100644 test_data/test.magnetron diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1948e81..342991b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -5,3 +5,7 @@ add_subdirectory(unit) add_executable(magnetron_santity_test santiy_test.c) target_link_libraries(magnetron_santity_test magnetron) target_include_directories(magnetron_santity_test PRIVATE ../magnetron) + +add_executable(magnetron_invariant_proof invariant_proof.cpp) +target_link_libraries(magnetron_invariant_proof magnetron) +target_include_directories(magnetron_invariant_proof PRIVATE ../magnetron) diff --git a/test/invariant_proof.cpp b/test/invariant_proof.cpp new file mode 100644 index 0000000..c220ef2 --- /dev/null +++ b/test/invariant_proof.cpp @@ -0,0 +1,40 @@ +// (c) 2025 Mario "Neo" Sieg. + +/* +** Some bruteforce exhaustive "inductive" proofs that the invariant division impl is correct for +** all x and y in {0, ..., (2^32)-1} \ {0} for y +*/ + +#include + +#include +#include +#include +#include +#include + +static constexpr std::uint32_t lim = std::numeric_limits::max(); + +static auto proof_division(std::uint32_t start, std::uint32_t end) -> void { + for (std::uint32_t x = start; x < end; ++x) { + for (std::uint32_t y = 1; y < lim; ++y) { + if ((x / y) != mag_ivdiv32(x, y, mag_ivdiv_mkdi(y))) [[unlikely]] { + std::cout << x << " / " << y << std::endl; + std::abort(); + } + } + } +} + +auto main() -> int { + std::uint32_t nt = std::max(1u, std::thread::hardware_concurrency()); + std::vector threads {}; + std::uint32_t chunk = lim / nt; + for (std::uint32_t i = 0; i < nt; ++i) { + std::uint32_t start = i*chunk; + std::uint32_t end = (i == nt - 1) ? lim : start + chunk; + threads.emplace_back(proof_division, start, end); + } + for (auto&& t : threads) t.join(); + return EXIT_SUCCESS; +} diff --git a/test/unit/ivdiv_proof.cpp b/test/unit/ivdiv_proof.cpp deleted file mode 100644 index 399fa28..0000000 --- a/test/unit/ivdiv_proof.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// (c) 2025 Mario "Neo" Sieg. - -#include "prelude.hpp" - -// Some bruteforce exhaustive "inductive" proofs that the invariant division impl is correct for all x and y in {0, ..., (2^32)-1} \ {0} for y - -TEST(ivdiv_proof, proof_div32) { - for (std::uint32_t x {}; x < std::numeric_limits::max(); ++x) { - for (std::uint32_t y {1}; y < std::numeric_limits::max(); ++y) { - if ((x / y) != mag_ivdiv32(x, y, mag_ivdiv_mkdi(y))) [[unlikely]] { - std::cout << x << " / " << y << std::endl; - ASSERT_EQ((x / y), mag_ivdiv32(x, y, mag_ivdiv_mkdi(y))); - } - } - } -} - -TEST(ivdiv_proof, proof_rem32) { - for (std::uint32_t x {}; x < std::numeric_limits::max(); ++x) { - for (std::uint32_t y {1}; y < std::numeric_limits::max(); ++y) { - if ((x % y) != mag_ivrem32(x, y, mag_ivdiv_mkdi(y))) [[unlikely]] { - std::cout << x << " / " << y << std::endl; - ASSERT_EQ((x % y), mag_ivrem32(x, y, mag_ivdiv_mkdi(y))); - } - } - } -} diff --git a/test_data/test.magnetron b/test_data/test.magnetron new file mode 100644 index 0000000000000000000000000000000000000000..e8638e2b30d40cc8206987223f085c551fe279e3 GIT binary patch literal 6536 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�KYacqGDs zb7GbX1H-$(bdM#P2D@?Ag#Uo`!yDx$4#{HpLS-IW{SG2`E#Uy^omefUharD_g+dii z&iCBRCENa>u;y}6@$dEc;IujP4m=SHDjJ~sNF&7+YnK<=cyej~CbTb(Ajkj2ll8fW zk`K>KIQ&o9k-kLW!40#y&`FxhV#a{1_a}-iUX5i|*5Kg0giY^Dar99IZakh&jT4?i zn&Uq7y|!Oy(_0`mBzj;|uOiyx7D$qnaEtpj_#iu-vyR|67uyJ>(zSewP183p7x@enBOOQhJ6b*zhXQUCZhELE|^o~Jue z=9V@3Dm#f;TR+qA;Ypk^!Io=QxMSNJZ-M5xaL>K~cDzvl;TK10f7&o8OBUmzzs6y4 zuM3uHDxi<+0v!Iq26aAo@PL*j4VUfX5~;6{@^}qX*&Nibp3imFVI|J2QJ}jP%pL?p3m9EP5~=9LwO&L*dv%b`5nZ(ctXm;WPto^>;v7^;W8~N~T!LRTzD0H*5OuhNQVVJTxJL?5D+0htqxN?LI5k zj_Rbq849G}Jryd_-j#=0KNc?R)aB~_9`C1` zMtjt_WWdc?k>njxPD3|7k=v8g5T3ae``2zk#lH@4g8VNOFkvxA=PzW(urlzt9}K24 z&Y14_h&pqW(C_OutUXai2_D6e^4kXX-`7QXMWaaJKm-{2=%G?^CaF1JhpC0F&}OM4 zOt+aYfuhw3ML@a>+N)Uv$+R2OdIzMl_6 z!MBN&(XYiSc8`S~V{J5kw~Y;d-@+#OU1CAXH8Om-j5}#G>g|hzEXC90J4Hu~P<{i! z=Yml$rUBxE9oS{UPWFp%!^YdgWU=0t0!GVY{P-0dVRM75cg9Nwwpq~BQ`MmJ%nl=+ z#$(~}t*Cz_2y?WIL`TI!@E+3xsU80cS$`j+;ib#C@k$w-Rr2GZz#qWN(gw-Pf>o}T z2z4*^pl1FQ2wprHDIy;ARUyQtMV-P<0Cy@GXb+H?#Uy|Su3%7%^7O`eF22n zG*am?;HCxh(CMflHfhCjS)(;c*^RngoAiomt&h;zv{^h@tB*(K%5cwOrq(ZE|z)_+PnONrvPHhov|MPBYA6|xy zM=wCzXcWu7_o2$q(=aMt3#6)^Stbq7Wb2>5mKR=LfJz!^Wbus)Bn{5Pep`L+id@Ri zC9>RNpFx4SGN5|j8$rX2W9+{|>$WiNRvChjGg*)=y_e69QpKF#1tGx804sOT;>4fS z!Nl{F_@I9rnC4C9BRiDXA>aVIT!=u`f>`YGSjCIQ3N@ zbbd3B9oo&%`Q5K#@9}?xsQdn4aMGOWT6S^F%AY78-3kk`&q0Op?eePAhaq0Yl_Qrk!p~7=X2j+nD-k7(FrfZ3hnIsWbfc6)!Af~D25 z%*%yR)`ha-MhC1+jzP(Jrf3y6HddaAor+2vY^=;?hwnl4ZwD~(xIpz!>x6aj=x|Si!_!vtqPce5v(S_kXPdE?lP9MAsEvl} z>@Zf<4{ybu Date: Fri, 31 Jan 2025 18:41:05 +0100 Subject: [PATCH 14/24] WIP on example-refractor --- magnetron/magnetron.c | 75 +++++++++--------- magnetron/magnetron.h | 5 +- magnetron/magnetron_internal.h | 13 ++- .../magnetron/_ffi_cdecl_generated.py | 6 +- python/magnetron_framework/magnetron/core.py | 67 ++++++++++------ python/optional-requirements.txt | 1 + python/tests/autograd.py | 30 +++++++ test/unit/autograd.cpp | 31 +++++--- test_data/test.magnetron | Bin 6536 -> 0 bytes 9 files changed, 143 insertions(+), 85 deletions(-) create mode 100644 python/tests/autograd.py delete mode 100644 test_data/test.magnetron diff --git a/magnetron/magnetron.c b/magnetron/magnetron.c index 331f3fb..606ae64 100644 --- a/magnetron/magnetron.c +++ b/magnetron/magnetron.c @@ -48,15 +48,24 @@ void mag_set_log_mode(bool enabled) { mag_log_enabled = enabled; } +static void MAG_COLDPROC mag_panic_dump(FILE* f, bool cc, const char* msg, va_list args) { + if (cc) fprintf(f, "%s", MAG_CC_RED); + vfprintf(f, msg, args); + if (cc) fprintf(f, "%s", MAG_CC_RESET); + fputc('\n', f); + fflush(f); +} + MAG_NORET MAG_COLDPROC MAG_EXPORT void mag_panic(const char* msg, ...) { - fprintf(stdout, "%s", MAG_CC_RED); va_list args; va_start(args, msg); - vfprintf(stdout, msg, args); + FILE* f = fopen("magnetron_panic.log", "w"); + if (f) { + mag_panic_dump(f, false, msg, args); + fclose(f), f = NULL; + } + mag_panic_dump(stdout, true, msg, args); va_end(args); - fprintf(stdout, "%s", MAG_CC_RESET); - fputc('\n', stdout); - fflush(stdout); abort(); } @@ -516,7 +525,6 @@ mag_ctx_t* mag_ctx_create2(const mag_device_descriptor_t* device_info) { mag_fixed_intrusive_pool_init(&ctx->tensor_pool, sizeof(mag_tensor_t), __alignof(mag_tensor_t), 4096); ctx->tr_id = mag_thread_id(); /* Get thread ID. */ - ctx->is_grad_recording = true; /* Enable gradient recording by default. */ /* Query and print host system information. */ mag_system_host_info_query(ctx); @@ -573,14 +581,6 @@ void mag_ctx_destroy(mag_ctx_t* ctx) { mag_log_info("magnetron context destroyed."); } -bool mag_ctx_is_grad_recorder_enabled(const mag_ctx_t* ctx) { - return ctx->is_grad_recording; -} - -void mag_ctx_enable_grad_recorder(mag_ctx_t* ctx, bool enabled) { - ctx->is_grad_recording = enabled; -} - mag_exec_mode_t mag_ctx_get_exec_mode(const mag_ctx_t* ctx) { return ctx->exec_mode; } void mag_ctx_set_exec_mode(mag_ctx_t* ctx, mag_exec_mode_t mode) { @@ -1605,7 +1605,7 @@ static mag_tensor_t* mag_tensor_create(mag_ctx_t* ctx, mag_dtype_t type, const i .dtype = type, .storage = {0}, .numel = numel, - .flags = MAG_TFLAG_REQUIRES_GRAD | (view ? MAG_TFLAG_VIEW : MAG_TFLAG_OWNER), + .flags = (view ? (MAG_TFLAG_VIEW | (view->flags & MAG_TFLAG_REQUIRES_GRAD)) : MAG_TFLAG_OWNER), .op = MAG_OP_NOP, .op_inputs = {0}, .op_params = {{0}}, @@ -2040,14 +2040,7 @@ void mag_tensor_print(const mag_tensor_t* t, bool with_header, bool with_data) { char strides[MAG_FMT_DIM_BUF_SIZE]; mag_fmt_dims(&shape, &t->shape, t->rank); mag_fmt_dims(&strides, &t->strides, MAG_MAX_DIMS); - static const char* flag_abbrs = "OVGE"; - mag_assert2(strlen(flag_abbrs) == MAG_TFLAG_LEN); - char flags[MAG_TFLAG_LEN+1] = {0}; - for (uint32_t i=0, k=0; i < MAG_TFLAG_LEN; ++i) - if (t->flags & (1 << i)) - flags[k++] = flag_abbrs[i]; - flags[MAG_TFLAG_LEN] = '\0'; - fprintf(f, "Tensor '%s', DType: %s, Rank: %" PRIi64 ", Elements: %" PRIi64 ", Shape: %s, Strides: %s, Mem: %.03f %s, Flags: %s (%x)\n", + fprintf(f, "Tensor '%s', DType: %s, Rank: %" PRIi64 ", Elements: %" PRIi64 ", Shape: %s, Strides: %s, Mem: %.03f %s, Flags: 0x%x\n", t->name, mag_dtype_meta_of(t->dtype)->name, t->rank, @@ -2056,7 +2049,6 @@ void mag_tensor_print(const mag_tensor_t* t, bool with_header, bool with_data) { strides, buf_size_cvt, buf_size_unit, - flags, t->flags ); } @@ -2158,10 +2150,15 @@ bool mag_tensor_is_contiguous(const mag_tensor_t* t) { } mag_tensor_t* mag_tensor_grad(const mag_tensor_t* t) { + mag_assert(t->flags & MAG_TFLAG_REQUIRES_GRAD && t->grad && t->grad->flags & MAG_TFLAG_IS_GRAD, "Tensor must require grad and have a valid grad tensor"); return t->grad; } -void mag_tensor_required_grad(mag_tensor_t* t, bool requires_grad) { +bool mag_tensor_requires_grad(const mag_tensor_t* t) { + return t->flags & MAG_TFLAG_REQUIRES_GRAD; +} + +void mag_tensor_set_requires_grad(mag_tensor_t* t, bool requires_grad) { if (requires_grad) t->flags |= MAG_TFLAG_REQUIRES_GRAD; else t->flags &= ~MAG_TFLAG_REQUIRES_GRAD; } @@ -2258,6 +2255,7 @@ static void mag_collect_topo_iterative(mag_tensor_t* root, mag_tensor_array_t* o } void mag_tensor_backward(mag_tensor_t* t) { + mag_assert(t->flags & MAG_TFLAG_REQUIRES_GRAD, "Tensor must require grad to backpropagate"); mag_tensor_array_t post_order; mag_tensor_array_init(&post_order); mag_collect_topo_iterative(t, &post_order); @@ -2268,7 +2266,7 @@ void mag_tensor_backward(mag_tensor_t* t) { t = post_order.data[id]; if (id == 0 && !t->grad) { mag_tensor_t* grad = mag_tensor_create(t->ctx, t->dtype, t->shape, t->rank, NULL, 0); - grad->flags |= MAG_TFLAG_IS_GRAD; + grad->flags = (grad->flags | MAG_TFLAG_IS_GRAD) & ~MAG_TFLAG_REQUIRES_GRAD; mag_tensor_fill(grad, 1.0f); t->grad = grad; } @@ -2276,30 +2274,32 @@ void mag_tensor_backward(mag_tensor_t* t) { mag_tensor_t* grads[MAG_MAX_OP_PARAMS] = {0}; mag_tensor_t* gout = t->grad; switch (t->op) { + case MAG_OP_CLONE: { + grads[0] = mag_clone(gout); + } break; + case MAG_OP_VIEW: { + grads[0] = mag_clone(gout); + } break; case MAG_OP_RELU: { mag_tensor_t* x = t->op_inputs[0]; mag_tensor_t* mask = mag_step(x); grads[0] = mag_mul(gout, mask); mag_tensor_decref(mask); - break; - } + } break; case MAG_OP_ADD: { grads[0] = mag_clone(gout); grads[1] = mag_clone(gout); - break; - } + } break; case MAG_OP_SUB: { grads[0] = mag_clone(gout); grads[1] = mag_neg(gout); - break; - } + } break; case MAG_OP_MUL: { mag_tensor_t* x = t->op_inputs[0]; mag_tensor_t* y = t->op_inputs[1]; grads[0] = mag_mul(gout, y); grads[1] = mag_mul(gout, x); - break; - } + } break; case MAG_OP_DIV: { mag_tensor_t* x = t->op_inputs[0]; mag_tensor_t* y = t->op_inputs[1]; @@ -2311,9 +2311,8 @@ void mag_tensor_backward(mag_tensor_t* t) { mag_tensor_decref(tmp); mag_tensor_decref(y2); mag_tensor_decref(tmp2); - break; - } - default: mag_panic("Unsupported op in backward!"); + } break; + default: mag_panic("Unsupported op in backward: %s", mag_op_meta_of(t->op)->mnemonic); } uint32_t numin = mag_op_meta_of(t->op)->argcount; for (uint32_t i = 0; i < numin; ++i) { @@ -2323,10 +2322,12 @@ void mag_tensor_backward(mag_tensor_t* t) { mag_tensor_t* gri = grads[i]; if (!gri) continue; if (!input->grad) { + gri->flags = (gri->flags | MAG_TFLAG_IS_GRAD) & ~MAG_TFLAG_REQUIRES_GRAD; input->grad = gri; } else { mag_tensor_t* acc = mag_add(input->grad, gri); mag_tensor_decref(input->grad); + acc->flags = (acc->flags | MAG_TFLAG_IS_GRAD) & ~MAG_TFLAG_REQUIRES_GRAD; input->grad = acc; mag_tensor_decref(gri); } diff --git a/magnetron/magnetron.h b/magnetron/magnetron.h index b7fd606..af9c7a8 100644 --- a/magnetron/magnetron.h +++ b/magnetron/magnetron.h @@ -96,8 +96,6 @@ typedef struct mag_device_descriptor_t { extern MAG_EXPORT mag_ctx_t* mag_ctx_create(mag_compute_device_type_t device); /* Create context with default config, and only specify device type. */ extern MAG_EXPORT mag_ctx_t* mag_ctx_create2(const mag_device_descriptor_t* device_info); /* Create context with customized device config, and only specify device type. */ -extern MAG_EXPORT bool mag_ctx_is_grad_recorder_enabled(const mag_ctx_t* ctx); /* Check if gradient recording is enabled */ -extern MAG_EXPORT void mag_ctx_enable_grad_recorder(mag_ctx_t* ctx, bool enabled); /* Enable/disable gradient recording */ extern MAG_EXPORT mag_exec_mode_t mag_ctx_get_exec_mode(const mag_ctx_t* ctx); /* Get execution mode */ extern MAG_EXPORT void mag_ctx_set_exec_mode(mag_ctx_t* ctx, mag_exec_mode_t mode); /* Set execution mode */ extern MAG_EXPORT mag_prng_algorithm_t mag_ctx_get_prng_algorithm(const mag_ctx_t* ctx); /* Get PRNG algorithm */ @@ -353,7 +351,8 @@ extern MAG_EXPORT bool mag_tensor_is_permuted(const mag_tensor_t* t); /* Check i extern MAG_EXPORT bool mag_tensor_is_contiguous(const mag_tensor_t* t); /* Check if the tensor memory is contiguous */ extern MAG_EXPORT mag_tensor_t* mag_tensor_grad(const mag_tensor_t* t); /* Get the gradient tensor of the tensor */ -extern MAG_EXPORT void mag_tensor_required_grad(mag_tensor_t* t, bool requires_grad); /* Set if the tensor requires gradient computation */ +extern MAG_EXPORT bool mag_tensor_requires_grad(const mag_tensor_t* t); /* Check if the tensor requires gradient computation */ +extern MAG_EXPORT void mag_tensor_set_requires_grad(mag_tensor_t* t, bool requires_grad); /* Set if the tensor requires gradient computation */ extern MAG_EXPORT void mag_tensor_backward(mag_tensor_t* t); /* Compute the gradient of the tensor */ extern MAG_EXPORT float mag_tensor_get_scalar_physical_index(mag_tensor_t* t, int64_t d0, int64_t d1, int64_t d2, int64_t d3, int64_t d4, int64_t d5); /* Get scalar value at physical index */ diff --git a/magnetron/magnetron_internal.h b/magnetron/magnetron_internal.h index e49a814..c03015a 100644 --- a/magnetron/magnetron_internal.h +++ b/magnetron/magnetron_internal.h @@ -708,7 +708,6 @@ struct mag_ctx_t { mag_fixed_intrusive_pool tensor_pool; /* Fixed-size memory pool for tensors. */ mag_exec_mode_t exec_mode; bool is_profiling; - bool is_grad_recording; mag_op_perf_info_t op_perf_mons_total[MAG_OP__NUM]; union { struct { @@ -735,13 +734,13 @@ struct mag_ctx_t { typedef enum mag_tensor_flags_t { MAG_TFLAG_NONE = 0, - MAG_TFLAG_OWNER = 1<<0, /* Tensor is the owner of the buffer. */ - MAG_TFLAG_VIEW = 1<<1, /* Tensor is a view. */ - MAG_TFLAG_IS_GRAD = 1<<2, /* Tensor is a gradient. */ - MAG_TFLAG_EXEC_EAGER = 1<<3, /* Tensor is executed eagerly. */ - MAG_TFLAG_REQUIRES_GRAD = 1<<4, /* Tensor requires gradient. */ + MAG_TFLAG_OWNER = 1<<0, /* Tensor is the owner of the buffer. */ + MAG_TFLAG_VIEW = 1<<1, /* Tensor is a view. */ + MAG_TFLAG_IS_GRAD = 1<<2, /* Tensor is a gradient. */ + MAG_TFLAG_EXEC_EAGER = 1<<3, /* Tensor is executed eagerly. */ + MAG_TFLAG_REQUIRES_GRAD = 1<<4, /* Tensor requires gradient. */ - MAG_TFLAG_LEN = 4 + MAG_TFLAG_LEN = 5 /* Number of flags. */ } mag_tensor_flags_t; mag_static_assert(MAG_TFLAG_LEN <= 0xff); diff --git a/python/magnetron_framework/magnetron/_ffi_cdecl_generated.py b/python/magnetron_framework/magnetron/_ffi_cdecl_generated.py index 41c09ed..61b1cb2 100644 --- a/python/magnetron_framework/magnetron/_ffi_cdecl_generated.py +++ b/python/magnetron_framework/magnetron/_ffi_cdecl_generated.py @@ -1,4 +1,2 @@ -# Autogenered by /home/mario/Documents/remote_projects/magnetron/python/magnetron_framework/bing_gen.py 2025-01-29 14:41:12.407408, do NOT edit! -__MAG_CDECLS: str = b'\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x43\x4f\x4d\x50\x55\x54\x45\x5f\x44\x45\x56\x49\x43\x45\x5f\x54\x59\x50\x45\x5f\x43\x50\x55\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4d\x50\x55\x54\x45\x5f\x44\x45\x56\x49\x43\x45\x5f\x54\x59\x50\x45\x5f\x47\x50\x55\x5f\x43\x55\x44\x41\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4d\x50\x55\x54\x45\x5f\x44\x45\x56\x49\x43\x45\x5f\x54\x59\x50\x45\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x67\x65\x74\x5f\x6e\x61\x6d\x65\x28\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x6f\x70\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x45\x58\x45\x43\x5f\x4d\x4f\x44\x45\x5f\x45\x41\x47\x45\x52\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x45\x58\x45\x43\x5f\x4d\x4f\x44\x45\x5f\x44\x45\x46\x45\x52\x52\x45\x44\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x45\x58\x45\x43\x5f\x4d\x4f\x44\x45\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x50\x52\x4e\x47\x5f\x4d\x45\x52\x53\x45\x4e\x4e\x45\x5f\x54\x57\x49\x53\x54\x45\x52\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x50\x52\x4e\x47\x5f\x50\x43\x47\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x50\x52\x4e\x47\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x74\x68\x72\x65\x61\x64\x5f\x73\x63\x68\x65\x64\x5f\x70\x72\x69\x6f\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x4e\x4f\x52\x4d\x41\x4c\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x4d\x45\x44\x49\x55\x4d\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x48\x49\x47\x48\x20\x3d\x20\x32\x2c\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x52\x45\x41\x4c\x54\x49\x4d\x45\x20\x3d\x20\x33\x2c\x0a\x7d\x20\x6d\x61\x67\x5f\x74\x68\x72\x65\x61\x64\x5f\x73\x63\x68\x65\x64\x5f\x70\x72\x69\x6f\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x63\x6f\x6c\x6f\x72\x5f\x63\x68\x61\x6e\x6e\x65\x6c\x73\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x41\x55\x54\x4f\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x47\x52\x41\x59\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x47\x52\x41\x59\x5f\x41\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x52\x47\x42\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x52\x47\x42\x41\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x63\x6f\x6c\x6f\x72\x5f\x63\x68\x61\x6e\x6e\x65\x6c\x73\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x2a\x20\x28\x2a\x6d\x61\x67\x5f\x67\x65\x74\x5f\x61\x6c\x6c\x6f\x63\x5f\x66\x6e\x28\x76\x6f\x69\x64\x29\x29\x28\x76\x6f\x69\x64\x2a\x20\x62\x6c\x6b\x2c\x20\x73\x69\x7a\x65\x5f\x74\x20\x73\x69\x7a\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x73\x65\x74\x5f\x61\x6c\x6c\x6f\x63\x5f\x66\x6e\x28\x76\x6f\x69\x64\x2a\x20\x28\x2a\x61\x6c\x6c\x6f\x63\x29\x28\x76\x6f\x69\x64\x2a\x20\x62\x6c\x6b\x2c\x20\x73\x69\x7a\x65\x5f\x74\x20\x73\x69\x7a\x65\x29\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x73\x65\x74\x5f\x6c\x6f\x67\x5f\x6d\x6f\x64\x65\x28\x62\x6f\x6f\x6c\x20\x65\x6e\x61\x62\x6c\x65\x64\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x68\x61\x72\x33\x32\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x64\x65\x73\x63\x72\x69\x70\x74\x6f\x72\x5f\x74\x20\x7b\x0a\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x3b\x0a\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x74\x68\x72\x65\x61\x64\x5f\x63\x6f\x75\x6e\x74\x3b\x0a\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x63\x75\x64\x61\x5f\x64\x65\x76\x69\x63\x65\x5f\x69\x64\x3b\x0a\x7d\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x64\x65\x73\x63\x72\x69\x70\x74\x6f\x72\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x63\x72\x65\x61\x74\x65\x28\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x64\x65\x76\x69\x63\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x63\x72\x65\x61\x74\x65\x32\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x64\x65\x73\x63\x72\x69\x70\x74\x6f\x72\x5f\x74\x2a\x20\x64\x65\x76\x69\x63\x65\x5f\x69\x6e\x66\x6f\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x73\x65\x74\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x20\x6d\x6f\x64\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x73\x65\x74\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x2c\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x73\x65\x65\x64\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x6f\x73\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x76\x69\x72\x74\x75\x61\x6c\x5f\x63\x6f\x72\x65\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x63\x6f\x72\x65\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x73\x6f\x63\x6b\x65\x74\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x6d\x65\x6d\x6f\x72\x79\x5f\x74\x6f\x74\x61\x6c\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x6d\x65\x6d\x6f\x72\x79\x5f\x66\x72\x65\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x69\x73\x5f\x6e\x75\x6d\x61\x5f\x73\x79\x73\x74\x65\x6d\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x73\x69\x7a\x65\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x74\x6f\x74\x61\x6c\x5f\x74\x65\x6e\x73\x6f\x72\x73\x5f\x63\x72\x65\x61\x74\x65\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x70\x72\x6f\x66\x69\x6c\x65\x5f\x73\x74\x61\x72\x74\x5f\x72\x65\x63\x6f\x72\x64\x69\x6e\x67\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x70\x72\x6f\x66\x69\x6c\x65\x5f\x73\x74\x6f\x70\x5f\x72\x65\x63\x6f\x72\x64\x69\x6e\x67\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x65\x78\x70\x6f\x72\x74\x5f\x63\x73\x76\x5f\x66\x69\x6c\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x64\x65\x73\x74\x72\x6f\x79\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x44\x54\x59\x50\x45\x5f\x46\x33\x32\x2c\x0a\x4d\x41\x47\x5f\x44\x54\x59\x50\x45\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x74\x20\x7b\x0a\x69\x6e\x74\x36\x34\x5f\x74\x20\x73\x69\x7a\x65\x3b\x0a\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6e\x61\x6d\x65\x3b\x0a\x7d\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x6f\x66\x28\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x70\x61\x63\x6b\x5f\x63\x6f\x6c\x6f\x72\x5f\x75\x38\x28\x75\x69\x6e\x74\x38\x5f\x74\x20\x72\x2c\x20\x75\x69\x6e\x74\x38\x5f\x74\x20\x67\x2c\x20\x75\x69\x6e\x74\x38\x5f\x74\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x70\x61\x63\x6b\x5f\x63\x6f\x6c\x6f\x72\x5f\x66\x33\x32\x28\x66\x6c\x6f\x61\x74\x20\x72\x2c\x20\x66\x6c\x6f\x61\x74\x20\x67\x2c\x20\x66\x6c\x6f\x61\x74\x20\x62\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x67\x72\x61\x70\x68\x5f\x65\x76\x61\x6c\x5f\x6f\x72\x64\x65\x72\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x47\x52\x41\x50\x48\x5f\x45\x56\x41\x4c\x5f\x4f\x52\x44\x45\x52\x5f\x46\x4f\x52\x57\x41\x52\x44\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x47\x52\x41\x50\x48\x5f\x45\x56\x41\x4c\x5f\x4f\x52\x44\x45\x52\x5f\x52\x45\x56\x45\x52\x53\x45\x20\x3d\x20\x31\x0a\x7d\x20\x6d\x61\x67\x5f\x67\x72\x61\x70\x68\x5f\x65\x76\x61\x6c\x5f\x6f\x72\x64\x65\x72\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x31\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x32\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x33\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x34\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x35\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x36\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x36\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x6c\x6f\x6e\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x76\x69\x65\x77\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x72\x61\x6e\x73\x70\x6f\x73\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x70\x65\x72\x6d\x75\x74\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x30\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x31\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x32\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x33\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x34\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x35\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x65\x61\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x69\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x61\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x6d\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x62\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x62\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6e\x65\x67\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6e\x65\x67\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6c\x6f\x67\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6c\x6f\x67\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x74\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x74\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6e\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x6f\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x6f\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x74\x65\x70\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x74\x65\x70\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x65\x78\x70\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x65\x78\x70\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x68\x61\x72\x64\x5f\x73\x69\x67\x6d\x6f\x69\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x68\x61\x72\x64\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x28\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x28\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x73\x28\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x70\x6f\x77\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x70\x6f\x77\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x61\x74\x6d\x75\x6c\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x6e\x63\x72\x65\x66\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x65\x63\x72\x65\x66\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x6f\x70\x79\x5f\x62\x75\x66\x66\x65\x72\x5f\x66\x72\x6f\x6d\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x76\x6f\x69\x64\x2a\x20\x64\x61\x74\x61\x2c\x20\x73\x69\x7a\x65\x5f\x74\x20\x73\x69\x7a\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x69\x6c\x6c\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x69\x6c\x6c\x5f\x72\x61\x6e\x64\x6f\x6d\x5f\x75\x6e\x69\x66\x6f\x72\x6d\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x66\x6c\x6f\x61\x74\x20\x6d\x69\x6e\x2c\x20\x66\x6c\x6f\x61\x74\x20\x6d\x61\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x69\x6c\x6c\x5f\x72\x61\x6e\x64\x6f\x6d\x5f\x6e\x6f\x72\x6d\x61\x6c\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x66\x6c\x6f\x61\x74\x20\x6d\x65\x61\x6e\x2c\x20\x66\x6c\x6f\x61\x74\x20\x73\x74\x64\x64\x65\x76\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x70\x61\x63\x6b\x65\x64\x5f\x72\x65\x66\x63\x6f\x75\x6e\x74\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x72\x65\x74\x61\x69\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x73\x69\x7a\x65\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x6d\x65\x6d\x6f\x72\x79\x5f\x75\x73\x61\x67\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x70\x72\x69\x6e\x74\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x62\x6f\x6f\x6c\x20\x77\x69\x74\x68\x5f\x68\x65\x61\x64\x65\x72\x2c\x20\x62\x6f\x6f\x6c\x20\x77\x69\x74\x68\x5f\x64\x61\x74\x61\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x6e\x61\x6d\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6e\x61\x6d\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x6d\x74\x5f\x6e\x61\x6d\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x6d\x74\x2c\x20\x2e\x2e\x2e\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x72\x61\x6e\x6b\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x69\x6e\x74\x36\x34\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x68\x61\x70\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x69\x6e\x74\x36\x34\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x74\x72\x69\x64\x65\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x74\x79\x70\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x61\x74\x61\x5f\x70\x74\x72\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x61\x74\x61\x5f\x73\x69\x7a\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6e\x75\x6d\x65\x6c\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6e\x75\x6d\x5f\x72\x6f\x77\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6e\x75\x6d\x5f\x63\x6f\x6c\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x73\x63\x61\x6c\x61\x72\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x76\x65\x63\x74\x6f\x72\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x6d\x61\x74\x72\x69\x78\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x76\x6f\x6c\x75\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x73\x68\x61\x70\x65\x5f\x65\x71\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x61\x72\x65\x5f\x73\x74\x72\x69\x64\x65\x73\x5f\x65\x71\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x61\x6e\x5f\x62\x72\x6f\x61\x64\x63\x61\x73\x74\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x74\x72\x61\x6e\x73\x70\x6f\x73\x65\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x70\x65\x72\x6d\x75\x74\x65\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x63\x6f\x6e\x74\x69\x67\x75\x6f\x75\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x66\x6c\x6f\x61\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x30\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x30\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x66\x6c\x6f\x61\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x76\x69\x72\x74\x75\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x76\x5f\x69\x64\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x76\x69\x72\x74\x75\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x76\x5f\x69\x64\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x65\x71\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x63\x6c\x6f\x73\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x2c\x20\x66\x6c\x6f\x61\x74\x20\x65\x70\x73\x2c\x20\x64\x6f\x75\x62\x6c\x65\x2a\x20\x70\x65\x72\x63\x65\x6e\x74\x5f\x65\x71\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x6d\x67\x5f\x64\x72\x61\x77\x5f\x62\x6f\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x78\x31\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x79\x31\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x78\x32\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x79\x32\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x77\x69\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x67\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x6d\x67\x5f\x64\x72\x61\x77\x5f\x74\x65\x78\x74\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x78\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x79\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x73\x69\x7a\x65\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x67\x62\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x74\x78\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x63\x74\x78\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x75\x73\x65\x72\x5f\x64\x61\x74\x61\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x75\x73\x65\x72\x5f\x64\x61\x74\x61\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x76\x6f\x69\x64\x2a\x20\x75\x64\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x61\x76\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6c\x6f\x61\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6c\x6f\x61\x64\x5f\x69\x6d\x61\x67\x65\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x2c\x20\x6d\x61\x67\x5f\x63\x6f\x6c\x6f\x72\x5f\x63\x68\x61\x6e\x6e\x65\x6c\x73\x5f\x74\x20\x63\x68\x61\x6e\x6e\x65\x6c\x73\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x65\x73\x69\x7a\x65\x5f\x77\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x65\x73\x69\x7a\x65\x5f\x68\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x61\x76\x65\x5f\x69\x6d\x61\x67\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x29\x3b\x0a'.decode( - 'utf-8' -) +# Autogenered by /Users/mariosieg/Documents/projects/magnetron/python/magnetron_framework/bing_gen.py 2025-01-31 18:00:29.869744, do NOT edit! +__MAG_CDECLS: str = b'\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x43\x4f\x4d\x50\x55\x54\x45\x5f\x44\x45\x56\x49\x43\x45\x5f\x54\x59\x50\x45\x5f\x43\x50\x55\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4d\x50\x55\x54\x45\x5f\x44\x45\x56\x49\x43\x45\x5f\x54\x59\x50\x45\x5f\x47\x50\x55\x5f\x43\x55\x44\x41\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4d\x50\x55\x54\x45\x5f\x44\x45\x56\x49\x43\x45\x5f\x54\x59\x50\x45\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x67\x65\x74\x5f\x6e\x61\x6d\x65\x28\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x6f\x70\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x45\x58\x45\x43\x5f\x4d\x4f\x44\x45\x5f\x45\x41\x47\x45\x52\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x45\x58\x45\x43\x5f\x4d\x4f\x44\x45\x5f\x44\x45\x46\x45\x52\x52\x45\x44\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x45\x58\x45\x43\x5f\x4d\x4f\x44\x45\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x50\x52\x4e\x47\x5f\x4d\x45\x52\x53\x45\x4e\x4e\x45\x5f\x54\x57\x49\x53\x54\x45\x52\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x50\x52\x4e\x47\x5f\x50\x43\x47\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x50\x52\x4e\x47\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x74\x68\x72\x65\x61\x64\x5f\x73\x63\x68\x65\x64\x5f\x70\x72\x69\x6f\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x4e\x4f\x52\x4d\x41\x4c\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x4d\x45\x44\x49\x55\x4d\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x48\x49\x47\x48\x20\x3d\x20\x32\x2c\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x52\x45\x41\x4c\x54\x49\x4d\x45\x20\x3d\x20\x33\x2c\x0a\x7d\x20\x6d\x61\x67\x5f\x74\x68\x72\x65\x61\x64\x5f\x73\x63\x68\x65\x64\x5f\x70\x72\x69\x6f\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x63\x6f\x6c\x6f\x72\x5f\x63\x68\x61\x6e\x6e\x65\x6c\x73\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x41\x55\x54\x4f\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x47\x52\x41\x59\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x47\x52\x41\x59\x5f\x41\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x52\x47\x42\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x52\x47\x42\x41\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x63\x6f\x6c\x6f\x72\x5f\x63\x68\x61\x6e\x6e\x65\x6c\x73\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x2a\x20\x28\x2a\x6d\x61\x67\x5f\x67\x65\x74\x5f\x61\x6c\x6c\x6f\x63\x5f\x66\x6e\x28\x76\x6f\x69\x64\x29\x29\x28\x76\x6f\x69\x64\x2a\x20\x62\x6c\x6b\x2c\x20\x73\x69\x7a\x65\x5f\x74\x20\x73\x69\x7a\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x73\x65\x74\x5f\x61\x6c\x6c\x6f\x63\x5f\x66\x6e\x28\x76\x6f\x69\x64\x2a\x20\x28\x2a\x61\x6c\x6c\x6f\x63\x29\x28\x76\x6f\x69\x64\x2a\x20\x62\x6c\x6b\x2c\x20\x73\x69\x7a\x65\x5f\x74\x20\x73\x69\x7a\x65\x29\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x73\x65\x74\x5f\x6c\x6f\x67\x5f\x6d\x6f\x64\x65\x28\x62\x6f\x6f\x6c\x20\x65\x6e\x61\x62\x6c\x65\x64\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x68\x61\x72\x33\x32\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x64\x65\x73\x63\x72\x69\x70\x74\x6f\x72\x5f\x74\x20\x7b\x0a\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x3b\x0a\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x74\x68\x72\x65\x61\x64\x5f\x63\x6f\x75\x6e\x74\x3b\x0a\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x63\x75\x64\x61\x5f\x64\x65\x76\x69\x63\x65\x5f\x69\x64\x3b\x0a\x7d\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x64\x65\x73\x63\x72\x69\x70\x74\x6f\x72\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x63\x72\x65\x61\x74\x65\x28\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x64\x65\x76\x69\x63\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x63\x72\x65\x61\x74\x65\x32\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x64\x65\x73\x63\x72\x69\x70\x74\x6f\x72\x5f\x74\x2a\x20\x64\x65\x76\x69\x63\x65\x5f\x69\x6e\x66\x6f\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x73\x65\x74\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x20\x6d\x6f\x64\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x73\x65\x74\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x2c\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x73\x65\x65\x64\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x6f\x73\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x76\x69\x72\x74\x75\x61\x6c\x5f\x63\x6f\x72\x65\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x63\x6f\x72\x65\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x73\x6f\x63\x6b\x65\x74\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x6d\x65\x6d\x6f\x72\x79\x5f\x74\x6f\x74\x61\x6c\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x6d\x65\x6d\x6f\x72\x79\x5f\x66\x72\x65\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x69\x73\x5f\x6e\x75\x6d\x61\x5f\x73\x79\x73\x74\x65\x6d\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x73\x69\x7a\x65\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x74\x6f\x74\x61\x6c\x5f\x74\x65\x6e\x73\x6f\x72\x73\x5f\x63\x72\x65\x61\x74\x65\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x70\x72\x6f\x66\x69\x6c\x65\x5f\x73\x74\x61\x72\x74\x5f\x72\x65\x63\x6f\x72\x64\x69\x6e\x67\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x70\x72\x6f\x66\x69\x6c\x65\x5f\x73\x74\x6f\x70\x5f\x72\x65\x63\x6f\x72\x64\x69\x6e\x67\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x65\x78\x70\x6f\x72\x74\x5f\x63\x73\x76\x5f\x66\x69\x6c\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x64\x65\x73\x74\x72\x6f\x79\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x44\x54\x59\x50\x45\x5f\x46\x33\x32\x2c\x0a\x4d\x41\x47\x5f\x44\x54\x59\x50\x45\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x74\x20\x7b\x0a\x69\x6e\x74\x36\x34\x5f\x74\x20\x73\x69\x7a\x65\x3b\x0a\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6e\x61\x6d\x65\x3b\x0a\x7d\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x6f\x66\x28\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x70\x61\x63\x6b\x5f\x63\x6f\x6c\x6f\x72\x5f\x75\x38\x28\x75\x69\x6e\x74\x38\x5f\x74\x20\x72\x2c\x20\x75\x69\x6e\x74\x38\x5f\x74\x20\x67\x2c\x20\x75\x69\x6e\x74\x38\x5f\x74\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x70\x61\x63\x6b\x5f\x63\x6f\x6c\x6f\x72\x5f\x66\x33\x32\x28\x66\x6c\x6f\x61\x74\x20\x72\x2c\x20\x66\x6c\x6f\x61\x74\x20\x67\x2c\x20\x66\x6c\x6f\x61\x74\x20\x62\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x67\x72\x61\x70\x68\x5f\x65\x76\x61\x6c\x5f\x6f\x72\x64\x65\x72\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x47\x52\x41\x50\x48\x5f\x45\x56\x41\x4c\x5f\x4f\x52\x44\x45\x52\x5f\x46\x4f\x52\x57\x41\x52\x44\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x47\x52\x41\x50\x48\x5f\x45\x56\x41\x4c\x5f\x4f\x52\x44\x45\x52\x5f\x52\x45\x56\x45\x52\x53\x45\x20\x3d\x20\x31\x0a\x7d\x20\x6d\x61\x67\x5f\x67\x72\x61\x70\x68\x5f\x65\x76\x61\x6c\x5f\x6f\x72\x64\x65\x72\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x31\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x32\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x33\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x34\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x35\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x36\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x36\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x6c\x6f\x6e\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x76\x69\x65\x77\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x72\x61\x6e\x73\x70\x6f\x73\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x70\x65\x72\x6d\x75\x74\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x30\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x31\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x32\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x33\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x34\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x35\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x65\x61\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x69\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x61\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x6d\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x62\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x62\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6e\x65\x67\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6e\x65\x67\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6c\x6f\x67\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6c\x6f\x67\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x74\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x74\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6e\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x6f\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x6f\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x74\x65\x70\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x74\x65\x70\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x65\x78\x70\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x65\x78\x70\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x68\x61\x72\x64\x5f\x73\x69\x67\x6d\x6f\x69\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x68\x61\x72\x64\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x28\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x28\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x73\x28\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x70\x6f\x77\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x70\x6f\x77\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x61\x74\x6d\x75\x6c\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x6e\x63\x72\x65\x66\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x65\x63\x72\x65\x66\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x6f\x70\x79\x5f\x62\x75\x66\x66\x65\x72\x5f\x66\x72\x6f\x6d\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x76\x6f\x69\x64\x2a\x20\x64\x61\x74\x61\x2c\x20\x73\x69\x7a\x65\x5f\x74\x20\x73\x69\x7a\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x69\x6c\x6c\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x69\x6c\x6c\x5f\x72\x61\x6e\x64\x6f\x6d\x5f\x75\x6e\x69\x66\x6f\x72\x6d\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x66\x6c\x6f\x61\x74\x20\x6d\x69\x6e\x2c\x20\x66\x6c\x6f\x61\x74\x20\x6d\x61\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x69\x6c\x6c\x5f\x72\x61\x6e\x64\x6f\x6d\x5f\x6e\x6f\x72\x6d\x61\x6c\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x66\x6c\x6f\x61\x74\x20\x6d\x65\x61\x6e\x2c\x20\x66\x6c\x6f\x61\x74\x20\x73\x74\x64\x64\x65\x76\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x70\x61\x63\x6b\x65\x64\x5f\x72\x65\x66\x63\x6f\x75\x6e\x74\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x72\x65\x74\x61\x69\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x73\x69\x7a\x65\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x6d\x65\x6d\x6f\x72\x79\x5f\x75\x73\x61\x67\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x70\x72\x69\x6e\x74\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x62\x6f\x6f\x6c\x20\x77\x69\x74\x68\x5f\x68\x65\x61\x64\x65\x72\x2c\x20\x62\x6f\x6f\x6c\x20\x77\x69\x74\x68\x5f\x64\x61\x74\x61\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x6e\x61\x6d\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6e\x61\x6d\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x6d\x74\x5f\x6e\x61\x6d\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x6d\x74\x2c\x20\x2e\x2e\x2e\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x72\x61\x6e\x6b\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x69\x6e\x74\x36\x34\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x68\x61\x70\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x69\x6e\x74\x36\x34\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x74\x72\x69\x64\x65\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x74\x79\x70\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x61\x74\x61\x5f\x70\x74\x72\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x61\x74\x61\x5f\x73\x69\x7a\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6e\x75\x6d\x65\x6c\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6e\x75\x6d\x5f\x72\x6f\x77\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6e\x75\x6d\x5f\x63\x6f\x6c\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x73\x63\x61\x6c\x61\x72\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x76\x65\x63\x74\x6f\x72\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x6d\x61\x74\x72\x69\x78\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x76\x6f\x6c\x75\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x73\x68\x61\x70\x65\x5f\x65\x71\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x61\x72\x65\x5f\x73\x74\x72\x69\x64\x65\x73\x5f\x65\x71\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x61\x6e\x5f\x62\x72\x6f\x61\x64\x63\x61\x73\x74\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x74\x72\x61\x6e\x73\x70\x6f\x73\x65\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x70\x65\x72\x6d\x75\x74\x65\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x63\x6f\x6e\x74\x69\x67\x75\x6f\x75\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x72\x61\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x72\x65\x71\x75\x69\x72\x65\x73\x5f\x67\x72\x61\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x72\x65\x71\x75\x69\x72\x65\x73\x5f\x67\x72\x61\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x62\x6f\x6f\x6c\x20\x72\x65\x71\x75\x69\x72\x65\x73\x5f\x67\x72\x61\x64\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x62\x61\x63\x6b\x77\x61\x72\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x66\x6c\x6f\x61\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x30\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x30\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x66\x6c\x6f\x61\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x76\x69\x72\x74\x75\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x76\x5f\x69\x64\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x76\x69\x72\x74\x75\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x76\x5f\x69\x64\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x65\x71\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x63\x6c\x6f\x73\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x2c\x20\x66\x6c\x6f\x61\x74\x20\x65\x70\x73\x2c\x20\x64\x6f\x75\x62\x6c\x65\x2a\x20\x70\x65\x72\x63\x65\x6e\x74\x5f\x65\x71\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x6d\x67\x5f\x64\x72\x61\x77\x5f\x62\x6f\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x78\x31\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x79\x31\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x78\x32\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x79\x32\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x77\x69\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x67\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x6d\x67\x5f\x64\x72\x61\x77\x5f\x74\x65\x78\x74\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x78\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x79\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x73\x69\x7a\x65\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x67\x62\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x74\x78\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x63\x74\x78\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x75\x73\x65\x72\x5f\x64\x61\x74\x61\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x75\x73\x65\x72\x5f\x64\x61\x74\x61\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x76\x6f\x69\x64\x2a\x20\x75\x64\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x61\x76\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6c\x6f\x61\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6c\x6f\x61\x64\x5f\x69\x6d\x61\x67\x65\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x2c\x20\x6d\x61\x67\x5f\x63\x6f\x6c\x6f\x72\x5f\x63\x68\x61\x6e\x6e\x65\x6c\x73\x5f\x74\x20\x63\x68\x61\x6e\x6e\x65\x6c\x73\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x65\x73\x69\x7a\x65\x5f\x77\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x65\x73\x69\x7a\x65\x5f\x68\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x61\x76\x65\x5f\x69\x6d\x61\x67\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x29\x3b\x0a'.decode('utf-8') diff --git a/python/magnetron_framework/magnetron/core.py b/python/magnetron_framework/magnetron/core.py index 4e18213..26565cf 100644 --- a/python/magnetron_framework/magnetron/core.py +++ b/python/magnetron_framework/magnetron/core.py @@ -108,14 +108,6 @@ def __init__( self._ptr = C.mag_ctx_create2(descriptor) self.execution_mode = execution_mode - @property - def enable_grad_recorder(self) -> bool: - return C.mag_ctx_is_grad_recorder_enabled(self._ptr) - - @enable_grad_recorder.setter - def enable_grad_recorder(self, enable: bool) -> None: - C.mag_ctx_enable_grad_recorder(self._ptr, enable) - @property def compute_device_name(self) -> str: return ffi.string(C.mag_ctx_get_compute_device_name(self._ptr)).decode('utf-8') @@ -213,12 +205,12 @@ def f(*args: tuple[object, ...], **kwargs: dict[str, object]) -> None: return f def __enter__(self) -> None: - Context.active().enable_grad_recorder = False + pass def __exit__( self, exc_type: object, exc_value: object, traceback: object ) -> None: - Context.active().enable_grad_recorder = True + pass return Scope() @@ -258,12 +250,14 @@ def _new( *, shape: tuple[int, ...], dtype: DType = DType.F32, + requires_grad: bool = False, name: str | None = None, ) -> None: assert 0 < len(shape) <= MAX_DIMS, f'Invalid number of dimensions: {len(shape)}' assert all(0 < dim <= DIM_MAX for dim in shape), 'Invalid dimension size' self._ctx = weakref.ref(ctx) self._ptr = self._DISPATCH[len(shape)](ctx._ptr, dtype.value, *shape) + self.requires_grad = requires_grad if name: self.name = name @@ -273,10 +267,11 @@ def empty( shape: tuple[int, ...], *, dtype: DType = DType.F32, - name: str | None = None, + requires_grad: bool = False, + name: str | None = None ) -> 'Tensor': tensor = cls(None) - tensor._new(Context.active(), shape=shape, dtype=dtype, name=name) + tensor._new(Context.active(), shape=shape, dtype=dtype, requires_grad=requires_grad, name=name) return tensor @classmethod @@ -286,10 +281,11 @@ def full( *, fill_value: float, dtype: DType = DType.F32, + requires_grad: bool = False, name: str | None = None, ) -> 'Tensor': tensor = cls(None) - tensor._new(Context.active(), shape=shape, dtype=dtype, name=name) + tensor._new(Context.active(), shape=shape, dtype=dtype, requires_grad=requires_grad, name=name) C.mag_tensor_fill(tensor._ptr, fill_value) return tensor @@ -299,6 +295,7 @@ def const( data: list[float, ...], *, dtype: DType = DType.F32, + requires_grad: bool = False, name: str | None = None, ) -> 'Tensor': def flatten_nested_lists(nested: object) -> tuple[tuple[int, ...], list[float]]: @@ -320,7 +317,7 @@ def flatten_nested_lists(nested: object) -> tuple[tuple[int, ...], list[float]]: shape, flattened_data = flatten_nested_lists(data) tensor = cls(None) - tensor._new(Context.active(), shape=shape, dtype=dtype, name=name) + tensor._new(Context.active(), shape=shape, dtype=dtype, requires_grad=requires_grad, name=name) size: int = len(flattened_data) * ffi.sizeof('float') C.mag_tensor_copy_buffer_from( tensor._ptr, ffi.new(f'float[{len(flattened_data)}]', flattened_data), size @@ -333,9 +330,10 @@ def zeros( shape: tuple[int, ...], *, dtype: DType = DType.F32, + requires_grad: bool = False, name: str | None = None, ) -> 'Tensor': - return cls.full(shape, fill_value=0.0, dtype=dtype, name=name) + return cls.full(shape, fill_value=0.0, dtype=dtype, requires_grad=requires_grad, name=name) @classmethod def uniform( @@ -344,10 +342,11 @@ def uniform( *, interval: (float, float) = (-1.0, 1.0), dtype: DType = DType.F32, + requires_grad: bool = False, name: str | None = None, ) -> 'Tensor': tensor = cls(None) - tensor._new(Context.active(), shape=shape, dtype=dtype, name=name) + tensor._new(Context.active(), shape=shape, dtype=dtype, requires_grad=requires_grad, name=name) if interval[1] < interval[0]: interval = (interval[1], interval[0]) C.mag_tensor_fill_random_uniform(tensor._ptr, interval[0], interval[1]) @@ -355,10 +354,16 @@ def uniform( @classmethod def normal( - cls, shape: tuple[int, ...], *, mean: float = 0.0, stddev: float = 1.0 + cls, + shape: tuple[int, ...], + *, + mean: float = 0.0, + stddev: float = 1.0, + requires_grad: bool = False, + name: str | None = None, ) -> 'Tensor': tensor = cls(None) - tensor._new(Context.active(), shape=shape, dtype=DType.F32) + tensor._new(Context.active(), shape=shape, dtype=DType.F32, requires_grad=requires_grad, name=name) C.mag_tensor_fill_random_normal(tensor._ptr, mean, stddev) return tensor @@ -421,6 +426,10 @@ def dtype(self) -> DType: def data_ptr(self) -> int: return int(ffi.cast('uintptr_t', C.mag_tensor_data_ptr(self._ptr))) + def item(self) -> float: + assert self.is_scalar, 'Tensor must be a scalar' + return self[0] + def tolist(self) -> list[float]: assert self.dtype == DType.F32, 'Invalid data type' return ffi.unpack( @@ -493,16 +502,24 @@ def is_contiguous(self) -> bool: return C.mag_tensor_is_contiguous(self._ptr) @property - def grad(self) -> object | None: # -> Tensor | None - Forward Reference ('Tensor') + None is a bug in Python, will be fixed in Python 3.5.3, so long we use object. - ptr: ffi.CData = C.mag_tensor_grad(self._ptr) - return Tensor(ptr) if ptr != ffi.NULL else None + def requires_grad(self) -> bool: + return C.mag_tensor_requires_grad(self._ptr) + + @requires_grad.setter + def requires_grad(self, require: bool) -> None: + C.mag_tensor_set_requires_grad(self._ptr, require) + + @property + def grad(self) -> 'Tensor': + assert self.requires_grad + return Tensor(C.mag_tensor_grad(self._ptr)) - @grad.setter - def grad(self, grad: 'Tensor') -> None: - C.mag_tensor_set_grad(self._ptr, grad._ptr) + def backward(self) -> None: + assert self.requires_grad + C.mag_tensor_backward(self._ptr) def zero_grad(self) -> None: - C.mag_tensor_zero_grad(self._ptr) + raise NotImplementedError('Not implemented yet') def is_close( self, other: 'Tensor', eps: float = -1.0, print_eq_percent: bool = False diff --git a/python/optional-requirements.txt b/python/optional-requirements.txt index d4313a0..6d8ddfd 100644 --- a/python/optional-requirements.txt +++ b/python/optional-requirements.txt @@ -6,6 +6,7 @@ wheel setuptools pytest numpy +torch matplotlib sphinx sphinx-autoapi diff --git a/python/tests/autograd.py b/python/tests/autograd.py new file mode 100644 index 0000000..3241fea --- /dev/null +++ b/python/tests/autograd.py @@ -0,0 +1,30 @@ +# (c) 2025 Mario "Neo" Sieg. + +from magnetron import * +import torch + +def test_autograd_1(): + x = Tensor.const([-4.0], requires_grad=True) + z = 2 * x + 2 + x + q = z.relu() + z * x + h = (z * z).relu() + y = h + q + q * x + assert x.requires_grad + assert z.requires_grad + assert q.requires_grad + assert h.requires_grad + assert y.requires_grad + y.backward() + xmg, ymg = x, y + + x = torch.Tensor([-4.0]) + x.requires_grad = True + z = 2 * x + 2 + x + q = z.relu() + z * x + h = (z * z).relu() + y = h + q + q * x + y.backward() + xpt, ypt = x, y + + assert ymg.item() == ypt.data.item() + #assert xmg.grad.item() == xpt.grad.item() diff --git a/test/unit/autograd.cpp b/test/unit/autograd.cpp index 0f966fe..552c753 100644 --- a/test/unit/autograd.cpp +++ b/test/unit/autograd.cpp @@ -13,21 +13,25 @@ TEST(autograd, bin_ops1) { mag_ctx_t* ctx = mag_ctx_create(MAG_COMPUTE_DEVICE_TYPE_CPU); auto* x = mag_tensor_create_1d(ctx, MAG_DTYPE_F32, 1); + mag_tensor_set_requires_grad(x, true); mag_tensor_fill(x, 3.0f); auto* y = mag_tensor_create_1d(ctx, MAG_DTYPE_F32, 1); mag_tensor_fill(y, 2.0f); + mag_tensor_set_requires_grad(y, true); auto* k = mag_tensor_create_1d(ctx, MAG_DTYPE_F32, 1); mag_tensor_fill(k, 10.0f); + mag_tensor_set_requires_grad(k, true); auto* z = mag_div(mag_mul(mag_add(x, y), mag_sub(x, y)), k); + mag_tensor_set_requires_grad(z, true); mag_tensor_backward(z); - ASSERT_NE(x->grad, nullptr); - ASSERT_NE(y->grad, nullptr); - ASSERT_NE(k->grad, nullptr); - ASSERT_NE(z->grad, nullptr); + ASSERT_NE(mag_tensor_grad(x), nullptr); + ASSERT_NE(mag_tensor_grad(y), nullptr); + ASSERT_NE(mag_tensor_grad(k), nullptr); + ASSERT_NE(mag_tensor_grad(z), nullptr); // check forward pass float vx = mag_tensor_get_scalar_virtual_index(x, 0); @@ -38,9 +42,9 @@ TEST(autograd, bin_ops1) { ASSERT_EQ(vz, 0.5f); // check backward pass - float gx = mag_tensor_get_scalar_virtual_index(x->grad, 0); - float gy = mag_tensor_get_scalar_virtual_index(y->grad, 0); - float gz = mag_tensor_get_scalar_virtual_index(z->grad, 0); + float gx = mag_tensor_get_scalar_virtual_index(mag_tensor_grad(x), 0); + float gy = mag_tensor_get_scalar_virtual_index(mag_tensor_grad(y), 0); + float gz = mag_tensor_get_scalar_virtual_index(mag_tensor_grad(z), 0); ASSERT_FLOAT_EQ(gx, 0.6f); // ∂z/∂x = 0.6 ASSERT_FLOAT_EQ(gy, -0.4f); // ∂z/∂y = -0.4 @@ -65,15 +69,18 @@ TEST(autograd, bin_ops2) { mag_ctx_t* ctx = mag_ctx_create(MAG_COMPUTE_DEVICE_TYPE_CPU); auto* two = mag_tensor_create_1d(ctx, MAG_DTYPE_F32, 1); + mag_tensor_set_requires_grad(two, true); mag_tensor_fill(two, 2.0f); auto* x = mag_tensor_create_1d(ctx, MAG_DTYPE_F32, 1); + mag_tensor_set_requires_grad(x, true); mag_tensor_fill(x, -4.0f); auto* z = mag_add(mag_add(mag_mul(two, x), two), x); auto* q = mag_add(mag_relu(z), mag_mul(z, x)); auto* h = mag_relu(mag_mul(z, z)); auto* y = mag_add(q, mag_add(mag_mul(q, x), h)); + mag_tensor_set_requires_grad(y, true); mag_tensor_backward(y); // check forward pass @@ -82,9 +89,15 @@ TEST(autograd, bin_ops2) { ASSERT_FLOAT_EQ(vx, -4.0f); ASSERT_FLOAT_EQ(vy, -20.0f); + ASSERT_NE(mag_tensor_grad(x), nullptr); + ASSERT_NE(mag_tensor_grad(y), nullptr); + ASSERT_NE(mag_tensor_grad(z), nullptr); + ASSERT_NE(mag_tensor_grad(q), nullptr); + ASSERT_NE(mag_tensor_grad(h), nullptr); + // check backward pass - float gx = mag_tensor_get_scalar_virtual_index(x->grad, 0); - float gy = mag_tensor_get_scalar_virtual_index(y->grad, 0); + float gx = mag_tensor_get_scalar_virtual_index(mag_tensor_grad(x), 0); + float gy = mag_tensor_get_scalar_virtual_index(mag_tensor_grad(y), 0); ASSERT_FLOAT_EQ(gx, 46.0f); // ∂z/∂x = 46.0 ASSERT_FLOAT_EQ(gy, 1.0f); // ∂z/∂y = 1.0 diff --git a/test_data/test.magnetron b/test_data/test.magnetron deleted file mode 100644 index e8638e2b30d40cc8206987223f085c551fe279e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6536 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�KYacqGDs zb7GbX1H-$(bdM#P2D@?Ag#Uo`!yDx$4#{HpLS-IW{SG2`E#Uy^omefUharD_g+dii z&iCBRCENa>u;y}6@$dEc;IujP4m=SHDjJ~sNF&7+YnK<=cyej~CbTb(Ajkj2ll8fW zk`K>KIQ&o9k-kLW!40#y&`FxhV#a{1_a}-iUX5i|*5Kg0giY^Dar99IZakh&jT4?i zn&Uq7y|!Oy(_0`mBzj;|uOiyx7D$qnaEtpj_#iu-vyR|67uyJ>(zSewP183p7x@enBOOQhJ6b*zhXQUCZhELE|^o~Jue z=9V@3Dm#f;TR+qA;Ypk^!Io=QxMSNJZ-M5xaL>K~cDzvl;TK10f7&o8OBUmzzs6y4 zuM3uHDxi<+0v!Iq26aAo@PL*j4VUfX5~;6{@^}qX*&Nibp3imFVI|J2QJ}jP%pL?p3m9EP5~=9LwO&L*dv%b`5nZ(ctXm;WPto^>;v7^;W8~N~T!LRTzD0H*5OuhNQVVJTxJL?5D+0htqxN?LI5k zj_Rbq849G}Jryd_-j#=0KNc?R)aB~_9`C1` zMtjt_WWdc?k>njxPD3|7k=v8g5T3ae``2zk#lH@4g8VNOFkvxA=PzW(urlzt9}K24 z&Y14_h&pqW(C_OutUXai2_D6e^4kXX-`7QXMWaaJKm-{2=%G?^CaF1JhpC0F&}OM4 zOt+aYfuhw3ML@a>+N)Uv$+R2OdIzMl_6 z!MBN&(XYiSc8`S~V{J5kw~Y;d-@+#OU1CAXH8Om-j5}#G>g|hzEXC90J4Hu~P<{i! z=Yml$rUBxE9oS{UPWFp%!^YdgWU=0t0!GVY{P-0dVRM75cg9Nwwpq~BQ`MmJ%nl=+ z#$(~}t*Cz_2y?WIL`TI!@E+3xsU80cS$`j+;ib#C@k$w-Rr2GZz#qWN(gw-Pf>o}T z2z4*^pl1FQ2wprHDIy;ARUyQtMV-P<0Cy@GXb+H?#Uy|Su3%7%^7O`eF22n zG*am?;HCxh(CMflHfhCjS)(;c*^RngoAiomt&h;zv{^h@tB*(K%5cwOrq(ZE|z)_+PnONrvPHhov|MPBYA6|xy zM=wCzXcWu7_o2$q(=aMt3#6)^Stbq7Wb2>5mKR=LfJz!^Wbus)Bn{5Pep`L+id@Ri zC9>RNpFx4SGN5|j8$rX2W9+{|>$WiNRvChjGg*)=y_e69QpKF#1tGx804sOT;>4fS z!Nl{F_@I9rnC4C9BRiDXA>aVIT!=u`f>`YGSjCIQ3N@ zbbd3B9oo&%`Q5K#@9}?xsQdn4aMGOWT6S^F%AY78-3kk`&q0Op?eePAhaq0Yl_Qrk!p~7=X2j+nD-k7(FrfZ3hnIsWbfc6)!Af~D25 z%*%yR)`ha-MhC1+jzP(Jrf3y6HddaAor+2vY^=;?hwnl4ZwD~(xIpz!>x6aj=x|Si!_!vtqPce5v(S_kXPdE?lP9MAsEvl} z>@Zf<4{ybu Date: Sun, 2 Feb 2025 15:55:39 +0100 Subject: [PATCH 15/24] Cleanup tests --- benchmark/benchmarks.cpp | 125 +- benchmark/prepare_system.sh | 1 - magnetron/magnetron.c | 2 +- magnetron/magnetron_internal.h | 22 +- .../magnetron/_ffi_cdecl_generated.py | 4 +- python/magnetron_framework/magnetron/core.py | 51 +- python/tests/autograd.py | 18 +- test/CMakeLists.txt | 9 +- test/invariant_proof.cpp | 23 +- test/{santiy_test.c => sanity_test.c} | 0 test/unit/googletest | 1 + test/unit/googletest/.clang-format | 4 - .../.github/ISSUE_TEMPLATE/00-bug_report.yml | 53 - .../ISSUE_TEMPLATE/10-feature_request.yml | 33 - .../.github/ISSUE_TEMPLATE/config.yml | 5 - test/unit/googletest/.gitignore | 89 - test/unit/googletest/BUILD.bazel | 236 - test/unit/googletest/CMakeLists.txt | 36 - test/unit/googletest/CONTRIBUTING.md | 141 - test/unit/googletest/CONTRIBUTORS | 66 - test/unit/googletest/LICENSE | 28 - test/unit/googletest/MODULE.bazel | 67 - test/unit/googletest/README.md | 142 - test/unit/googletest/WORKSPACE | 62 - test/unit/googletest/WORKSPACE.bzlmod | 35 - test/unit/googletest/ci/linux-presubmit.sh | 139 - test/unit/googletest/ci/macos-presubmit.sh | 77 - test/unit/googletest/ci/windows-presubmit.bat | 63 - test/unit/googletest/docs/_config.yml | 1 - .../unit/googletest/docs/_data/navigation.yml | 43 - .../googletest/docs/_layouts/default.html | 58 - test/unit/googletest/docs/_sass/main.scss | 200 - test/unit/googletest/docs/advanced.md | 2446 ------ .../googletest/docs/assets/css/style.scss | 5 - .../docs/community_created_documentation.md | 7 - test/unit/googletest/docs/faq.md | 671 -- .../unit/googletest/docs/gmock_cheat_sheet.md | 241 - test/unit/googletest/docs/gmock_cook_book.md | 4379 --------- test/unit/googletest/docs/gmock_faq.md | 390 - .../unit/googletest/docs/gmock_for_dummies.md | 702 -- test/unit/googletest/docs/index.md | 22 - test/unit/googletest/docs/pkgconfig.md | 144 - test/unit/googletest/docs/platforms.md | 8 - test/unit/googletest/docs/primer.md | 482 - test/unit/googletest/docs/quickstart-bazel.md | 146 - test/unit/googletest/docs/quickstart-cmake.md | 157 - .../unit/googletest/docs/reference/actions.md | 115 - .../googletest/docs/reference/assertions.md | 640 -- .../googletest/docs/reference/matchers.md | 302 - .../unit/googletest/docs/reference/mocking.md | 588 -- .../unit/googletest/docs/reference/testing.md | 1453 --- test/unit/googletest/docs/samples.md | 22 - test/unit/googletest/fake_fuchsia_sdk.bzl | 33 - .../unit/googletest/googlemock/CMakeLists.txt | 210 - test/unit/googletest/googlemock/README.md | 40 - .../googletest/googlemock/cmake/gmock.pc.in | 10 - .../googlemock/cmake/gmock_main.pc.in | 10 - .../unit/googletest/googlemock/docs/README.md | 4 - .../googlemock/include/gmock/gmock-actions.h | 2360 ----- .../include/gmock/gmock-cardinalities.h | 159 - .../include/gmock/gmock-function-mocker.h | 519 -- .../googlemock/include/gmock/gmock-matchers.h | 5677 ------------ .../include/gmock/gmock-more-actions.h | 660 -- .../include/gmock/gmock-more-matchers.h | 120 - .../include/gmock/gmock-nice-strict.h | 277 - .../include/gmock/gmock-spec-builders.h | 2146 ----- .../googlemock/include/gmock/gmock.h | 97 - .../include/gmock/internal/custom/README.md | 18 - .../internal/custom/gmock-generated-actions.h | 7 - .../gmock/internal/custom/gmock-matchers.h | 37 - .../gmock/internal/custom/gmock-port.h | 40 - .../gmock/internal/gmock-internal-utils.h | 489 -- .../include/gmock/internal/gmock-port.h | 140 - .../include/gmock/internal/gmock-pp.h | 279 - .../googletest/googlemock/src/gmock-all.cc | 46 - .../googlemock/src/gmock-cardinalities.cc | 155 - .../googlemock/src/gmock-internal-utils.cc | 258 - .../googlemock/src/gmock-matchers.cc | 478 - .../googlemock/src/gmock-spec-builders.cc | 792 -- test/unit/googletest/googlemock/src/gmock.cc | 225 - .../googletest/googlemock/src/gmock_main.cc | 73 - .../googletest/googlemock/test/BUILD.bazel | 118 - .../googlemock/test/gmock-actions_test.cc | 2217 ----- .../test/gmock-cardinalities_test.cc | 424 - .../test/gmock-function-mocker_test.cc | 998 --- .../test/gmock-internal-utils_test.cc | 766 -- .../test/gmock-matchers-arithmetic_test.cc | 1516 ---- .../test/gmock-matchers-comparisons_test.cc | 2373 ----- .../test/gmock-matchers-containers_test.cc | 3155 ------- .../test/gmock-matchers-misc_test.cc | 1863 ---- .../googlemock/test/gmock-matchers_test.h | 192 - .../test/gmock-more-actions_test.cc | 1588 ---- .../googlemock/test/gmock-nice-strict_test.cc | 541 -- .../googlemock/test/gmock-port_test.cc | 42 - .../googlemock/test/gmock-pp-string_test.cc | 205 - .../googlemock/test/gmock-pp_test.cc | 83 - .../test/gmock-spec-builders_test.cc | 2600 ------ .../googlemock/test/gmock_all_test.cc | 49 - .../googlemock/test/gmock_ex_test.cc | 80 - .../googlemock/test/gmock_leak_test.py | 113 - .../googlemock/test/gmock_leak_test_.cc | 99 - .../googlemock/test/gmock_link2_test.cc | 38 - .../googlemock/test/gmock_link_test.cc | 38 - .../googlemock/test/gmock_link_test.h | 693 -- .../googlemock/test/gmock_output_test.py | 190 - .../googlemock/test/gmock_output_test_.cc | 286 - .../test/gmock_output_test_golden.txt | 335 - .../googlemock/test/gmock_stress_test.cc | 227 - .../googletest/googlemock/test/gmock_test.cc | 179 - .../googlemock/test/gmock_test_utils.py | 91 - .../unit/googletest/googletest/CMakeLists.txt | 330 - test/unit/googletest/googletest/README.md | 231 - .../googletest/cmake/Config.cmake.in | 13 - .../googletest/googletest/cmake/gtest.pc.in | 9 - .../googletest/cmake/gtest_main.pc.in | 10 - .../googletest/cmake/internal_utils.cmake | 334 - .../googletest/cmake/libgtest.la.in | 21 - .../unit/googletest/googletest/docs/README.md | 4 - .../include/gtest/gtest-assertion-result.h | 237 - .../include/gtest/gtest-death-test.h | 345 - .../googletest/include/gtest/gtest-matchers.h | 923 -- .../googletest/include/gtest/gtest-message.h | 251 - .../include/gtest/gtest-param-test.h | 546 -- .../googletest/include/gtest/gtest-printers.h | 1197 --- .../googletest/include/gtest/gtest-spi.h | 250 - .../include/gtest/gtest-test-part.h | 192 - .../include/gtest/gtest-typed-test.h | 335 - .../googletest/include/gtest/gtest.h | 2338 ----- .../include/gtest/gtest_pred_impl.h | 279 - .../googletest/include/gtest/gtest_prod.h | 60 - .../include/gtest/internal/custom/README.md | 44 - .../gtest/internal/custom/gtest-port.h | 37 - .../gtest/internal/custom/gtest-printers.h | 42 - .../include/gtest/internal/custom/gtest.h | 37 - .../internal/gtest-death-test-internal.h | 306 - .../include/gtest/internal/gtest-filepath.h | 233 - .../include/gtest/internal/gtest-internal.h | 1521 ---- .../include/gtest/internal/gtest-param-util.h | 1030 --- .../include/gtest/internal/gtest-port-arch.h | 124 - .../include/gtest/internal/gtest-port.h | 2536 ------ .../include/gtest/internal/gtest-string.h | 178 - .../include/gtest/internal/gtest-type-util.h | 220 - .../googletest/samples/prime_tables.h | 125 - .../googletest/googletest/samples/sample1.cc | 66 - .../googletest/googletest/samples/sample1.h | 41 - .../googletest/samples/sample10_unittest.cc | 138 - .../googletest/samples/sample1_unittest.cc | 148 - .../googletest/googletest/samples/sample2.cc | 54 - .../googletest/googletest/samples/sample2.h | 79 - .../googletest/samples/sample2_unittest.cc | 107 - .../googletest/samples/sample3-inl.h | 171 - .../googletest/samples/sample3_unittest.cc | 146 - .../googletest/googletest/samples/sample4.cc | 50 - .../googletest/googletest/samples/sample4.h | 53 - .../googletest/samples/sample4_unittest.cc | 53 - .../googletest/samples/sample5_unittest.cc | 189 - .../googletest/samples/sample6_unittest.cc | 214 - .../googletest/samples/sample7_unittest.cc | 113 - .../googletest/samples/sample8_unittest.cc | 154 - .../googletest/samples/sample9_unittest.cc | 148 - .../googletest/googletest/src/gtest-all.cc | 49 - .../googletest/src/gtest-assertion-result.cc | 77 - .../googletest/src/gtest-death-test.cc | 1587 ---- .../googletest/src/gtest-filepath.cc | 414 - .../googletest/src/gtest-internal-inl.h | 1230 --- .../googletest/src/gtest-matchers.cc | 98 - .../googletest/googletest/src/gtest-port.cc | 1434 --- .../googletest/src/gtest-printers.cc | 555 -- .../googletest/src/gtest-test-part.cc | 106 - .../googletest/src/gtest-typed-test.cc | 108 - test/unit/googletest/googletest/src/gtest.cc | 6975 --------------- .../googletest/googletest/src/gtest_main.cc | 66 - .../googletest/googletest/test/BUILD.bazel | 595 -- .../googletest-break-on-failure-unittest.py | 195 - .../googletest-break-on-failure-unittest_.cc | 83 - .../test/googletest-catch-exceptions-test.py | 315 - .../test/googletest-catch-exceptions-test_.cc | 289 - .../googletest/test/googletest-color-test.py | 130 - .../googletest/test/googletest-color-test_.cc | 60 - .../test/googletest-death-test-test.cc | 1512 ---- .../test/googletest-death-test_ex_test.cc | 91 - .../test/googletest-env-var-test.py | 120 - .../test/googletest-env-var-test_.cc | 130 - .../test/googletest-failfast-unittest.py | 461 - .../test/googletest-failfast-unittest_.cc | 166 - .../test/googletest-filepath-test.cc | 671 -- .../test/googletest-filter-unittest.py | 746 -- .../test/googletest-filter-unittest_.cc | 106 - .../googletest-global-environment-unittest.py | 141 - ...googletest-global-environment-unittest_.cc | 58 - .../test/googletest-json-outfiles-test.py | 180 - .../test/googletest-json-output-unittest.py | 835 -- .../test/googletest-list-tests-unittest.py | 225 - .../test/googletest-list-tests-unittest_.cc | 143 - .../test/googletest-listener-test.cc | 509 -- .../test/googletest-message-test.cc | 184 - .../test/googletest-options-test.cc | 225 - .../googletest-output-test-golden-lin.txt | 1201 --- .../googletest/test/googletest-output-test.py | 385 - .../test/googletest-output-test_.cc | 1058 --- ...oogletest-param-test-invalid-name1-test.py | 63 - ...ogletest-param-test-invalid-name1-test_.cc | 46 - ...oogletest-param-test-invalid-name2-test.py | 63 - ...ogletest-param-test-invalid-name2-test_.cc | 52 - .../test/googletest-param-test-test.cc | 1173 --- .../test/googletest-param-test-test.h | 49 - .../test/googletest-param-test2-test.cc | 58 - .../googletest/test/googletest-port-test.cc | 1301 --- .../test/googletest-printers-test.cc | 2034 ----- .../test/googletest-setuptestsuite-test.py | 58 - .../test/googletest-setuptestsuite-test_.cc | 44 - .../test/googletest-shuffle-test.py | 378 - .../test/googletest-shuffle-test_.cc | 99 - .../test/googletest-test-part-test.cc | 220 - .../test/googletest-throw-on-failure-test.py | 166 - .../test/googletest-throw-on-failure-test_.cc | 71 - .../test/googletest-uninitialized-test.py | 70 - .../test/googletest-uninitialized-test_.cc | 39 - .../googletest/test/gtest-typed-test2_test.cc | 39 - .../googletest/test/gtest-typed-test_test.cc | 423 - .../googletest/test/gtest-typed-test_test.h | 57 - .../test/gtest-unittest-api_test.cc | 328 - .../googletest/test/gtest_all_test.cc | 46 - .../test/gtest_assert_by_exception_test.cc | 112 - .../googletest/test/gtest_dirs_test.cc | 101 - .../googletest/test/gtest_environment_test.cc | 187 - .../googletest/test/gtest_help_test.py | 183 - .../googletest/test/gtest_help_test_.cc | 44 - .../googletest/test/gtest_json_test_utils.py | 67 - .../test/gtest_list_output_unittest.py | 289 - .../test/gtest_list_output_unittest_.cc | 77 - .../googletest/test/gtest_main_unittest.cc | 42 - .../googletest/test/gtest_no_test_unittest.cc | 54 - .../test/gtest_pred_impl_unittest.cc | 2070 ----- .../test/gtest_premature_exit_test.cc | 128 - .../googletest/test/gtest_prod_test.cc | 56 - .../googletest/test/gtest_repeat_test.cc | 220 - .../test/gtest_skip_check_output_test.py | 60 - ...test_skip_environment_check_output_test.py | 55 - .../gtest_skip_in_environment_setup_test.cc | 50 - .../googletest/test/gtest_skip_test.cc | 51 - .../googletest/test/gtest_sole_header_test.cc | 54 - .../googletest/test/gtest_stress_test.cc | 245 - .../gtest_test_macro_stack_footprint_test.cc | 89 - .../googletest/test/gtest_test_utils.py | 262 - .../googletest/test/gtest_testbridge_test.py | 63 - .../googletest/test/gtest_testbridge_test_.cc | 42 - .../test/gtest_throw_on_failure_ex_test.cc | 90 - .../googletest/test/gtest_unittest.cc | 7824 ----------------- .../test/gtest_xml_outfile1_test_.cc | 43 - .../test/gtest_xml_outfile2_test_.cc | 77 - .../test/gtest_xml_outfiles_test.py | 147 - .../test/gtest_xml_output_unittest.py | 472 - .../test/gtest_xml_output_unittest_.cc | 197 - .../googletest/test/gtest_xml_test_utils.py | 242 - .../googletest/googletest/test/production.cc | 35 - .../googletest/googletest/test/production.h | 55 - test/unit/googletest/googletest_deps.bzl | 28 - 258 files changed, 124 insertions(+), 111551 deletions(-) rename test/{santiy_test.c => sanity_test.c} (100%) create mode 160000 test/unit/googletest delete mode 100644 test/unit/googletest/.clang-format delete mode 100644 test/unit/googletest/.github/ISSUE_TEMPLATE/00-bug_report.yml delete mode 100644 test/unit/googletest/.github/ISSUE_TEMPLATE/10-feature_request.yml delete mode 100644 test/unit/googletest/.github/ISSUE_TEMPLATE/config.yml delete mode 100644 test/unit/googletest/.gitignore delete mode 100644 test/unit/googletest/BUILD.bazel delete mode 100644 test/unit/googletest/CMakeLists.txt delete mode 100644 test/unit/googletest/CONTRIBUTING.md delete mode 100644 test/unit/googletest/CONTRIBUTORS delete mode 100644 test/unit/googletest/LICENSE delete mode 100644 test/unit/googletest/MODULE.bazel delete mode 100644 test/unit/googletest/README.md delete mode 100644 test/unit/googletest/WORKSPACE delete mode 100644 test/unit/googletest/WORKSPACE.bzlmod delete mode 100644 test/unit/googletest/ci/linux-presubmit.sh delete mode 100644 test/unit/googletest/ci/macos-presubmit.sh delete mode 100644 test/unit/googletest/ci/windows-presubmit.bat delete mode 100644 test/unit/googletest/docs/_config.yml delete mode 100644 test/unit/googletest/docs/_data/navigation.yml delete mode 100644 test/unit/googletest/docs/_layouts/default.html delete mode 100644 test/unit/googletest/docs/_sass/main.scss delete mode 100644 test/unit/googletest/docs/advanced.md delete mode 100644 test/unit/googletest/docs/assets/css/style.scss delete mode 100644 test/unit/googletest/docs/community_created_documentation.md delete mode 100644 test/unit/googletest/docs/faq.md delete mode 100644 test/unit/googletest/docs/gmock_cheat_sheet.md delete mode 100644 test/unit/googletest/docs/gmock_cook_book.md delete mode 100644 test/unit/googletest/docs/gmock_faq.md delete mode 100644 test/unit/googletest/docs/gmock_for_dummies.md delete mode 100644 test/unit/googletest/docs/index.md delete mode 100644 test/unit/googletest/docs/pkgconfig.md delete mode 100644 test/unit/googletest/docs/platforms.md delete mode 100644 test/unit/googletest/docs/primer.md delete mode 100644 test/unit/googletest/docs/quickstart-bazel.md delete mode 100644 test/unit/googletest/docs/quickstart-cmake.md delete mode 100644 test/unit/googletest/docs/reference/actions.md delete mode 100644 test/unit/googletest/docs/reference/assertions.md delete mode 100644 test/unit/googletest/docs/reference/matchers.md delete mode 100644 test/unit/googletest/docs/reference/mocking.md delete mode 100644 test/unit/googletest/docs/reference/testing.md delete mode 100644 test/unit/googletest/docs/samples.md delete mode 100644 test/unit/googletest/fake_fuchsia_sdk.bzl delete mode 100644 test/unit/googletest/googlemock/CMakeLists.txt delete mode 100644 test/unit/googletest/googlemock/README.md delete mode 100644 test/unit/googletest/googlemock/cmake/gmock.pc.in delete mode 100644 test/unit/googletest/googlemock/cmake/gmock_main.pc.in delete mode 100644 test/unit/googletest/googlemock/docs/README.md delete mode 100644 test/unit/googletest/googlemock/include/gmock/gmock-actions.h delete mode 100644 test/unit/googletest/googlemock/include/gmock/gmock-cardinalities.h delete mode 100644 test/unit/googletest/googlemock/include/gmock/gmock-function-mocker.h delete mode 100644 test/unit/googletest/googlemock/include/gmock/gmock-matchers.h delete mode 100644 test/unit/googletest/googlemock/include/gmock/gmock-more-actions.h delete mode 100644 test/unit/googletest/googlemock/include/gmock/gmock-more-matchers.h delete mode 100644 test/unit/googletest/googlemock/include/gmock/gmock-nice-strict.h delete mode 100644 test/unit/googletest/googlemock/include/gmock/gmock-spec-builders.h delete mode 100644 test/unit/googletest/googlemock/include/gmock/gmock.h delete mode 100644 test/unit/googletest/googlemock/include/gmock/internal/custom/README.md delete mode 100644 test/unit/googletest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h delete mode 100644 test/unit/googletest/googlemock/include/gmock/internal/custom/gmock-matchers.h delete mode 100644 test/unit/googletest/googlemock/include/gmock/internal/custom/gmock-port.h delete mode 100644 test/unit/googletest/googlemock/include/gmock/internal/gmock-internal-utils.h delete mode 100644 test/unit/googletest/googlemock/include/gmock/internal/gmock-port.h delete mode 100644 test/unit/googletest/googlemock/include/gmock/internal/gmock-pp.h delete mode 100644 test/unit/googletest/googlemock/src/gmock-all.cc delete mode 100644 test/unit/googletest/googlemock/src/gmock-cardinalities.cc delete mode 100644 test/unit/googletest/googlemock/src/gmock-internal-utils.cc delete mode 100644 test/unit/googletest/googlemock/src/gmock-matchers.cc delete mode 100644 test/unit/googletest/googlemock/src/gmock-spec-builders.cc delete mode 100644 test/unit/googletest/googlemock/src/gmock.cc delete mode 100644 test/unit/googletest/googlemock/src/gmock_main.cc delete mode 100644 test/unit/googletest/googlemock/test/BUILD.bazel delete mode 100644 test/unit/googletest/googlemock/test/gmock-actions_test.cc delete mode 100644 test/unit/googletest/googlemock/test/gmock-cardinalities_test.cc delete mode 100644 test/unit/googletest/googlemock/test/gmock-function-mocker_test.cc delete mode 100644 test/unit/googletest/googlemock/test/gmock-internal-utils_test.cc delete mode 100644 test/unit/googletest/googlemock/test/gmock-matchers-arithmetic_test.cc delete mode 100644 test/unit/googletest/googlemock/test/gmock-matchers-comparisons_test.cc delete mode 100644 test/unit/googletest/googlemock/test/gmock-matchers-containers_test.cc delete mode 100644 test/unit/googletest/googlemock/test/gmock-matchers-misc_test.cc delete mode 100644 test/unit/googletest/googlemock/test/gmock-matchers_test.h delete mode 100644 test/unit/googletest/googlemock/test/gmock-more-actions_test.cc delete mode 100644 test/unit/googletest/googlemock/test/gmock-nice-strict_test.cc delete mode 100644 test/unit/googletest/googlemock/test/gmock-port_test.cc delete mode 100644 test/unit/googletest/googlemock/test/gmock-pp-string_test.cc delete mode 100644 test/unit/googletest/googlemock/test/gmock-pp_test.cc delete mode 100644 test/unit/googletest/googlemock/test/gmock-spec-builders_test.cc delete mode 100644 test/unit/googletest/googlemock/test/gmock_all_test.cc delete mode 100644 test/unit/googletest/googlemock/test/gmock_ex_test.cc delete mode 100755 test/unit/googletest/googlemock/test/gmock_leak_test.py delete mode 100644 test/unit/googletest/googlemock/test/gmock_leak_test_.cc delete mode 100644 test/unit/googletest/googlemock/test/gmock_link2_test.cc delete mode 100644 test/unit/googletest/googlemock/test/gmock_link_test.cc delete mode 100644 test/unit/googletest/googlemock/test/gmock_link_test.h delete mode 100755 test/unit/googletest/googlemock/test/gmock_output_test.py delete mode 100644 test/unit/googletest/googlemock/test/gmock_output_test_.cc delete mode 100644 test/unit/googletest/googlemock/test/gmock_output_test_golden.txt delete mode 100644 test/unit/googletest/googlemock/test/gmock_stress_test.cc delete mode 100644 test/unit/googletest/googlemock/test/gmock_test.cc delete mode 100755 test/unit/googletest/googlemock/test/gmock_test_utils.py delete mode 100644 test/unit/googletest/googletest/CMakeLists.txt delete mode 100644 test/unit/googletest/googletest/README.md delete mode 100644 test/unit/googletest/googletest/cmake/Config.cmake.in delete mode 100644 test/unit/googletest/googletest/cmake/gtest.pc.in delete mode 100644 test/unit/googletest/googletest/cmake/gtest_main.pc.in delete mode 100644 test/unit/googletest/googletest/cmake/internal_utils.cmake delete mode 100644 test/unit/googletest/googletest/cmake/libgtest.la.in delete mode 100644 test/unit/googletest/googletest/docs/README.md delete mode 100644 test/unit/googletest/googletest/include/gtest/gtest-assertion-result.h delete mode 100644 test/unit/googletest/googletest/include/gtest/gtest-death-test.h delete mode 100644 test/unit/googletest/googletest/include/gtest/gtest-matchers.h delete mode 100644 test/unit/googletest/googletest/include/gtest/gtest-message.h delete mode 100644 test/unit/googletest/googletest/include/gtest/gtest-param-test.h delete mode 100644 test/unit/googletest/googletest/include/gtest/gtest-printers.h delete mode 100644 test/unit/googletest/googletest/include/gtest/gtest-spi.h delete mode 100644 test/unit/googletest/googletest/include/gtest/gtest-test-part.h delete mode 100644 test/unit/googletest/googletest/include/gtest/gtest-typed-test.h delete mode 100644 test/unit/googletest/googletest/include/gtest/gtest.h delete mode 100644 test/unit/googletest/googletest/include/gtest/gtest_pred_impl.h delete mode 100644 test/unit/googletest/googletest/include/gtest/gtest_prod.h delete mode 100644 test/unit/googletest/googletest/include/gtest/internal/custom/README.md delete mode 100644 test/unit/googletest/googletest/include/gtest/internal/custom/gtest-port.h delete mode 100644 test/unit/googletest/googletest/include/gtest/internal/custom/gtest-printers.h delete mode 100644 test/unit/googletest/googletest/include/gtest/internal/custom/gtest.h delete mode 100644 test/unit/googletest/googletest/include/gtest/internal/gtest-death-test-internal.h delete mode 100644 test/unit/googletest/googletest/include/gtest/internal/gtest-filepath.h delete mode 100644 test/unit/googletest/googletest/include/gtest/internal/gtest-internal.h delete mode 100644 test/unit/googletest/googletest/include/gtest/internal/gtest-param-util.h delete mode 100644 test/unit/googletest/googletest/include/gtest/internal/gtest-port-arch.h delete mode 100644 test/unit/googletest/googletest/include/gtest/internal/gtest-port.h delete mode 100644 test/unit/googletest/googletest/include/gtest/internal/gtest-string.h delete mode 100644 test/unit/googletest/googletest/include/gtest/internal/gtest-type-util.h delete mode 100644 test/unit/googletest/googletest/samples/prime_tables.h delete mode 100644 test/unit/googletest/googletest/samples/sample1.cc delete mode 100644 test/unit/googletest/googletest/samples/sample1.h delete mode 100644 test/unit/googletest/googletest/samples/sample10_unittest.cc delete mode 100644 test/unit/googletest/googletest/samples/sample1_unittest.cc delete mode 100644 test/unit/googletest/googletest/samples/sample2.cc delete mode 100644 test/unit/googletest/googletest/samples/sample2.h delete mode 100644 test/unit/googletest/googletest/samples/sample2_unittest.cc delete mode 100644 test/unit/googletest/googletest/samples/sample3-inl.h delete mode 100644 test/unit/googletest/googletest/samples/sample3_unittest.cc delete mode 100644 test/unit/googletest/googletest/samples/sample4.cc delete mode 100644 test/unit/googletest/googletest/samples/sample4.h delete mode 100644 test/unit/googletest/googletest/samples/sample4_unittest.cc delete mode 100644 test/unit/googletest/googletest/samples/sample5_unittest.cc delete mode 100644 test/unit/googletest/googletest/samples/sample6_unittest.cc delete mode 100644 test/unit/googletest/googletest/samples/sample7_unittest.cc delete mode 100644 test/unit/googletest/googletest/samples/sample8_unittest.cc delete mode 100644 test/unit/googletest/googletest/samples/sample9_unittest.cc delete mode 100644 test/unit/googletest/googletest/src/gtest-all.cc delete mode 100644 test/unit/googletest/googletest/src/gtest-assertion-result.cc delete mode 100644 test/unit/googletest/googletest/src/gtest-death-test.cc delete mode 100644 test/unit/googletest/googletest/src/gtest-filepath.cc delete mode 100644 test/unit/googletest/googletest/src/gtest-internal-inl.h delete mode 100644 test/unit/googletest/googletest/src/gtest-matchers.cc delete mode 100644 test/unit/googletest/googletest/src/gtest-port.cc delete mode 100644 test/unit/googletest/googletest/src/gtest-printers.cc delete mode 100644 test/unit/googletest/googletest/src/gtest-test-part.cc delete mode 100644 test/unit/googletest/googletest/src/gtest-typed-test.cc delete mode 100644 test/unit/googletest/googletest/src/gtest.cc delete mode 100644 test/unit/googletest/googletest/src/gtest_main.cc delete mode 100644 test/unit/googletest/googletest/test/BUILD.bazel delete mode 100755 test/unit/googletest/googletest/test/googletest-break-on-failure-unittest.py delete mode 100644 test/unit/googletest/googletest/test/googletest-break-on-failure-unittest_.cc delete mode 100755 test/unit/googletest/googletest/test/googletest-catch-exceptions-test.py delete mode 100644 test/unit/googletest/googletest/test/googletest-catch-exceptions-test_.cc delete mode 100755 test/unit/googletest/googletest/test/googletest-color-test.py delete mode 100644 test/unit/googletest/googletest/test/googletest-color-test_.cc delete mode 100644 test/unit/googletest/googletest/test/googletest-death-test-test.cc delete mode 100644 test/unit/googletest/googletest/test/googletest-death-test_ex_test.cc delete mode 100755 test/unit/googletest/googletest/test/googletest-env-var-test.py delete mode 100644 test/unit/googletest/googletest/test/googletest-env-var-test_.cc delete mode 100755 test/unit/googletest/googletest/test/googletest-failfast-unittest.py delete mode 100644 test/unit/googletest/googletest/test/googletest-failfast-unittest_.cc delete mode 100644 test/unit/googletest/googletest/test/googletest-filepath-test.cc delete mode 100755 test/unit/googletest/googletest/test/googletest-filter-unittest.py delete mode 100644 test/unit/googletest/googletest/test/googletest-filter-unittest_.cc delete mode 100644 test/unit/googletest/googletest/test/googletest-global-environment-unittest.py delete mode 100644 test/unit/googletest/googletest/test/googletest-global-environment-unittest_.cc delete mode 100644 test/unit/googletest/googletest/test/googletest-json-outfiles-test.py delete mode 100644 test/unit/googletest/googletest/test/googletest-json-output-unittest.py delete mode 100755 test/unit/googletest/googletest/test/googletest-list-tests-unittest.py delete mode 100644 test/unit/googletest/googletest/test/googletest-list-tests-unittest_.cc delete mode 100644 test/unit/googletest/googletest/test/googletest-listener-test.cc delete mode 100644 test/unit/googletest/googletest/test/googletest-message-test.cc delete mode 100644 test/unit/googletest/googletest/test/googletest-options-test.cc delete mode 100644 test/unit/googletest/googletest/test/googletest-output-test-golden-lin.txt delete mode 100755 test/unit/googletest/googletest/test/googletest-output-test.py delete mode 100644 test/unit/googletest/googletest/test/googletest-output-test_.cc delete mode 100644 test/unit/googletest/googletest/test/googletest-param-test-invalid-name1-test.py delete mode 100644 test/unit/googletest/googletest/test/googletest-param-test-invalid-name1-test_.cc delete mode 100644 test/unit/googletest/googletest/test/googletest-param-test-invalid-name2-test.py delete mode 100644 test/unit/googletest/googletest/test/googletest-param-test-invalid-name2-test_.cc delete mode 100644 test/unit/googletest/googletest/test/googletest-param-test-test.cc delete mode 100644 test/unit/googletest/googletest/test/googletest-param-test-test.h delete mode 100644 test/unit/googletest/googletest/test/googletest-param-test2-test.cc delete mode 100644 test/unit/googletest/googletest/test/googletest-port-test.cc delete mode 100644 test/unit/googletest/googletest/test/googletest-printers-test.cc delete mode 100755 test/unit/googletest/googletest/test/googletest-setuptestsuite-test.py delete mode 100644 test/unit/googletest/googletest/test/googletest-setuptestsuite-test_.cc delete mode 100755 test/unit/googletest/googletest/test/googletest-shuffle-test.py delete mode 100644 test/unit/googletest/googletest/test/googletest-shuffle-test_.cc delete mode 100644 test/unit/googletest/googletest/test/googletest-test-part-test.cc delete mode 100755 test/unit/googletest/googletest/test/googletest-throw-on-failure-test.py delete mode 100644 test/unit/googletest/googletest/test/googletest-throw-on-failure-test_.cc delete mode 100755 test/unit/googletest/googletest/test/googletest-uninitialized-test.py delete mode 100644 test/unit/googletest/googletest/test/googletest-uninitialized-test_.cc delete mode 100644 test/unit/googletest/googletest/test/gtest-typed-test2_test.cc delete mode 100644 test/unit/googletest/googletest/test/gtest-typed-test_test.cc delete mode 100644 test/unit/googletest/googletest/test/gtest-typed-test_test.h delete mode 100644 test/unit/googletest/googletest/test/gtest-unittest-api_test.cc delete mode 100644 test/unit/googletest/googletest/test/gtest_all_test.cc delete mode 100644 test/unit/googletest/googletest/test/gtest_assert_by_exception_test.cc delete mode 100644 test/unit/googletest/googletest/test/gtest_dirs_test.cc delete mode 100644 test/unit/googletest/googletest/test/gtest_environment_test.cc delete mode 100755 test/unit/googletest/googletest/test/gtest_help_test.py delete mode 100644 test/unit/googletest/googletest/test/gtest_help_test_.cc delete mode 100644 test/unit/googletest/googletest/test/gtest_json_test_utils.py delete mode 100644 test/unit/googletest/googletest/test/gtest_list_output_unittest.py delete mode 100644 test/unit/googletest/googletest/test/gtest_list_output_unittest_.cc delete mode 100644 test/unit/googletest/googletest/test/gtest_main_unittest.cc delete mode 100644 test/unit/googletest/googletest/test/gtest_no_test_unittest.cc delete mode 100644 test/unit/googletest/googletest/test/gtest_pred_impl_unittest.cc delete mode 100644 test/unit/googletest/googletest/test/gtest_premature_exit_test.cc delete mode 100644 test/unit/googletest/googletest/test/gtest_prod_test.cc delete mode 100644 test/unit/googletest/googletest/test/gtest_repeat_test.cc delete mode 100755 test/unit/googletest/googletest/test/gtest_skip_check_output_test.py delete mode 100755 test/unit/googletest/googletest/test/gtest_skip_environment_check_output_test.py delete mode 100644 test/unit/googletest/googletest/test/gtest_skip_in_environment_setup_test.cc delete mode 100644 test/unit/googletest/googletest/test/gtest_skip_test.cc delete mode 100644 test/unit/googletest/googletest/test/gtest_sole_header_test.cc delete mode 100644 test/unit/googletest/googletest/test/gtest_stress_test.cc delete mode 100644 test/unit/googletest/googletest/test/gtest_test_macro_stack_footprint_test.cc delete mode 100755 test/unit/googletest/googletest/test/gtest_test_utils.py delete mode 100755 test/unit/googletest/googletest/test/gtest_testbridge_test.py delete mode 100644 test/unit/googletest/googletest/test/gtest_testbridge_test_.cc delete mode 100644 test/unit/googletest/googletest/test/gtest_throw_on_failure_ex_test.cc delete mode 100644 test/unit/googletest/googletest/test/gtest_unittest.cc delete mode 100644 test/unit/googletest/googletest/test/gtest_xml_outfile1_test_.cc delete mode 100644 test/unit/googletest/googletest/test/gtest_xml_outfile2_test_.cc delete mode 100755 test/unit/googletest/googletest/test/gtest_xml_outfiles_test.py delete mode 100755 test/unit/googletest/googletest/test/gtest_xml_output_unittest.py delete mode 100644 test/unit/googletest/googletest/test/gtest_xml_output_unittest_.cc delete mode 100755 test/unit/googletest/googletest/test/gtest_xml_test_utils.py delete mode 100644 test/unit/googletest/googletest/test/production.cc delete mode 100644 test/unit/googletest/googletest/test/production.h delete mode 100644 test/unit/googletest/googletest_deps.bzl diff --git a/benchmark/benchmarks.cpp b/benchmark/benchmarks.cpp index 8222170..85d8cd0 100644 --- a/benchmark/benchmarks.cpp +++ b/benchmark/benchmarks.cpp @@ -9,109 +9,40 @@ #include "magnetron_internal.h" -static auto bench_cpu_compute(std::int64_t numel_per_dim) -> void { - ankerl::nanobench::Bench bench {}; - bench.title("Parallel MM Big Tensor | Numel per Dim: " + std::to_string(numel_per_dim)) - .unit("MM") - .warmup(100) - .relative(true) - .performanceCounters(true); - - std::cout << "Benchmarking Parallel MM on CPU with Numel per Dim: " << numel_per_dim << std::endl; - - auto exec_bench = [&](std::uint32_t threads) { - mag_device_descriptor_t desc {}; - desc.type = MAG_COMPUTE_DEVICE_TYPE_CPU; - desc.thread_count = threads; - mag_ctx_t* ctx = mag_ctx_create2(&desc); - mag_tensor_t* A = mag_tensor_create_2d(ctx, MAG_DTYPE_F32, numel_per_dim, numel_per_dim); - mag_tensor_fill_random_normal(A, 0.0f, 1.0f); - mag_tensor_t* B = mag_tensor_create_2d(ctx, MAG_DTYPE_F32, numel_per_dim, numel_per_dim); - mag_tensor_fill_random_normal(B, 0.0f, 1.0f); - bench.run("Parallel MM on " + std::to_string(threads) + " threads, Elems = " + std::to_string(A->numel), [&] { - mag_tensor_t* R = mag_matmul(A, B); - ankerl::nanobench::doNotOptimizeAway(R); - mag_tensor_decref(R); - }); - - ankerl::nanobench::doNotOptimizeAway(ctx); - mag_tensor_decref(B); - mag_tensor_decref(A); - mag_ctx_destroy(ctx); - }; - - std::uint32_t num_threads = std::max(1u, std::thread::hardware_concurrency()); +static auto bench_cpu_compute(ankerl::nanobench::Bench& bench, std::int64_t numel_per_dim) -> void { + mag_device_descriptor_t desc {}; + desc.type = MAG_COMPUTE_DEVICE_TYPE_CPU; + mag_ctx_t* ctx = mag_ctx_create2(&desc); + mag_tensor_t* A = mag_tensor_create_2d(ctx, MAG_DTYPE_F32, numel_per_dim, numel_per_dim); + mag_tensor_fill_random_normal(A, 0.0f, 1.0f); + mag_tensor_t* B = mag_tensor_create_2d(ctx, MAG_DTYPE_F32, numel_per_dim, numel_per_dim); + mag_tensor_fill_random_normal(B, 0.0f, 1.0f); + bench.run("Parallel Elems = " + std::to_string(A->numel), [&] { + mag_tensor_t* R = mag_add(A, B); + ankerl::nanobench::doNotOptimizeAway(R); + mag_tensor_decref(R); + }); - for (std::uint32_t i=1; i <= num_threads;) { - exec_bench(i); - if (i == 1) ++i; - else i += 2; - } + ankerl::nanobench::doNotOptimizeAway(ctx); + mag_tensor_decref(B); + mag_tensor_decref(A); + mag_ctx_destroy(ctx); } auto main() -> int { - std::vector data {}; - std::uniform_int_distribution dist(1, UINT32_MAX); - std::mt19937 gen(std::random_device{}()); - int N = 10000; - data.reserve(N); - for (int i=0; i setups {}; - setups.reserve(N); - for (int i=0; igrad) { + if (!t->grad) { mag_tensor_t* grad = mag_tensor_create(t->ctx, t->dtype, t->shape, t->rank, NULL, 0); grad->flags = (grad->flags | MAG_TFLAG_IS_GRAD) & ~MAG_TFLAG_REQUIRES_GRAD; mag_tensor_fill(grad, 1.0f); diff --git a/magnetron/magnetron_internal.h b/magnetron/magnetron_internal.h index c03015a..61ba8eb 100644 --- a/magnetron/magnetron_internal.h +++ b/magnetron/magnetron_internal.h @@ -358,18 +358,28 @@ static inline void* mag_pincr(void** p, size_t sz, size_t align) { ** Torbjörn Granlund and Peter L. Montgomery, "Division by Invariant Integers Using Multiplication", ACM SIGPLAN Notices, Issue 6, Vol 29, 61-72, June 1994. ** http://gmplib.org/~tege/divcnst-pldi94.pdf */ -static inline uint64_t mag_ivdiv_mkdi(uint32_t divisor) { /* Create packed division info from devisor. */ + +typedef struct mag_ivdiv_t { + uint32_t s1; + uint32_t s2; + uint32_t d; +} mag_ivdiv_t; + +static inline mag_ivdiv_t mag_ivdiv_mkdi(uint32_t divisor) { /* Create packed division info from devisor. */ uint32_t l = (divisor-1) ? 32 - __builtin_clz((divisor-1)) : 0; uint32_t s1 = l > 1 ? 1 : l&0xff; uint32_t s2 = !l ? 0 : (l-1)&0xff; - return ((((1ull<>32)>>32; - return (t + ((x - t)>>((di>>8)&0xff)))>>(di&0xff); + uint32_t t = (uint64_t)x*(di.d)>>32; + return (t + ((x - t)>>((di.s1)&0xff)))>>(di.s2); } -static inline uint32_t mag_ivrem32(uint32_t x, uint32_t y, uint64_t ctx) { /* r = x % y. Fast remainder using invariant multiplication */ +static inline uint32_t mag_ivrem32(uint32_t x, uint32_t y, mag_ivdiv_t ctx) { /* r = x % y. Fast remainder using invariant multiplication */ return x - y*mag_ivdiv32(x, y, ctx); } diff --git a/python/magnetron_framework/magnetron/_ffi_cdecl_generated.py b/python/magnetron_framework/magnetron/_ffi_cdecl_generated.py index 61b1cb2..7b50652 100644 --- a/python/magnetron_framework/magnetron/_ffi_cdecl_generated.py +++ b/python/magnetron_framework/magnetron/_ffi_cdecl_generated.py @@ -1,2 +1,4 @@ # Autogenered by /Users/mariosieg/Documents/projects/magnetron/python/magnetron_framework/bing_gen.py 2025-01-31 18:00:29.869744, do NOT edit! -__MAG_CDECLS: str = b'\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x43\x4f\x4d\x50\x55\x54\x45\x5f\x44\x45\x56\x49\x43\x45\x5f\x54\x59\x50\x45\x5f\x43\x50\x55\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4d\x50\x55\x54\x45\x5f\x44\x45\x56\x49\x43\x45\x5f\x54\x59\x50\x45\x5f\x47\x50\x55\x5f\x43\x55\x44\x41\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4d\x50\x55\x54\x45\x5f\x44\x45\x56\x49\x43\x45\x5f\x54\x59\x50\x45\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x67\x65\x74\x5f\x6e\x61\x6d\x65\x28\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x6f\x70\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x45\x58\x45\x43\x5f\x4d\x4f\x44\x45\x5f\x45\x41\x47\x45\x52\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x45\x58\x45\x43\x5f\x4d\x4f\x44\x45\x5f\x44\x45\x46\x45\x52\x52\x45\x44\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x45\x58\x45\x43\x5f\x4d\x4f\x44\x45\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x50\x52\x4e\x47\x5f\x4d\x45\x52\x53\x45\x4e\x4e\x45\x5f\x54\x57\x49\x53\x54\x45\x52\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x50\x52\x4e\x47\x5f\x50\x43\x47\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x50\x52\x4e\x47\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x74\x68\x72\x65\x61\x64\x5f\x73\x63\x68\x65\x64\x5f\x70\x72\x69\x6f\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x4e\x4f\x52\x4d\x41\x4c\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x4d\x45\x44\x49\x55\x4d\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x48\x49\x47\x48\x20\x3d\x20\x32\x2c\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x52\x45\x41\x4c\x54\x49\x4d\x45\x20\x3d\x20\x33\x2c\x0a\x7d\x20\x6d\x61\x67\x5f\x74\x68\x72\x65\x61\x64\x5f\x73\x63\x68\x65\x64\x5f\x70\x72\x69\x6f\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x63\x6f\x6c\x6f\x72\x5f\x63\x68\x61\x6e\x6e\x65\x6c\x73\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x41\x55\x54\x4f\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x47\x52\x41\x59\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x47\x52\x41\x59\x5f\x41\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x52\x47\x42\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x52\x47\x42\x41\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x63\x6f\x6c\x6f\x72\x5f\x63\x68\x61\x6e\x6e\x65\x6c\x73\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x2a\x20\x28\x2a\x6d\x61\x67\x5f\x67\x65\x74\x5f\x61\x6c\x6c\x6f\x63\x5f\x66\x6e\x28\x76\x6f\x69\x64\x29\x29\x28\x76\x6f\x69\x64\x2a\x20\x62\x6c\x6b\x2c\x20\x73\x69\x7a\x65\x5f\x74\x20\x73\x69\x7a\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x73\x65\x74\x5f\x61\x6c\x6c\x6f\x63\x5f\x66\x6e\x28\x76\x6f\x69\x64\x2a\x20\x28\x2a\x61\x6c\x6c\x6f\x63\x29\x28\x76\x6f\x69\x64\x2a\x20\x62\x6c\x6b\x2c\x20\x73\x69\x7a\x65\x5f\x74\x20\x73\x69\x7a\x65\x29\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x73\x65\x74\x5f\x6c\x6f\x67\x5f\x6d\x6f\x64\x65\x28\x62\x6f\x6f\x6c\x20\x65\x6e\x61\x62\x6c\x65\x64\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x68\x61\x72\x33\x32\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x64\x65\x73\x63\x72\x69\x70\x74\x6f\x72\x5f\x74\x20\x7b\x0a\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x3b\x0a\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x74\x68\x72\x65\x61\x64\x5f\x63\x6f\x75\x6e\x74\x3b\x0a\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x63\x75\x64\x61\x5f\x64\x65\x76\x69\x63\x65\x5f\x69\x64\x3b\x0a\x7d\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x64\x65\x73\x63\x72\x69\x70\x74\x6f\x72\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x63\x72\x65\x61\x74\x65\x28\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x64\x65\x76\x69\x63\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x63\x72\x65\x61\x74\x65\x32\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x64\x65\x73\x63\x72\x69\x70\x74\x6f\x72\x5f\x74\x2a\x20\x64\x65\x76\x69\x63\x65\x5f\x69\x6e\x66\x6f\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x73\x65\x74\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x20\x6d\x6f\x64\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x73\x65\x74\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x2c\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x73\x65\x65\x64\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x6f\x73\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x76\x69\x72\x74\x75\x61\x6c\x5f\x63\x6f\x72\x65\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x63\x6f\x72\x65\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x73\x6f\x63\x6b\x65\x74\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x6d\x65\x6d\x6f\x72\x79\x5f\x74\x6f\x74\x61\x6c\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x6d\x65\x6d\x6f\x72\x79\x5f\x66\x72\x65\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x69\x73\x5f\x6e\x75\x6d\x61\x5f\x73\x79\x73\x74\x65\x6d\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x73\x69\x7a\x65\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x74\x6f\x74\x61\x6c\x5f\x74\x65\x6e\x73\x6f\x72\x73\x5f\x63\x72\x65\x61\x74\x65\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x70\x72\x6f\x66\x69\x6c\x65\x5f\x73\x74\x61\x72\x74\x5f\x72\x65\x63\x6f\x72\x64\x69\x6e\x67\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x70\x72\x6f\x66\x69\x6c\x65\x5f\x73\x74\x6f\x70\x5f\x72\x65\x63\x6f\x72\x64\x69\x6e\x67\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x65\x78\x70\x6f\x72\x74\x5f\x63\x73\x76\x5f\x66\x69\x6c\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x64\x65\x73\x74\x72\x6f\x79\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x44\x54\x59\x50\x45\x5f\x46\x33\x32\x2c\x0a\x4d\x41\x47\x5f\x44\x54\x59\x50\x45\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x74\x20\x7b\x0a\x69\x6e\x74\x36\x34\x5f\x74\x20\x73\x69\x7a\x65\x3b\x0a\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6e\x61\x6d\x65\x3b\x0a\x7d\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x6f\x66\x28\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x70\x61\x63\x6b\x5f\x63\x6f\x6c\x6f\x72\x5f\x75\x38\x28\x75\x69\x6e\x74\x38\x5f\x74\x20\x72\x2c\x20\x75\x69\x6e\x74\x38\x5f\x74\x20\x67\x2c\x20\x75\x69\x6e\x74\x38\x5f\x74\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x70\x61\x63\x6b\x5f\x63\x6f\x6c\x6f\x72\x5f\x66\x33\x32\x28\x66\x6c\x6f\x61\x74\x20\x72\x2c\x20\x66\x6c\x6f\x61\x74\x20\x67\x2c\x20\x66\x6c\x6f\x61\x74\x20\x62\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x67\x72\x61\x70\x68\x5f\x65\x76\x61\x6c\x5f\x6f\x72\x64\x65\x72\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x47\x52\x41\x50\x48\x5f\x45\x56\x41\x4c\x5f\x4f\x52\x44\x45\x52\x5f\x46\x4f\x52\x57\x41\x52\x44\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x47\x52\x41\x50\x48\x5f\x45\x56\x41\x4c\x5f\x4f\x52\x44\x45\x52\x5f\x52\x45\x56\x45\x52\x53\x45\x20\x3d\x20\x31\x0a\x7d\x20\x6d\x61\x67\x5f\x67\x72\x61\x70\x68\x5f\x65\x76\x61\x6c\x5f\x6f\x72\x64\x65\x72\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x31\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x32\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x33\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x34\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x35\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x36\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x36\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x6c\x6f\x6e\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x76\x69\x65\x77\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x72\x61\x6e\x73\x70\x6f\x73\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x70\x65\x72\x6d\x75\x74\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x30\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x31\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x32\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x33\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x34\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x35\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x65\x61\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x69\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x61\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x6d\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x62\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x62\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6e\x65\x67\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6e\x65\x67\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6c\x6f\x67\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6c\x6f\x67\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x74\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x74\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6e\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x6f\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x6f\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x74\x65\x70\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x74\x65\x70\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x65\x78\x70\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x65\x78\x70\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x68\x61\x72\x64\x5f\x73\x69\x67\x6d\x6f\x69\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x68\x61\x72\x64\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x28\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x28\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x73\x28\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x70\x6f\x77\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x70\x6f\x77\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x61\x74\x6d\x75\x6c\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x6e\x63\x72\x65\x66\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x65\x63\x72\x65\x66\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x6f\x70\x79\x5f\x62\x75\x66\x66\x65\x72\x5f\x66\x72\x6f\x6d\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x76\x6f\x69\x64\x2a\x20\x64\x61\x74\x61\x2c\x20\x73\x69\x7a\x65\x5f\x74\x20\x73\x69\x7a\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x69\x6c\x6c\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x69\x6c\x6c\x5f\x72\x61\x6e\x64\x6f\x6d\x5f\x75\x6e\x69\x66\x6f\x72\x6d\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x66\x6c\x6f\x61\x74\x20\x6d\x69\x6e\x2c\x20\x66\x6c\x6f\x61\x74\x20\x6d\x61\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x69\x6c\x6c\x5f\x72\x61\x6e\x64\x6f\x6d\x5f\x6e\x6f\x72\x6d\x61\x6c\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x66\x6c\x6f\x61\x74\x20\x6d\x65\x61\x6e\x2c\x20\x66\x6c\x6f\x61\x74\x20\x73\x74\x64\x64\x65\x76\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x70\x61\x63\x6b\x65\x64\x5f\x72\x65\x66\x63\x6f\x75\x6e\x74\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x72\x65\x74\x61\x69\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x73\x69\x7a\x65\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x6d\x65\x6d\x6f\x72\x79\x5f\x75\x73\x61\x67\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x70\x72\x69\x6e\x74\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x62\x6f\x6f\x6c\x20\x77\x69\x74\x68\x5f\x68\x65\x61\x64\x65\x72\x2c\x20\x62\x6f\x6f\x6c\x20\x77\x69\x74\x68\x5f\x64\x61\x74\x61\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x6e\x61\x6d\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6e\x61\x6d\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x6d\x74\x5f\x6e\x61\x6d\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x6d\x74\x2c\x20\x2e\x2e\x2e\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x72\x61\x6e\x6b\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x69\x6e\x74\x36\x34\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x68\x61\x70\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x69\x6e\x74\x36\x34\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x74\x72\x69\x64\x65\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x74\x79\x70\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x61\x74\x61\x5f\x70\x74\x72\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x61\x74\x61\x5f\x73\x69\x7a\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6e\x75\x6d\x65\x6c\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6e\x75\x6d\x5f\x72\x6f\x77\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6e\x75\x6d\x5f\x63\x6f\x6c\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x73\x63\x61\x6c\x61\x72\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x76\x65\x63\x74\x6f\x72\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x6d\x61\x74\x72\x69\x78\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x76\x6f\x6c\x75\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x73\x68\x61\x70\x65\x5f\x65\x71\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x61\x72\x65\x5f\x73\x74\x72\x69\x64\x65\x73\x5f\x65\x71\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x61\x6e\x5f\x62\x72\x6f\x61\x64\x63\x61\x73\x74\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x74\x72\x61\x6e\x73\x70\x6f\x73\x65\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x70\x65\x72\x6d\x75\x74\x65\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x63\x6f\x6e\x74\x69\x67\x75\x6f\x75\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x72\x61\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x72\x65\x71\x75\x69\x72\x65\x73\x5f\x67\x72\x61\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x72\x65\x71\x75\x69\x72\x65\x73\x5f\x67\x72\x61\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x62\x6f\x6f\x6c\x20\x72\x65\x71\x75\x69\x72\x65\x73\x5f\x67\x72\x61\x64\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x62\x61\x63\x6b\x77\x61\x72\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x66\x6c\x6f\x61\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x30\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x30\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x66\x6c\x6f\x61\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x76\x69\x72\x74\x75\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x76\x5f\x69\x64\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x76\x69\x72\x74\x75\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x76\x5f\x69\x64\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x65\x71\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x63\x6c\x6f\x73\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x2c\x20\x66\x6c\x6f\x61\x74\x20\x65\x70\x73\x2c\x20\x64\x6f\x75\x62\x6c\x65\x2a\x20\x70\x65\x72\x63\x65\x6e\x74\x5f\x65\x71\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x6d\x67\x5f\x64\x72\x61\x77\x5f\x62\x6f\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x78\x31\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x79\x31\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x78\x32\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x79\x32\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x77\x69\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x67\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x6d\x67\x5f\x64\x72\x61\x77\x5f\x74\x65\x78\x74\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x78\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x79\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x73\x69\x7a\x65\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x67\x62\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x74\x78\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x63\x74\x78\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x75\x73\x65\x72\x5f\x64\x61\x74\x61\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x75\x73\x65\x72\x5f\x64\x61\x74\x61\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x76\x6f\x69\x64\x2a\x20\x75\x64\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x61\x76\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6c\x6f\x61\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6c\x6f\x61\x64\x5f\x69\x6d\x61\x67\x65\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x2c\x20\x6d\x61\x67\x5f\x63\x6f\x6c\x6f\x72\x5f\x63\x68\x61\x6e\x6e\x65\x6c\x73\x5f\x74\x20\x63\x68\x61\x6e\x6e\x65\x6c\x73\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x65\x73\x69\x7a\x65\x5f\x77\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x65\x73\x69\x7a\x65\x5f\x68\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x61\x76\x65\x5f\x69\x6d\x61\x67\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x29\x3b\x0a'.decode('utf-8') +__MAG_CDECLS: str = b'\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x43\x4f\x4d\x50\x55\x54\x45\x5f\x44\x45\x56\x49\x43\x45\x5f\x54\x59\x50\x45\x5f\x43\x50\x55\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4d\x50\x55\x54\x45\x5f\x44\x45\x56\x49\x43\x45\x5f\x54\x59\x50\x45\x5f\x47\x50\x55\x5f\x43\x55\x44\x41\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4d\x50\x55\x54\x45\x5f\x44\x45\x56\x49\x43\x45\x5f\x54\x59\x50\x45\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x67\x65\x74\x5f\x6e\x61\x6d\x65\x28\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x6f\x70\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x45\x58\x45\x43\x5f\x4d\x4f\x44\x45\x5f\x45\x41\x47\x45\x52\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x45\x58\x45\x43\x5f\x4d\x4f\x44\x45\x5f\x44\x45\x46\x45\x52\x52\x45\x44\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x45\x58\x45\x43\x5f\x4d\x4f\x44\x45\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x50\x52\x4e\x47\x5f\x4d\x45\x52\x53\x45\x4e\x4e\x45\x5f\x54\x57\x49\x53\x54\x45\x52\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x50\x52\x4e\x47\x5f\x50\x43\x47\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x50\x52\x4e\x47\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x74\x68\x72\x65\x61\x64\x5f\x73\x63\x68\x65\x64\x5f\x70\x72\x69\x6f\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x4e\x4f\x52\x4d\x41\x4c\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x4d\x45\x44\x49\x55\x4d\x20\x3d\x20\x31\x2c\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x48\x49\x47\x48\x20\x3d\x20\x32\x2c\x0a\x4d\x41\x47\x5f\x54\x48\x52\x45\x41\x44\x5f\x53\x43\x48\x45\x44\x5f\x50\x52\x49\x4f\x5f\x52\x45\x41\x4c\x54\x49\x4d\x45\x20\x3d\x20\x33\x2c\x0a\x7d\x20\x6d\x61\x67\x5f\x74\x68\x72\x65\x61\x64\x5f\x73\x63\x68\x65\x64\x5f\x70\x72\x69\x6f\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x63\x6f\x6c\x6f\x72\x5f\x63\x68\x61\x6e\x6e\x65\x6c\x73\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x41\x55\x54\x4f\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x47\x52\x41\x59\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x47\x52\x41\x59\x5f\x41\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x52\x47\x42\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x52\x47\x42\x41\x2c\x0a\x4d\x41\x47\x5f\x43\x4f\x4c\x4f\x52\x5f\x43\x48\x41\x4e\x4e\x45\x4c\x53\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x63\x6f\x6c\x6f\x72\x5f\x63\x68\x61\x6e\x6e\x65\x6c\x73\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x2a\x20\x28\x2a\x6d\x61\x67\x5f\x67\x65\x74\x5f\x61\x6c\x6c\x6f\x63\x5f\x66\x6e\x28\x76\x6f\x69\x64\x29\x29\x28\x76\x6f\x69\x64\x2a\x20\x62\x6c\x6b\x2c\x20\x73\x69\x7a\x65\x5f\x74\x20\x73\x69\x7a\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x73\x65\x74\x5f\x61\x6c\x6c\x6f\x63\x5f\x66\x6e\x28\x76\x6f\x69\x64\x2a\x20\x28\x2a\x61\x6c\x6c\x6f\x63\x29\x28\x76\x6f\x69\x64\x2a\x20\x62\x6c\x6b\x2c\x20\x73\x69\x7a\x65\x5f\x74\x20\x73\x69\x7a\x65\x29\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x73\x65\x74\x5f\x6c\x6f\x67\x5f\x6d\x6f\x64\x65\x28\x62\x6f\x6f\x6c\x20\x65\x6e\x61\x62\x6c\x65\x64\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x68\x61\x72\x33\x32\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x64\x65\x73\x63\x72\x69\x70\x74\x6f\x72\x5f\x74\x20\x7b\x0a\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x3b\x0a\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x74\x68\x72\x65\x61\x64\x5f\x63\x6f\x75\x6e\x74\x3b\x0a\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x63\x75\x64\x61\x5f\x64\x65\x76\x69\x63\x65\x5f\x69\x64\x3b\x0a\x7d\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x64\x65\x73\x63\x72\x69\x70\x74\x6f\x72\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x63\x72\x65\x61\x74\x65\x28\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x64\x65\x76\x69\x63\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x63\x72\x65\x61\x74\x65\x32\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x64\x65\x76\x69\x63\x65\x5f\x64\x65\x73\x63\x72\x69\x70\x74\x6f\x72\x5f\x74\x2a\x20\x64\x65\x76\x69\x63\x65\x5f\x69\x6e\x66\x6f\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x73\x65\x74\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x65\x78\x65\x63\x5f\x6d\x6f\x64\x65\x5f\x74\x20\x6d\x6f\x64\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x73\x65\x74\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x70\x72\x6e\x67\x5f\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x5f\x74\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x2c\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x73\x65\x65\x64\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x74\x79\x70\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x6f\x6d\x70\x75\x74\x65\x5f\x64\x65\x76\x69\x63\x65\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x6f\x73\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x76\x69\x72\x74\x75\x61\x6c\x5f\x63\x6f\x72\x65\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x63\x6f\x72\x65\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x63\x70\x75\x5f\x73\x6f\x63\x6b\x65\x74\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x6d\x65\x6d\x6f\x72\x79\x5f\x74\x6f\x74\x61\x6c\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x6d\x65\x6d\x6f\x72\x79\x5f\x66\x72\x65\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x69\x73\x5f\x6e\x75\x6d\x61\x5f\x73\x79\x73\x74\x65\x6d\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x73\x69\x7a\x65\x5f\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x67\x65\x74\x5f\x74\x6f\x74\x61\x6c\x5f\x74\x65\x6e\x73\x6f\x72\x73\x5f\x63\x72\x65\x61\x74\x65\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x70\x72\x6f\x66\x69\x6c\x65\x5f\x73\x74\x61\x72\x74\x5f\x72\x65\x63\x6f\x72\x64\x69\x6e\x67\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x70\x72\x6f\x66\x69\x6c\x65\x5f\x73\x74\x6f\x70\x5f\x72\x65\x63\x6f\x72\x64\x69\x6e\x67\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x65\x78\x70\x6f\x72\x74\x5f\x63\x73\x76\x5f\x66\x69\x6c\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x64\x65\x73\x74\x72\x6f\x79\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x44\x54\x59\x50\x45\x5f\x46\x33\x32\x2c\x0a\x4d\x41\x47\x5f\x44\x54\x59\x50\x45\x5f\x5f\x4e\x55\x4d\x0a\x7d\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x73\x74\x72\x75\x63\x74\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x74\x20\x7b\x0a\x69\x6e\x74\x36\x34\x5f\x74\x20\x73\x69\x7a\x65\x3b\x0a\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6e\x61\x6d\x65\x3b\x0a\x7d\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x6d\x65\x74\x61\x5f\x6f\x66\x28\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x70\x61\x63\x6b\x5f\x63\x6f\x6c\x6f\x72\x5f\x75\x38\x28\x75\x69\x6e\x74\x38\x5f\x74\x20\x72\x2c\x20\x75\x69\x6e\x74\x38\x5f\x74\x20\x67\x2c\x20\x75\x69\x6e\x74\x38\x5f\x74\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x6d\x61\x67\x5f\x70\x61\x63\x6b\x5f\x63\x6f\x6c\x6f\x72\x5f\x66\x33\x32\x28\x66\x6c\x6f\x61\x74\x20\x72\x2c\x20\x66\x6c\x6f\x61\x74\x20\x67\x2c\x20\x66\x6c\x6f\x61\x74\x20\x62\x29\x3b\x0a\x74\x79\x70\x65\x64\x65\x66\x20\x65\x6e\x75\x6d\x20\x6d\x61\x67\x5f\x67\x72\x61\x70\x68\x5f\x65\x76\x61\x6c\x5f\x6f\x72\x64\x65\x72\x5f\x74\x20\x7b\x0a\x4d\x41\x47\x5f\x47\x52\x41\x50\x48\x5f\x45\x56\x41\x4c\x5f\x4f\x52\x44\x45\x52\x5f\x46\x4f\x52\x57\x41\x52\x44\x20\x3d\x20\x30\x2c\x0a\x4d\x41\x47\x5f\x47\x52\x41\x50\x48\x5f\x45\x56\x41\x4c\x5f\x4f\x52\x44\x45\x52\x5f\x52\x45\x56\x45\x52\x53\x45\x20\x3d\x20\x31\x0a\x7d\x20\x6d\x61\x67\x5f\x67\x72\x61\x70\x68\x5f\x65\x76\x61\x6c\x5f\x6f\x72\x64\x65\x72\x5f\x74\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x31\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x32\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x33\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x34\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x35\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x72\x65\x61\x74\x65\x5f\x36\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x74\x79\x70\x65\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x36\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x6c\x6f\x6e\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x76\x69\x65\x77\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x72\x61\x6e\x73\x70\x6f\x73\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x70\x65\x72\x6d\x75\x74\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x30\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x31\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x32\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x33\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x34\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x64\x35\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x65\x61\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x69\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x61\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x6d\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x62\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x62\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6e\x65\x67\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6e\x65\x67\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6c\x6f\x67\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6c\x6f\x67\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x74\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x71\x72\x74\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6e\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x6f\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x63\x6f\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x74\x65\x70\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x74\x65\x70\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x65\x78\x70\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x65\x78\x70\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x6f\x66\x74\x6d\x61\x78\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x68\x61\x72\x64\x5f\x73\x69\x67\x6d\x6f\x69\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x68\x61\x72\x64\x5f\x73\x69\x67\x6d\x6f\x69\x64\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x69\x6c\x75\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x61\x6e\x68\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x72\x65\x6c\x75\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x5f\x64\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x67\x65\x6c\x75\x5f\x64\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x28\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x28\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x79\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x61\x64\x64\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x73\x28\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x73\x75\x62\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x75\x6c\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x64\x69\x76\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x70\x6f\x77\x73\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x70\x6f\x77\x73\x5f\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x69\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x6d\x61\x74\x6d\x75\x6c\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x6e\x63\x72\x65\x66\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x65\x63\x72\x65\x66\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x6f\x70\x79\x5f\x62\x75\x66\x66\x65\x72\x5f\x66\x72\x6f\x6d\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x76\x6f\x69\x64\x2a\x20\x64\x61\x74\x61\x2c\x20\x73\x69\x7a\x65\x5f\x74\x20\x73\x69\x7a\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x69\x6c\x6c\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x69\x6c\x6c\x5f\x72\x61\x6e\x64\x6f\x6d\x5f\x75\x6e\x69\x66\x6f\x72\x6d\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x66\x6c\x6f\x61\x74\x20\x6d\x69\x6e\x2c\x20\x66\x6c\x6f\x61\x74\x20\x6d\x61\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x69\x6c\x6c\x5f\x72\x61\x6e\x64\x6f\x6d\x5f\x6e\x6f\x72\x6d\x61\x6c\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x66\x6c\x6f\x61\x74\x20\x6d\x65\x61\x6e\x2c\x20\x66\x6c\x6f\x61\x74\x20\x73\x74\x64\x64\x65\x76\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x75\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x70\x61\x63\x6b\x65\x64\x5f\x72\x65\x66\x63\x6f\x75\x6e\x74\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x72\x65\x74\x61\x69\x6e\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x73\x69\x7a\x65\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x6d\x65\x6d\x6f\x72\x79\x5f\x75\x73\x61\x67\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x70\x72\x69\x6e\x74\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x62\x6f\x6f\x6c\x20\x77\x69\x74\x68\x5f\x68\x65\x61\x64\x65\x72\x2c\x20\x62\x6f\x6f\x6c\x20\x77\x69\x74\x68\x5f\x64\x61\x74\x61\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x6e\x61\x6d\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6e\x61\x6d\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x66\x6d\x74\x5f\x6e\x61\x6d\x65\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x6d\x74\x2c\x20\x2e\x2e\x2e\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x6e\x61\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x72\x61\x6e\x6b\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x69\x6e\x74\x36\x34\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x68\x61\x70\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x63\x6f\x6e\x73\x74\x20\x69\x6e\x74\x36\x34\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x74\x72\x69\x64\x65\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x64\x74\x79\x70\x65\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x74\x79\x70\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x61\x74\x61\x5f\x70\x74\x72\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x64\x61\x74\x61\x5f\x73\x69\x7a\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6e\x75\x6d\x65\x6c\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6e\x75\x6d\x5f\x72\x6f\x77\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6e\x75\x6d\x5f\x63\x6f\x6c\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x73\x63\x61\x6c\x61\x72\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x76\x65\x63\x74\x6f\x72\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x6d\x61\x74\x72\x69\x78\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x76\x6f\x6c\x75\x6d\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x73\x68\x61\x70\x65\x5f\x65\x71\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x61\x72\x65\x5f\x73\x74\x72\x69\x64\x65\x73\x5f\x65\x71\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x63\x61\x6e\x5f\x62\x72\x6f\x61\x64\x63\x61\x73\x74\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x74\x72\x61\x6e\x73\x70\x6f\x73\x65\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x70\x65\x72\x6d\x75\x74\x65\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x63\x6f\x6e\x74\x69\x67\x75\x6f\x75\x73\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x72\x61\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x72\x65\x71\x75\x69\x72\x65\x73\x5f\x67\x72\x61\x64\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x72\x65\x71\x75\x69\x72\x65\x73\x5f\x67\x72\x61\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x62\x6f\x6f\x6c\x20\x72\x65\x71\x75\x69\x72\x65\x73\x5f\x67\x72\x61\x64\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x62\x61\x63\x6b\x77\x61\x72\x64\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x66\x6c\x6f\x61\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x30\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x70\x68\x79\x73\x69\x63\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x30\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x31\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x32\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x33\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x34\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x64\x35\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x66\x6c\x6f\x61\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x76\x69\x72\x74\x75\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x76\x5f\x69\x64\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x73\x63\x61\x6c\x61\x72\x5f\x76\x69\x72\x74\x75\x61\x6c\x5f\x69\x6e\x64\x65\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x36\x34\x5f\x74\x20\x76\x5f\x69\x64\x78\x2c\x20\x66\x6c\x6f\x61\x74\x20\x78\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x65\x71\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x62\x6f\x6f\x6c\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x73\x5f\x63\x6c\x6f\x73\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x61\x2c\x20\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x62\x2c\x20\x66\x6c\x6f\x61\x74\x20\x65\x70\x73\x2c\x20\x64\x6f\x75\x62\x6c\x65\x2a\x20\x70\x65\x72\x63\x65\x6e\x74\x5f\x65\x71\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x6d\x67\x5f\x64\x72\x61\x77\x5f\x62\x6f\x78\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x78\x31\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x79\x31\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x78\x32\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x79\x32\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x77\x69\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x67\x62\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x69\x6d\x67\x5f\x64\x72\x61\x77\x5f\x74\x65\x78\x74\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x78\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x79\x2c\x20\x69\x6e\x74\x33\x32\x5f\x74\x20\x73\x69\x7a\x65\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x67\x62\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x74\x78\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x63\x74\x78\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x67\x65\x74\x5f\x75\x73\x65\x72\x5f\x64\x61\x74\x61\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x65\x74\x5f\x75\x73\x65\x72\x5f\x64\x61\x74\x61\x28\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x76\x6f\x69\x64\x2a\x20\x75\x64\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x61\x76\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6c\x6f\x61\x64\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x6c\x6f\x61\x64\x5f\x69\x6d\x61\x67\x65\x28\x6d\x61\x67\x5f\x63\x74\x78\x5f\x74\x2a\x20\x63\x74\x78\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x2c\x20\x6d\x61\x67\x5f\x63\x6f\x6c\x6f\x72\x5f\x63\x68\x61\x6e\x6e\x65\x6c\x73\x5f\x74\x20\x63\x68\x61\x6e\x6e\x65\x6c\x73\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x65\x73\x69\x7a\x65\x5f\x77\x2c\x20\x75\x69\x6e\x74\x33\x32\x5f\x74\x20\x72\x65\x73\x69\x7a\x65\x5f\x68\x29\x3b\x0a\x65\x78\x74\x65\x72\x6e\x20\x20\x20\x76\x6f\x69\x64\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x73\x61\x76\x65\x5f\x69\x6d\x61\x67\x65\x28\x63\x6f\x6e\x73\x74\x20\x6d\x61\x67\x5f\x74\x65\x6e\x73\x6f\x72\x5f\x74\x2a\x20\x74\x2c\x20\x63\x6f\x6e\x73\x74\x20\x63\x68\x61\x72\x2a\x20\x66\x69\x6c\x65\x29\x3b\x0a'.decode( + 'utf-8' +) diff --git a/python/magnetron_framework/magnetron/core.py b/python/magnetron_framework/magnetron/core.py index 26565cf..e445893 100644 --- a/python/magnetron_framework/magnetron/core.py +++ b/python/magnetron_framework/magnetron/core.py @@ -268,10 +268,16 @@ def empty( *, dtype: DType = DType.F32, requires_grad: bool = False, - name: str | None = None + name: str | None = None, ) -> 'Tensor': tensor = cls(None) - tensor._new(Context.active(), shape=shape, dtype=dtype, requires_grad=requires_grad, name=name) + tensor._new( + Context.active(), + shape=shape, + dtype=dtype, + requires_grad=requires_grad, + name=name, + ) return tensor @classmethod @@ -285,7 +291,13 @@ def full( name: str | None = None, ) -> 'Tensor': tensor = cls(None) - tensor._new(Context.active(), shape=shape, dtype=dtype, requires_grad=requires_grad, name=name) + tensor._new( + Context.active(), + shape=shape, + dtype=dtype, + requires_grad=requires_grad, + name=name, + ) C.mag_tensor_fill(tensor._ptr, fill_value) return tensor @@ -317,7 +329,13 @@ def flatten_nested_lists(nested: object) -> tuple[tuple[int, ...], list[float]]: shape, flattened_data = flatten_nested_lists(data) tensor = cls(None) - tensor._new(Context.active(), shape=shape, dtype=dtype, requires_grad=requires_grad, name=name) + tensor._new( + Context.active(), + shape=shape, + dtype=dtype, + requires_grad=requires_grad, + name=name, + ) size: int = len(flattened_data) * ffi.sizeof('float') C.mag_tensor_copy_buffer_from( tensor._ptr, ffi.new(f'float[{len(flattened_data)}]', flattened_data), size @@ -333,7 +351,9 @@ def zeros( requires_grad: bool = False, name: str | None = None, ) -> 'Tensor': - return cls.full(shape, fill_value=0.0, dtype=dtype, requires_grad=requires_grad, name=name) + return cls.full( + shape, fill_value=0.0, dtype=dtype, requires_grad=requires_grad, name=name + ) @classmethod def uniform( @@ -346,7 +366,13 @@ def uniform( name: str | None = None, ) -> 'Tensor': tensor = cls(None) - tensor._new(Context.active(), shape=shape, dtype=dtype, requires_grad=requires_grad, name=name) + tensor._new( + Context.active(), + shape=shape, + dtype=dtype, + requires_grad=requires_grad, + name=name, + ) if interval[1] < interval[0]: interval = (interval[1], interval[0]) C.mag_tensor_fill_random_uniform(tensor._ptr, interval[0], interval[1]) @@ -363,7 +389,13 @@ def normal( name: str | None = None, ) -> 'Tensor': tensor = cls(None) - tensor._new(Context.active(), shape=shape, dtype=DType.F32, requires_grad=requires_grad, name=name) + tensor._new( + Context.active(), + shape=shape, + dtype=DType.F32, + requires_grad=requires_grad, + name=name, + ) C.mag_tensor_fill_random_normal(tensor._ptr, mean, stddev) return tensor @@ -512,7 +544,10 @@ def requires_grad(self, require: bool) -> None: @property def grad(self) -> 'Tensor': assert self.requires_grad - return Tensor(C.mag_tensor_grad(self._ptr)) + ptr: ffi.CData = C.mag_tensor_grad(self._ptr) + assert ptr != ffi.NULL, 'Gradient tensor is not allocated' + C.mag_tensor_retain(ptr) + return Tensor(ptr) def backward(self) -> None: assert self.requires_grad diff --git a/python/tests/autograd.py b/python/tests/autograd.py index 3241fea..e89c0f1 100644 --- a/python/tests/autograd.py +++ b/python/tests/autograd.py @@ -1,21 +1,17 @@ # (c) 2025 Mario "Neo" Sieg. -from magnetron import * +import magnetron as mag import torch def test_autograd_1(): - x = Tensor.const([-4.0], requires_grad=True) + x = mag.Tensor.const([-4.0], requires_grad=True) z = 2 * x + 2 + x q = z.relu() + z * x h = (z * z).relu() y = h + q + q * x - assert x.requires_grad - assert z.requires_grad - assert q.requires_grad - assert h.requires_grad - assert y.requires_grad y.backward() - xmg, ymg = x, y + magx, magy = x, y + print(magx.grad) x = torch.Tensor([-4.0]) x.requires_grad = True @@ -24,7 +20,7 @@ def test_autograd_1(): h = (z * z).relu() y = h + q + q * x y.backward() - xpt, ypt = x, y + torchx, torchy = x, y - assert ymg.item() == ypt.data.item() - #assert xmg.grad.item() == xpt.grad.item() + assert magy.item() == torchy.data.item() + assert magx.grad.item() == torchx.grad.item() diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 342991b..72fe0e4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,10 +2,13 @@ add_subdirectory(unit) -add_executable(magnetron_santity_test santiy_test.c) -target_link_libraries(magnetron_santity_test magnetron) -target_include_directories(magnetron_santity_test PRIVATE ../magnetron) +add_executable(magnetron_sanity_test sanity_test.c) +target_link_libraries(magnetron_sanity_test magnetron) +target_include_directories(magnetron_sanity_test PRIVATE ../magnetron) add_executable(magnetron_invariant_proof invariant_proof.cpp) target_link_libraries(magnetron_invariant_proof magnetron) target_include_directories(magnetron_invariant_proof PRIVATE ../magnetron) +if (NOT WIN32) + target_compile_options(magnetron_invariant_proof PRIVATE -Ofast -march=native) +endif() diff --git a/test/invariant_proof.cpp b/test/invariant_proof.cpp index c220ef2..e365b9b 100644 --- a/test/invariant_proof.cpp +++ b/test/invariant_proof.cpp @@ -15,9 +15,9 @@ static constexpr std::uint32_t lim = std::numeric_limits::max(); -static auto proof_division(std::uint32_t start, std::uint32_t end) -> void { +static auto proof_div(std::uint32_t start, std::uint32_t end) -> void { for (std::uint32_t x = start; x < end; ++x) { - for (std::uint32_t y = 1; y < lim; ++y) { + for (std::uint32_t y = 1; y < lim/4; ++y) { if ((x / y) != mag_ivdiv32(x, y, mag_ivdiv_mkdi(y))) [[unlikely]] { std::cout << x << " / " << y << std::endl; std::abort(); @@ -26,15 +26,30 @@ static auto proof_division(std::uint32_t start, std::uint32_t end) -> void { } } +static auto proof_rem(std::uint32_t start, std::uint32_t end) -> void { + for (std::uint32_t x = start; x < end; ++x) { + for (std::uint32_t y = 1; y < lim; ++y) { + if ((x % y) != mag_ivrem32(x, y, mag_ivdiv_mkdi(y))) [[unlikely]] { + std::cout << x << " % " << y << std::endl; + std::abort(); + } + } + } +} + auto main() -> int { std::uint32_t nt = std::max(1u, std::thread::hardware_concurrency()); std::vector threads {}; std::uint32_t chunk = lim / nt; for (std::uint32_t i = 0; i < nt; ++i) { std::uint32_t start = i*chunk; - std::uint32_t end = (i == nt - 1) ? lim : start + chunk; - threads.emplace_back(proof_division, start, end); + std::uint32_t end = (i == nt-1) ? lim : start + chunk; + threads.emplace_back([=] { + proof_div(start, end); + proof_rem(start, end); + }); } for (auto&& t : threads) t.join(); + std::cout << "Proof successful!" << std::endl; return EXIT_SUCCESS; } diff --git a/test/santiy_test.c b/test/sanity_test.c similarity index 100% rename from test/santiy_test.c rename to test/sanity_test.c diff --git a/test/unit/googletest b/test/unit/googletest new file mode 160000 index 0000000..e235eb3 --- /dev/null +++ b/test/unit/googletest @@ -0,0 +1 @@ +Subproject commit e235eb34c6c4fed790ccdad4b16394301360dcd4 diff --git a/test/unit/googletest/.clang-format b/test/unit/googletest/.clang-format deleted file mode 100644 index 5b9bfe6..0000000 --- a/test/unit/googletest/.clang-format +++ /dev/null @@ -1,4 +0,0 @@ -# Run manually to reformat a file: -# clang-format -i --style=file -Language: Cpp -BasedOnStyle: Google diff --git a/test/unit/googletest/.github/ISSUE_TEMPLATE/00-bug_report.yml b/test/unit/googletest/.github/ISSUE_TEMPLATE/00-bug_report.yml deleted file mode 100644 index 586779a..0000000 --- a/test/unit/googletest/.github/ISSUE_TEMPLATE/00-bug_report.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: Bug Report -description: Let us know that something does not work as expected. -title: "[Bug]: Please title this bug report" -body: - - type: textarea - id: what-happened - attributes: - label: Describe the issue - description: What happened, and what did you expect to happen? - validations: - required: true - - type: textarea - id: steps - attributes: - label: Steps to reproduce the problem - description: It is important that we are able to reproduce the problem that you are experiencing. Please provide all code and relevant steps to reproduce the problem, including your `BUILD`/`CMakeLists.txt` file and build commands. Links to a GitHub branch or [godbolt.org](https://godbolt.org/) that demonstrate the problem are also helpful. - validations: - required: true - - type: textarea - id: version - attributes: - label: What version of GoogleTest are you using? - description: Please include the output of `git rev-parse HEAD` or the GoogleTest release version number that you are using. - validations: - required: true - - type: textarea - id: os - attributes: - label: What operating system and version are you using? - description: If you are using a Linux distribution please include the name and version of the distribution as well. - validations: - required: true - - type: textarea - id: compiler - attributes: - label: What compiler and version are you using? - description: Please include the output of `gcc -v` or `clang -v`, or the equivalent for your compiler. - validations: - required: true - - type: textarea - id: buildsystem - attributes: - label: What build system are you using? - description: Please include the output of `bazel --version` or `cmake --version`, or the equivalent for your build system. - validations: - required: true - - type: textarea - id: additional - attributes: - label: Additional context - description: Add any other context about the problem here. - validations: - required: false diff --git a/test/unit/googletest/.github/ISSUE_TEMPLATE/10-feature_request.yml b/test/unit/googletest/.github/ISSUE_TEMPLATE/10-feature_request.yml deleted file mode 100644 index f3bbc09..0000000 --- a/test/unit/googletest/.github/ISSUE_TEMPLATE/10-feature_request.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Feature request -description: Propose a new feature. -title: "[FR]: Please title this feature request" -labels: "enhancement" -body: - - type: textarea - id: version - attributes: - label: Does the feature exist in the most recent commit? - description: We recommend using the latest commit from GitHub in your projects. - validations: - required: true - - type: textarea - id: why - attributes: - label: Why do we need this feature? - description: Ideally, explain why a combination of existing features cannot be used instead. - validations: - required: true - - type: textarea - id: proposal - attributes: - label: Describe the proposal. - description: Include a detailed description of the feature, with usage examples. - validations: - required: true - - type: textarea - id: platform - attributes: - label: Is the feature specific to an operating system, compiler, or build system version? - description: If it is, please specify which versions. - validations: - required: true diff --git a/test/unit/googletest/.github/ISSUE_TEMPLATE/config.yml b/test/unit/googletest/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 65170d1..0000000 --- a/test/unit/googletest/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,5 +0,0 @@ -blank_issues_enabled: false -contact_links: - - name: Get Help - url: https://github.com/google/googletest/discussions - about: Please ask and answer questions here. diff --git a/test/unit/googletest/.gitignore b/test/unit/googletest/.gitignore deleted file mode 100644 index f0df39d..0000000 --- a/test/unit/googletest/.gitignore +++ /dev/null @@ -1,89 +0,0 @@ -# Ignore CI build directory -build/ -xcuserdata -cmake-build-debug/ -.idea/ -bazel-bin -bazel-genfiles -bazel-googletest -bazel-out -bazel-testlogs -MODULE.bazel.lock -# python -*.pyc - -# Visual Studio files -.vs -*.sdf -*.opensdf -*.VC.opendb -*.suo -*.user -_ReSharper.Caches/ -Win32-Debug/ -Win32-Release/ -x64-Debug/ -x64-Release/ - -# VSCode files -.cache/ -cmake-variants.yaml - -# Ignore autoconf / automake files -Makefile.in -aclocal.m4 -configure -build-aux/ -autom4te.cache/ -googletest/m4/libtool.m4 -googletest/m4/ltoptions.m4 -googletest/m4/ltsugar.m4 -googletest/m4/ltversion.m4 -googletest/m4/lt~obsolete.m4 -googlemock/m4 - -# Ignore generated directories. -googlemock/fused-src/ -googletest/fused-src/ - -# macOS files -.DS_Store -googletest/.DS_Store -googletest/xcode/.DS_Store - -# Ignore cmake generated directories and files. -CMakeFiles -CTestTestfile.cmake -Makefile -cmake_install.cmake -googlemock/CMakeFiles -googlemock/CTestTestfile.cmake -googlemock/Makefile -googlemock/cmake_install.cmake -googlemock/gtest -/bin -/googlemock/gmock.dir -/googlemock/gmock_main.dir -/googlemock/RUN_TESTS.vcxproj.filters -/googlemock/RUN_TESTS.vcxproj -/googlemock/INSTALL.vcxproj.filters -/googlemock/INSTALL.vcxproj -/googlemock/gmock_main.vcxproj.filters -/googlemock/gmock_main.vcxproj -/googlemock/gmock.vcxproj.filters -/googlemock/gmock.vcxproj -/googlemock/gmock.sln -/googlemock/ALL_BUILD.vcxproj.filters -/googlemock/ALL_BUILD.vcxproj -/lib -/Win32 -/ZERO_CHECK.vcxproj.filters -/ZERO_CHECK.vcxproj -/RUN_TESTS.vcxproj.filters -/RUN_TESTS.vcxproj -/INSTALL.vcxproj.filters -/INSTALL.vcxproj -/googletest-distribution.sln -/CMakeCache.txt -/ALL_BUILD.vcxproj.filters -/ALL_BUILD.vcxproj diff --git a/test/unit/googletest/BUILD.bazel b/test/unit/googletest/BUILD.bazel deleted file mode 100644 index 0306468..0000000 --- a/test/unit/googletest/BUILD.bazel +++ /dev/null @@ -1,236 +0,0 @@ -# Copyright 2017 Google Inc. -# All Rights Reserved. -# -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# Bazel Build for Google C++ Testing Framework(Google Test) - -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -exports_files(["LICENSE"]) - -config_setting( - name = "qnx", - constraint_values = ["@platforms//os:qnx"], -) - -config_setting( - name = "windows", - constraint_values = ["@platforms//os:windows"], -) - -config_setting( - name = "freebsd", - constraint_values = ["@platforms//os:freebsd"], -) - -config_setting( - name = "openbsd", - constraint_values = ["@platforms//os:openbsd"], -) - -# NOTE: Fuchsia is not an officially supported platform. -config_setting( - name = "fuchsia", - constraint_values = ["@platforms//os:fuchsia"], -) - -config_setting( - name = "msvc_compiler", - flag_values = { - "@bazel_tools//tools/cpp:compiler": "msvc-cl", - }, - visibility = [":__subpackages__"], -) - -config_setting( - name = "has_absl", - values = {"define": "absl=1"}, -) - -# Library that defines the FRIEND_TEST macro. -cc_library( - name = "gtest_prod", - hdrs = ["googletest/include/gtest/gtest_prod.h"], - includes = ["googletest/include"], -) - -# Google Test including Google Mock -cc_library( - name = "gtest", - srcs = glob( - include = [ - "googletest/src/*.cc", - "googletest/src/*.h", - "googletest/include/gtest/**/*.h", - "googlemock/src/*.cc", - "googlemock/include/gmock/**/*.h", - ], - exclude = [ - "googletest/src/gtest-all.cc", - "googletest/src/gtest_main.cc", - "googlemock/src/gmock-all.cc", - "googlemock/src/gmock_main.cc", - ], - ), - hdrs = glob([ - "googletest/include/gtest/*.h", - "googlemock/include/gmock/*.h", - ]), - copts = select({ - ":qnx": [], - ":windows": [], - "//conditions:default": ["-pthread"], - }), - defines = select({ - ":has_absl": ["GTEST_HAS_ABSL=1"], - "//conditions:default": [], - }), - features = select({ - ":windows": ["windows_export_all_symbols"], - "//conditions:default": [], - }), - includes = [ - "googlemock", - "googlemock/include", - "googletest", - "googletest/include", - ], - linkopts = select({ - ":qnx": ["-lregex"], - ":windows": [], - ":freebsd": [ - "-lm", - "-pthread", - ], - ":openbsd": [ - "-lm", - "-pthread", - ], - "//conditions:default": ["-pthread"], - }), - deps = select({ - ":has_absl": [ - "@abseil-cpp//absl/container:flat_hash_set", - "@abseil-cpp//absl/debugging:failure_signal_handler", - "@abseil-cpp//absl/debugging:stacktrace", - "@abseil-cpp//absl/debugging:symbolize", - "@abseil-cpp//absl/flags:flag", - "@abseil-cpp//absl/flags:parse", - "@abseil-cpp//absl/flags:reflection", - "@abseil-cpp//absl/flags:usage", - "@abseil-cpp//absl/strings", - "@abseil-cpp//absl/types:any", - "@abseil-cpp//absl/types:optional", - "@abseil-cpp//absl/types:variant", - "@re2//:re2", - ], - "//conditions:default": [], - }) + select({ - # `gtest-death-test.cc` has `EXPECT_DEATH` that spawns a process, - # expects it to crash and inspects its logs with the given matcher, - # so that's why these libraries are needed. - # Otherwise, builds targeting Fuchsia would fail to compile. - ":fuchsia": [ - "@fuchsia_sdk//pkg/fdio", - "@fuchsia_sdk//pkg/syslog", - "@fuchsia_sdk//pkg/zx", - ], - "//conditions:default": [], - }), -) - -cc_library( - name = "gtest_main", - srcs = ["googlemock/src/gmock_main.cc"], - features = select({ - ":windows": ["windows_export_all_symbols"], - "//conditions:default": [], - }), - deps = [":gtest"], -) - -# The following rules build samples of how to use gTest. -cc_library( - name = "gtest_sample_lib", - srcs = [ - "googletest/samples/sample1.cc", - "googletest/samples/sample2.cc", - "googletest/samples/sample4.cc", - ], - hdrs = [ - "googletest/samples/prime_tables.h", - "googletest/samples/sample1.h", - "googletest/samples/sample2.h", - "googletest/samples/sample3-inl.h", - "googletest/samples/sample4.h", - ], - features = select({ - ":windows": ["windows_export_all_symbols"], - "//conditions:default": [], - }), -) - -cc_test( - name = "gtest_samples", - size = "small", - # All Samples except: - # sample9 (main) - # sample10 (main and takes a command line option and needs to be separate) - srcs = [ - "googletest/samples/sample1_unittest.cc", - "googletest/samples/sample2_unittest.cc", - "googletest/samples/sample3_unittest.cc", - "googletest/samples/sample4_unittest.cc", - "googletest/samples/sample5_unittest.cc", - "googletest/samples/sample6_unittest.cc", - "googletest/samples/sample7_unittest.cc", - "googletest/samples/sample8_unittest.cc", - ], - linkstatic = 0, - deps = [ - "gtest_sample_lib", - ":gtest_main", - ], -) - -cc_test( - name = "sample9_unittest", - size = "small", - srcs = ["googletest/samples/sample9_unittest.cc"], - deps = [":gtest"], -) - -cc_test( - name = "sample10_unittest", - size = "small", - srcs = ["googletest/samples/sample10_unittest.cc"], - deps = [":gtest"], -) diff --git a/test/unit/googletest/CMakeLists.txt b/test/unit/googletest/CMakeLists.txt deleted file mode 100644 index 512e5c3..0000000 --- a/test/unit/googletest/CMakeLists.txt +++ /dev/null @@ -1,36 +0,0 @@ -# Note: CMake support is community-based. The maintainers do not use CMake -# internally. - -cmake_minimum_required(VERSION 3.13) - -project(googletest-distribution) -set(GOOGLETEST_VERSION 1.15.2) - -if(NOT CYGWIN AND NOT MSYS AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL QNX) - set(CMAKE_CXX_EXTENSIONS OFF) -endif() - -enable_testing() - -include(CMakeDependentOption) -include(GNUInstallDirs) - -# Note that googlemock target already builds googletest. -option(BUILD_GMOCK "Builds the googlemock subproject" ON) -option(INSTALL_GTEST "Enable installation of googletest. (Projects embedding googletest may want to turn this OFF.)" ON) -option(GTEST_HAS_ABSL "Use Abseil and RE2. Requires Abseil and RE2 to be separately added to the build." OFF) - -if(GTEST_HAS_ABSL) - if(NOT TARGET absl::base) - find_package(absl REQUIRED) - endif() - if(NOT TARGET re2::re2) - find_package(re2 REQUIRED) - endif() -endif() - -if(BUILD_GMOCK) - add_subdirectory( googlemock ) -else() - add_subdirectory( googletest ) -endif() diff --git a/test/unit/googletest/CONTRIBUTING.md b/test/unit/googletest/CONTRIBUTING.md deleted file mode 100644 index ab5a47b..0000000 --- a/test/unit/googletest/CONTRIBUTING.md +++ /dev/null @@ -1,141 +0,0 @@ -# How to become a contributor and submit your own code - -## Contributor License Agreements - -We'd love to accept your patches! Before we can take them, we have to jump a -couple of legal hurdles. - -Please fill out either the individual or corporate Contributor License Agreement -(CLA). - -* If you are an individual writing original source code and you're sure you - own the intellectual property, then you'll need to sign an - [individual CLA](https://developers.google.com/open-source/cla/individual). -* If you work for a company that wants to allow you to contribute your work, - then you'll need to sign a - [corporate CLA](https://developers.google.com/open-source/cla/corporate). - -Follow either of the two links above to access the appropriate CLA and -instructions for how to sign and return it. Once we receive it, we'll be able to -accept your pull requests. - -## Are you a Googler? - -If you are a Googler, please make an attempt to submit an internal contribution -rather than a GitHub Pull Request. If you are not able to submit internally, a -PR is acceptable as an alternative. - -## Contributing A Patch - -1. Submit an issue describing your proposed change to the - [issue tracker](https://github.com/google/googletest/issues). -2. Please don't mix more than one logical change per submittal, because it - makes the history hard to follow. If you want to make a change that doesn't - have a corresponding issue in the issue tracker, please create one. -3. Also, coordinate with team members that are listed on the issue in question. - This ensures that work isn't being duplicated and communicating your plan - early also generally leads to better patches. -4. If your proposed change is accepted, and you haven't already done so, sign a - Contributor License Agreement - ([see details above](#contributor-license-agreements)). -5. Fork the desired repo, develop and test your code changes. -6. Ensure that your code adheres to the existing style in the sample to which - you are contributing. -7. Ensure that your code has an appropriate set of unit tests which all pass. -8. Submit a pull request. - -## The Google Test and Google Mock Communities - -The Google Test community exists primarily through the -[discussion group](https://groups.google.com/group/googletestframework) and the -GitHub repository. Likewise, the Google Mock community exists primarily through -their own [discussion group](https://groups.google.com/group/googlemock). You -are definitely encouraged to contribute to the discussion and you can also help -us to keep the effectiveness of the group high by following and promoting the -guidelines listed here. - -### Please Be Friendly - -Showing courtesy and respect to others is a vital part of the Google culture, -and we strongly encourage everyone participating in Google Test development to -join us in accepting nothing less. Of course, being courteous is not the same as -failing to constructively disagree with each other, but it does mean that we -should be respectful of each other when enumerating the 42 technical reasons -that a particular proposal may not be the best choice. There's never a reason to -be antagonistic or dismissive toward anyone who is sincerely trying to -contribute to a discussion. - -Sure, C++ testing is serious business and all that, but it's also a lot of fun. -Let's keep it that way. Let's strive to be one of the friendliest communities in -all of open source. - -As always, discuss Google Test in the official GoogleTest discussion group. You -don't have to actually submit code in order to sign up. Your participation -itself is a valuable contribution. - -## Style - -To keep the source consistent, readable, diffable and easy to merge, we use a -fairly rigid coding style, as defined by the -[google-styleguide](https://github.com/google/styleguide) project. All patches -will be expected to conform to the style outlined -[here](https://google.github.io/styleguide/cppguide.html). Use -[.clang-format](https://github.com/google/googletest/blob/main/.clang-format) to -check your formatting. - -## Requirements for Contributors - -If you plan to contribute a patch, you need to build Google Test, Google Mock, -and their own tests from a git checkout, which has further requirements: - -* [Python](https://www.python.org/) v3.6 or newer (for running some of the - tests and re-generating certain source files from templates) -* [CMake](https://cmake.org/) v2.8.12 or newer - -## Developing Google Test and Google Mock - -This section discusses how to make your own changes to the Google Test project. - -### Testing Google Test and Google Mock Themselves - -To make sure your changes work as intended and don't break existing -functionality, you'll want to compile and run Google Test and GoogleMock's own -tests. For that you can use CMake: - -``` -mkdir mybuild -cd mybuild -cmake -Dgtest_build_tests=ON -Dgmock_build_tests=ON ${GTEST_REPO_DIR} -``` - -To choose between building only Google Test or Google Mock, you may modify your -cmake command to be one of each - -``` -cmake -Dgtest_build_tests=ON ${GTEST_DIR} # sets up Google Test tests -cmake -Dgmock_build_tests=ON ${GMOCK_DIR} # sets up Google Mock tests -``` - -Make sure you have Python installed, as some of Google Test's tests are written -in Python. If the cmake command complains about not being able to find Python -(`Could NOT find PythonInterp (missing: PYTHON_EXECUTABLE)`), try telling it -explicitly where your Python executable can be found: - -``` -cmake -DPYTHON_EXECUTABLE=path/to/python ... -``` - -Next, you can build Google Test and / or Google Mock and all desired tests. On -\*nix, this is usually done by - -``` -make -``` - -To run the tests, do - -``` -make test -``` - -All tests should pass. diff --git a/test/unit/googletest/CONTRIBUTORS b/test/unit/googletest/CONTRIBUTORS deleted file mode 100644 index ccea41e..0000000 --- a/test/unit/googletest/CONTRIBUTORS +++ /dev/null @@ -1,66 +0,0 @@ -# This file contains a list of people who've made non-trivial -# contribution to the Google C++ Testing Framework project. People -# who commit code to the project are encouraged to add their names -# here. Please keep the list sorted by first names. - -Ajay Joshi -Balázs Dán -Benoit Sigoure -Bharat Mediratta -Bogdan Piloca -Chandler Carruth -Chris Prince -Chris Taylor -Dan Egnor -Dave MacLachlan -David Anderson -Dean Sturtevant -Eric Roman -Gene Volovich -Hady Zalek -Hal Burch -Jeffrey Yasskin -Jim Keller -Joe Walnes -Jon Wray -Jói Sigurðsson -Keir Mierle -Keith Ray -Kenton Varda -Kostya Serebryany -Krystian Kuzniarek -Lev Makhlis -Manuel Klimek -Mario Tanev -Mark Paskin -Markus Heule -Martijn Vels -Matthew Simmons -Mika Raento -Mike Bland -Miklós Fazekas -Neal Norwitz -Nermin Ozkiranartli -Owen Carlsen -Paneendra Ba -Pasi Valminen -Patrick Hanna -Patrick Riley -Paul Menage -Peter Kaminski -Piotr Kaminski -Preston Jackson -Rainer Klaffenboeck -Russ Cox -Russ Rufer -Sean Mcafee -Sigurður Ásgeirsson -Soyeon Kim -Sverre Sundsdal -Szymon Sobik -Takeshi Yoshino -Tracy Bialik -Vadim Berman -Vlad Losev -Wolfgang Klier -Zhanyong Wan diff --git a/test/unit/googletest/LICENSE b/test/unit/googletest/LICENSE deleted file mode 100644 index 1941a11..0000000 --- a/test/unit/googletest/LICENSE +++ /dev/null @@ -1,28 +0,0 @@ -Copyright 2008, Google Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/test/unit/googletest/MODULE.bazel b/test/unit/googletest/MODULE.bazel deleted file mode 100644 index c9a52e0..0000000 --- a/test/unit/googletest/MODULE.bazel +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright 2024 Google Inc. -# All Rights Reserved. -# -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -# https://bazel.build/external/overview#bzlmod - -module( - name = "googletest", - version = "head", - compatibility_level = 1, -) - -# Only direct dependencies need to be listed below. -# Please keep the versions in sync with the versions in the WORKSPACE file. - -bazel_dep(name = "abseil-cpp", - version = "20240116.2") - -bazel_dep(name = "platforms", - version = "0.0.10") - -bazel_dep(name = "re2", - version = "2024-07-02") - -bazel_dep(name = "rules_python", - version = "0.34.0", - dev_dependency = True) - -# https://rules-python.readthedocs.io/en/stable/toolchains.html#library-modules-with-dev-only-python-usage -python = use_extension( - "@rules_python//python/extensions:python.bzl", - "python", - dev_dependency = True -) - -python.toolchain(python_version = "3.12", - is_default = True, - ignore_root_user_error = True) - -fake_fuchsia_sdk = use_repo_rule("//:fake_fuchsia_sdk.bzl", "fake_fuchsia_sdk") -fake_fuchsia_sdk(name = "fuchsia_sdk") diff --git a/test/unit/googletest/README.md b/test/unit/googletest/README.md deleted file mode 100644 index 03c70a1..0000000 --- a/test/unit/googletest/README.md +++ /dev/null @@ -1,142 +0,0 @@ -# GoogleTest - -### Announcements - -#### Live at Head - -GoogleTest now follows the -[Abseil Live at Head philosophy](https://abseil.io/about/philosophy#upgrade-support). -We recommend -[updating to the latest commit in the `main` branch as often as possible](https://github.com/abseil/abseil-cpp/blob/master/FAQ.md#what-is-live-at-head-and-how-do-i-do-it). -We do publish occasional semantic versions, tagged with -`v${major}.${minor}.${patch}` (e.g. `v1.15.2`). - -#### Documentation Updates - -Our documentation is now live on GitHub Pages at -https://google.github.io/googletest/. We recommend browsing the documentation on -GitHub Pages rather than directly in the repository. - -#### Release 1.15.2 - -[Release 1.15.2](https://github.com/google/googletest/releases/tag/v1.15.2) is -now available. - -The 1.15.x branch requires at least C++14. - -#### Continuous Integration - -We use Google's internal systems for continuous integration. - -#### Coming Soon - -* We are planning to take a dependency on - [Abseil](https://github.com/abseil/abseil-cpp). - -## Welcome to **GoogleTest**, Google's C++ test framework! - -This repository is a merger of the formerly separate GoogleTest and GoogleMock -projects. These were so closely related that it makes sense to maintain and -release them together. - -### Getting Started - -See the [GoogleTest User's Guide](https://google.github.io/googletest/) for -documentation. We recommend starting with the -[GoogleTest Primer](https://google.github.io/googletest/primer.html). - -More information about building GoogleTest can be found at -[googletest/README.md](googletest/README.md). - -## Features - -* xUnit test framework: \ - Googletest is based on the [xUnit](https://en.wikipedia.org/wiki/XUnit) - testing framework, a popular architecture for unit testing -* Test discovery: \ - Googletest automatically discovers and runs your tests, eliminating the need - to manually register your tests -* Rich set of assertions: \ - Googletest provides a variety of assertions, such as equality, inequality, - exceptions, and more, making it easy to test your code -* User-defined assertions: \ - You can define your own assertions with Googletest, making it simple to - write tests that are specific to your code -* Death tests: \ - Googletest supports death tests, which verify that your code exits in a - certain way, making it useful for testing error-handling code -* Fatal and non-fatal failures: \ - You can specify whether a test failure should be treated as fatal or - non-fatal with Googletest, allowing tests to continue running even if a - failure occurs -* Value-parameterized tests: \ - Googletest supports value-parameterized tests, which run multiple times with - different input values, making it useful for testing functions that take - different inputs -* Type-parameterized tests: \ - Googletest also supports type-parameterized tests, which run with different - data types, making it useful for testing functions that work with different - data types -* Various options for running tests: \ - Googletest provides many options for running tests including running - individual tests, running tests in a specific order and running tests in - parallel - -## Supported Platforms - -GoogleTest follows Google's -[Foundational C++ Support Policy](https://opensource.google/documentation/policies/cplusplus-support). -See -[this table](https://github.com/google/oss-policies-info/blob/main/foundational-cxx-support-matrix.md) -for a list of currently supported versions of compilers, platforms, and build -tools. - -## Who Is Using GoogleTest? - -In addition to many internal projects at Google, GoogleTest is also used by the -following notable projects: - -* The [Chromium projects](https://www.chromium.org/) (behind the Chrome - browser and Chrome OS). -* The [LLVM](https://llvm.org/) compiler. -* [Protocol Buffers](https://github.com/google/protobuf), Google's data - interchange format. -* The [OpenCV](https://opencv.org/) computer vision library. - -## Related Open Source Projects - -[GTest Runner](https://github.com/nholthaus/gtest-runner) is a Qt5 based -automated test-runner and Graphical User Interface with powerful features for -Windows and Linux platforms. - -[GoogleTest UI](https://github.com/ospector/gtest-gbar) is a test runner that -runs your test binary, allows you to track its progress via a progress bar, and -displays a list of test failures. Clicking on one shows failure text. GoogleTest -UI is written in C#. - -[GTest TAP Listener](https://github.com/kinow/gtest-tap-listener) is an event -listener for GoogleTest that implements the -[TAP protocol](https://en.wikipedia.org/wiki/Test_Anything_Protocol) for test -result output. If your test runner understands TAP, you may find it useful. - -[gtest-parallel](https://github.com/google/gtest-parallel) is a test runner that -runs tests from your binary in parallel to provide significant speed-up. - -[GoogleTest Adapter](https://marketplace.visualstudio.com/items?itemName=DavidSchuldenfrei.gtest-adapter) -is a VS Code extension allowing to view GoogleTest in a tree view and run/debug -your tests. - -[C++ TestMate](https://github.com/matepek/vscode-catch2-test-adapter) is a VS -Code extension allowing to view GoogleTest in a tree view and run/debug your -tests. - -[Cornichon](https://pypi.org/project/cornichon/) is a small Gherkin DSL parser -that generates stub code for GoogleTest. - -## Contributing Changes - -Please read -[`CONTRIBUTING.md`](https://github.com/google/googletest/blob/main/CONTRIBUTING.md) -for details on how to contribute to this project. - -Happy testing! diff --git a/test/unit/googletest/WORKSPACE b/test/unit/googletest/WORKSPACE deleted file mode 100644 index 63f7681..0000000 --- a/test/unit/googletest/WORKSPACE +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright 2024 Google Inc. -# All Rights Reserved. -# -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -workspace(name = "googletest") - -load("//:googletest_deps.bzl", "googletest_deps") -googletest_deps() - -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") - -http_archive( - name = "rules_python", - sha256 = "d71d2c67e0bce986e1c5a7731b4693226867c45bfe0b7c5e0067228a536fc580", - strip_prefix = "rules_python-0.29.0", - urls = ["https://github.com/bazelbuild/rules_python/releases/download/0.29.0/rules_python-0.29.0.tar.gz"], -) - -# https://github.com/bazelbuild/rules_python/releases/tag/0.29.0 -load("@rules_python//python:repositories.bzl", "py_repositories") -py_repositories() - -http_archive( - name = "bazel_skylib", - sha256 = "cd55a062e763b9349921f0f5db8c3933288dc8ba4f76dd9416aac68acee3cb94", - urls = ["https://github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz"], -) - -http_archive( - name = "platforms", - urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/platforms/releases/download/0.0.10/platforms-0.0.10.tar.gz", - "https://github.com/bazelbuild/platforms/releases/download/0.0.10/platforms-0.0.10.tar.gz", - ], - sha256 = "218efe8ee736d26a3572663b374a253c012b716d8af0c07e842e82f238a0a7ee", -) diff --git a/test/unit/googletest/WORKSPACE.bzlmod b/test/unit/googletest/WORKSPACE.bzlmod deleted file mode 100644 index 381432c..0000000 --- a/test/unit/googletest/WORKSPACE.bzlmod +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2024 Google Inc. -# All Rights Reserved. -# -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -# https://bazel.build/external/migration#workspace.bzlmod -# -# This file is intentionally empty. When bzlmod is enabled and this -# file exists, the content of WORKSPACE is ignored. This prevents -# bzlmod builds from unintentionally depending on the WORKSPACE file. diff --git a/test/unit/googletest/ci/linux-presubmit.sh b/test/unit/googletest/ci/linux-presubmit.sh deleted file mode 100644 index 6d2b3fb..0000000 --- a/test/unit/googletest/ci/linux-presubmit.sh +++ /dev/null @@ -1,139 +0,0 @@ -#!/bin/bash -# -# Copyright 2020, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -set -euox pipefail - -readonly LINUX_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20240523" -readonly LINUX_GCC_FLOOR_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-floor:20230120" - -if [[ -z ${GTEST_ROOT:-} ]]; then - GTEST_ROOT="$(realpath $(dirname ${0})/..)" -fi - -if [[ -z ${STD:-} ]]; then - STD="c++14 c++17 c++20" -fi - -# Test the CMake build -for cc in /usr/local/bin/gcc /opt/llvm/clang/bin/clang; do - for cmake_off_on in OFF ON; do - time docker run \ - --volume="${GTEST_ROOT}:/src:ro" \ - --tmpfs="/build:exec" \ - --workdir="/build" \ - --rm \ - --env="CC=${cc}" \ - --env=CXXFLAGS="-Werror -Wdeprecated" \ - ${LINUX_LATEST_CONTAINER} \ - /bin/bash -c " - cmake /src \ - -DCMAKE_CXX_STANDARD=14 \ - -Dgtest_build_samples=ON \ - -Dgtest_build_tests=ON \ - -Dgmock_build_tests=ON \ - -Dcxx_no_exception=${cmake_off_on} \ - -Dcxx_no_rtti=${cmake_off_on} && \ - make -j$(nproc) && \ - ctest -j$(nproc) --output-on-failure" - done -done - -# Do one test with an older version of GCC -# TODO(googletest-team): This currently uses Bazel 5. When upgrading to a -# version of Bazel that supports Bzlmod, add --enable_bzlmod=false to keep test -# coverage for the old WORKSPACE dependency management. -time docker run \ - --volume="${GTEST_ROOT}:/src:ro" \ - --workdir="/src" \ - --rm \ - --env="CC=/usr/local/bin/gcc" \ - --env="BAZEL_CXXOPTS=-std=c++14" \ - ${LINUX_GCC_FLOOR_CONTAINER} \ - /usr/local/bin/bazel test ... \ - --copt="-Wall" \ - --copt="-Werror" \ - --copt="-Wuninitialized" \ - --copt="-Wundef" \ - --copt="-Wno-error=pragmas" \ - --features=external_include_paths \ - --keep_going \ - --show_timestamps \ - --test_output=errors - -# Test GCC -for std in ${STD}; do - for absl in 0 1; do - time docker run \ - --volume="${GTEST_ROOT}:/src:ro" \ - --workdir="/src" \ - --rm \ - --env="CC=/usr/local/bin/gcc" \ - --env="BAZEL_CXXOPTS=-std=${std}" \ - ${LINUX_LATEST_CONTAINER} \ - /usr/local/bin/bazel test ... \ - --copt="-Wall" \ - --copt="-Werror" \ - --copt="-Wuninitialized" \ - --copt="-Wundef" \ - --define="absl=${absl}" \ - --enable_bzlmod=true \ - --features=external_include_paths \ - --keep_going \ - --show_timestamps \ - --test_output=errors - done -done - -# Test Clang -for std in ${STD}; do - for absl in 0 1; do - time docker run \ - --volume="${GTEST_ROOT}:/src:ro" \ - --workdir="/src" \ - --rm \ - --env="CC=/opt/llvm/clang/bin/clang" \ - --env="BAZEL_CXXOPTS=-std=${std}" \ - ${LINUX_LATEST_CONTAINER} \ - /usr/local/bin/bazel test ... \ - --copt="--gcc-toolchain=/usr/local" \ - --copt="-Wall" \ - --copt="-Werror" \ - --copt="-Wuninitialized" \ - --copt="-Wundef" \ - --define="absl=${absl}" \ - --enable_bzlmod=true \ - --features=external_include_paths \ - --keep_going \ - --linkopt="--gcc-toolchain=/usr/local" \ - --show_timestamps \ - --test_output=errors - done -done diff --git a/test/unit/googletest/ci/macos-presubmit.sh b/test/unit/googletest/ci/macos-presubmit.sh deleted file mode 100644 index 70eaa74..0000000 --- a/test/unit/googletest/ci/macos-presubmit.sh +++ /dev/null @@ -1,77 +0,0 @@ -#!/bin/bash -# -# Copyright 2020, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -set -euox pipefail - -if [[ -z ${GTEST_ROOT:-} ]]; then - GTEST_ROOT="$(realpath $(dirname ${0})/..)" -fi - -# Test the CMake build -for cmake_off_on in OFF ON; do - BUILD_DIR=$(mktemp -d build_dir.XXXXXXXX) - cd ${BUILD_DIR} - time cmake ${GTEST_ROOT} \ - -DCMAKE_CXX_STANDARD=14 \ - -Dgtest_build_samples=ON \ - -Dgtest_build_tests=ON \ - -Dgmock_build_tests=ON \ - -Dcxx_no_exception=${cmake_off_on} \ - -Dcxx_no_rtti=${cmake_off_on} - time make - time ctest -j$(nproc) --output-on-failure -done - -# Test the Bazel build - -# If we are running on Kokoro, check for a versioned Bazel binary. -KOKORO_GFILE_BAZEL_BIN="bazel-7.0.0-darwin-x86_64" -if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -f ${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN} ]]; then - BAZEL_BIN="${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN}" - chmod +x ${BAZEL_BIN} -else - BAZEL_BIN="bazel" -fi - -cd ${GTEST_ROOT} -for absl in 0 1; do - ${BAZEL_BIN} test ... \ - --copt="-Wall" \ - --copt="-Werror" \ - --copt="-Wundef" \ - --cxxopt="-std=c++14" \ - --define="absl=${absl}" \ - --enable_bzlmod=true \ - --features=external_include_paths \ - --keep_going \ - --show_timestamps \ - --test_output=errors -done diff --git a/test/unit/googletest/ci/windows-presubmit.bat b/test/unit/googletest/ci/windows-presubmit.bat deleted file mode 100644 index 1adc1a1..0000000 --- a/test/unit/googletest/ci/windows-presubmit.bat +++ /dev/null @@ -1,63 +0,0 @@ -SETLOCAL ENABLEDELAYEDEXPANSION - -SET BAZEL_EXE=%KOKORO_GFILE_DIR%\bazel-7.0.0-windows-x86_64.exe - -SET PATH=C:\Python34;%PATH% -SET BAZEL_PYTHON=C:\python34\python.exe -SET BAZEL_SH=C:\tools\msys64\usr\bin\bash.exe -SET CMAKE_BIN="cmake.exe" -SET CTEST_BIN="ctest.exe" -SET CTEST_OUTPUT_ON_FAILURE=1 -SET CMAKE_BUILD_PARALLEL_LEVEL=16 -SET CTEST_PARALLEL_LEVEL=16 - -IF EXIST git\googletest ( - CD git\googletest -) ELSE IF EXIST github\googletest ( - CD github\googletest -) - -IF %errorlevel% neq 0 EXIT /B 1 - -:: ---------------------------------------------------------------------------- -:: CMake -MKDIR cmake_msvc2022 -CD cmake_msvc2022 - -%CMAKE_BIN% .. ^ - -G "Visual Studio 17 2022" ^ - -DPYTHON_EXECUTABLE:FILEPATH=c:\python37\python.exe ^ - -DPYTHON_INCLUDE_DIR:PATH=c:\python37\include ^ - -DPYTHON_LIBRARY:FILEPATH=c:\python37\lib\site-packages\pip ^ - -Dgtest_build_samples=ON ^ - -Dgtest_build_tests=ON ^ - -Dgmock_build_tests=ON -IF %errorlevel% neq 0 EXIT /B 1 - -%CMAKE_BIN% --build . --target ALL_BUILD --config Debug -- -maxcpucount -IF %errorlevel% neq 0 EXIT /B 1 - -%CTEST_BIN% -C Debug --timeout 600 -IF %errorlevel% neq 0 EXIT /B 1 - -CD .. -RMDIR /S /Q cmake_msvc2022 - -:: ---------------------------------------------------------------------------- -:: Bazel - -:: The default home directory on Kokoro is a long path which causes errors -:: because of Windows limitations on path length. -:: --output_user_root=C:\tmp causes Bazel to use a shorter path. -SET BAZEL_VS=C:\Program Files\Microsoft Visual Studio\2022\Community -%BAZEL_EXE% ^ - --output_user_root=C:\tmp ^ - test ... ^ - --compilation_mode=dbg ^ - --copt=/std:c++14 ^ - --copt=/WX ^ - --enable_bzlmod=true ^ - --keep_going ^ - --test_output=errors ^ - --test_tag_filters=-no_test_msvc2017 -IF %errorlevel% neq 0 EXIT /B 1 diff --git a/test/unit/googletest/docs/_config.yml b/test/unit/googletest/docs/_config.yml deleted file mode 100644 index d12867e..0000000 --- a/test/unit/googletest/docs/_config.yml +++ /dev/null @@ -1 +0,0 @@ -title: GoogleTest diff --git a/test/unit/googletest/docs/_data/navigation.yml b/test/unit/googletest/docs/_data/navigation.yml deleted file mode 100644 index 9f33327..0000000 --- a/test/unit/googletest/docs/_data/navigation.yml +++ /dev/null @@ -1,43 +0,0 @@ -nav: -- section: "Get Started" - items: - - title: "Supported Platforms" - url: "/platforms.html" - - title: "Quickstart: Bazel" - url: "/quickstart-bazel.html" - - title: "Quickstart: CMake" - url: "/quickstart-cmake.html" -- section: "Guides" - items: - - title: "GoogleTest Primer" - url: "/primer.html" - - title: "Advanced Topics" - url: "/advanced.html" - - title: "Mocking for Dummies" - url: "/gmock_for_dummies.html" - - title: "Mocking Cookbook" - url: "/gmock_cook_book.html" - - title: "Mocking Cheat Sheet" - url: "/gmock_cheat_sheet.html" -- section: "References" - items: - - title: "Testing Reference" - url: "/reference/testing.html" - - title: "Mocking Reference" - url: "/reference/mocking.html" - - title: "Assertions" - url: "/reference/assertions.html" - - title: "Matchers" - url: "/reference/matchers.html" - - title: "Actions" - url: "/reference/actions.html" - - title: "Testing FAQ" - url: "/faq.html" - - title: "Mocking FAQ" - url: "/gmock_faq.html" - - title: "Code Samples" - url: "/samples.html" - - title: "Using pkg-config" - url: "/pkgconfig.html" - - title: "Community Documentation" - url: "/community_created_documentation.html" diff --git a/test/unit/googletest/docs/_layouts/default.html b/test/unit/googletest/docs/_layouts/default.html deleted file mode 100644 index c7f331b..0000000 --- a/test/unit/googletest/docs/_layouts/default.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - -{% seo %} - - - - - -

-
-
- {{ content }} -
- -
- - - - diff --git a/test/unit/googletest/docs/_sass/main.scss b/test/unit/googletest/docs/_sass/main.scss deleted file mode 100644 index 92edc87..0000000 --- a/test/unit/googletest/docs/_sass/main.scss +++ /dev/null @@ -1,200 +0,0 @@ -// Styles for GoogleTest docs website on GitHub Pages. -// Color variables are defined in -// https://github.com/pages-themes/primer/tree/master/_sass/primer-support/lib/variables - -$sidebar-width: 260px; - -body { - display: flex; - margin: 0; -} - -.sidebar { - background: $black; - color: $text-white; - flex-shrink: 0; - height: 100vh; - overflow: auto; - position: sticky; - top: 0; - width: $sidebar-width; -} - -.sidebar h1 { - font-size: 1.5em; -} - -.sidebar h2 { - color: $gray-light; - font-size: 0.8em; - font-weight: normal; - margin-bottom: 0.8em; - padding-left: 2.5em; - text-transform: uppercase; -} - -.sidebar .header { - background: $black; - padding: 2em; - position: sticky; - top: 0; - width: 100%; -} - -.sidebar .header a { - color: $text-white; - text-decoration: none; -} - -.sidebar .nav-toggle { - display: none; -} - -.sidebar .expander { - cursor: pointer; - display: none; - height: 3em; - position: absolute; - right: 1em; - top: 1.5em; - width: 3em; -} - -.sidebar .expander .arrow { - border: solid $white; - border-width: 0 3px 3px 0; - display: block; - height: 0.7em; - margin: 1em auto; - transform: rotate(45deg); - transition: transform 0.5s; - width: 0.7em; -} - -.sidebar nav { - width: 100%; -} - -.sidebar nav ul { - list-style-type: none; - margin-bottom: 1em; - padding: 0; - - &:last-child { - margin-bottom: 2em; - } - - a { - text-decoration: none; - } - - li { - color: $text-white; - padding-left: 2em; - text-decoration: none; - } - - li.active { - background: $border-gray-darker; - font-weight: bold; - } - - li:hover { - background: $border-gray-darker; - } -} - -.main { - background-color: $bg-gray; - width: calc(100% - #{$sidebar-width}); -} - -.main .main-inner { - background-color: $white; - padding: 2em; -} - -.main .footer { - margin: 0; - padding: 2em; -} - -.main table th { - text-align: left; -} - -.main .callout { - border-left: 0.25em solid $white; - padding: 1em; - - a { - text-decoration: underline; - } - - &.important { - background-color: $bg-yellow-light; - border-color: $bg-yellow; - color: $black; - } - - &.note { - background-color: $bg-blue-light; - border-color: $text-blue; - color: $text-blue; - } - - &.tip { - background-color: $green-000; - border-color: $green-700; - color: $green-700; - } - - &.warning { - background-color: $red-000; - border-color: $text-red; - color: $text-red; - } -} - -.main .good pre { - background-color: $bg-green-light; -} - -.main .bad pre { - background-color: $red-000; -} - -@media all and (max-width: 768px) { - body { - flex-direction: column; - } - - .sidebar { - height: auto; - position: relative; - width: 100%; - } - - .sidebar .expander { - display: block; - } - - .sidebar nav { - height: 0; - overflow: hidden; - } - - .sidebar .nav-toggle:checked { - & ~ nav { - height: auto; - } - - & + .expander .arrow { - transform: rotate(-135deg); - } - } - - .main { - width: 100%; - } -} diff --git a/test/unit/googletest/docs/advanced.md b/test/unit/googletest/docs/advanced.md deleted file mode 100644 index 240588a..0000000 --- a/test/unit/googletest/docs/advanced.md +++ /dev/null @@ -1,2446 +0,0 @@ -# Advanced GoogleTest Topics - -## Introduction - -Now that you have read the [GoogleTest Primer](primer.md) and learned how to -write tests using GoogleTest, it's time to learn some new tricks. This document -will show you more assertions as well as how to construct complex failure -messages, propagate fatal failures, reuse and speed up your test fixtures, and -use various flags with your tests. - -## More Assertions - -This section covers some less frequently used, but still significant, -assertions. - -### Explicit Success and Failure - -See [Explicit Success and Failure](reference/assertions.md#success-failure) in -the Assertions Reference. - -### Exception Assertions - -See [Exception Assertions](reference/assertions.md#exceptions) in the Assertions -Reference. - -### Predicate Assertions for Better Error Messages - -Even though GoogleTest has a rich set of assertions, they can never be complete, -as it's impossible (nor a good idea) to anticipate all scenarios a user might -run into. Therefore, sometimes a user has to use `EXPECT_TRUE()` to check a -complex expression, for lack of a better macro. This has the problem of not -showing you the values of the parts of the expression, making it hard to -understand what went wrong. As a workaround, some users choose to construct the -failure message by themselves, streaming it into `EXPECT_TRUE()`. However, this -is awkward especially when the expression has side-effects or is expensive to -evaluate. - -GoogleTest gives you three different options to solve this problem: - -#### Using an Existing Boolean Function - -If you already have a function or functor that returns `bool` (or a type that -can be implicitly converted to `bool`), you can use it in a *predicate -assertion* to get the function arguments printed for free. See -[`EXPECT_PRED*`](reference/assertions.md#EXPECT_PRED) in the Assertions -Reference for details. - -#### Using a Function That Returns an AssertionResult - -While `EXPECT_PRED*()` and friends are handy for a quick job, the syntax is not -satisfactory: you have to use different macros for different arities, and it -feels more like Lisp than C++. The `::testing::AssertionResult` class solves -this problem. - -An `AssertionResult` object represents the result of an assertion (whether it's -a success or a failure, and an associated message). You can create an -`AssertionResult` using one of these factory functions: - -```c++ -namespace testing { - -// Returns an AssertionResult object to indicate that an assertion has -// succeeded. -AssertionResult AssertionSuccess(); - -// Returns an AssertionResult object to indicate that an assertion has -// failed. -AssertionResult AssertionFailure(); - -} -``` - -You can then use the `<<` operator to stream messages to the `AssertionResult` -object. - -To provide more readable messages in Boolean assertions (e.g. `EXPECT_TRUE()`), -write a predicate function that returns `AssertionResult` instead of `bool`. For -example, if you define `IsEven()` as: - -```c++ -testing::AssertionResult IsEven(int n) { - if ((n % 2) == 0) - return testing::AssertionSuccess(); - else - return testing::AssertionFailure() << n << " is odd"; -} -``` - -instead of: - -```c++ -bool IsEven(int n) { - return (n % 2) == 0; -} -``` - -the failed assertion `EXPECT_TRUE(IsEven(Fib(4)))` will print: - -```none -Value of: IsEven(Fib(4)) - Actual: false (3 is odd) -Expected: true -``` - -instead of a more opaque - -```none -Value of: IsEven(Fib(4)) - Actual: false -Expected: true -``` - -If you want informative messages in `EXPECT_FALSE` and `ASSERT_FALSE` as well -(one third of Boolean assertions in the Google code base are negative ones), and -are fine with making the predicate slower in the success case, you can supply a -success message: - -```c++ -testing::AssertionResult IsEven(int n) { - if ((n % 2) == 0) - return testing::AssertionSuccess() << n << " is even"; - else - return testing::AssertionFailure() << n << " is odd"; -} -``` - -Then the statement `EXPECT_FALSE(IsEven(Fib(6)))` will print - -```none - Value of: IsEven(Fib(6)) - Actual: true (8 is even) - Expected: false -``` - -#### Using a Predicate-Formatter - -If you find the default message generated by -[`EXPECT_PRED*`](reference/assertions.md#EXPECT_PRED) and -[`EXPECT_TRUE`](reference/assertions.md#EXPECT_TRUE) unsatisfactory, or some -arguments to your predicate do not support streaming to `ostream`, you can -instead use *predicate-formatter assertions* to *fully* customize how the -message is formatted. See -[`EXPECT_PRED_FORMAT*`](reference/assertions.md#EXPECT_PRED_FORMAT) in the -Assertions Reference for details. - -### Floating-Point Comparison - -See [Floating-Point Comparison](reference/assertions.md#floating-point) in the -Assertions Reference. - -#### Floating-Point Predicate-Format Functions - -Some floating-point operations are useful, but not that often used. In order to -avoid an explosion of new macros, we provide them as predicate-format functions -that can be used in the predicate assertion macro -[`EXPECT_PRED_FORMAT2`](reference/assertions.md#EXPECT_PRED_FORMAT), for -example: - -```c++ -using ::testing::FloatLE; -using ::testing::DoubleLE; -... -EXPECT_PRED_FORMAT2(FloatLE, val1, val2); -EXPECT_PRED_FORMAT2(DoubleLE, val1, val2); -``` - -The above code verifies that `val1` is less than, or approximately equal to, -`val2`. - -### Asserting Using gMock Matchers - -See [`EXPECT_THAT`](reference/assertions.md#EXPECT_THAT) in the Assertions -Reference. - -### More String Assertions - -(Please read the [previous](#asserting-using-gmock-matchers) section first if -you haven't.) - -You can use the gMock [string matchers](reference/matchers.md#string-matchers) -with [`EXPECT_THAT`](reference/assertions.md#EXPECT_THAT) to do more string -comparison tricks (sub-string, prefix, suffix, regular expression, and etc). For -example, - -```c++ -using ::testing::HasSubstr; -using ::testing::MatchesRegex; -... - ASSERT_THAT(foo_string, HasSubstr("needle")); - EXPECT_THAT(bar_string, MatchesRegex("\\w*\\d+")); -``` - -### Windows HRESULT assertions - -See [Windows HRESULT Assertions](reference/assertions.md#HRESULT) in the -Assertions Reference. - -### Type Assertions - -You can call the function - -```c++ -::testing::StaticAssertTypeEq(); -``` - -to assert that types `T1` and `T2` are the same. The function does nothing if -the assertion is satisfied. If the types are different, the function call will -fail to compile, the compiler error message will say that `T1 and T2 are not the -same type` and most likely (depending on the compiler) show you the actual -values of `T1` and `T2`. This is mainly useful inside template code. - -**Caveat**: When used inside a member function of a class template or a function -template, `StaticAssertTypeEq()` is effective only if the function is -instantiated. For example, given: - -```c++ -template class Foo { - public: - void Bar() { testing::StaticAssertTypeEq(); } -}; -``` - -the code: - -```c++ -void Test1() { Foo foo; } -``` - -will not generate a compiler error, as `Foo::Bar()` is never actually -instantiated. Instead, you need: - -```c++ -void Test2() { Foo foo; foo.Bar(); } -``` - -to cause a compiler error. - -### Assertion Placement - -You can use assertions in any C++ function. In particular, it doesn't have to be -a method of the test fixture class. The one constraint is that assertions that -generate a fatal failure (`FAIL*` and `ASSERT_*`) can only be used in -void-returning functions. This is a consequence of Google's not using -exceptions. By placing it in a non-void function you'll get a confusing compile -error like `"error: void value not ignored as it ought to be"` or `"cannot -initialize return object of type 'bool' with an rvalue of type 'void'"` or -`"error: no viable conversion from 'void' to 'string'"`. - -If you need to use fatal assertions in a function that returns non-void, one -option is to make the function return the value in an out parameter instead. For -example, you can rewrite `T2 Foo(T1 x)` to `void Foo(T1 x, T2* result)`. You -need to make sure that `*result` contains some sensible value even when the -function returns prematurely. As the function now returns `void`, you can use -any assertion inside of it. - -If changing the function's type is not an option, you should just use assertions -that generate non-fatal failures, such as `ADD_FAILURE*` and `EXPECT_*`. - -{: .callout .note} -NOTE: Constructors and destructors are not considered void-returning functions, -according to the C++ language specification, and so you may not use fatal -assertions in them; you'll get a compilation error if you try. Instead, either -call `abort` and crash the entire test executable, or put the fatal assertion in -a `SetUp`/`TearDown` function; see -[constructor/destructor vs. `SetUp`/`TearDown`](faq.md#CtorVsSetUp) - -{: .callout .warning} -WARNING: A fatal assertion in a helper function (private void-returning method) -called from a constructor or destructor does not terminate the current test, as -your intuition might suggest: it merely returns from the constructor or -destructor early, possibly leaving your object in a partially-constructed or -partially-destructed state! You almost certainly want to `abort` or use -`SetUp`/`TearDown` instead. - -## Skipping test execution - -Related to the assertions `SUCCEED()` and `FAIL()`, you can prevent further test -execution at runtime with the `GTEST_SKIP()` macro. This is useful when you need -to check for preconditions of the system under test during runtime and skip -tests in a meaningful way. - -`GTEST_SKIP()` can be used in individual test cases or in the `SetUp()` methods -of classes derived from either `::testing::Environment` or `::testing::Test`. -For example: - -```c++ -TEST(SkipTest, DoesSkip) { - GTEST_SKIP() << "Skipping single test"; - EXPECT_EQ(0, 1); // Won't fail; it won't be executed -} - -class SkipFixture : public ::testing::Test { - protected: - void SetUp() override { - GTEST_SKIP() << "Skipping all tests for this fixture"; - } -}; - -// Tests for SkipFixture won't be executed. -TEST_F(SkipFixture, SkipsOneTest) { - EXPECT_EQ(5, 7); // Won't fail -} -``` - -As with assertion macros, you can stream a custom message into `GTEST_SKIP()`. - -## Teaching GoogleTest How to Print Your Values - -When a test assertion such as `EXPECT_EQ` fails, GoogleTest prints the argument -values to help you debug. It does this using a user-extensible value printer. - -This printer knows how to print built-in C++ types, native arrays, STL -containers, and any type that supports the `<<` operator. For other types, it -prints the raw bytes in the value and hopes that you the user can figure it out. - -As mentioned earlier, the printer is *extensible*. That means you can teach it -to do a better job at printing your particular type than to dump the bytes. To -do that, define an `AbslStringify()` overload as a `friend` function template -for your type: - -```cpp -namespace foo { - -class Point { // We want GoogleTest to be able to print instances of this. - ... - // Provide a friend overload. - template - friend void AbslStringify(Sink& sink, const Point& point) { - absl::Format(&sink, "(%d, %d)", point.x, point.y); - } - - int x; - int y; -}; - -// If you can't declare the function in the class it's important that the -// AbslStringify overload is defined in the SAME namespace that defines Point. -// C++'s look-up rules rely on that. -enum class EnumWithStringify { kMany = 0, kChoices = 1 }; - -template -void AbslStringify(Sink& sink, EnumWithStringify e) { - absl::Format(&sink, "%s", e == EnumWithStringify::kMany ? "Many" : "Choices"); -} - -} // namespace foo -``` - -{: .callout .note} -Note: `AbslStringify()` utilizes a generic "sink" buffer to construct its -string. For more information about supported operations on `AbslStringify()`'s -sink, see go/abslstringify. - -`AbslStringify()` can also use `absl::StrFormat`'s catch-all `%v` type specifier -within its own format strings to perform type deduction. `Point` above could be -formatted as `"(%v, %v)"` for example, and deduce the `int` values as `%d`. - -Sometimes, `AbslStringify()` might not be an option: your team may wish to print -types with extra debugging information for testing purposes only. If so, you can -instead define a `PrintTo()` function like this: - -```c++ -#include - -namespace foo { - -class Point { - ... - friend void PrintTo(const Point& point, std::ostream* os) { - *os << "(" << point.x << "," << point.y << ")"; - } - - int x; - int y; -}; - -// If you can't declare the function in the class it's important that PrintTo() -// is defined in the SAME namespace that defines Point. C++'s look-up rules -// rely on that. -void PrintTo(const Point& point, std::ostream* os) { - *os << "(" << point.x << "," << point.y << ")"; -} - -} // namespace foo -``` - -If you have defined both `AbslStringify()` and `PrintTo()`, the latter will be -used by GoogleTest. This allows you to customize how the value appears in -GoogleTest's output without affecting code that relies on the behavior of -`AbslStringify()`. - -If you have an existing `<<` operator and would like to define an -`AbslStringify()`, the latter will be used for GoogleTest printing. - -If you want to print a value `x` using GoogleTest's value printer yourself, just -call `::testing::PrintToString(x)`, which returns an `std::string`: - -```c++ -vector > point_ints = GetPointIntVector(); - -EXPECT_TRUE(IsCorrectPointIntVector(point_ints)) - << "point_ints = " << testing::PrintToString(point_ints); -``` - -For more details regarding `AbslStringify()` and its integration with other -libraries, see go/abslstringify. - -## Death Tests - -In many applications, there are assertions that can cause application failure if -a condition is not met. These consistency checks, which ensure that the program -is in a known good state, are there to fail at the earliest possible time after -some program state is corrupted. If the assertion checks the wrong condition, -then the program may proceed in an erroneous state, which could lead to memory -corruption, security holes, or worse. Hence it is vitally important to test that -such assertion statements work as expected. - -Since these precondition checks cause the processes to die, we call such tests -_death tests_. More generally, any test that checks that a program terminates -(except by throwing an exception) in an expected fashion is also a death test. - -Note that if a piece of code throws an exception, we don't consider it "death" -for the purpose of death tests, as the caller of the code could catch the -exception and avoid the crash. If you want to verify exceptions thrown by your -code, see [Exception Assertions](#ExceptionAssertions). - -If you want to test `EXPECT_*()/ASSERT_*()` failures in your test code, see -["Catching" Failures](#catching-failures). - -### How to Write a Death Test - -GoogleTest provides assertion macros to support death tests. See -[Death Assertions](reference/assertions.md#death) in the Assertions Reference -for details. - -To write a death test, simply use one of the macros inside your test function. -For example, - -```c++ -TEST(MyDeathTest, Foo) { - // This death test uses a compound statement. - ASSERT_DEATH({ - int n = 5; - Foo(&n); - }, "Error on line .* of Foo()"); -} - -TEST(MyDeathTest, NormalExit) { - EXPECT_EXIT(NormalExit(), testing::ExitedWithCode(0), "Success"); -} - -TEST(MyDeathTest, KillProcess) { - EXPECT_EXIT(KillProcess(), testing::KilledBySignal(SIGKILL), - "Sending myself unblockable signal"); -} -``` - -verifies that: - -* calling `Foo(5)` causes the process to die with the given error message, -* calling `NormalExit()` causes the process to print `"Success"` to stderr and - exit with exit code 0, and -* calling `KillProcess()` kills the process with signal `SIGKILL`. - -The test function body may contain other assertions and statements as well, if -necessary. - -Note that a death test only cares about three things: - -1. does `statement` abort or exit the process? -2. (in the case of `ASSERT_EXIT` and `EXPECT_EXIT`) does the exit status - satisfy `predicate`? Or (in the case of `ASSERT_DEATH` and `EXPECT_DEATH`) - is the exit status non-zero? And -3. does the stderr output match `matcher`? - -In particular, if `statement` generates an `ASSERT_*` or `EXPECT_*` failure, it -will **not** cause the death test to fail, as GoogleTest assertions don't abort -the process. - -### Death Test Naming - -{: .callout .important} -IMPORTANT: We strongly recommend you to follow the convention of naming your -**test suite** (not test) `*DeathTest` when it contains a death test, as -demonstrated in the above example. The -[Death Tests And Threads](#death-tests-and-threads) section below explains why. - -If a test fixture class is shared by normal tests and death tests, you can use -`using` or `typedef` to introduce an alias for the fixture class and avoid -duplicating its code: - -```c++ -class FooTest : public testing::Test { ... }; - -using FooDeathTest = FooTest; - -TEST_F(FooTest, DoesThis) { - // normal test -} - -TEST_F(FooDeathTest, DoesThat) { - // death test -} -``` - -### Regular Expression Syntax - -When built with Bazel and using Abseil, GoogleTest uses the -[RE2](https://github.com/google/re2/wiki/Syntax) syntax. Otherwise, for POSIX -systems (Linux, Cygwin, Mac), GoogleTest uses the -[POSIX extended regular expression](https://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap09.html#tag_09_04) -syntax. To learn about POSIX syntax, you may want to read this -[Wikipedia entry](https://en.wikipedia.org/wiki/Regular_expression#POSIX_extended). - -On Windows, GoogleTest uses its own simple regular expression implementation. It -lacks many features. For example, we don't support union (`"x|y"`), grouping -(`"(xy)"`), brackets (`"[xy]"`), and repetition count (`"x{5,7}"`), among -others. Below is what we do support (`A` denotes a literal character, period -(`.`), or a single `\\ ` escape sequence; `x` and `y` denote regular -expressions.): - -Expression | Meaning ----------- | -------------------------------------------------------------- -`c` | matches any literal character `c` -`\\d` | matches any decimal digit -`\\D` | matches any character that's not a decimal digit -`\\f` | matches `\f` -`\\n` | matches `\n` -`\\r` | matches `\r` -`\\s` | matches any ASCII whitespace, including `\n` -`\\S` | matches any character that's not a whitespace -`\\t` | matches `\t` -`\\v` | matches `\v` -`\\w` | matches any letter, `_`, or decimal digit -`\\W` | matches any character that `\\w` doesn't match -`\\c` | matches any literal character `c`, which must be a punctuation -`.` | matches any single character except `\n` -`A?` | matches 0 or 1 occurrences of `A` -`A*` | matches 0 or many occurrences of `A` -`A+` | matches 1 or many occurrences of `A` -`^` | matches the beginning of a string (not that of each line) -`$` | matches the end of a string (not that of each line) -`xy` | matches `x` followed by `y` - -To help you determine which capability is available on your system, GoogleTest -defines macros to govern which regular expression it is using. The macros are: -`GTEST_USES_SIMPLE_RE=1` or `GTEST_USES_POSIX_RE=1`. If you want your death -tests to work in all cases, you can either `#if` on these macros or use the more -limited syntax only. - -### How It Works - -See [Death Assertions](reference/assertions.md#death) in the Assertions -Reference. - -### Death Tests And Threads - -The reason for the two death test styles has to do with thread safety. Due to -well-known problems with forking in the presence of threads, death tests should -be run in a single-threaded context. Sometimes, however, it isn't feasible to -arrange that kind of environment. For example, statically-initialized modules -may start threads before main is ever reached. Once threads have been created, -it may be difficult or impossible to clean them up. - -GoogleTest has three features intended to raise awareness of threading issues. - -1. A warning is emitted if multiple threads are running when a death test is - encountered. -2. Test suites with a name ending in "DeathTest" are run before all other - tests. -3. It uses `clone()` instead of `fork()` to spawn the child process on Linux - (`clone()` is not available on Cygwin and Mac), as `fork()` is more likely - to cause the child to hang when the parent process has multiple threads. - -It's perfectly fine to create threads inside a death test statement; they are -executed in a separate process and cannot affect the parent. - -### Death Test Styles - -The "threadsafe" death test style was introduced in order to help mitigate the -risks of testing in a possibly multithreaded environment. It trades increased -test execution time (potentially dramatically so) for improved thread safety. - -The automated testing framework does not set the style flag. You can choose a -particular style of death tests by setting the flag programmatically: - -```c++ -GTEST_FLAG_SET(death_test_style, "threadsafe"); -``` - -You can do this in `main()` to set the style for all death tests in the binary, -or in individual tests. Recall that flags are saved before running each test and -restored afterwards, so you need not do that yourself. For example: - -```c++ -int main(int argc, char** argv) { - testing::InitGoogleTest(&argc, argv); - GTEST_FLAG_SET(death_test_style, "fast"); - return RUN_ALL_TESTS(); -} - -TEST(MyDeathTest, TestOne) { - GTEST_FLAG_SET(death_test_style, "threadsafe"); - // This test is run in the "threadsafe" style: - ASSERT_DEATH(ThisShouldDie(), ""); -} - -TEST(MyDeathTest, TestTwo) { - // This test is run in the "fast" style: - ASSERT_DEATH(ThisShouldDie(), ""); -} -``` - -### Caveats - -The `statement` argument of `ASSERT_EXIT()` can be any valid C++ statement. If -it leaves the current function via a `return` statement or by throwing an -exception, the death test is considered to have failed. Some GoogleTest macros -may return from the current function (e.g. `ASSERT_TRUE()`), so be sure to avoid -them in `statement`. - -Since `statement` runs in the child process, any in-memory side effect (e.g. -modifying a variable, releasing memory, etc) it causes will *not* be observable -in the parent process. In particular, if you release memory in a death test, -your program will fail the heap check as the parent process will never see the -memory reclaimed. To solve this problem, you can - -1. try not to free memory in a death test; -2. free the memory again in the parent process; or -3. do not use the heap checker in your program. - -Due to an implementation detail, you cannot place multiple death test assertions -on the same line; otherwise, compilation will fail with an unobvious error -message. - -Despite the improved thread safety afforded by the "threadsafe" style of death -test, thread problems such as deadlock are still possible in the presence of -handlers registered with `pthread_atfork(3)`. - -## Using Assertions in Sub-routines - -{: .callout .note} -Note: If you want to put a series of test assertions in a subroutine to check -for a complex condition, consider using -[a custom GMock matcher](gmock_cook_book.md#NewMatchers) instead. This lets you -provide a more readable error message in case of failure and avoid all of the -issues described below. - -### Adding Traces to Assertions - -If a test sub-routine is called from several places, when an assertion inside it -fails, it can be hard to tell which invocation of the sub-routine the failure is -from. You can alleviate this problem using extra logging or custom failure -messages, but that usually clutters up your tests. A better solution is to use -the `SCOPED_TRACE` macro or the `ScopedTrace` utility: - -```c++ -SCOPED_TRACE(message); -``` - -```c++ -ScopedTrace trace("file_path", line_number, message); -``` - -where `message` can be anything streamable to `std::ostream`. `SCOPED_TRACE` -macro will cause the current file name, line number, and the given message to be -added in every failure message. `ScopedTrace` accepts explicit file name and -line number in arguments, which is useful for writing test helpers. The effect -will be undone when the control leaves the current lexical scope. - -For example, - -```c++ -10: void Sub1(int n) { -11: EXPECT_EQ(Bar(n), 1); -12: EXPECT_EQ(Bar(n + 1), 2); -13: } -14: -15: TEST(FooTest, Bar) { -16: { -17: SCOPED_TRACE("A"); // This trace point will be included in -18: // every failure in this scope. -19: Sub1(1); -20: } -21: // Now it won't. -22: Sub1(9); -23: } -``` - -could result in messages like these: - -```none -path/to/foo_test.cc:11: Failure -Value of: Bar(n) -Expected: 1 - Actual: 2 -Google Test trace: -path/to/foo_test.cc:17: A - -path/to/foo_test.cc:12: Failure -Value of: Bar(n + 1) -Expected: 2 - Actual: 3 -``` - -Without the trace, it would've been difficult to know which invocation of -`Sub1()` the two failures come from respectively. (You could add an extra -message to each assertion in `Sub1()` to indicate the value of `n`, but that's -tedious.) - -Some tips on using `SCOPED_TRACE`: - -1. With a suitable message, it's often enough to use `SCOPED_TRACE` at the - beginning of a sub-routine, instead of at each call site. -2. When calling sub-routines inside a loop, make the loop iterator part of the - message in `SCOPED_TRACE` such that you can know which iteration the failure - is from. -3. Sometimes the line number of the trace point is enough for identifying the - particular invocation of a sub-routine. In this case, you don't have to - choose a unique message for `SCOPED_TRACE`. You can simply use `""`. -4. You can use `SCOPED_TRACE` in an inner scope when there is one in the outer - scope. In this case, all active trace points will be included in the failure - messages, in reverse order they are encountered. -5. The trace dump is clickable in Emacs - hit `return` on a line number and - you'll be taken to that line in the source file! - -### Propagating Fatal Failures - -A common pitfall when using `ASSERT_*` and `FAIL*` is not understanding that -when they fail they only abort the _current function_, not the entire test. For -example, the following test will segfault: - -```c++ -void Subroutine() { - // Generates a fatal failure and aborts the current function. - ASSERT_EQ(1, 2); - - // The following won't be executed. - ... -} - -TEST(FooTest, Bar) { - Subroutine(); // The intended behavior is for the fatal failure - // in Subroutine() to abort the entire test. - - // The actual behavior: the function goes on after Subroutine() returns. - int* p = nullptr; - *p = 3; // Segfault! -} -``` - -To alleviate this, GoogleTest provides three different solutions. You could use -either exceptions, the `(ASSERT|EXPECT)_NO_FATAL_FAILURE` assertions or the -`HasFatalFailure()` function. They are described in the following two -subsections. - -#### Asserting on Subroutines with an exception - -The following code can turn ASSERT-failure into an exception: - -```c++ -class ThrowListener : public testing::EmptyTestEventListener { - void OnTestPartResult(const testing::TestPartResult& result) override { - if (result.type() == testing::TestPartResult::kFatalFailure) { - throw testing::AssertionException(result); - } - } -}; -int main(int argc, char** argv) { - ... - testing::UnitTest::GetInstance()->listeners().Append(new ThrowListener); - return RUN_ALL_TESTS(); -} -``` - -This listener should be added after other listeners if you have any, otherwise -they won't see failed `OnTestPartResult`. - -#### Asserting on Subroutines - -As shown above, if your test calls a subroutine that has an `ASSERT_*` failure -in it, the test will continue after the subroutine returns. This may not be what -you want. - -Often people want fatal failures to propagate like exceptions. For that -GoogleTest offers the following macros: - -Fatal assertion | Nonfatal assertion | Verifies -------------------------------------- | ------------------------------------- | -------- -`ASSERT_NO_FATAL_FAILURE(statement);` | `EXPECT_NO_FATAL_FAILURE(statement);` | `statement` doesn't generate any new fatal failures in the current thread. - -Only failures in the thread that executes the assertion are checked to determine -the result of this type of assertions. If `statement` creates new threads, -failures in these threads are ignored. - -Examples: - -```c++ -ASSERT_NO_FATAL_FAILURE(Foo()); - -int i; -EXPECT_NO_FATAL_FAILURE({ - i = Bar(); -}); -``` - -Assertions from multiple threads are currently not supported on Windows. - -#### Checking for Failures in the Current Test - -`HasFatalFailure()` in the `::testing::Test` class returns `true` if an -assertion in the current test has suffered a fatal failure. This allows -functions to catch fatal failures in a sub-routine and return early. - -```c++ -class Test { - public: - ... - static bool HasFatalFailure(); -}; -``` - -The typical usage, which basically simulates the behavior of a thrown exception, -is: - -```c++ -TEST(FooTest, Bar) { - Subroutine(); - // Aborts if Subroutine() had a fatal failure. - if (HasFatalFailure()) return; - - // The following won't be executed. - ... -} -``` - -If `HasFatalFailure()` is used outside of `TEST()` , `TEST_F()` , or a test -fixture, you must add the `::testing::Test::` prefix, as in: - -```c++ -if (testing::Test::HasFatalFailure()) return; -``` - -Similarly, `HasNonfatalFailure()` returns `true` if the current test has at -least one non-fatal failure, and `HasFailure()` returns `true` if the current -test has at least one failure of either kind. - -## Logging Additional Information - -In your test code, you can call `RecordProperty("key", value)` to log additional -information, where `value` can be either a string or an `int`. The *last* value -recorded for a key will be emitted to the -[XML output](#generating-an-xml-report) if you specify one. For example, the -test - -```c++ -TEST_F(WidgetUsageTest, MinAndMaxWidgets) { - RecordProperty("MaximumWidgets", ComputeMaxUsage()); - RecordProperty("MinimumWidgets", ComputeMinUsage()); -} -``` - -will output XML like this: - -```xml - ... - - ... -``` - -{: .callout .note} -> NOTE: -> -> * `RecordProperty()` is a static member of the `Test` class. Therefore it -> needs to be prefixed with `::testing::Test::` if used outside of the -> `TEST` body and the test fixture class. -> * *`key`* must be a valid XML attribute name, and cannot conflict with the -> ones already used by GoogleTest (`name`, `status`, `time`, `classname`, -> `type_param`, and `value_param`). -> * Calling `RecordProperty()` outside of the lifespan of a test is allowed. -> If it's called outside of a test but between a test suite's -> `SetUpTestSuite()` and `TearDownTestSuite()` methods, it will be -> attributed to the XML element for the test suite. If it's called outside -> of all test suites (e.g. in a test environment), it will be attributed to -> the top-level XML element. - -## Sharing Resources Between Tests in the Same Test Suite - -GoogleTest creates a new test fixture object for each test in order to make -tests independent and easier to debug. However, sometimes tests use resources -that are expensive to set up, making the one-copy-per-test model prohibitively -expensive. - -If the tests don't change the resource, there's no harm in their sharing a -single resource copy. So, in addition to per-test set-up/tear-down, GoogleTest -also supports per-test-suite set-up/tear-down. To use it: - -1. In your test fixture class (say `FooTest` ), declare as `static` some member - variables to hold the shared resources. -2. Outside your test fixture class (typically just below it), define those - member variables, optionally giving them initial values. -3. In the same test fixture class, define a public member function `static void - SetUpTestSuite()` (remember not to spell it as **`SetupTestSuite`** with a - small `u`!) to set up the shared resources and a `static void - TearDownTestSuite()` function to tear them down. - -That's it! GoogleTest automatically calls `SetUpTestSuite()` before running the -*first test* in the `FooTest` test suite (i.e. before creating the first -`FooTest` object), and calls `TearDownTestSuite()` after running the *last test* -in it (i.e. after deleting the last `FooTest` object). In between, the tests can -use the shared resources. - -Remember that the test order is undefined, so your code can't depend on a test -preceding or following another. Also, the tests must either not modify the state -of any shared resource, or, if they do modify the state, they must restore the -state to its original value before passing control to the next test. - -Note that `SetUpTestSuite()` may be called multiple times for a test fixture -class that has derived classes, so you should not expect code in the function -body to be run only once. Also, derived classes still have access to shared -resources defined as static members, so careful consideration is needed when -managing shared resources to avoid memory leaks if shared resources are not -properly cleaned up in `TearDownTestSuite()`. - -Here's an example of per-test-suite set-up and tear-down: - -```c++ -class FooTest : public testing::Test { - protected: - // Per-test-suite set-up. - // Called before the first test in this test suite. - // Can be omitted if not needed. - static void SetUpTestSuite() { - shared_resource_ = new ...; - - // If `shared_resource_` is **not deleted** in `TearDownTestSuite()`, - // reallocation should be prevented because `SetUpTestSuite()` may be called - // in subclasses of FooTest and lead to memory leak. - // - // if (shared_resource_ == nullptr) { - // shared_resource_ = new ...; - // } - } - - // Per-test-suite tear-down. - // Called after the last test in this test suite. - // Can be omitted if not needed. - static void TearDownTestSuite() { - delete shared_resource_; - shared_resource_ = nullptr; - } - - // You can define per-test set-up logic as usual. - void SetUp() override { ... } - - // You can define per-test tear-down logic as usual. - void TearDown() override { ... } - - // Some expensive resource shared by all tests. - static T* shared_resource_; -}; - -T* FooTest::shared_resource_ = nullptr; - -TEST_F(FooTest, Test1) { - ... you can refer to shared_resource_ here ... -} - -TEST_F(FooTest, Test2) { - ... you can refer to shared_resource_ here ... -} -``` - -{: .callout .note} -NOTE: Though the above code declares `SetUpTestSuite()` protected, it may -sometimes be necessary to declare it public, such as when using it with -`TEST_P`. - -## Global Set-Up and Tear-Down - -Just as you can do set-up and tear-down at the test level and the test suite -level, you can also do it at the test program level. Here's how. - -First, you subclass the `::testing::Environment` class to define a test -environment, which knows how to set-up and tear-down: - -```c++ -class Environment : public ::testing::Environment { - public: - ~Environment() override {} - - // Override this to define how to set up the environment. - void SetUp() override {} - - // Override this to define how to tear down the environment. - void TearDown() override {} -}; -``` - -Then, you register an instance of your environment class with GoogleTest by -calling the `::testing::AddGlobalTestEnvironment()` function: - -```c++ -Environment* AddGlobalTestEnvironment(Environment* env); -``` - -Now, when `RUN_ALL_TESTS()` is invoked, it first calls the `SetUp()` method. The -tests are then executed, provided that none of the environments have reported -fatal failures and `GTEST_SKIP()` has not been invoked. Finally, `TearDown()` is -called. - -Note that `SetUp()` and `TearDown()` are only invoked if there is at least one -test to be performed. Importantly, `TearDown()` is executed even if the test is -not run due to a fatal failure or `GTEST_SKIP()`. - -Calling `SetUp()` and `TearDown()` for each iteration depends on the flag -`gtest_recreate_environments_when_repeating`. `SetUp()` and `TearDown()` are -called for each environment object when the object is recreated for each -iteration. However, if test environments are not recreated for each iteration, -`SetUp()` is called only on the first iteration, and `TearDown()` is called only -on the last iteration. - -It's OK to register multiple environment objects. In this suite, their `SetUp()` -will be called in the order they are registered, and their `TearDown()` will be -called in the reverse order. - -Note that GoogleTest takes ownership of the registered environment objects. -Therefore **do not delete them** by yourself. - -You should call `AddGlobalTestEnvironment()` before `RUN_ALL_TESTS()` is called, -probably in `main()`. If you use `gtest_main`, you need to call this before -`main()` starts for it to take effect. One way to do this is to define a global -variable like this: - -```c++ -testing::Environment* const foo_env = - testing::AddGlobalTestEnvironment(new FooEnvironment); -``` - -However, we strongly recommend you to write your own `main()` and call -`AddGlobalTestEnvironment()` there, as relying on initialization of global -variables makes the code harder to read and may cause problems when you register -multiple environments from different translation units and the environments have -dependencies among them (remember that the compiler doesn't guarantee the order -in which global variables from different translation units are initialized). - -## Value-Parameterized Tests - -*Value-parameterized tests* allow you to test your code with different -parameters without writing multiple copies of the same test. This is useful in a -number of situations, for example: - -* You have a piece of code whose behavior is affected by one or more - command-line flags. You want to make sure your code performs correctly for - various values of those flags. -* You want to test different implementations of an OO interface. -* You want to test your code over various inputs (a.k.a. data-driven testing). - This feature is easy to abuse, so please exercise your good sense when doing - it! - -### How to Write Value-Parameterized Tests - -To write value-parameterized tests, first you should define a fixture class. It -must be derived from both `testing::Test` and `testing::WithParamInterface` -(the latter is a pure interface), where `T` is the type of your parameter -values. For convenience, you can just derive the fixture class from -`testing::TestWithParam`, which itself is derived from both `testing::Test` -and `testing::WithParamInterface`. `T` can be any copyable type. If it's a -raw pointer, you are responsible for managing the lifespan of the pointed -values. - -{: .callout .note} -NOTE: If your test fixture defines `SetUpTestSuite()` or `TearDownTestSuite()` -they must be declared **public** rather than **protected** in order to use -`TEST_P`. - -```c++ -class FooTest : - public testing::TestWithParam { - // You can implement all the usual fixture class members here. - // To access the test parameter, call GetParam() from class - // TestWithParam. -}; - -// Or, when you want to add parameters to a pre-existing fixture class: -class BaseTest : public testing::Test { - ... -}; -class BarTest : public BaseTest, - public testing::WithParamInterface { - ... -}; -``` - -Then, use the `TEST_P` macro to define as many test patterns using this fixture -as you want. The `_P` suffix is for "parameterized" or "pattern", whichever you -prefer to think. - -```c++ -TEST_P(FooTest, DoesBlah) { - // Inside a test, access the test parameter with the GetParam() method - // of the TestWithParam class: - EXPECT_TRUE(foo.Blah(GetParam())); - ... -} - -TEST_P(FooTest, HasBlahBlah) { - ... -} -``` - -Finally, you can use the `INSTANTIATE_TEST_SUITE_P` macro to instantiate the -test suite with any set of parameters you want. GoogleTest defines a number of -functions for generating test parameters—see details at -[`INSTANTIATE_TEST_SUITE_P`](reference/testing.md#INSTANTIATE_TEST_SUITE_P) in -the Testing Reference. - -For example, the following statement will instantiate tests from the `FooTest` -test suite each with parameter values `"meeny"`, `"miny"`, and `"moe"` using the -[`Values`](reference/testing.md#param-generators) parameter generator: - -```c++ -INSTANTIATE_TEST_SUITE_P(MeenyMinyMoe, - FooTest, - testing::Values("meeny", "miny", "moe")); -``` - -{: .callout .note} -NOTE: The code above must be placed at global or namespace scope, not at -function scope. - -The first argument to `INSTANTIATE_TEST_SUITE_P` is a unique name for the -instantiation of the test suite. The next argument is the name of the test -pattern, and the last is the -[parameter generator](reference/testing.md#param-generators). - -The parameter generator expression is not evaluated until GoogleTest is -initialized (via `InitGoogleTest()`). Any prior initialization done in the -`main` function will be accessible from the parameter generator, for example, -the results of flag parsing. - -You can instantiate a test pattern more than once, so to distinguish different -instances of the pattern, the instantiation name is added as a prefix to the -actual test suite name. Remember to pick unique prefixes for different -instantiations. The tests from the instantiation above will have these names: - -* `MeenyMinyMoe/FooTest.DoesBlah/0` for `"meeny"` -* `MeenyMinyMoe/FooTest.DoesBlah/1` for `"miny"` -* `MeenyMinyMoe/FooTest.DoesBlah/2` for `"moe"` -* `MeenyMinyMoe/FooTest.HasBlahBlah/0` for `"meeny"` -* `MeenyMinyMoe/FooTest.HasBlahBlah/1` for `"miny"` -* `MeenyMinyMoe/FooTest.HasBlahBlah/2` for `"moe"` - -You can use these names in [`--gtest_filter`](#running-a-subset-of-the-tests). - -The following statement will instantiate all tests from `FooTest` again, each -with parameter values `"cat"` and `"dog"` using the -[`ValuesIn`](reference/testing.md#param-generators) parameter generator: - -```c++ -constexpr absl::string_view kPets[] = {"cat", "dog"}; -INSTANTIATE_TEST_SUITE_P(Pets, FooTest, testing::ValuesIn(kPets)); -``` - -The tests from the instantiation above will have these names: - -* `Pets/FooTest.DoesBlah/0` for `"cat"` -* `Pets/FooTest.DoesBlah/1` for `"dog"` -* `Pets/FooTest.HasBlahBlah/0` for `"cat"` -* `Pets/FooTest.HasBlahBlah/1` for `"dog"` - -Please note that `INSTANTIATE_TEST_SUITE_P` will instantiate *all* tests in the -given test suite, whether their definitions come before or *after* the -`INSTANTIATE_TEST_SUITE_P` statement. - -Additionally, by default, every `TEST_P` without a corresponding -`INSTANTIATE_TEST_SUITE_P` causes a failing test in test suite -`GoogleTestVerification`. If you have a test suite where that omission is not an -error, for example it is in a library that may be linked in for other reasons or -where the list of test cases is dynamic and may be empty, then this check can be -suppressed by tagging the test suite: - -```c++ -GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(FooTest); -``` - -You can see [sample7_unittest.cc] and [sample8_unittest.cc] for more examples. - -[sample7_unittest.cc]: https://github.com/google/googletest/blob/main/googletest/samples/sample7_unittest.cc "Parameterized Test example" -[sample8_unittest.cc]: https://github.com/google/googletest/blob/main/googletest/samples/sample8_unittest.cc "Parameterized Test example with multiple parameters" - -### Creating Value-Parameterized Abstract Tests - -In the above, we define and instantiate `FooTest` in the *same* source file. -Sometimes you may want to define value-parameterized tests in a library and let -other people instantiate them later. This pattern is known as *abstract tests*. -As an example of its application, when you are designing an interface you can -write a standard suite of abstract tests (perhaps using a factory function as -the test parameter) that all implementations of the interface are expected to -pass. When someone implements the interface, they can instantiate your suite to -get all the interface-conformance tests for free. - -To define abstract tests, you should organize your code like this: - -1. Put the definition of the parameterized test fixture class (e.g. `FooTest`) - in a header file, say `foo_param_test.h`. Think of this as *declaring* your - abstract tests. -2. Put the `TEST_P` definitions in `foo_param_test.cc`, which includes - `foo_param_test.h`. Think of this as *implementing* your abstract tests. - -Once they are defined, you can instantiate them by including `foo_param_test.h`, -invoking `INSTANTIATE_TEST_SUITE_P()`, and depending on the library target that -contains `foo_param_test.cc`. You can instantiate the same abstract test suite -multiple times, possibly in different source files. - -### Specifying Names for Value-Parameterized Test Parameters - -The optional last argument to `INSTANTIATE_TEST_SUITE_P()` allows the user to -specify a function or functor that generates custom test name suffixes based on -the test parameters. The function should accept one argument of type -`testing::TestParamInfo`, and return `std::string`. - -`testing::PrintToStringParamName` is a builtin test suffix generator that -returns the value of `testing::PrintToString(GetParam())`. It does not work for -`std::string` or C strings. - -{: .callout .note} -NOTE: test names must be non-empty, unique, and may only contain ASCII -alphanumeric characters. In particular, they -[should not contain underscores](faq.md#why-should-test-suite-names-and-test-names-not-contain-underscore) - -```c++ -class MyTestSuite : public testing::TestWithParam {}; - -TEST_P(MyTestSuite, MyTest) -{ - std::cout << "Example Test Param: " << GetParam() << std::endl; -} - -INSTANTIATE_TEST_SUITE_P(MyGroup, MyTestSuite, testing::Range(0, 10), - testing::PrintToStringParamName()); -``` - -Providing a custom functor allows for more control over test parameter name -generation, especially for types where the automatic conversion does not -generate helpful parameter names (e.g. strings as demonstrated above). The -following example illustrates this for multiple parameters, an enumeration type -and a string, and also demonstrates how to combine generators. It uses a lambda -for conciseness: - -```c++ -enum class MyType { MY_FOO = 0, MY_BAR = 1 }; - -class MyTestSuite : public testing::TestWithParam> { -}; - -INSTANTIATE_TEST_SUITE_P( - MyGroup, MyTestSuite, - testing::Combine( - testing::Values(MyType::MY_FOO, MyType::MY_BAR), - testing::Values("A", "B")), - [](const testing::TestParamInfo& info) { - std::string name = absl::StrCat( - std::get<0>(info.param) == MyType::MY_FOO ? "Foo" : "Bar", - std::get<1>(info.param)); - absl::c_replace_if(name, [](char c) { return !std::isalnum(c); }, '_'); - return name; - }); -``` - -## Typed Tests - -Suppose you have multiple implementations of the same interface and want to make -sure that all of them satisfy some common requirements. Or, you may have defined -several types that are supposed to conform to the same "concept" and you want to -verify it. In both cases, you want the same test logic repeated for different -types. - -While you can write one `TEST` or `TEST_F` for each type you want to test (and -you may even factor the test logic into a function template that you invoke from -the `TEST`), it's tedious and doesn't scale: if you want `m` tests over `n` -types, you'll end up writing `m*n` `TEST`s. - -*Typed tests* allow you to repeat the same test logic over a list of types. You -only need to write the test logic once, although you must know the type list -when writing typed tests. Here's how you do it: - -First, define a fixture class template. It should be parameterized by a type. -Remember to derive it from `::testing::Test`: - -```c++ -template -class FooTest : public testing::Test { - public: - ... - using List = std::list; - static T shared_; - T value_; -}; -``` - -Next, associate a list of types with the test suite, which will be repeated for -each type in the list: - -```c++ -using MyTypes = ::testing::Types; -TYPED_TEST_SUITE(FooTest, MyTypes); -``` - -The type alias (`using` or `typedef`) is necessary for the `TYPED_TEST_SUITE` -macro to parse correctly. Otherwise the compiler will think that each comma in -the type list introduces a new macro argument. - -Then, use `TYPED_TEST()` instead of `TEST_F()` to define a typed test for this -test suite. You can repeat this as many times as you want: - -```c++ -TYPED_TEST(FooTest, DoesBlah) { - // Inside a test, refer to the special name TypeParam to get the type - // parameter. Since we are inside a derived class template, C++ requires - // us to visit the members of FooTest via 'this'. - TypeParam n = this->value_; - - // To visit static members of the fixture, add the 'TestFixture::' - // prefix. - n += TestFixture::shared_; - - // To refer to typedefs in the fixture, add the 'typename TestFixture::' - // prefix. The 'typename' is required to satisfy the compiler. - typename TestFixture::List values; - - values.push_back(n); - ... -} - -TYPED_TEST(FooTest, HasPropertyA) { ... } -``` - -You can see [sample6_unittest.cc] for a complete example. - -[sample6_unittest.cc]: https://github.com/google/googletest/blob/main/googletest/samples/sample6_unittest.cc "Typed Test example" - -## Type-Parameterized Tests - -*Type-parameterized tests* are like typed tests, except that they don't require -you to know the list of types ahead of time. Instead, you can define the test -logic first and instantiate it with different type lists later. You can even -instantiate it more than once in the same program. - -If you are designing an interface or concept, you can define a suite of -type-parameterized tests to verify properties that any valid implementation of -the interface/concept should have. Then, the author of each implementation can -just instantiate the test suite with their type to verify that it conforms to -the requirements, without having to write similar tests repeatedly. Here's an -example: - -First, define a fixture class template, as we did with typed tests: - -```c++ -template -class FooTest : public testing::Test { - void DoSomethingInteresting(); - ... -}; -``` - -Next, declare that you will define a type-parameterized test suite: - -```c++ -TYPED_TEST_SUITE_P(FooTest); -``` - -Then, use `TYPED_TEST_P()` to define a type-parameterized test. You can repeat -this as many times as you want: - -```c++ -TYPED_TEST_P(FooTest, DoesBlah) { - // Inside a test, refer to TypeParam to get the type parameter. - TypeParam n = 0; - - // You will need to use `this` explicitly to refer to fixture members. - this->DoSomethingInteresting() - ... -} - -TYPED_TEST_P(FooTest, HasPropertyA) { ... } -``` - -Now the tricky part: you need to register all test patterns using the -`REGISTER_TYPED_TEST_SUITE_P` macro before you can instantiate them. The first -argument of the macro is the test suite name; the rest are the names of the -tests in this test suite: - -```c++ -REGISTER_TYPED_TEST_SUITE_P(FooTest, - DoesBlah, HasPropertyA); -``` - -Finally, you are free to instantiate the pattern with the types you want. If you -put the above code in a header file, you can `#include` it in multiple C++ -source files and instantiate it multiple times. - -```c++ -using MyTypes = ::testing::Types; -INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes); -``` - -To distinguish different instances of the pattern, the first argument to the -`INSTANTIATE_TYPED_TEST_SUITE_P` macro is a prefix that will be added to the -actual test suite name. Remember to pick unique prefixes for different -instances. - -In the special case where the type list contains only one type, you can write -that type directly without `::testing::Types<...>`, like this: - -```c++ -INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, int); -``` - -You can see [sample6_unittest.cc] for a complete example. - -## Testing Private Code - -If you change your software's internal implementation, your tests should not -break as long as the change is not observable by users. Therefore, **per the -black-box testing principle, most of the time you should test your code through -its public interfaces.** - -**If you still find yourself needing to test internal implementation code, -consider if there's a better design.** The desire to test internal -implementation is often a sign that the class is doing too much. Consider -extracting an implementation class, and testing it. Then use that implementation -class in the original class. - -If you absolutely have to test non-public interface code though, you can. There -are two cases to consider: - -* Static functions ( *not* the same as static member functions!) or unnamed - namespaces, and -* Private or protected class members - -To test them, we use the following special techniques: - -* Both static functions and definitions/declarations in an unnamed namespace - are only visible within the same translation unit. To test them, you can - `#include` the entire `.cc` file being tested in your `*_test.cc` file. - (#including `.cc` files is not a good way to reuse code - you should not do - this in production code!) - - However, a better approach is to move the private code into the - `foo::internal` namespace, where `foo` is the namespace your project - normally uses, and put the private declarations in a `*-internal.h` file. - Your production `.cc` files and your tests are allowed to include this - internal header, but your clients are not. This way, you can fully test your - internal implementation without leaking it to your clients. - -* Private class members are only accessible from within the class or by - friends. To access a class' private members, you can declare your test - fixture as a friend to the class and define accessors in your fixture. Tests - using the fixture can then access the private members of your production - class via the accessors in the fixture. Note that even though your fixture - is a friend to your production class, your tests are not automatically - friends to it, as they are technically defined in sub-classes of the - fixture. - - Another way to test private members is to refactor them into an - implementation class, which is then declared in a `*-internal.h` file. Your - clients aren't allowed to include this header but your tests can. Such is - called the - [Pimpl](https://www.gamedev.net/articles/programming/general-and-gameplay-programming/the-c-pimpl-r1794/) - (Private Implementation) idiom. - - Or, you can declare an individual test as a friend of your class by adding - this line in the class body: - - ```c++ - FRIEND_TEST(TestSuiteName, TestName); - ``` - - For example, - - ```c++ - // foo.h - class Foo { - ... - private: - FRIEND_TEST(FooTest, BarReturnsZeroOnNull); - - int Bar(void* x); - }; - - // foo_test.cc - ... - TEST(FooTest, BarReturnsZeroOnNull) { - Foo foo; - EXPECT_EQ(foo.Bar(NULL), 0); // Uses Foo's private member Bar(). - } - ``` - - Pay special attention when your class is defined in a namespace. If you want - your test fixtures and tests to be friends of your class, then they must be - defined in the exact same namespace (no anonymous or inline namespaces). - - For example, if the code to be tested looks like: - - ```c++ - namespace my_namespace { - - class Foo { - friend class FooTest; - FRIEND_TEST(FooTest, Bar); - FRIEND_TEST(FooTest, Baz); - ... definition of the class Foo ... - }; - - } // namespace my_namespace - ``` - - Your test code should be something like: - - ```c++ - namespace my_namespace { - - class FooTest : public testing::Test { - protected: - ... - }; - - TEST_F(FooTest, Bar) { ... } - TEST_F(FooTest, Baz) { ... } - - } // namespace my_namespace - ``` - -## "Catching" Failures - -If you are building a testing utility on top of GoogleTest, you'll want to test -your utility. What framework would you use to test it? GoogleTest, of course. - -The challenge is to verify that your testing utility reports failures correctly. -In frameworks that report a failure by throwing an exception, you could catch -the exception and assert on it. But GoogleTest doesn't use exceptions, so how do -we test that a piece of code generates an expected failure? - -`"gtest/gtest-spi.h"` contains some constructs to do this. -After #including this header, you can use - -```c++ - EXPECT_FATAL_FAILURE(statement, substring); -``` - -to assert that `statement` generates a fatal (e.g. `ASSERT_*`) failure in the -current thread whose message contains the given `substring`, or use - -```c++ - EXPECT_NONFATAL_FAILURE(statement, substring); -``` - -if you are expecting a non-fatal (e.g. `EXPECT_*`) failure. - -Only failures in the current thread are checked to determine the result of this -type of expectations. If `statement` creates new threads, failures in these -threads are also ignored. If you want to catch failures in other threads as -well, use one of the following macros instead: - -```c++ - EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substring); - EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substring); -``` - -{: .callout .note} -NOTE: Assertions from multiple threads are currently not supported on Windows. - -For technical reasons, there are some caveats: - -1. You cannot stream a failure message to either macro. - -2. `statement` in `EXPECT_FATAL_FAILURE{_ON_ALL_THREADS}()` cannot reference - local non-static variables or non-static members of `this` object. - -3. `statement` in `EXPECT_FATAL_FAILURE{_ON_ALL_THREADS}()` cannot return a - value. - -## Registering tests programmatically - -The `TEST` macros handle the vast majority of all use cases, but there are few -where runtime registration logic is required. For those cases, the framework -provides the `::testing::RegisterTest` that allows callers to register arbitrary -tests dynamically. - -This is an advanced API only to be used when the `TEST` macros are insufficient. -The macros should be preferred when possible, as they avoid most of the -complexity of calling this function. - -It provides the following signature: - -```c++ -template -TestInfo* RegisterTest(const char* test_suite_name, const char* test_name, - const char* type_param, const char* value_param, - const char* file, int line, Factory factory); -``` - -The `factory` argument is a factory callable (move-constructible) object or -function pointer that creates a new instance of the Test object. It handles -ownership to the caller. The signature of the callable is `Fixture*()`, where -`Fixture` is the test fixture class for the test. All tests registered with the -same `test_suite_name` must return the same fixture type. This is checked at -runtime. - -The framework will infer the fixture class from the factory and will call the -`SetUpTestSuite` and `TearDownTestSuite` for it. - -Must be called before `RUN_ALL_TESTS()` is invoked, otherwise behavior is -undefined. - -Use case example: - -```c++ -class MyFixture : public testing::Test { - public: - // All of these optional, just like in regular macro usage. - static void SetUpTestSuite() { ... } - static void TearDownTestSuite() { ... } - void SetUp() override { ... } - void TearDown() override { ... } -}; - -class MyTest : public MyFixture { - public: - explicit MyTest(int data) : data_(data) {} - void TestBody() override { ... } - - private: - int data_; -}; - -void RegisterMyTests(const std::vector& values) { - for (int v : values) { - testing::RegisterTest( - "MyFixture", ("Test" + std::to_string(v)).c_str(), nullptr, - std::to_string(v).c_str(), - __FILE__, __LINE__, - // Important to use the fixture type as the return type here. - [=]() -> MyFixture* { return new MyTest(v); }); - } -} -... -int main(int argc, char** argv) { - testing::InitGoogleTest(&argc, argv); - std::vector values_to_test = LoadValuesFromConfig(); - RegisterMyTests(values_to_test); - ... - return RUN_ALL_TESTS(); -} -``` - -## Getting the Current Test's Name - -Sometimes a function may need to know the name of the currently running test. -For example, you may be using the `SetUp()` method of your test fixture to set -the golden file name based on which test is running. The -[`TestInfo`](reference/testing.md#TestInfo) class has this information. - -To obtain a `TestInfo` object for the currently running test, call -`current_test_info()` on the [`UnitTest`](reference/testing.md#UnitTest) -singleton object: - -```c++ - // Gets information about the currently running test. - // Do NOT delete the returned object - it's managed by the UnitTest class. - const testing::TestInfo* const test_info = - testing::UnitTest::GetInstance()->current_test_info(); - - printf("We are in test %s of test suite %s.\n", - test_info->name(), - test_info->test_suite_name()); -``` - -`current_test_info()` returns a null pointer if no test is running. In -particular, you cannot find the test suite name in `SetUpTestSuite()`, -`TearDownTestSuite()` (where you know the test suite name implicitly), or -functions called from them. - -## Extending GoogleTest by Handling Test Events - -GoogleTest provides an **event listener API** to let you receive notifications -about the progress of a test program and test failures. The events you can -listen to include the start and end of the test program, a test suite, or a test -method, among others. You may use this API to augment or replace the standard -console output, replace the XML output, or provide a completely different form -of output, such as a GUI or a database. You can also use test events as -checkpoints to implement a resource leak checker, for example. - -### Defining Event Listeners - -To define a event listener, you subclass either -[`testing::TestEventListener`](reference/testing.md#TestEventListener) or -[`testing::EmptyTestEventListener`](reference/testing.md#EmptyTestEventListener) -The former is an (abstract) interface, where *each pure virtual method can be -overridden to handle a test event* (For example, when a test starts, the -`OnTestStart()` method will be called.). The latter provides an empty -implementation of all methods in the interface, such that a subclass only needs -to override the methods it cares about. - -When an event is fired, its context is passed to the handler function as an -argument. The following argument types are used: - -* UnitTest reflects the state of the entire test program, -* TestSuite has information about a test suite, which can contain one or more - tests, -* TestInfo contains the state of a test, and -* TestPartResult represents the result of a test assertion. - -An event handler function can examine the argument it receives to find out -interesting information about the event and the test program's state. - -Here's an example: - -```c++ - class MinimalistPrinter : public testing::EmptyTestEventListener { - // Called before a test starts. - void OnTestStart(const testing::TestInfo& test_info) override { - printf("*** Test %s.%s starting.\n", - test_info.test_suite_name(), test_info.name()); - } - - // Called after a failed assertion or a SUCCESS(). - void OnTestPartResult(const testing::TestPartResult& test_part_result) override { - printf("%s in %s:%d\n%s\n", - test_part_result.failed() ? "*** Failure" : "Success", - test_part_result.file_name(), - test_part_result.line_number(), - test_part_result.summary()); - } - - // Called after a test ends. - void OnTestEnd(const testing::TestInfo& test_info) override { - printf("*** Test %s.%s ending.\n", - test_info.test_suite_name(), test_info.name()); - } - }; -``` - -### Using Event Listeners - -To use the event listener you have defined, add an instance of it to the -GoogleTest event listener list (represented by class -[`TestEventListeners`](reference/testing.md#TestEventListeners) - note the "s" -at the end of the name) in your `main()` function, before calling -`RUN_ALL_TESTS()`: - -```c++ -int main(int argc, char** argv) { - testing::InitGoogleTest(&argc, argv); - // Gets hold of the event listener list. - testing::TestEventListeners& listeners = - testing::UnitTest::GetInstance()->listeners(); - // Adds a listener to the end. GoogleTest takes the ownership. - listeners.Append(new MinimalistPrinter); - return RUN_ALL_TESTS(); -} -``` - -There's only one problem: the default test result printer is still in effect, so -its output will mingle with the output from your minimalist printer. To suppress -the default printer, just release it from the event listener list and delete it. -You can do so by adding one line: - -```c++ - ... - delete listeners.Release(listeners.default_result_printer()); - listeners.Append(new MinimalistPrinter); - return RUN_ALL_TESTS(); -``` - -Now, sit back and enjoy a completely different output from your tests. For more -details, see [sample9_unittest.cc]. - -[sample9_unittest.cc]: https://github.com/google/googletest/blob/main/googletest/samples/sample9_unittest.cc "Event listener example" - -You may append more than one listener to the list. When an `On*Start()` or -`OnTestPartResult()` event is fired, the listeners will receive it in the order -they appear in the list (since new listeners are added to the end of the list, -the default text printer and the default XML generator will receive the event -first). An `On*End()` event will be received by the listeners in the *reverse* -order. This allows output by listeners added later to be framed by output from -listeners added earlier. - -### Generating Failures in Listeners - -You may use failure-raising macros (`EXPECT_*()`, `ASSERT_*()`, `FAIL()`, etc) -when processing an event. There are some restrictions: - -1. You cannot generate any failure in `OnTestPartResult()` (otherwise it will - cause `OnTestPartResult()` to be called recursively). -2. A listener that handles `OnTestPartResult()` is not allowed to generate any - failure. - -When you add listeners to the listener list, you should put listeners that -handle `OnTestPartResult()` *before* listeners that can generate failures. This -ensures that failures generated by the latter are attributed to the right test -by the former. - -See [sample10_unittest.cc] for an example of a failure-raising listener. - -[sample10_unittest.cc]: https://github.com/google/googletest/blob/main/googletest/samples/sample10_unittest.cc "Failure-raising listener example" - -## Running Test Programs: Advanced Options - -GoogleTest test programs are ordinary executables. Once built, you can run them -directly and affect their behavior via the following environment variables -and/or command line flags. For the flags to work, your programs must call -`::testing::InitGoogleTest()` before calling `RUN_ALL_TESTS()`. - -To see a list of supported flags and their usage, please run your test program -with the `--help` flag. - -If an option is specified both by an environment variable and by a flag, the -latter takes precedence. - -### Selecting Tests - -#### Listing Test Names - -Sometimes it is necessary to list the available tests in a program before -running them so that a filter may be applied if needed. Including the flag -`--gtest_list_tests` overrides all other flags and lists tests in the following -format: - -```none -TestSuite1. - TestName1 - TestName2 -TestSuite2. - TestName -``` - -None of the tests listed are actually run if the flag is provided. There is no -corresponding environment variable for this flag. - -#### Running a Subset of the Tests - -By default, a GoogleTest program runs all tests the user has defined. Sometimes, -you want to run only a subset of the tests (e.g. for debugging or quickly -verifying a change). If you set the `GTEST_FILTER` environment variable or the -`--gtest_filter` flag to a filter string, GoogleTest will only run the tests -whose full names (in the form of `TestSuiteName.TestName`) match the filter. - -The format of a filter is a '`:`'-separated list of wildcard patterns (called -the *positive patterns*) optionally followed by a '`-`' and another -'`:`'-separated pattern list (called the *negative patterns*). A test matches -the filter if and only if it matches any of the positive patterns but does not -match any of the negative patterns. - -A pattern may contain `'*'` (matches any string) or `'?'` (matches any single -character). For convenience, the filter `'*-NegativePatterns'` can be also -written as `'-NegativePatterns'`. - -For example: - -* `./foo_test` Has no flag, and thus runs all its tests. -* `./foo_test --gtest_filter=*` Also runs everything, due to the single - match-everything `*` value. -* `./foo_test --gtest_filter=FooTest.*` Runs everything in test suite - `FooTest` . -* `./foo_test --gtest_filter=*Null*:*Constructor*` Runs any test whose full - name contains either `"Null"` or `"Constructor"` . -* `./foo_test --gtest_filter=-*DeathTest.*` Runs all non-death tests. -* `./foo_test --gtest_filter=FooTest.*-FooTest.Bar` Runs everything in test - suite `FooTest` except `FooTest.Bar`. -* `./foo_test --gtest_filter=FooTest.*:BarTest.*-FooTest.Bar:BarTest.Foo` Runs - everything in test suite `FooTest` except `FooTest.Bar` and everything in - test suite `BarTest` except `BarTest.Foo`. - -#### Stop test execution upon first failure - -By default, a GoogleTest program runs all tests the user has defined. In some -cases (e.g. iterative test development & execution) it may be desirable stop -test execution upon first failure (trading improved latency for completeness). -If `GTEST_FAIL_FAST` environment variable or `--gtest_fail_fast` flag is set, -the test runner will stop execution as soon as the first test failure is found. - -#### Temporarily Disabling Tests - -If you have a broken test that you cannot fix right away, you can add the -`DISABLED_` prefix to its name. This will exclude it from execution. This is -better than commenting out the code or using `#if 0`, as disabled tests are -still compiled (and thus won't rot). - -If you need to disable all tests in a test suite, you can either add `DISABLED_` -to the front of the name of each test, or alternatively add it to the front of -the test suite name. - -For example, the following tests won't be run by GoogleTest, even though they -will still be compiled: - -```c++ -// Tests that Foo does Abc. -TEST(FooTest, DISABLED_DoesAbc) { ... } - -class DISABLED_BarTest : public testing::Test { ... }; - -// Tests that Bar does Xyz. -TEST_F(DISABLED_BarTest, DoesXyz) { ... } -``` - -{: .callout .note} -NOTE: This feature should only be used for temporary pain-relief. You still have -to fix the disabled tests at a later date. As a reminder, GoogleTest will print -a banner warning you if a test program contains any disabled tests. - -{: .callout .tip} -TIP: You can easily count the number of disabled tests you have using -`grep`. This number can be used as a metric for -improving your test quality. - -#### Temporarily Enabling Disabled Tests - -To include disabled tests in test execution, just invoke the test program with -the `--gtest_also_run_disabled_tests` flag or set the -`GTEST_ALSO_RUN_DISABLED_TESTS` environment variable to a value other than `0`. -You can combine this with the `--gtest_filter` flag to further select which -disabled tests to run. - -### Repeating the Tests - -Once in a while you'll run into a test whose result is hit-or-miss. Perhaps it -will fail only 1% of the time, making it rather hard to reproduce the bug under -a debugger. This can be a major source of frustration. - -The `--gtest_repeat` flag allows you to repeat all (or selected) test methods in -a program many times. Hopefully, a flaky test will eventually fail and give you -a chance to debug. Here's how to use it: - -```none -$ foo_test --gtest_repeat=1000 -Repeat foo_test 1000 times and don't stop at failures. - -$ foo_test --gtest_repeat=-1 -A negative count means repeating forever. - -$ foo_test --gtest_repeat=1000 --gtest_break_on_failure -Repeat foo_test 1000 times, stopping at the first failure. This -is especially useful when running under a debugger: when the test -fails, it will drop into the debugger and you can then inspect -variables and stacks. - -$ foo_test --gtest_repeat=1000 --gtest_filter=FooBar.* -Repeat the tests whose name matches the filter 1000 times. -``` - -If your test program contains -[global set-up/tear-down](#global-set-up-and-tear-down) code, it will be -repeated in each iteration as well, as the flakiness may be in it. To avoid -repeating global set-up/tear-down, specify -`--gtest_recreate_environments_when_repeating=false`{.nowrap}. - -You can also specify the repeat count by setting the `GTEST_REPEAT` environment -variable. - -### Shuffling the Tests - -You can specify the `--gtest_shuffle` flag (or set the `GTEST_SHUFFLE` -environment variable to `1`) to run the tests in a program in a random order. -This helps to reveal bad dependencies between tests. - -By default, GoogleTest uses a random seed calculated from the current time. -Therefore you'll get a different order every time. The console output includes -the random seed value, such that you can reproduce an order-related test failure -later. To specify the random seed explicitly, use the `--gtest_random_seed=SEED` -flag (or set the `GTEST_RANDOM_SEED` environment variable), where `SEED` is an -integer in the range [0, 99999]. The seed value 0 is special: it tells -GoogleTest to do the default behavior of calculating the seed from the current -time. - -If you combine this with `--gtest_repeat=N`, GoogleTest will pick a different -random seed and re-shuffle the tests in each iteration. - -### Distributing Test Functions to Multiple Machines - -If you have more than one machine you can use to run a test program, you might -want to run the test functions in parallel and get the result faster. We call -this technique *sharding*, where each machine is called a *shard*. - -GoogleTest is compatible with test sharding. To take advantage of this feature, -your test runner (not part of GoogleTest) needs to do the following: - -1. Allocate a number of machines (shards) to run the tests. -1. On each shard, set the `GTEST_TOTAL_SHARDS` environment variable to the total - number of shards. It must be the same for all shards. -1. On each shard, set the `GTEST_SHARD_INDEX` environment variable to the index - of the shard. Different shards must be assigned different indices, which - must be in the range `[0, GTEST_TOTAL_SHARDS - 1]`. -1. Run the same test program on all shards. When GoogleTest sees the above two - environment variables, it will select a subset of the test functions to run. - Across all shards, each test function in the program will be run exactly - once. -1. Wait for all shards to finish, then collect and report the results. - -Your project may have tests that were written without GoogleTest and thus don't -understand this protocol. In order for your test runner to figure out which test -supports sharding, it can set the environment variable `GTEST_SHARD_STATUS_FILE` -to a non-existent file path. If a test program supports sharding, it will create -this file to acknowledge that fact; otherwise it will not create it. The actual -contents of the file are not important at this time, although we may put some -useful information in it in the future. - -Here's an example to make it clear. Suppose you have a test program `foo_test` -that contains the following 5 test functions: - -``` -TEST(A, V) -TEST(A, W) -TEST(B, X) -TEST(B, Y) -TEST(B, Z) -``` - -Suppose you have 3 machines at your disposal. To run the test functions in -parallel, you would set `GTEST_TOTAL_SHARDS` to 3 on all machines, and set -`GTEST_SHARD_INDEX` to 0, 1, and 2 on the machines respectively. Then you would -run the same `foo_test` on each machine. - -GoogleTest reserves the right to change how the work is distributed across the -shards, but here's one possible scenario: - -* Machine #0 runs `A.V` and `B.X`. -* Machine #1 runs `A.W` and `B.Y`. -* Machine #2 runs `B.Z`. - -### Controlling Test Output - -#### Colored Terminal Output - -GoogleTest can use colors in its terminal output to make it easier to spot the -important information: - -
...
-[----------] 1 test from FooTest
-[ RUN      ] FooTest.DoesAbc
-[       OK ] FooTest.DoesAbc
-[----------] 2 tests from BarTest
-[ RUN      ] BarTest.HasXyzProperty
-[       OK ] BarTest.HasXyzProperty
-[ RUN      ] BarTest.ReturnsTrueOnSuccess
-... some error messages ...
-[   FAILED ] BarTest.ReturnsTrueOnSuccess
-...
-[==========] 30 tests from 14 test suites ran.
-[   PASSED ] 28 tests.
-[   FAILED ] 2 tests, listed below:
-[   FAILED ] BarTest.ReturnsTrueOnSuccess
-[   FAILED ] AnotherTest.DoesXyz
-
- 2 FAILED TESTS
-
- -You can set the `GTEST_COLOR` environment variable or the `--gtest_color` -command line flag to `yes`, `no`, or `auto` (the default) to enable colors, -disable colors, or let GoogleTest decide. When the value is `auto`, GoogleTest -will use colors if and only if the output goes to a terminal and (on non-Windows -platforms) the `TERM` environment variable is set to `xterm` or `xterm-color`. - -#### Suppressing test passes - -By default, GoogleTest prints 1 line of output for each test, indicating if it -passed or failed. To show only test failures, run the test program with -`--gtest_brief=1`, or set the GTEST_BRIEF environment variable to `1`. - -#### Suppressing the Elapsed Time - -By default, GoogleTest prints the time it takes to run each test. To disable -that, run the test program with the `--gtest_print_time=0` command line flag, or -set the GTEST_PRINT_TIME environment variable to `0`. - -#### Suppressing UTF-8 Text Output - -In case of assertion failures, GoogleTest prints expected and actual values of -type `string` both as hex-encoded strings as well as in readable UTF-8 text if -they contain valid non-ASCII UTF-8 characters. If you want to suppress the UTF-8 -text because, for example, you don't have an UTF-8 compatible output medium, run -the test program with `--gtest_print_utf8=0` or set the `GTEST_PRINT_UTF8` -environment variable to `0`. - -#### Generating an XML Report - -GoogleTest can emit a detailed XML report to a file in addition to its normal -textual output. The report contains the duration of each test, and thus can help -you identify slow tests. - -To generate the XML report, set the `GTEST_OUTPUT` environment variable or the -`--gtest_output` flag to the string `"xml:path_to_output_file"`, which will -create the file at the given location. You can also just use the string `"xml"`, -in which case the output can be found in the `test_detail.xml` file in the -current directory. - -If you specify a directory (for example, `"xml:output/directory/"` on Linux or -`"xml:output\directory\"` on Windows), GoogleTest will create the XML file in -that directory, named after the test executable (e.g. `foo_test.xml` for test -program `foo_test` or `foo_test.exe`). If the file already exists (perhaps left -over from a previous run), GoogleTest will pick a different name (e.g. -`foo_test_1.xml`) to avoid overwriting it. - -The report is based on the `junitreport` Ant task. Since that format was -originally intended for Java, a little interpretation is required to make it -apply to GoogleTest tests, as shown here: - -```xml - - - - - - - - - -``` - -* The root `` element corresponds to the entire test program. -* `` elements correspond to GoogleTest test suites. -* `` elements correspond to GoogleTest test functions. - -For instance, the following program - -```c++ -TEST(MathTest, Addition) { ... } -TEST(MathTest, Subtraction) { ... } -TEST(LogicTest, NonContradiction) { ... } -``` - -could generate this report: - -```xml - - - - - ... - ... - - - - - - - - - -``` - -Things to note: - -* The `tests` attribute of a `` or `` element tells how - many test functions the GoogleTest program or test suite contains, while the - `failures` attribute tells how many of them failed. - -* The `time` attribute expresses the duration of the test, test suite, or - entire test program in seconds. - -* The `timestamp` attribute records the local date and time of the test - execution. - -* The `file` and `line` attributes record the source file location, where the - test was defined. - -* Each `` element corresponds to a single failed GoogleTest - assertion. - -#### Generating a JSON Report - -GoogleTest can also emit a JSON report as an alternative format to XML. To -generate the JSON report, set the `GTEST_OUTPUT` environment variable or the -`--gtest_output` flag to the string `"json:path_to_output_file"`, which will -create the file at the given location. You can also just use the string -`"json"`, in which case the output can be found in the `test_detail.json` file -in the current directory. - -The report format conforms to the following JSON Schema: - -```json -{ - "$schema": "https://json-schema.org/schema#", - "type": "object", - "definitions": { - "TestCase": { - "type": "object", - "properties": { - "name": { "type": "string" }, - "tests": { "type": "integer" }, - "failures": { "type": "integer" }, - "disabled": { "type": "integer" }, - "time": { "type": "string" }, - "testsuite": { - "type": "array", - "items": { - "$ref": "#/definitions/TestInfo" - } - } - } - }, - "TestInfo": { - "type": "object", - "properties": { - "name": { "type": "string" }, - "file": { "type": "string" }, - "line": { "type": "integer" }, - "status": { - "type": "string", - "enum": ["RUN", "NOTRUN"] - }, - "time": { "type": "string" }, - "classname": { "type": "string" }, - "failures": { - "type": "array", - "items": { - "$ref": "#/definitions/Failure" - } - } - } - }, - "Failure": { - "type": "object", - "properties": { - "failures": { "type": "string" }, - "type": { "type": "string" } - } - } - }, - "properties": { - "tests": { "type": "integer" }, - "failures": { "type": "integer" }, - "disabled": { "type": "integer" }, - "errors": { "type": "integer" }, - "timestamp": { - "type": "string", - "format": "date-time" - }, - "time": { "type": "string" }, - "name": { "type": "string" }, - "testsuites": { - "type": "array", - "items": { - "$ref": "#/definitions/TestCase" - } - } - } -} -``` - -The report uses the format that conforms to the following Proto3 using the -[JSON encoding](https://developers.google.com/protocol-buffers/docs/proto3#json): - -```proto -syntax = "proto3"; - -package googletest; - -import "google/protobuf/timestamp.proto"; -import "google/protobuf/duration.proto"; - -message UnitTest { - int32 tests = 1; - int32 failures = 2; - int32 disabled = 3; - int32 errors = 4; - google.protobuf.Timestamp timestamp = 5; - google.protobuf.Duration time = 6; - string name = 7; - repeated TestCase testsuites = 8; -} - -message TestCase { - string name = 1; - int32 tests = 2; - int32 failures = 3; - int32 disabled = 4; - int32 errors = 5; - google.protobuf.Duration time = 6; - repeated TestInfo testsuite = 7; -} - -message TestInfo { - string name = 1; - string file = 6; - int32 line = 7; - enum Status { - RUN = 0; - NOTRUN = 1; - } - Status status = 2; - google.protobuf.Duration time = 3; - string classname = 4; - message Failure { - string failures = 1; - string type = 2; - } - repeated Failure failures = 5; -} -``` - -For instance, the following program - -```c++ -TEST(MathTest, Addition) { ... } -TEST(MathTest, Subtraction) { ... } -TEST(LogicTest, NonContradiction) { ... } -``` - -could generate this report: - -```json -{ - "tests": 3, - "failures": 1, - "errors": 0, - "time": "0.035s", - "timestamp": "2011-10-31T18:52:42Z", - "name": "AllTests", - "testsuites": [ - { - "name": "MathTest", - "tests": 2, - "failures": 1, - "errors": 0, - "time": "0.015s", - "testsuite": [ - { - "name": "Addition", - "file": "test.cpp", - "line": 1, - "status": "RUN", - "time": "0.007s", - "classname": "", - "failures": [ - { - "message": "Value of: add(1, 1)\n Actual: 3\nExpected: 2", - "type": "" - }, - { - "message": "Value of: add(1, -1)\n Actual: 1\nExpected: 0", - "type": "" - } - ] - }, - { - "name": "Subtraction", - "file": "test.cpp", - "line": 2, - "status": "RUN", - "time": "0.005s", - "classname": "" - } - ] - }, - { - "name": "LogicTest", - "tests": 1, - "failures": 0, - "errors": 0, - "time": "0.005s", - "testsuite": [ - { - "name": "NonContradiction", - "file": "test.cpp", - "line": 3, - "status": "RUN", - "time": "0.005s", - "classname": "" - } - ] - } - ] -} -``` - -{: .callout .important} -IMPORTANT: The exact format of the JSON document is subject to change. - -### Controlling How Failures Are Reported - -#### Detecting Test Premature Exit - -Google Test implements the _premature-exit-file_ protocol for test runners to -catch any kind of unexpected exits of test programs. Upon start, Google Test -creates the file which will be automatically deleted after all work has been -finished. Then, the test runner can check if this file exists. In case the file -remains undeleted, the inspected test has exited prematurely. - -This feature is enabled only if the `TEST_PREMATURE_EXIT_FILE` environment -variable has been set. - -#### Turning Assertion Failures into Break-Points - -When running test programs under a debugger, it's very convenient if the -debugger can catch an assertion failure and automatically drop into interactive -mode. GoogleTest's *break-on-failure* mode supports this behavior. - -To enable it, set the `GTEST_BREAK_ON_FAILURE` environment variable to a value -other than `0`. Alternatively, you can use the `--gtest_break_on_failure` -command line flag. - -#### Disabling Catching Test-Thrown Exceptions - -GoogleTest can be used either with or without exceptions enabled. If a test -throws a C++ exception or (on Windows) a structured exception (SEH), by default -GoogleTest catches it, reports it as a test failure, and continues with the next -test method. This maximizes the coverage of a test run. Also, on Windows an -uncaught exception will cause a pop-up window, so catching the exceptions allows -you to run the tests automatically. - -When debugging the test failures, however, you may instead want the exceptions -to be handled by the debugger, such that you can examine the call stack when an -exception is thrown. To achieve that, set the `GTEST_CATCH_EXCEPTIONS` -environment variable to `0`, or use the `--gtest_catch_exceptions=0` flag when -running the tests. - -### Sanitizer Integration - -The -[Undefined Behavior Sanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html), -[Address Sanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer), -and -[Thread Sanitizer](https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual) -all provide weak functions that you can override to trigger explicit failures -when they detect sanitizer errors, such as creating a reference from `nullptr`. -To override these functions, place definitions for them in a source file that -you compile as part of your main binary: - -``` -extern "C" { -void __ubsan_on_report() { - FAIL() << "Encountered an undefined behavior sanitizer error"; -} -void __asan_on_error() { - FAIL() << "Encountered an address sanitizer error"; -} -void __tsan_on_report() { - FAIL() << "Encountered a thread sanitizer error"; -} -} // extern "C" -``` - -After compiling your project with one of the sanitizers enabled, if a particular -test triggers a sanitizer error, GoogleTest will report that it failed. diff --git a/test/unit/googletest/docs/assets/css/style.scss b/test/unit/googletest/docs/assets/css/style.scss deleted file mode 100644 index bb30f41..0000000 --- a/test/unit/googletest/docs/assets/css/style.scss +++ /dev/null @@ -1,5 +0,0 @@ ---- ---- - -@import "jekyll-theme-primer"; -@import "main"; diff --git a/test/unit/googletest/docs/community_created_documentation.md b/test/unit/googletest/docs/community_created_documentation.md deleted file mode 100644 index 4569075..0000000 --- a/test/unit/googletest/docs/community_created_documentation.md +++ /dev/null @@ -1,7 +0,0 @@ -# Community-Created Documentation - -The following is a list, in no particular order, of links to documentation -created by the Googletest community. - -* [Googlemock Insights](https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles/blob/master/googletest/insights.md), - by [ElectricRCAircraftGuy](https://github.com/ElectricRCAircraftGuy) diff --git a/test/unit/googletest/docs/faq.md b/test/unit/googletest/docs/faq.md deleted file mode 100644 index c7d10b5..0000000 --- a/test/unit/googletest/docs/faq.md +++ /dev/null @@ -1,671 +0,0 @@ -# GoogleTest FAQ - -## Why should test suite names and test names not contain underscore? - -{: .callout .note} -Note: GoogleTest reserves underscore (`_`) for special-purpose keywords, such as -[the `DISABLED_` prefix](advanced.md#temporarily-disabling-tests), in addition -to the following rationale. - -Underscore (`_`) is special, as C++ reserves the following to be used by the -compiler and the standard library: - -1. any identifier that starts with an `_` followed by an upper-case letter, and -2. any identifier that contains two consecutive underscores (i.e. `__`) - *anywhere* in its name. - -User code is *prohibited* from using such identifiers. - -Now let's look at what this means for `TEST` and `TEST_F`. - -Currently `TEST(TestSuiteName, TestName)` generates a class named -`TestSuiteName_TestName_Test`. What happens if `TestSuiteName` or `TestName` -contains `_`? - -1. If `TestSuiteName` starts with an `_` followed by an upper-case letter (say, - `_Foo`), we end up with `_Foo_TestName_Test`, which is reserved and thus - invalid. -2. If `TestSuiteName` ends with an `_` (say, `Foo_`), we get - `Foo__TestName_Test`, which is invalid. -3. If `TestName` starts with an `_` (say, `_Bar`), we get - `TestSuiteName__Bar_Test`, which is invalid. -4. If `TestName` ends with an `_` (say, `Bar_`), we get - `TestSuiteName_Bar__Test`, which is invalid. - -So clearly `TestSuiteName` and `TestName` cannot start or end with `_` -(Actually, `TestSuiteName` can start with `_`—as long as the `_` isn't followed -by an upper-case letter. But that's getting complicated. So for simplicity we -just say that it cannot start with `_`.). - -It may seem fine for `TestSuiteName` and `TestName` to contain `_` in the -middle. However, consider this: - -```c++ -TEST(Time, Flies_Like_An_Arrow) { ... } -TEST(Time_Flies, Like_An_Arrow) { ... } -``` - -Now, the two `TEST`s will both generate the same class -(`Time_Flies_Like_An_Arrow_Test`). That's not good. - -So for simplicity, we just ask the users to avoid `_` in `TestSuiteName` and -`TestName`. The rule is more constraining than necessary, but it's simple and -easy to remember. It also gives GoogleTest some wiggle room in case its -implementation needs to change in the future. - -If you violate the rule, there may not be immediate consequences, but your test -may (just may) break with a new compiler (or a new version of the compiler you -are using) or with a new version of GoogleTest. Therefore it's best to follow -the rule. - -## Why does GoogleTest support `EXPECT_EQ(NULL, ptr)` and `ASSERT_EQ(NULL, ptr)` but not `EXPECT_NE(NULL, ptr)` and `ASSERT_NE(NULL, ptr)`? - -First of all, you can use `nullptr` with each of these macros, e.g. -`EXPECT_EQ(ptr, nullptr)`, `EXPECT_NE(ptr, nullptr)`, `ASSERT_EQ(ptr, nullptr)`, -`ASSERT_NE(ptr, nullptr)`. This is the preferred syntax in the style guide -because `nullptr` does not have the type problems that `NULL` does. - -Due to some peculiarity of C++, it requires some non-trivial template meta -programming tricks to support using `NULL` as an argument of the `EXPECT_XX()` -and `ASSERT_XX()` macros. Therefore we only do it where it's most needed -(otherwise we make the implementation of GoogleTest harder to maintain and more -error-prone than necessary). - -Historically, the `EXPECT_EQ()` macro took the *expected* value as its first -argument and the *actual* value as the second, though this argument order is now -discouraged. It was reasonable that someone wanted -to write `EXPECT_EQ(NULL, some_expression)`, and this indeed was requested -several times. Therefore we implemented it. - -The need for `EXPECT_NE(NULL, ptr)` wasn't nearly as strong. When the assertion -fails, you already know that `ptr` must be `NULL`, so it doesn't add any -information to print `ptr` in this case. That means `EXPECT_TRUE(ptr != NULL)` -works just as well. - -If we were to support `EXPECT_NE(NULL, ptr)`, for consistency we'd have to -support `EXPECT_NE(ptr, NULL)` as well. This means using the template meta -programming tricks twice in the implementation, making it even harder to -understand and maintain. We believe the benefit doesn't justify the cost. - -Finally, with the growth of the gMock matcher library, we are encouraging people -to use the unified `EXPECT_THAT(value, matcher)` syntax more often in tests. One -significant advantage of the matcher approach is that matchers can be easily -combined to form new matchers, while the `EXPECT_NE`, etc, macros cannot be -easily combined. Therefore we want to invest more in the matchers than in the -`EXPECT_XX()` macros. - -## I need to test that different implementations of an interface satisfy some common requirements. Should I use typed tests or value-parameterized tests? - -For testing various implementations of the same interface, either typed tests or -value-parameterized tests can get it done. It's really up to you the user to -decide which is more convenient for you, depending on your particular case. Some -rough guidelines: - -* Typed tests can be easier to write if instances of the different - implementations can be created the same way, modulo the type. For example, - if all these implementations have a public default constructor (such that - you can write `new TypeParam`), or if their factory functions have the same - form (e.g. `CreateInstance()`). -* Value-parameterized tests can be easier to write if you need different code - patterns to create different implementations' instances, e.g. `new Foo` vs - `new Bar(5)`. To accommodate for the differences, you can write factory - function wrappers and pass these function pointers to the tests as their - parameters. -* When a typed test fails, the default output includes the name of the type, - which can help you quickly identify which implementation is wrong. - Value-parameterized tests only show the number of the failed iteration by - default. You will need to define a function that returns the iteration name - and pass it as the third parameter to INSTANTIATE_TEST_SUITE_P to have more - useful output. -* When using typed tests, you need to make sure you are testing against the - interface type, not the concrete types (in other words, you want to make - sure `implicit_cast(my_concrete_impl)` works, not just that - `my_concrete_impl` works). It's less likely to make mistakes in this area - when using value-parameterized tests. - -I hope I didn't confuse you more. :-) If you don't mind, I'd suggest you to give -both approaches a try. Practice is a much better way to grasp the subtle -differences between the two tools. Once you have some concrete experience, you -can much more easily decide which one to use the next time. - -## My death test modifies some state, but the change seems lost after the death test finishes. Why? - -Death tests (`EXPECT_DEATH`, etc.) are executed in a sub-process s.t. the -expected crash won't kill the test program (i.e. the parent process). As a -result, any in-memory side effects they incur are observable in their respective -sub-processes, but not in the parent process. You can think of them as running -in a parallel universe, more or less. - -In particular, if you use mocking and the death test statement invokes some mock -methods, the parent process will think the calls have never occurred. Therefore, -you may want to move your `EXPECT_CALL` statements inside the `EXPECT_DEATH` -macro. - -## EXPECT_EQ(htonl(blah), blah_blah) generates weird compiler errors in opt mode. Is this a GoogleTest bug? - -Actually, the bug is in `htonl()`. - -According to `'man htonl'`, `htonl()` is a *function*, which means it's valid to -use `htonl` as a function pointer. However, in opt mode `htonl()` is defined as -a *macro*, which breaks this usage. - -Worse, the macro definition of `htonl()` uses a `gcc` extension and is *not* -standard C++. That hacky implementation has some ad hoc limitations. In -particular, it prevents you from writing `Foo()`, where `Foo` -is a template that has an integral argument. - -The implementation of `EXPECT_EQ(a, b)` uses `sizeof(... a ...)` inside a -template argument, and thus doesn't compile in opt mode when `a` contains a call -to `htonl()`. It is difficult to make `EXPECT_EQ` bypass the `htonl()` bug, as -the solution must work with different compilers on various platforms. - -## The compiler complains about "undefined references" to some static const member variables, but I did define them in the class body. What's wrong? - -If your class has a static data member: - -```c++ -// foo.h -class Foo { - ... - static const int kBar = 100; -}; -``` - -you also need to define it *outside* of the class body in `foo.cc`: - -```c++ -const int Foo::kBar; // No initializer here. -``` - -Otherwise your code is **invalid C++**, and may break in unexpected ways. In -particular, using it in GoogleTest comparison assertions (`EXPECT_EQ`, etc.) -will generate an "undefined reference" linker error. The fact that "it used to -work" doesn't mean it's valid. It just means that you were lucky. :-) - -If the declaration of the static data member is `constexpr` then it is -implicitly an `inline` definition, and a separate definition in `foo.cc` is not -needed: - -```c++ -// foo.h -class Foo { - ... - static constexpr int kBar = 100; // Defines kBar, no need to do it in foo.cc. -}; -``` - -## Can I derive a test fixture from another? - -Yes. - -Each test fixture has a corresponding and same named test suite. This means only -one test suite can use a particular fixture. Sometimes, however, multiple test -cases may want to use the same or slightly different fixtures. For example, you -may want to make sure that all of a GUI library's test suites don't leak -important system resources like fonts and brushes. - -In GoogleTest, you share a fixture among test suites by putting the shared logic -in a base test fixture, then deriving from that base a separate fixture for each -test suite that wants to use this common logic. You then use `TEST_F()` to write -tests using each derived fixture. - -Typically, your code looks like this: - -```c++ -// Defines a base test fixture. -class BaseTest : public ::testing::Test { - protected: - ... -}; - -// Derives a fixture FooTest from BaseTest. -class FooTest : public BaseTest { - protected: - void SetUp() override { - BaseTest::SetUp(); // Sets up the base fixture first. - ... additional set-up work ... - } - - void TearDown() override { - ... clean-up work for FooTest ... - BaseTest::TearDown(); // Remember to tear down the base fixture - // after cleaning up FooTest! - } - - ... functions and variables for FooTest ... -}; - -// Tests that use the fixture FooTest. -TEST_F(FooTest, Bar) { ... } -TEST_F(FooTest, Baz) { ... } - -... additional fixtures derived from BaseTest ... -``` - -If necessary, you can continue to derive test fixtures from a derived fixture. -GoogleTest has no limit on how deep the hierarchy can be. - -For a complete example using derived test fixtures, see -[sample5_unittest.cc](https://github.com/google/googletest/blob/main/googletest/samples/sample5_unittest.cc). - -## My compiler complains "void value not ignored as it ought to be." What does this mean? - -You're probably using an `ASSERT_*()` in a function that doesn't return `void`. -`ASSERT_*()` can only be used in `void` functions, due to exceptions being -disabled by our build system. Please see more details -[here](advanced.md#assertion-placement). - -## My death test hangs (or seg-faults). How do I fix it? - -In GoogleTest, death tests are run in a child process and the way they work is -delicate. To write death tests you really need to understand how they work—see -the details at [Death Assertions](reference/assertions.md#death) in the -Assertions Reference. - -In particular, death tests don't like having multiple threads in the parent -process. So the first thing you can try is to eliminate creating threads outside -of `EXPECT_DEATH()`. For example, you may want to use mocks or fake objects -instead of real ones in your tests. - -Sometimes this is impossible as some library you must use may be creating -threads before `main()` is even reached. In this case, you can try to minimize -the chance of conflicts by either moving as many activities as possible inside -`EXPECT_DEATH()` (in the extreme case, you want to move everything inside), or -leaving as few things as possible in it. Also, you can try to set the death test -style to `"threadsafe"`, which is safer but slower, and see if it helps. - -If you go with thread-safe death tests, remember that they rerun the test -program from the beginning in the child process. Therefore make sure your -program can run side-by-side with itself and is deterministic. - -In the end, this boils down to good concurrent programming. You have to make -sure that there are no race conditions or deadlocks in your program. No silver -bullet - sorry! - -## Should I use the constructor/destructor of the test fixture or SetUp()/TearDown()? {#CtorVsSetUp} - -The first thing to remember is that GoogleTest does **not** reuse the same test -fixture object across multiple tests. For each `TEST_F`, GoogleTest will create -a **fresh** test fixture object, immediately call `SetUp()`, run the test body, -call `TearDown()`, and then delete the test fixture object. - -When you need to write per-test set-up and tear-down logic, you have the choice -between using the test fixture constructor/destructor or `SetUp()`/`TearDown()`. -The former is usually preferred, as it has the following benefits: - -* By initializing a member variable in the constructor, we have the option to - make it `const`, which helps prevent accidental changes to its value and - makes the tests more obviously correct. -* In case we need to subclass the test fixture class, the subclass' - constructor is guaranteed to call the base class' constructor *first*, and - the subclass' destructor is guaranteed to call the base class' destructor - *afterward*. With `SetUp()/TearDown()`, a subclass may make the mistake of - forgetting to call the base class' `SetUp()/TearDown()` or call them at the - wrong time. - -You may still want to use `SetUp()/TearDown()` in the following cases: - -* C++ does not allow virtual function calls in constructors and destructors. - You can call a method declared as virtual, but it will not use dynamic - dispatch. It will use the definition from the class the constructor of which - is currently executing. This is because calling a virtual method before the - derived class constructor has a chance to run is very dangerous - the - virtual method might operate on uninitialized data. Therefore, if you need - to call a method that will be overridden in a derived class, you have to use - `SetUp()/TearDown()`. -* In the body of a constructor (or destructor), it's not possible to use the - `ASSERT_xx` macros. Therefore, if the set-up operation could cause a fatal - test failure that should prevent the test from running, it's necessary to - use `abort` and abort the whole test - executable, or to use `SetUp()` instead of a constructor. -* If the tear-down operation could throw an exception, you must use - `TearDown()` as opposed to the destructor, as throwing in a destructor leads - to undefined behavior and usually will kill your program right away. Note - that many standard libraries (like STL) may throw when exceptions are - enabled in the compiler. Therefore you should prefer `TearDown()` if you - want to write portable tests that work with or without exceptions. -* The GoogleTest team is considering making the assertion macros throw on - platforms where exceptions are enabled (e.g. Windows, Mac OS, and Linux - client-side), which will eliminate the need for the user to propagate - failures from a subroutine to its caller. Therefore, you shouldn't use - GoogleTest assertions in a destructor if your code could run on such a - platform. - -## The compiler complains "no matching function to call" when I use `ASSERT_PRED*`. How do I fix it? - -See details for [`EXPECT_PRED*`](reference/assertions.md#EXPECT_PRED) in the -Assertions Reference. - -## My compiler complains about "ignoring return value" when I call RUN_ALL_TESTS(). Why? - -Some people had been ignoring the return value of `RUN_ALL_TESTS()`. That is, -instead of - -```c++ - return RUN_ALL_TESTS(); -``` - -they write - -```c++ - RUN_ALL_TESTS(); -``` - -This is **wrong and dangerous**. The testing services needs to see the return -value of `RUN_ALL_TESTS()` in order to determine if a test has passed. If your -`main()` function ignores it, your test will be considered successful even if it -has a GoogleTest assertion failure. Very bad. - -We have decided to fix this (thanks to Michael Chastain for the idea). Now, your -code will no longer be able to ignore `RUN_ALL_TESTS()` when compiled with -`gcc`. If you do so, you'll get a compiler error. - -If you see the compiler complaining about you ignoring the return value of -`RUN_ALL_TESTS()`, the fix is simple: just make sure its value is used as the -return value of `main()`. - -But how could we introduce a change that breaks existing tests? Well, in this -case, the code was already broken in the first place, so we didn't break it. :-) - -## My compiler complains that a constructor (or destructor) cannot return a value. What's going on? - -Due to a peculiarity of C++, in order to support the syntax for streaming -messages to an `ASSERT_*`, e.g. - -```c++ - ASSERT_EQ(1, Foo()) << "blah blah" << foo; -``` - -we had to give up using `ASSERT*` and `FAIL*` (but not `EXPECT*` and -`ADD_FAILURE*`) in constructors and destructors. The workaround is to move the -content of your constructor/destructor to a private void member function, or -switch to `EXPECT_*()` if that works. This -[section](advanced.md#assertion-placement) in the user's guide explains it. - -## My SetUp() function is not called. Why? - -C++ is case-sensitive. Did you spell it as `Setup()`? - -Similarly, sometimes people spell `SetUpTestSuite()` as `SetupTestSuite()` and -wonder why it's never called. - -## I have several test suites which share the same test fixture logic; do I have to define a new test fixture class for each of them? This seems pretty tedious. - -You don't have to. Instead of - -```c++ -class FooTest : public BaseTest {}; - -TEST_F(FooTest, Abc) { ... } -TEST_F(FooTest, Def) { ... } - -class BarTest : public BaseTest {}; - -TEST_F(BarTest, Abc) { ... } -TEST_F(BarTest, Def) { ... } -``` - -you can simply `typedef` the test fixtures: - -```c++ -typedef BaseTest FooTest; - -TEST_F(FooTest, Abc) { ... } -TEST_F(FooTest, Def) { ... } - -typedef BaseTest BarTest; - -TEST_F(BarTest, Abc) { ... } -TEST_F(BarTest, Def) { ... } -``` - -## GoogleTest output is buried in a whole bunch of LOG messages. What do I do? - -The GoogleTest output is meant to be a concise and human-friendly report. If -your test generates textual output itself, it will mix with the GoogleTest -output, making it hard to read. However, there is an easy solution to this -problem. - -Since `LOG` messages go to stderr, we decided to let GoogleTest output go to -stdout. This way, you can easily separate the two using redirection. For -example: - -```shell -$ ./my_test > gtest_output.txt -``` - -## Why should I prefer test fixtures over global variables? - -There are several good reasons: - -1. It's likely your test needs to change the states of its global variables. - This makes it difficult to keep side effects from escaping one test and - contaminating others, making debugging difficult. By using fixtures, each - test has a fresh set of variables that's different (but with the same - names). Thus, tests are kept independent of each other. -2. Global variables pollute the global namespace. -3. Test fixtures can be reused via subclassing, which cannot be done easily - with global variables. This is useful if many test suites have something in - common. - -## What can the statement argument in ASSERT_DEATH() be? - -`ASSERT_DEATH(statement, matcher)` (or any death assertion macro) can be used -wherever *`statement`* is valid. So basically *`statement`* can be any C++ -statement that makes sense in the current context. In particular, it can -reference global and/or local variables, and can be: - -* a simple function call (often the case), -* a complex expression, or -* a compound statement. - -Some examples are shown here: - -```c++ -// A death test can be a simple function call. -TEST(MyDeathTest, FunctionCall) { - ASSERT_DEATH(Xyz(5), "Xyz failed"); -} - -// Or a complex expression that references variables and functions. -TEST(MyDeathTest, ComplexExpression) { - const bool c = Condition(); - ASSERT_DEATH((c ? Func1(0) : object2.Method("test")), - "(Func1|Method) failed"); -} - -// Death assertions can be used anywhere in a function. In -// particular, they can be inside a loop. -TEST(MyDeathTest, InsideLoop) { - // Verifies that Foo(0), Foo(1), ..., and Foo(4) all die. - for (int i = 0; i < 5; i++) { - EXPECT_DEATH_M(Foo(i), "Foo has \\d+ errors", - ::testing::Message() << "where i is " << i); - } -} - -// A death assertion can contain a compound statement. -TEST(MyDeathTest, CompoundStatement) { - // Verifies that at lease one of Bar(0), Bar(1), ..., and - // Bar(4) dies. - ASSERT_DEATH({ - for (int i = 0; i < 5; i++) { - Bar(i); - } - }, - "Bar has \\d+ errors"); -} -``` - -## I have a fixture class `FooTest`, but `TEST_F(FooTest, Bar)` gives me error ``"no matching function for call to `FooTest::FooTest()'"``. Why? - -GoogleTest needs to be able to create objects of your test fixture class, so it -must have a default constructor. Normally the compiler will define one for you. -However, there are cases where you have to define your own: - -* If you explicitly declare a non-default constructor for class `FooTest` - (`DISALLOW_EVIL_CONSTRUCTORS()` does this), then you need to define a - default constructor, even if it would be empty. -* If `FooTest` has a const non-static data member, then you have to define the - default constructor *and* initialize the const member in the initializer - list of the constructor. (Early versions of `gcc` doesn't force you to - initialize the const member. It's a bug that has been fixed in `gcc 4`.) - -## Why does ASSERT_DEATH complain about previous threads that were already joined? - -With the Linux pthread library, there is no turning back once you cross the line -from a single thread to multiple threads. The first time you create a thread, a -manager thread is created in addition, so you get 3, not 2, threads. Later when -the thread you create joins the main thread, the thread count decrements by 1, -but the manager thread will never be killed, so you still have 2 threads, which -means you cannot safely run a death test. - -The new NPTL thread library doesn't suffer from this problem, as it doesn't -create a manager thread. However, if you don't control which machine your test -runs on, you shouldn't depend on this. - -## Why does GoogleTest require the entire test suite, instead of individual tests, to be named `*DeathTest` when it uses `ASSERT_DEATH`? - -GoogleTest does not interleave tests from different test suites. That is, it -runs all tests in one test suite first, and then runs all tests in the next test -suite, and so on. GoogleTest does this because it needs to set up a test suite -before the first test in it is run, and tear it down afterwards. Splitting up -the test case would require multiple set-up and tear-down processes, which is -inefficient and makes the semantics unclean. - -If we were to determine the order of tests based on test name instead of test -case name, then we would have a problem with the following situation: - -```c++ -TEST_F(FooTest, AbcDeathTest) { ... } -TEST_F(FooTest, Uvw) { ... } - -TEST_F(BarTest, DefDeathTest) { ... } -TEST_F(BarTest, Xyz) { ... } -``` - -Since `FooTest.AbcDeathTest` needs to run before `BarTest.Xyz`, and we don't -interleave tests from different test suites, we need to run all tests in the -`FooTest` case before running any test in the `BarTest` case. This contradicts -with the requirement to run `BarTest.DefDeathTest` before `FooTest.Uvw`. - -## But I don't like calling my entire test suite `*DeathTest` when it contains both death tests and non-death tests. What do I do? - -You don't have to, but if you like, you may split up the test suite into -`FooTest` and `FooDeathTest`, where the names make it clear that they are -related: - -```c++ -class FooTest : public ::testing::Test { ... }; - -TEST_F(FooTest, Abc) { ... } -TEST_F(FooTest, Def) { ... } - -using FooDeathTest = FooTest; - -TEST_F(FooDeathTest, Uvw) { ... EXPECT_DEATH(...) ... } -TEST_F(FooDeathTest, Xyz) { ... ASSERT_DEATH(...) ... } -``` - -## GoogleTest prints the LOG messages in a death test's child process only when the test fails. How can I see the LOG messages when the death test succeeds? - -Printing the LOG messages generated by the statement inside `EXPECT_DEATH()` -makes it harder to search for real problems in the parent's log. Therefore, -GoogleTest only prints them when the death test has failed. - -If you really need to see such LOG messages, a workaround is to temporarily -break the death test (e.g. by changing the regex pattern it is expected to -match). Admittedly, this is a hack. We'll consider a more permanent solution -after the fork-and-exec-style death tests are implemented. - -## The compiler complains about `no match for 'operator<<'` when I use an assertion. What gives? - -If you use a user-defined type `FooType` in an assertion, you must make sure -there is an `std::ostream& operator<<(std::ostream&, const FooType&)` function -defined such that we can print a value of `FooType`. - -In addition, if `FooType` is declared in a name space, the `<<` operator also -needs to be defined in the *same* name space. See -[Tip of the Week #49](https://abseil.io/tips/49) for details. - -## How do I suppress the memory leak messages on Windows? - -Since the statically initialized GoogleTest singleton requires allocations on -the heap, the Visual C++ memory leak detector will report memory leaks at the -end of the program run. The easiest way to avoid this is to use the -`_CrtMemCheckpoint` and `_CrtMemDumpAllObjectsSince` calls to not report any -statically initialized heap objects. See MSDN for more details and additional -heap check/debug routines. - -## How can my code detect if it is running in a test? - -If you write code that sniffs whether it's running in a test and does different -things accordingly, you are leaking test-only logic into production code and -there is no easy way to ensure that the test-only code paths aren't run by -mistake in production. Such cleverness also leads to -[Heisenbugs](https://en.wikipedia.org/wiki/Heisenbug). Therefore we strongly -advise against the practice, and GoogleTest doesn't provide a way to do it. - -In general, the recommended way to cause the code to behave differently under -test is [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection). You can inject -different functionality from the test and from the production code. Since your -production code doesn't link in the for-test logic at all (the -[`testonly`](https://docs.bazel.build/versions/master/be/common-definitions.html#common.testonly) attribute for BUILD targets helps to ensure -that), there is no danger in accidentally running it. - -However, if you *really*, *really*, *really* have no choice, and if you follow -the rule of ending your test program names with `_test`, you can use the -*horrible* hack of sniffing your executable name (`argv[0]` in `main()`) to know -whether the code is under test. - -## How do I temporarily disable a test? - -If you have a broken test that you cannot fix right away, you can add the -`DISABLED_` prefix to its name. This will exclude it from execution. This is -better than commenting out the code or using `#if 0`, as disabled tests are -still compiled (and thus won't rot). - -To include disabled tests in test execution, just invoke the test program with -the `--gtest_also_run_disabled_tests` flag. - -## Is it OK if I have two separate `TEST(Foo, Bar)` test methods defined in different namespaces? - -Yes. - -The rule is **all test methods in the same test suite must use the same fixture -class**. This means that the following is **allowed** because both tests use the -same fixture class (`::testing::Test`). - -```c++ -namespace foo { -TEST(CoolTest, DoSomething) { - SUCCEED(); -} -} // namespace foo - -namespace bar { -TEST(CoolTest, DoSomething) { - SUCCEED(); -} -} // namespace bar -``` - -However, the following code is **not allowed** and will produce a runtime error -from GoogleTest because the test methods are using different test fixture -classes with the same test suite name. - -```c++ -namespace foo { -class CoolTest : public ::testing::Test {}; // Fixture foo::CoolTest -TEST_F(CoolTest, DoSomething) { - SUCCEED(); -} -} // namespace foo - -namespace bar { -class CoolTest : public ::testing::Test {}; // Fixture: bar::CoolTest -TEST_F(CoolTest, DoSomething) { - SUCCEED(); -} -} // namespace bar -``` diff --git a/test/unit/googletest/docs/gmock_cheat_sheet.md b/test/unit/googletest/docs/gmock_cheat_sheet.md deleted file mode 100644 index ddafaaa..0000000 --- a/test/unit/googletest/docs/gmock_cheat_sheet.md +++ /dev/null @@ -1,241 +0,0 @@ -# gMock Cheat Sheet - -## Defining a Mock Class - -### Mocking a Normal Class {#MockClass} - -Given - -```cpp -class Foo { - public: - virtual ~Foo(); - virtual int GetSize() const = 0; - virtual string Describe(const char* name) = 0; - virtual string Describe(int type) = 0; - virtual bool Process(Bar elem, int count) = 0; -}; -``` - -(note that `~Foo()` **must** be virtual) we can define its mock as - -```cpp -#include - -class MockFoo : public Foo { - public: - MOCK_METHOD(int, GetSize, (), (const, override)); - MOCK_METHOD(string, Describe, (const char* name), (override)); - MOCK_METHOD(string, Describe, (int type), (override)); - MOCK_METHOD(bool, Process, (Bar elem, int count), (override)); -}; -``` - -To create a "nice" mock, which ignores all uninteresting calls, a "naggy" mock, -which warns on all uninteresting calls, or a "strict" mock, which treats them as -failures: - -```cpp -using ::testing::NiceMock; -using ::testing::NaggyMock; -using ::testing::StrictMock; - -NiceMock nice_foo; // The type is a subclass of MockFoo. -NaggyMock naggy_foo; // The type is a subclass of MockFoo. -StrictMock strict_foo; // The type is a subclass of MockFoo. -``` - -{: .callout .note} -**Note:** A mock object is currently naggy by default. We may make it nice by -default in the future. - -### Mocking a Class Template {#MockTemplate} - -Class templates can be mocked just like any class. - -To mock - -```cpp -template -class StackInterface { - public: - virtual ~StackInterface(); - virtual int GetSize() const = 0; - virtual void Push(const Elem& x) = 0; -}; -``` - -(note that all member functions that are mocked, including `~StackInterface()` -**must** be virtual). - -```cpp -template -class MockStack : public StackInterface { - public: - MOCK_METHOD(int, GetSize, (), (const, override)); - MOCK_METHOD(void, Push, (const Elem& x), (override)); -}; -``` - -### Specifying Calling Conventions for Mock Functions - -If your mock function doesn't use the default calling convention, you can -specify it by adding `Calltype(convention)` to `MOCK_METHOD`'s 4th parameter. -For example, - -```cpp - MOCK_METHOD(bool, Foo, (int n), (Calltype(STDMETHODCALLTYPE))); - MOCK_METHOD(int, Bar, (double x, double y), - (const, Calltype(STDMETHODCALLTYPE))); -``` - -where `STDMETHODCALLTYPE` is defined by `` on Windows. - -## Using Mocks in Tests {#UsingMocks} - -The typical work flow is: - -1. Import the gMock names you need to use. All gMock symbols are in the - `testing` namespace unless they are macros or otherwise noted. -2. Create the mock objects. -3. Optionally, set the default actions of the mock objects. -4. Set your expectations on the mock objects (How will they be called? What - will they do?). -5. Exercise code that uses the mock objects; if necessary, check the result - using googletest assertions. -6. When a mock object is destructed, gMock automatically verifies that all - expectations on it have been satisfied. - -Here's an example: - -```cpp -using ::testing::Return; // #1 - -TEST(BarTest, DoesThis) { - MockFoo foo; // #2 - - ON_CALL(foo, GetSize()) // #3 - .WillByDefault(Return(1)); - // ... other default actions ... - - EXPECT_CALL(foo, Describe(5)) // #4 - .Times(3) - .WillRepeatedly(Return("Category 5")); - // ... other expectations ... - - EXPECT_EQ(MyProductionFunction(&foo), "good"); // #5 -} // #6 -``` - -## Setting Default Actions {#OnCall} - -gMock has a **built-in default action** for any function that returns `void`, -`bool`, a numeric value, or a pointer. In C++11, it will additionally returns -the default-constructed value, if one exists for the given type. - -To customize the default action for functions with return type `T`, use -[`DefaultValue`](reference/mocking.md#DefaultValue). For example: - -```cpp - // Sets the default action for return type std::unique_ptr to - // creating a new Buzz every time. - DefaultValue>::SetFactory( - [] { return std::make_unique(AccessLevel::kInternal); }); - - // When this fires, the default action of MakeBuzz() will run, which - // will return a new Buzz object. - EXPECT_CALL(mock_buzzer_, MakeBuzz("hello")).Times(AnyNumber()); - - auto buzz1 = mock_buzzer_.MakeBuzz("hello"); - auto buzz2 = mock_buzzer_.MakeBuzz("hello"); - EXPECT_NE(buzz1, nullptr); - EXPECT_NE(buzz2, nullptr); - EXPECT_NE(buzz1, buzz2); - - // Resets the default action for return type std::unique_ptr, - // to avoid interfere with other tests. - DefaultValue>::Clear(); -``` - -To customize the default action for a particular method of a specific mock -object, use [`ON_CALL`](reference/mocking.md#ON_CALL). `ON_CALL` has a similar -syntax to `EXPECT_CALL`, but it is used for setting default behaviors when you -do not require that the mock method is called. See -[Knowing When to Expect](gmock_cook_book.md#UseOnCall) for a more detailed -discussion. - -## Setting Expectations {#ExpectCall} - -See [`EXPECT_CALL`](reference/mocking.md#EXPECT_CALL) in the Mocking Reference. - -## Matchers {#MatcherList} - -See the [Matchers Reference](reference/matchers.md). - -## Actions {#ActionList} - -See the [Actions Reference](reference/actions.md). - -## Cardinalities {#CardinalityList} - -See the [`Times` clause](reference/mocking.md#EXPECT_CALL.Times) of -`EXPECT_CALL` in the Mocking Reference. - -## Expectation Order - -By default, expectations can be matched in *any* order. If some or all -expectations must be matched in a given order, you can use the -[`After` clause](reference/mocking.md#EXPECT_CALL.After) or -[`InSequence` clause](reference/mocking.md#EXPECT_CALL.InSequence) of -`EXPECT_CALL`, or use an [`InSequence` object](reference/mocking.md#InSequence). - -## Verifying and Resetting a Mock - -gMock will verify the expectations on a mock object when it is destructed, or -you can do it earlier: - -```cpp -using ::testing::Mock; -... -// Verifies and removes the expectations on mock_obj; -// returns true if and only if successful. -Mock::VerifyAndClearExpectations(&mock_obj); -... -// Verifies and removes the expectations on mock_obj; -// also removes the default actions set by ON_CALL(); -// returns true if and only if successful. -Mock::VerifyAndClear(&mock_obj); -``` - -Do not set new expectations after verifying and clearing a mock after its use. -Setting expectations after code that exercises the mock has undefined behavior. -See [Using Mocks in Tests](gmock_for_dummies.md#using-mocks-in-tests) for more -information. - -You can also tell gMock that a mock object can be leaked and doesn't need to be -verified: - -```cpp -Mock::AllowLeak(&mock_obj); -``` - -## Mock Classes - -gMock defines a convenient mock class template - -```cpp -class MockFunction { - public: - MOCK_METHOD(R, Call, (A1, ..., An)); -}; -``` - -See this [recipe](gmock_cook_book.md#UsingCheckPoints) for one application of -it. - -## Flags - -| Flag | Description | -| :----------------------------- | :---------------------------------------- | -| `--gmock_catch_leaked_mocks=0` | Don't report leaked mock objects as failures. | -| `--gmock_verbose=LEVEL` | Sets the default verbosity level (`info`, `warning`, or `error`) of Google Mock messages. | diff --git a/test/unit/googletest/docs/gmock_cook_book.md b/test/unit/googletest/docs/gmock_cook_book.md deleted file mode 100644 index f1b10b4..0000000 --- a/test/unit/googletest/docs/gmock_cook_book.md +++ /dev/null @@ -1,4379 +0,0 @@ -# gMock Cookbook - -You can find recipes for using gMock here. If you haven't yet, please read -[the dummy guide](gmock_for_dummies.md) first to make sure you understand the -basics. - -{: .callout .note} -**Note:** gMock lives in the `testing` name space. For readability, it is -recommended to write `using ::testing::Foo;` once in your file before using the -name `Foo` defined by gMock. We omit such `using` statements in this section for -brevity, but you should do it in your own code. - -## Creating Mock Classes - -Mock classes are defined as normal classes, using the `MOCK_METHOD` macro to -generate mocked methods. The macro gets 3 or 4 parameters: - -```cpp -class MyMock { - public: - MOCK_METHOD(ReturnType, MethodName, (Args...)); - MOCK_METHOD(ReturnType, MethodName, (Args...), (Specs...)); -}; -``` - -The first 3 parameters are simply the method declaration, split into 3 parts. -The 4th parameter accepts a closed list of qualifiers, which affect the -generated method: - -* **`const`** - Makes the mocked method a `const` method. Required if - overriding a `const` method. -* **`override`** - Marks the method with `override`. Recommended if overriding - a `virtual` method. -* **`noexcept`** - Marks the method with `noexcept`. Required if overriding a - `noexcept` method. -* **`Calltype(...)`** - Sets the call type for the method (e.g. to - `STDMETHODCALLTYPE`), useful in Windows. -* **`ref(...)`** - Marks the method with the reference qualification - specified. Required if overriding a method that has reference - qualifications. Eg `ref(&)` or `ref(&&)`. - -### Dealing with unprotected commas - -Unprotected commas, i.e. commas which are not surrounded by parentheses, prevent -`MOCK_METHOD` from parsing its arguments correctly: - -{: .bad} -```cpp -class MockFoo { - public: - MOCK_METHOD(std::pair, GetPair, ()); // Won't compile! - MOCK_METHOD(bool, CheckMap, (std::map, bool)); // Won't compile! -}; -``` - -Solution 1 - wrap with parentheses: - -{: .good} -```cpp -class MockFoo { - public: - MOCK_METHOD((std::pair), GetPair, ()); - MOCK_METHOD(bool, CheckMap, ((std::map), bool)); -}; -``` - -Note that wrapping a return or argument type with parentheses is, in general, -invalid C++. `MOCK_METHOD` removes the parentheses. - -Solution 2 - define an alias: - -{: .good} -```cpp -class MockFoo { - public: - using BoolAndInt = std::pair; - MOCK_METHOD(BoolAndInt, GetPair, ()); - using MapIntDouble = std::map; - MOCK_METHOD(bool, CheckMap, (MapIntDouble, bool)); -}; -``` - -### Mocking Private or Protected Methods - -You must always put a mock method definition (`MOCK_METHOD`) in a `public:` -section of the mock class, regardless of the method being mocked being `public`, -`protected`, or `private` in the base class. This allows `ON_CALL` and -`EXPECT_CALL` to reference the mock function from outside of the mock class. -(Yes, C++ allows a subclass to change the access level of a virtual function in -the base class.) Example: - -```cpp -class Foo { - public: - ... - virtual bool Transform(Gadget* g) = 0; - - protected: - virtual void Resume(); - - private: - virtual int GetTimeOut(); -}; - -class MockFoo : public Foo { - public: - ... - MOCK_METHOD(bool, Transform, (Gadget* g), (override)); - - // The following must be in the public section, even though the - // methods are protected or private in the base class. - MOCK_METHOD(void, Resume, (), (override)); - MOCK_METHOD(int, GetTimeOut, (), (override)); -}; -``` - -### Mocking Overloaded Methods - -You can mock overloaded functions as usual. No special attention is required: - -```cpp -class Foo { - ... - - // Must be virtual as we'll inherit from Foo. - virtual ~Foo(); - - // Overloaded on the types and/or numbers of arguments. - virtual int Add(Element x); - virtual int Add(int times, Element x); - - // Overloaded on the const-ness of this object. - virtual Bar& GetBar(); - virtual const Bar& GetBar() const; -}; - -class MockFoo : public Foo { - ... - MOCK_METHOD(int, Add, (Element x), (override)); - MOCK_METHOD(int, Add, (int times, Element x), (override)); - - MOCK_METHOD(Bar&, GetBar, (), (override)); - MOCK_METHOD(const Bar&, GetBar, (), (const, override)); -}; -``` - -{: .callout .note} -**Note:** if you don't mock all versions of the overloaded method, the compiler -will give you a warning about some methods in the base class being hidden. To -fix that, use `using` to bring them in scope: - -```cpp -class MockFoo : public Foo { - ... - using Foo::Add; - MOCK_METHOD(int, Add, (Element x), (override)); - // We don't want to mock int Add(int times, Element x); - ... -}; -``` - -### Mocking Class Templates - -You can mock class templates just like any class. - -```cpp -template -class StackInterface { - ... - // Must be virtual as we'll inherit from StackInterface. - virtual ~StackInterface(); - - virtual int GetSize() const = 0; - virtual void Push(const Elem& x) = 0; -}; - -template -class MockStack : public StackInterface { - ... - MOCK_METHOD(int, GetSize, (), (override)); - MOCK_METHOD(void, Push, (const Elem& x), (override)); -}; -``` - -### Mocking Non-virtual Methods {#MockingNonVirtualMethods} - -gMock can mock non-virtual functions to be used in Hi-perf dependency injection. - -In this case, instead of sharing a common base class with the real class, your -mock class will be *unrelated* to the real class, but contain methods with the -same signatures. The syntax for mocking non-virtual methods is the *same* as -mocking virtual methods (just don't add `override`): - -```cpp -// A simple packet stream class. None of its members is virtual. -class ConcretePacketStream { - public: - void AppendPacket(Packet* new_packet); - const Packet* GetPacket(size_t packet_number) const; - size_t NumberOfPackets() const; - ... -}; - -// A mock packet stream class. It inherits from no other, but defines -// GetPacket() and NumberOfPackets(). -class MockPacketStream { - public: - MOCK_METHOD(const Packet*, GetPacket, (size_t packet_number), (const)); - MOCK_METHOD(size_t, NumberOfPackets, (), (const)); - ... -}; -``` - -Note that the mock class doesn't define `AppendPacket()`, unlike the real class. -That's fine as long as the test doesn't need to call it. - -Next, you need a way to say that you want to use `ConcretePacketStream` in -production code, and use `MockPacketStream` in tests. Since the functions are -not virtual and the two classes are unrelated, you must specify your choice at -*compile time* (as opposed to run time). - -One way to do it is to templatize your code that needs to use a packet stream. -More specifically, you will give your code a template type argument for the type -of the packet stream. In production, you will instantiate your template with -`ConcretePacketStream` as the type argument. In tests, you will instantiate the -same template with `MockPacketStream`. For example, you may write: - -```cpp -template -void CreateConnection(PacketStream* stream) { ... } - -template -class PacketReader { - public: - void ReadPackets(PacketStream* stream, size_t packet_num); -}; -``` - -Then you can use `CreateConnection()` and -`PacketReader` in production code, and use -`CreateConnection()` and `PacketReader` in -tests. - -```cpp - MockPacketStream mock_stream; - EXPECT_CALL(mock_stream, ...)...; - .. set more expectations on mock_stream ... - PacketReader reader(&mock_stream); - ... exercise reader ... -``` - -### Mocking Free Functions - -It is not possible to directly mock a free function (i.e. a C-style function or -a static method). If you need to, you can rewrite your code to use an interface -(abstract class). - -Instead of calling a free function (say, `OpenFile`) directly, introduce an -interface for it and have a concrete subclass that calls the free function: - -```cpp -class FileInterface { - public: - ... - virtual bool Open(const char* path, const char* mode) = 0; -}; - -class File : public FileInterface { - public: - ... - bool Open(const char* path, const char* mode) override { - return OpenFile(path, mode); - } -}; -``` - -Your code should talk to `FileInterface` to open a file. Now it's easy to mock -out the function. - -This may seem like a lot of hassle, but in practice you often have multiple -related functions that you can put in the same interface, so the per-function -syntactic overhead will be much lower. - -If you are concerned about the performance overhead incurred by virtual -functions, and profiling confirms your concern, you can combine this with the -recipe for [mocking non-virtual methods](#MockingNonVirtualMethods). - -Alternatively, instead of introducing a new interface, you can rewrite your code -to accept a std::function instead of the free function, and then use -[MockFunction](#MockFunction) to mock the std::function. - -### Old-Style `MOCK_METHODn` Macros - -Before the generic `MOCK_METHOD` macro -[was introduced in 2018](https://github.com/google/googletest/commit/c5f08bf91944ce1b19bcf414fa1760e69d20afc2), -mocks where created using a family of macros collectively called `MOCK_METHODn`. -These macros are still supported, though migration to the new `MOCK_METHOD` is -recommended. - -The macros in the `MOCK_METHODn` family differ from `MOCK_METHOD`: - -* The general structure is `MOCK_METHODn(MethodName, ReturnType(Args))`, - instead of `MOCK_METHOD(ReturnType, MethodName, (Args))`. -* The number `n` must equal the number of arguments. -* When mocking a const method, one must use `MOCK_CONST_METHODn`. -* When mocking a class template, the macro name must be suffixed with `_T`. -* In order to specify the call type, the macro name must be suffixed with - `_WITH_CALLTYPE`, and the call type is the first macro argument. - -Old macros and their new equivalents: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Simple
OldMOCK_METHOD1(Foo, bool(int))
NewMOCK_METHOD(bool, Foo, (int))
Const Method
OldMOCK_CONST_METHOD1(Foo, bool(int))
NewMOCK_METHOD(bool, Foo, (int), (const))
Method in a Class Template
OldMOCK_METHOD1_T(Foo, bool(int))
NewMOCK_METHOD(bool, Foo, (int))
Const Method in a Class Template
OldMOCK_CONST_METHOD1_T(Foo, bool(int))
NewMOCK_METHOD(bool, Foo, (int), (const))
Method with Call Type
OldMOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int))
NewMOCK_METHOD(bool, Foo, (int), (Calltype(STDMETHODCALLTYPE)))
Const Method with Call Type
OldMOCK_CONST_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int))
NewMOCK_METHOD(bool, Foo, (int), (const, Calltype(STDMETHODCALLTYPE)))
Method with Call Type in a Class Template
OldMOCK_METHOD1_T_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int))
NewMOCK_METHOD(bool, Foo, (int), (Calltype(STDMETHODCALLTYPE)))
Const Method with Call Type in a Class Template
OldMOCK_CONST_METHOD1_T_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int))
NewMOCK_METHOD(bool, Foo, (int), (const, Calltype(STDMETHODCALLTYPE)))
- -### The Nice, the Strict, and the Naggy {#NiceStrictNaggy} - -If a mock method has no `EXPECT_CALL` spec but is called, we say that it's an -"uninteresting call", and the default action (which can be specified using -`ON_CALL()`) of the method will be taken. Currently, an uninteresting call will -also by default cause gMock to print a warning. - -However, sometimes you may want to ignore these uninteresting calls, and -sometimes you may want to treat them as errors. gMock lets you make the decision -on a per-mock-object basis. - -Suppose your test uses a mock class `MockFoo`: - -```cpp -TEST(...) { - MockFoo mock_foo; - EXPECT_CALL(mock_foo, DoThis()); - ... code that uses mock_foo ... -} -``` - -If a method of `mock_foo` other than `DoThis()` is called, you will get a -warning. However, if you rewrite your test to use `NiceMock` instead, -you can suppress the warning: - -```cpp -using ::testing::NiceMock; - -TEST(...) { - NiceMock mock_foo; - EXPECT_CALL(mock_foo, DoThis()); - ... code that uses mock_foo ... -} -``` - -`NiceMock` is a subclass of `MockFoo`, so it can be used wherever -`MockFoo` is accepted. - -It also works if `MockFoo`'s constructor takes some arguments, as -`NiceMock` "inherits" `MockFoo`'s constructors: - -```cpp -using ::testing::NiceMock; - -TEST(...) { - NiceMock mock_foo(5, "hi"); // Calls MockFoo(5, "hi"). - EXPECT_CALL(mock_foo, DoThis()); - ... code that uses mock_foo ... -} -``` - -The usage of `StrictMock` is similar, except that it makes all uninteresting -calls failures: - -```cpp -using ::testing::StrictMock; - -TEST(...) { - StrictMock mock_foo; - EXPECT_CALL(mock_foo, DoThis()); - ... code that uses mock_foo ... - - // The test will fail if a method of mock_foo other than DoThis() - // is called. -} -``` - -{: .callout .note} -NOTE: `NiceMock` and `StrictMock` only affects *uninteresting* calls (calls of -*methods* with no expectations); they do not affect *unexpected* calls (calls of -methods with expectations, but they don't match). See -[Understanding Uninteresting vs Unexpected Calls](#uninteresting-vs-unexpected). - -There are some caveats though (sadly they are side effects of C++'s -limitations): - -1. `NiceMock` and `StrictMock` only work for mock methods - defined using the `MOCK_METHOD` macro **directly** in the `MockFoo` class. - If a mock method is defined in a **base class** of `MockFoo`, the "nice" or - "strict" modifier may not affect it, depending on the compiler. In - particular, nesting `NiceMock` and `StrictMock` (e.g. - `NiceMock >`) is **not** supported. -2. `NiceMock` and `StrictMock` may not work correctly if the - destructor of `MockFoo` is not virtual. We would like to fix this, but it - requires cleaning up existing tests. - -Finally, you should be **very cautious** about when to use naggy or strict -mocks, as they tend to make tests more brittle and harder to maintain. When you -refactor your code without changing its externally visible behavior, ideally you -shouldn't need to update any tests. If your code interacts with a naggy mock, -however, you may start to get spammed with warnings as the result of your -change. Worse, if your code interacts with a strict mock, your tests may start -to fail and you'll be forced to fix them. Our general recommendation is to use -nice mocks (not yet the default) most of the time, use naggy mocks (the current -default) when developing or debugging tests, and use strict mocks only as the -last resort. - -### Simplifying the Interface without Breaking Existing Code {#SimplerInterfaces} - -Sometimes a method has a long list of arguments that is mostly uninteresting. -For example: - -```cpp -class LogSink { - public: - ... - virtual void send(LogSeverity severity, const char* full_filename, - const char* base_filename, int line, - const struct tm* tm_time, - const char* message, size_t message_len) = 0; -}; -``` - -This method's argument list is lengthy and hard to work with (the `message` -argument is not even 0-terminated). If we mock it as is, using the mock will be -awkward. If, however, we try to simplify this interface, we'll need to fix all -clients depending on it, which is often infeasible. - -The trick is to redispatch the method in the mock class: - -```cpp -class ScopedMockLog : public LogSink { - public: - ... - void send(LogSeverity severity, const char* full_filename, - const char* base_filename, int line, const tm* tm_time, - const char* message, size_t message_len) override { - // We are only interested in the log severity, full file name, and - // log message. - Log(severity, full_filename, std::string(message, message_len)); - } - - // Implements the mock method: - // - // void Log(LogSeverity severity, - // const string& file_path, - // const string& message); - MOCK_METHOD(void, Log, - (LogSeverity severity, const string& file_path, - const string& message)); -}; -``` - -By defining a new mock method with a trimmed argument list, we make the mock -class more user-friendly. - -This technique may also be applied to make overloaded methods more amenable to -mocking. For example, when overloads have been used to implement default -arguments: - -```cpp -class MockTurtleFactory : public TurtleFactory { - public: - Turtle* MakeTurtle(int length, int weight) override { ... } - Turtle* MakeTurtle(int length, int weight, int speed) override { ... } - - // the above methods delegate to this one: - MOCK_METHOD(Turtle*, DoMakeTurtle, ()); -}; -``` - -This allows tests that don't care which overload was invoked to avoid specifying -argument matchers: - -```cpp -ON_CALL(factory, DoMakeTurtle) - .WillByDefault(Return(MakeMockTurtle())); -``` - -### Alternative to Mocking Concrete Classes - -Often you may find yourself using classes that don't implement interfaces. In -order to test your code that uses such a class (let's call it `Concrete`), you -may be tempted to make the methods of `Concrete` virtual and then mock it. - -Try not to do that. - -Making a non-virtual function virtual is a big decision. It creates an extension -point where subclasses can tweak your class' behavior. This weakens your control -on the class because now it's harder to maintain the class invariants. You -should make a function virtual only when there is a valid reason for a subclass -to override it. - -Mocking concrete classes directly is problematic as it creates a tight coupling -between the class and the tests - any small change in the class may invalidate -your tests and make test maintenance a pain. - -To avoid such problems, many programmers have been practicing "coding to -interfaces": instead of talking to the `Concrete` class, your code would define -an interface and talk to it. Then you implement that interface as an adaptor on -top of `Concrete`. In tests, you can easily mock that interface to observe how -your code is doing. - -This technique incurs some overhead: - -* You pay the cost of virtual function calls (usually not a problem). -* There is more abstraction for the programmers to learn. - -However, it can also bring significant benefits in addition to better -testability: - -* `Concrete`'s API may not fit your problem domain very well, as you may not - be the only client it tries to serve. By designing your own interface, you - have a chance to tailor it to your need - you may add higher-level - functionalities, rename stuff, etc instead of just trimming the class. This - allows you to write your code (user of the interface) in a more natural way, - which means it will be more readable, more maintainable, and you'll be more - productive. -* If `Concrete`'s implementation ever has to change, you don't have to rewrite - everywhere it is used. Instead, you can absorb the change in your - implementation of the interface, and your other code and tests will be - insulated from this change. - -Some people worry that if everyone is practicing this technique, they will end -up writing lots of redundant code. This concern is totally understandable. -However, there are two reasons why it may not be the case: - -* Different projects may need to use `Concrete` in different ways, so the best - interfaces for them will be different. Therefore, each of them will have its - own domain-specific interface on top of `Concrete`, and they will not be the - same code. -* If enough projects want to use the same interface, they can always share it, - just like they have been sharing `Concrete`. You can check in the interface - and the adaptor somewhere near `Concrete` (perhaps in a `contrib` - sub-directory) and let many projects use it. - -You need to weigh the pros and cons carefully for your particular problem, but -I'd like to assure you that the Java community has been practicing this for a -long time and it's a proven effective technique applicable in a wide variety of -situations. :-) - -### Delegating Calls to a Fake {#DelegatingToFake} - -Some times you have a non-trivial fake implementation of an interface. For -example: - -```cpp -class Foo { - public: - virtual ~Foo() {} - virtual char DoThis(int n) = 0; - virtual void DoThat(const char* s, int* p) = 0; -}; - -class FakeFoo : public Foo { - public: - char DoThis(int n) override { - return (n > 0) ? '+' : - (n < 0) ? '-' : '0'; - } - - void DoThat(const char* s, int* p) override { - *p = strlen(s); - } -}; -``` - -Now you want to mock this interface such that you can set expectations on it. -However, you also want to use `FakeFoo` for the default behavior, as duplicating -it in the mock object is, well, a lot of work. - -When you define the mock class using gMock, you can have it delegate its default -action to a fake class you already have, using this pattern: - -```cpp -class MockFoo : public Foo { - public: - // Normal mock method definitions using gMock. - MOCK_METHOD(char, DoThis, (int n), (override)); - MOCK_METHOD(void, DoThat, (const char* s, int* p), (override)); - - // Delegates the default actions of the methods to a FakeFoo object. - // This must be called *before* the custom ON_CALL() statements. - void DelegateToFake() { - ON_CALL(*this, DoThis).WillByDefault([this](int n) { - return fake_.DoThis(n); - }); - ON_CALL(*this, DoThat).WillByDefault([this](const char* s, int* p) { - fake_.DoThat(s, p); - }); - } - - private: - FakeFoo fake_; // Keeps an instance of the fake in the mock. -}; -``` - -With that, you can use `MockFoo` in your tests as usual. Just remember that if -you don't explicitly set an action in an `ON_CALL()` or `EXPECT_CALL()`, the -fake will be called upon to do it.: - -```cpp -using ::testing::_; - -TEST(AbcTest, Xyz) { - MockFoo foo; - - foo.DelegateToFake(); // Enables the fake for delegation. - - // Put your ON_CALL(foo, ...)s here, if any. - - // No action specified, meaning to use the default action. - EXPECT_CALL(foo, DoThis(5)); - EXPECT_CALL(foo, DoThat(_, _)); - - int n = 0; - EXPECT_EQ(foo.DoThis(5), '+'); // FakeFoo::DoThis() is invoked. - foo.DoThat("Hi", &n); // FakeFoo::DoThat() is invoked. - EXPECT_EQ(n, 2); -} -``` - -**Some tips:** - -* If you want, you can still override the default action by providing your own - `ON_CALL()` or using `.WillOnce()` / `.WillRepeatedly()` in `EXPECT_CALL()`. -* In `DelegateToFake()`, you only need to delegate the methods whose fake - implementation you intend to use. - -* The general technique discussed here works for overloaded methods, but - you'll need to tell the compiler which version you mean. To disambiguate a - mock function (the one you specify inside the parentheses of `ON_CALL()`), - use [this technique](#SelectOverload); to disambiguate a fake function (the - one you place inside `Invoke()`), use a `static_cast` to specify the - function's type. For instance, if class `Foo` has methods `char DoThis(int - n)` and `bool DoThis(double x) const`, and you want to invoke the latter, - you need to write `Invoke(&fake_, static_cast(&FakeFoo::DoThis))` instead of `Invoke(&fake_, &FakeFoo::DoThis)` - (The strange-looking thing inside the angled brackets of `static_cast` is - the type of a function pointer to the second `DoThis()` method.). - -* Having to mix a mock and a fake is often a sign of something gone wrong. - Perhaps you haven't got used to the interaction-based way of testing yet. Or - perhaps your interface is taking on too many roles and should be split up. - Therefore, **don't abuse this**. We would only recommend to do it as an - intermediate step when you are refactoring your code. - -Regarding the tip on mixing a mock and a fake, here's an example on why it may -be a bad sign: Suppose you have a class `System` for low-level system -operations. In particular, it does file and I/O operations. And suppose you want -to test how your code uses `System` to do I/O, and you just want the file -operations to work normally. If you mock out the entire `System` class, you'll -have to provide a fake implementation for the file operation part, which -suggests that `System` is taking on too many roles. - -Instead, you can define a `FileOps` interface and an `IOOps` interface and split -`System`'s functionalities into the two. Then you can mock `IOOps` without -mocking `FileOps`. - -### Delegating Calls to a Real Object - -When using testing doubles (mocks, fakes, stubs, and etc), sometimes their -behaviors will differ from those of the real objects. This difference could be -either intentional (as in simulating an error such that you can test the error -handling code) or unintentional. If your mocks have different behaviors than the -real objects by mistake, you could end up with code that passes the tests but -fails in production. - -You can use the *delegating-to-real* technique to ensure that your mock has the -same behavior as the real object while retaining the ability to validate calls. -This technique is very similar to the [delegating-to-fake](#DelegatingToFake) -technique, the difference being that we use a real object instead of a fake. -Here's an example: - -```cpp -using ::testing::AtLeast; - -class MockFoo : public Foo { - public: - MockFoo() { - // By default, all calls are delegated to the real object. - ON_CALL(*this, DoThis).WillByDefault([this](int n) { - return real_.DoThis(n); - }); - ON_CALL(*this, DoThat).WillByDefault([this](const char* s, int* p) { - real_.DoThat(s, p); - }); - ... - } - MOCK_METHOD(char, DoThis, ...); - MOCK_METHOD(void, DoThat, ...); - ... - private: - Foo real_; -}; - -... - MockFoo mock; - EXPECT_CALL(mock, DoThis()) - .Times(3); - EXPECT_CALL(mock, DoThat("Hi")) - .Times(AtLeast(1)); - ... use mock in test ... -``` - -With this, gMock will verify that your code made the right calls (with the right -arguments, in the right order, called the right number of times, etc), and a -real object will answer the calls (so the behavior will be the same as in -production). This gives you the best of both worlds. - -### Delegating Calls to a Parent Class - -Ideally, you should code to interfaces, whose methods are all pure virtual. In -reality, sometimes you do need to mock a virtual method that is not pure (i.e, -it already has an implementation). For example: - -```cpp -class Foo { - public: - virtual ~Foo(); - - virtual void Pure(int n) = 0; - virtual int Concrete(const char* str) { ... } -}; - -class MockFoo : public Foo { - public: - // Mocking a pure method. - MOCK_METHOD(void, Pure, (int n), (override)); - // Mocking a concrete method. Foo::Concrete() is shadowed. - MOCK_METHOD(int, Concrete, (const char* str), (override)); -}; -``` - -Sometimes you may want to call `Foo::Concrete()` instead of -`MockFoo::Concrete()`. Perhaps you want to do it as part of a stub action, or -perhaps your test doesn't need to mock `Concrete()` at all (but it would be -oh-so painful to have to define a new mock class whenever you don't need to mock -one of its methods). - -You can call `Foo::Concrete()` inside an action by: - -```cpp -... - EXPECT_CALL(foo, Concrete).WillOnce([&foo](const char* str) { - return foo.Foo::Concrete(str); - }); -``` - -or tell the mock object that you don't want to mock `Concrete()`: - -```cpp -... - ON_CALL(foo, Concrete).WillByDefault([&foo](const char* str) { - return foo.Foo::Concrete(str); - }); -``` - -(Why don't we just write `{ return foo.Concrete(str); }`? If you do that, -`MockFoo::Concrete()` will be called (and cause an infinite recursion) since -`Foo::Concrete()` is virtual. That's just how C++ works.) - -## Using Matchers - -### Matching Argument Values Exactly - -You can specify exactly which arguments a mock method is expecting: - -```cpp -using ::testing::Return; -... - EXPECT_CALL(foo, DoThis(5)) - .WillOnce(Return('a')); - EXPECT_CALL(foo, DoThat("Hello", bar)); -``` - -### Using Simple Matchers - -You can use matchers to match arguments that have a certain property: - -```cpp -using ::testing::NotNull; -using ::testing::Return; -... - EXPECT_CALL(foo, DoThis(Ge(5))) // The argument must be >= 5. - .WillOnce(Return('a')); - EXPECT_CALL(foo, DoThat("Hello", NotNull())); - // The second argument must not be NULL. -``` - -A frequently used matcher is `_`, which matches anything: - -```cpp - EXPECT_CALL(foo, DoThat(_, NotNull())); -``` - -### Combining Matchers {#CombiningMatchers} - -You can build complex matchers from existing ones using `AllOf()`, -`AllOfArray()`, `AnyOf()`, `AnyOfArray()` and `Not()`: - -```cpp -using ::testing::AllOf; -using ::testing::Gt; -using ::testing::HasSubstr; -using ::testing::Ne; -using ::testing::Not; -... - // The argument must be > 5 and != 10. - EXPECT_CALL(foo, DoThis(AllOf(Gt(5), - Ne(10)))); - - // The first argument must not contain sub-string "blah". - EXPECT_CALL(foo, DoThat(Not(HasSubstr("blah")), - NULL)); -``` - -Matchers are function objects, and parametrized matchers can be composed just -like any other function. However because their types can be long and rarely -provide meaningful information, it can be easier to express them with C++14 -generic lambdas to avoid specifying types. For example, - -```cpp -using ::testing::Contains; -using ::testing::Property; - -inline constexpr auto HasFoo = [](const auto& f) { - return Property("foo", &MyClass::foo, Contains(f)); -}; -... - EXPECT_THAT(x, HasFoo("blah")); -``` - -### Casting Matchers {#SafeMatcherCast} - -gMock matchers are statically typed, meaning that the compiler can catch your -mistake if you use a matcher of the wrong type (for example, if you use `Eq(5)` -to match a `string` argument). Good for you! - -Sometimes, however, you know what you're doing and want the compiler to give you -some slack. One example is that you have a matcher for `long` and the argument -you want to match is `int`. While the two types aren't exactly the same, there -is nothing really wrong with using a `Matcher` to match an `int` - after -all, we can first convert the `int` argument to a `long` losslessly before -giving it to the matcher. - -To support this need, gMock gives you the `SafeMatcherCast(m)` function. It -casts a matcher `m` to type `Matcher`. To ensure safety, gMock checks that -(let `U` be the type `m` accepts : - -1. Type `T` can be *implicitly* cast to type `U`; -2. When both `T` and `U` are built-in arithmetic types (`bool`, integers, and - floating-point numbers), the conversion from `T` to `U` is not lossy (in - other words, any value representable by `T` can also be represented by `U`); - and -3. When `U` is a reference, `T` must also be a reference (as the underlying - matcher may be interested in the address of the `U` value). - -The code won't compile if any of these conditions isn't met. - -Here's one example: - -```cpp -using ::testing::SafeMatcherCast; - -// A base class and a child class. -class Base { ... }; -class Derived : public Base { ... }; - -class MockFoo : public Foo { - public: - MOCK_METHOD(void, DoThis, (Derived* derived), (override)); -}; - -... - MockFoo foo; - // m is a Matcher we got from somewhere. - EXPECT_CALL(foo, DoThis(SafeMatcherCast(m))); -``` - -If you find `SafeMatcherCast(m)` too limiting, you can use a similar function -`MatcherCast(m)`. The difference is that `MatcherCast` works as long as you -can `static_cast` type `T` to type `U`. - -`MatcherCast` essentially lets you bypass C++'s type system (`static_cast` isn't -always safe as it could throw away information, for example), so be careful not -to misuse/abuse it. - -### Selecting Between Overloaded Functions {#SelectOverload} - -If you expect an overloaded function to be called, the compiler may need some -help on which overloaded version it is. - -To disambiguate functions overloaded on the const-ness of this object, use the -`Const()` argument wrapper. - -```cpp -using ::testing::ReturnRef; - -class MockFoo : public Foo { - ... - MOCK_METHOD(Bar&, GetBar, (), (override)); - MOCK_METHOD(const Bar&, GetBar, (), (const, override)); -}; - -... - MockFoo foo; - Bar bar1, bar2; - EXPECT_CALL(foo, GetBar()) // The non-const GetBar(). - .WillOnce(ReturnRef(bar1)); - EXPECT_CALL(Const(foo), GetBar()) // The const GetBar(). - .WillOnce(ReturnRef(bar2)); -``` - -(`Const()` is defined by gMock and returns a `const` reference to its argument.) - -To disambiguate overloaded functions with the same number of arguments but -different argument types, you may need to specify the exact type of a matcher, -either by wrapping your matcher in `Matcher()`, or using a matcher whose -type is fixed (`TypedEq`, `An()`, etc): - -```cpp -using ::testing::An; -using ::testing::Matcher; -using ::testing::TypedEq; - -class MockPrinter : public Printer { - public: - MOCK_METHOD(void, Print, (int n), (override)); - MOCK_METHOD(void, Print, (char c), (override)); -}; - -TEST(PrinterTest, Print) { - MockPrinter printer; - - EXPECT_CALL(printer, Print(An())); // void Print(int); - EXPECT_CALL(printer, Print(Matcher(Lt(5)))); // void Print(int); - EXPECT_CALL(printer, Print(TypedEq('a'))); // void Print(char); - - printer.Print(3); - printer.Print(6); - printer.Print('a'); -} -``` - -### Performing Different Actions Based on the Arguments - -When a mock method is called, the *last* matching expectation that's still -active will be selected (think "newer overrides older"). So, you can make a -method do different things depending on its argument values like this: - -```cpp -using ::testing::_; -using ::testing::Lt; -using ::testing::Return; -... - // The default case. - EXPECT_CALL(foo, DoThis(_)) - .WillRepeatedly(Return('b')); - // The more specific case. - EXPECT_CALL(foo, DoThis(Lt(5))) - .WillRepeatedly(Return('a')); -``` - -Now, if `foo.DoThis()` is called with a value less than 5, `'a'` will be -returned; otherwise `'b'` will be returned. - -### Matching Multiple Arguments as a Whole - -Sometimes it's not enough to match the arguments individually. For example, we -may want to say that the first argument must be less than the second argument. -The `With()` clause allows us to match all arguments of a mock function as a -whole. For example, - -```cpp -using ::testing::_; -using ::testing::Ne; -using ::testing::Lt; -... - EXPECT_CALL(foo, InRange(Ne(0), _)) - .With(Lt()); -``` - -says that the first argument of `InRange()` must not be 0, and must be less than -the second argument. - -The expression inside `With()` must be a matcher of type `Matcher>`, where `A1`, ..., `An` are the types of the function arguments. - -You can also write `AllArgs(m)` instead of `m` inside `.With()`. The two forms -are equivalent, but `.With(AllArgs(Lt()))` is more readable than `.With(Lt())`. - -You can use `Args(m)` to match the `n` selected arguments (as a -tuple) against `m`. For example, - -```cpp -using ::testing::_; -using ::testing::AllOf; -using ::testing::Args; -using ::testing::Lt; -... - EXPECT_CALL(foo, Blah) - .With(AllOf(Args<0, 1>(Lt()), Args<1, 2>(Lt()))); -``` - -says that `Blah` will be called with arguments `x`, `y`, and `z` where `x < y < -z`. Note that in this example, it wasn't necessary to specify the positional -matchers. - -As a convenience and example, gMock provides some matchers for 2-tuples, -including the `Lt()` matcher above. See -[Multi-argument Matchers](reference/matchers.md#MultiArgMatchers) for the -complete list. - -Note that if you want to pass the arguments to a predicate of your own (e.g. -`.With(Args<0, 1>(Truly(&MyPredicate)))`), that predicate MUST be written to -take a `std::tuple` as its argument; gMock will pass the `n` selected arguments -as *one* single tuple to the predicate. - -### Using Matchers as Predicates - -Have you noticed that a matcher is just a fancy predicate that also knows how to -describe itself? Many existing algorithms take predicates as arguments (e.g. -those defined in STL's `` header), and it would be a shame if gMock -matchers were not allowed to participate. - -Luckily, you can use a matcher where a unary predicate functor is expected by -wrapping it inside the `Matches()` function. For example, - -```cpp -#include -#include - -using ::testing::Matches; -using ::testing::Ge; - -vector v; -... -// How many elements in v are >= 10? -const int count = count_if(v.begin(), v.end(), Matches(Ge(10))); -``` - -Since you can build complex matchers from simpler ones easily using gMock, this -gives you a way to conveniently construct composite predicates (doing the same -using STL's `` header is just painful). For example, here's a -predicate that's satisfied by any number that is >= 0, <= 100, and != 50: - -```cpp -using ::testing::AllOf; -using ::testing::Ge; -using ::testing::Le; -using ::testing::Matches; -using ::testing::Ne; -... -Matches(AllOf(Ge(0), Le(100), Ne(50))) -``` - -### Using Matchers in googletest Assertions - -See [`EXPECT_THAT`](reference/assertions.md#EXPECT_THAT) in the Assertions -Reference. - -### Using Predicates as Matchers - -gMock provides a set of built-in matchers for matching arguments with expected -values—see the [Matchers Reference](reference/matchers.md) for more information. -In case you find the built-in set lacking, you can use an arbitrary unary -predicate function or functor as a matcher - as long as the predicate accepts a -value of the type you want. You do this by wrapping the predicate inside the -`Truly()` function, for example: - -```cpp -using ::testing::Truly; - -int IsEven(int n) { return (n % 2) == 0 ? 1 : 0; } -... - // Bar() must be called with an even number. - EXPECT_CALL(foo, Bar(Truly(IsEven))); -``` - -Note that the predicate function / functor doesn't have to return `bool`. It -works as long as the return value can be used as the condition in the statement -`if (condition) ...`. - -### Matching Arguments that Are Not Copyable - -When you do an `EXPECT_CALL(mock_obj, Foo(bar))`, gMock saves away a copy of -`bar`. When `Foo()` is called later, gMock compares the argument to `Foo()` with -the saved copy of `bar`. This way, you don't need to worry about `bar` being -modified or destroyed after the `EXPECT_CALL()` is executed. The same is true -when you use matchers like `Eq(bar)`, `Le(bar)`, and so on. - -But what if `bar` cannot be copied (i.e. has no copy constructor)? You could -define your own matcher function or callback and use it with `Truly()`, as the -previous couple of recipes have shown. Or, you may be able to get away from it -if you can guarantee that `bar` won't be changed after the `EXPECT_CALL()` is -executed. Just tell gMock that it should save a reference to `bar`, instead of a -copy of it. Here's how: - -```cpp -using ::testing::Eq; -using ::testing::Lt; -... - // Expects that Foo()'s argument == bar. - EXPECT_CALL(mock_obj, Foo(Eq(std::ref(bar)))); - - // Expects that Foo()'s argument < bar. - EXPECT_CALL(mock_obj, Foo(Lt(std::ref(bar)))); -``` - -Remember: if you do this, don't change `bar` after the `EXPECT_CALL()`, or the -result is undefined. - -### Validating a Member of an Object - -Often a mock function takes a reference to object as an argument. When matching -the argument, you may not want to compare the entire object against a fixed -object, as that may be over-specification. Instead, you may need to validate a -certain member variable or the result of a certain getter method of the object. -You can do this with `Field()` and `Property()`. More specifically, - -```cpp -Field(&Foo::bar, m) -``` - -is a matcher that matches a `Foo` object whose `bar` member variable satisfies -matcher `m`. - -```cpp -Property(&Foo::baz, m) -``` - -is a matcher that matches a `Foo` object whose `baz()` method returns a value -that satisfies matcher `m`. - -For example: - -| Expression | Description | -| :--------------------------- | :--------------------------------------- | -| `Field(&Foo::number, Ge(3))` | Matches `x` where `x.number >= 3`. | -| `Property(&Foo::name, StartsWith("John "))` | Matches `x` where `x.name()` starts with `"John "`. | - -Note that in `Property(&Foo::baz, ...)`, method `baz()` must take no argument -and be declared as `const`. Don't use `Property()` against member functions that -you do not own, because taking addresses of functions is fragile and generally -not part of the contract of the function. - -`Field()` and `Property()` can also match plain pointers to objects. For -instance, - -```cpp -using ::testing::Field; -using ::testing::Ge; -... -Field(&Foo::number, Ge(3)) -``` - -matches a plain pointer `p` where `p->number >= 3`. If `p` is `NULL`, the match -will always fail regardless of the inner matcher. - -What if you want to validate more than one members at the same time? Remember -that there are [`AllOf()` and `AllOfArray()`](#CombiningMatchers). - -Finally `Field()` and `Property()` provide overloads that take the field or -property names as the first argument to include it in the error message. This -can be useful when creating combined matchers. - -```cpp -using ::testing::AllOf; -using ::testing::Field; -using ::testing::Matcher; -using ::testing::SafeMatcherCast; - -Matcher IsFoo(const Foo& foo) { - return AllOf(Field("some_field", &Foo::some_field, foo.some_field), - Field("other_field", &Foo::other_field, foo.other_field), - Field("last_field", &Foo::last_field, foo.last_field)); -} -``` - -### Validating the Value Pointed to by a Pointer Argument - -C++ functions often take pointers as arguments. You can use matchers like -`IsNull()`, `NotNull()`, and other comparison matchers to match a pointer, but -what if you want to make sure the value *pointed to* by the pointer, instead of -the pointer itself, has a certain property? Well, you can use the `Pointee(m)` -matcher. - -`Pointee(m)` matches a pointer if and only if `m` matches the value the pointer -points to. For example: - -```cpp -using ::testing::Ge; -using ::testing::Pointee; -... - EXPECT_CALL(foo, Bar(Pointee(Ge(3)))); -``` - -expects `foo.Bar()` to be called with a pointer that points to a value greater -than or equal to 3. - -One nice thing about `Pointee()` is that it treats a `NULL` pointer as a match -failure, so you can write `Pointee(m)` instead of - -```cpp -using ::testing::AllOf; -using ::testing::NotNull; -using ::testing::Pointee; -... - AllOf(NotNull(), Pointee(m)) -``` - -without worrying that a `NULL` pointer will crash your test. - -Also, did we tell you that `Pointee()` works with both raw pointers **and** -smart pointers (`std::unique_ptr`, `std::shared_ptr`, etc)? - -What if you have a pointer to pointer? You guessed it - you can use nested -`Pointee()` to probe deeper inside the value. For example, -`Pointee(Pointee(Lt(3)))` matches a pointer that points to a pointer that points -to a number less than 3 (what a mouthful...). - -### Defining a Custom Matcher Class {#CustomMatcherClass} - -Most matchers can be simply defined using [the MATCHER* macros](#NewMatchers), -which are terse and flexible, and produce good error messages. However, these -macros are not very explicit about the interfaces they create and are not always -suitable, especially for matchers that will be widely reused. - -For more advanced cases, you may need to define your own matcher class. A custom -matcher allows you to test a specific invariant property of that object. Let's -take a look at how to do so. - -Imagine you have a mock function that takes an object of type `Foo`, which has -an `int bar()` method and an `int baz()` method. You want to constrain that the -argument's `bar()` value plus its `baz()` value is a given number. (This is an -invariant.) Here's how we can write and use a matcher class to do so: - -```cpp -class BarPlusBazEqMatcher { - public: - using is_gtest_matcher = void; - - explicit BarPlusBazEqMatcher(int expected_sum) - : expected_sum_(expected_sum) {} - - bool MatchAndExplain(const Foo& foo, - std::ostream* /* listener */) const { - return (foo.bar() + foo.baz()) == expected_sum_; - } - - void DescribeTo(std::ostream* os) const { - *os << "bar() + baz() equals " << expected_sum_; - } - - void DescribeNegationTo(std::ostream* os) const { - *os << "bar() + baz() does not equal " << expected_sum_; - } - private: - const int expected_sum_; -}; - -::testing::Matcher BarPlusBazEq(int expected_sum) { - return BarPlusBazEqMatcher(expected_sum); -} - -... - Foo foo; - EXPECT_THAT(foo, BarPlusBazEq(5))...; -``` - -### Matching Containers - -Sometimes an STL container (e.g. list, vector, map, ...) is passed to a mock -function and you may want to validate it. Since most STL containers support the -`==` operator, you can write `Eq(expected_container)` or simply -`expected_container` to match a container exactly. - -Sometimes, though, you may want to be more flexible (for example, the first -element must be an exact match, but the second element can be any positive -number, and so on). Also, containers used in tests often have a small number of -elements, and having to define the expected container out-of-line is a bit of a -hassle. - -You can use the `ElementsAre()` or `UnorderedElementsAre()` matcher in such -cases: - -```cpp -using ::testing::_; -using ::testing::ElementsAre; -using ::testing::Gt; -... - MOCK_METHOD(void, Foo, (const vector& numbers), (override)); -... - EXPECT_CALL(mock, Foo(ElementsAre(1, Gt(0), _, 5))); -``` - -The above matcher says that the container must have 4 elements, which must be 1, -greater than 0, anything, and 5 respectively. - -If you instead write: - -```cpp -using ::testing::_; -using ::testing::Gt; -using ::testing::UnorderedElementsAre; -... - MOCK_METHOD(void, Foo, (const vector& numbers), (override)); -... - EXPECT_CALL(mock, Foo(UnorderedElementsAre(1, Gt(0), _, 5))); -``` - -It means that the container must have 4 elements, which (under some permutation) -must be 1, greater than 0, anything, and 5 respectively. - -As an alternative you can place the arguments in a C-style array and use -`ElementsAreArray()` or `UnorderedElementsAreArray()` instead: - -```cpp -using ::testing::ElementsAreArray; -... - // ElementsAreArray accepts an array of element values. - const int expected_vector1[] = {1, 5, 2, 4, ...}; - EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector1))); - - // Or, an array of element matchers. - Matcher expected_vector2[] = {1, Gt(2), _, 3, ...}; - EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector2))); -``` - -In case the array needs to be dynamically created (and therefore the array size -cannot be inferred by the compiler), you can give `ElementsAreArray()` an -additional argument to specify the array size: - -```cpp -using ::testing::ElementsAreArray; -... - int* const expected_vector3 = new int[count]; - ... fill expected_vector3 with values ... - EXPECT_CALL(mock, Foo(ElementsAreArray(expected_vector3, count))); -``` - -Use `Pair` when comparing maps or other associative containers. - -{% raw %} - -```cpp -using ::testing::UnorderedElementsAre; -using ::testing::Pair; -... - absl::flat_hash_map m = {{"a", 1}, {"b", 2}, {"c", 3}}; - EXPECT_THAT(m, UnorderedElementsAre( - Pair("a", 1), Pair("b", 2), Pair("c", 3))); -``` - -{% endraw %} - -**Tips:** - -* `ElementsAre*()` can be used to match *any* container that implements the - STL iterator pattern (i.e. it has a `const_iterator` type and supports - `begin()/end()`), not just the ones defined in STL. It will even work with - container types yet to be written - as long as they follows the above - pattern. -* You can use nested `ElementsAre*()` to match nested (multi-dimensional) - containers. -* If the container is passed by pointer instead of by reference, just write - `Pointee(ElementsAre*(...))`. -* The order of elements *matters* for `ElementsAre*()`. If you are using it - with containers whose element order are undefined (such as a - `std::unordered_map`) you should use `UnorderedElementsAre`. - -### Sharing Matchers - -Under the hood, a gMock matcher object consists of a pointer to a ref-counted -implementation object. Copying matchers is allowed and very efficient, as only -the pointer is copied. When the last matcher that references the implementation -object dies, the implementation object will be deleted. - -Therefore, if you have some complex matcher that you want to use again and -again, there is no need to build it every time. Just assign it to a matcher -variable and use that variable repeatedly! For example, - -```cpp -using ::testing::AllOf; -using ::testing::Gt; -using ::testing::Le; -using ::testing::Matcher; -... - Matcher in_range = AllOf(Gt(5), Le(10)); - ... use in_range as a matcher in multiple EXPECT_CALLs ... -``` - -### Matchers must have no side-effects {#PureMatchers} - -{: .callout .warning} -WARNING: gMock does not guarantee when or how many times a matcher will be -invoked. Therefore, all matchers must be *purely functional*: they cannot have -any side effects, and the match result must not depend on anything other than -the matcher's parameters and the value being matched. - -This requirement must be satisfied no matter how a matcher is defined (e.g., if -it is one of the standard matchers, or a custom matcher). In particular, a -matcher can never call a mock function, as that will affect the state of the -mock object and gMock. - -## Setting Expectations - -### Knowing When to Expect {#UseOnCall} - -**`ON_CALL`** is likely the *single most under-utilized construct* in gMock. - -There are basically two constructs for defining the behavior of a mock object: -`ON_CALL` and `EXPECT_CALL`. The difference? `ON_CALL` defines what happens when -a mock method is called, but doesn't imply any expectation on the method -being called. `EXPECT_CALL` not only defines the behavior, but also sets an -expectation that the method will be called with the given arguments, for the -given number of times (and *in the given order* when you specify the order -too). - -Since `EXPECT_CALL` does more, isn't it better than `ON_CALL`? Not really. Every -`EXPECT_CALL` adds a constraint on the behavior of the code under test. Having -more constraints than necessary is *baaad* - even worse than not having enough -constraints. - -This may be counter-intuitive. How could tests that verify more be worse than -tests that verify less? Isn't verification the whole point of tests? - -The answer lies in *what* a test should verify. **A good test verifies the -contract of the code.** If a test over-specifies, it doesn't leave enough -freedom to the implementation. As a result, changing the implementation without -breaking the contract (e.g. refactoring and optimization), which should be -perfectly fine to do, can break such tests. Then you have to spend time fixing -them, only to see them broken again the next time the implementation is changed. - -Keep in mind that one doesn't have to verify more than one property in one test. -In fact, **it's a good style to verify only one thing in one test.** If you do -that, a bug will likely break only one or two tests instead of dozens (which -case would you rather debug?). If you are also in the habit of giving tests -descriptive names that tell what they verify, you can often easily guess what's -wrong just from the test log itself. - -So use `ON_CALL` by default, and only use `EXPECT_CALL` when you actually intend -to verify that the call is made. For example, you may have a bunch of `ON_CALL`s -in your test fixture to set the common mock behavior shared by all tests in the -same group, and write (scarcely) different `EXPECT_CALL`s in different `TEST_F`s -to verify different aspects of the code's behavior. Compared with the style -where each `TEST` has many `EXPECT_CALL`s, this leads to tests that are more -resilient to implementational changes (and thus less likely to require -maintenance) and makes the intent of the tests more obvious (so they are easier -to maintain when you do need to maintain them). - -If you are bothered by the "Uninteresting mock function call" message printed -when a mock method without an `EXPECT_CALL` is called, you may use a `NiceMock` -instead to suppress all such messages for the mock object, or suppress the -message for specific methods by adding `EXPECT_CALL(...).Times(AnyNumber())`. DO -NOT suppress it by blindly adding an `EXPECT_CALL(...)`, or you'll have a test -that's a pain to maintain. - -### Ignoring Uninteresting Calls - -If you are not interested in how a mock method is called, just don't say -anything about it. In this case, if the method is ever called, gMock will -perform its default action to allow the test program to continue. If you are not -happy with the default action taken by gMock, you can override it using -`DefaultValue::Set()` (described [here](#DefaultValue)) or `ON_CALL()`. - -Please note that once you expressed interest in a particular mock method (via -`EXPECT_CALL()`), all invocations to it must match some expectation. If this -function is called but the arguments don't match any `EXPECT_CALL()` statement, -it will be an error. - -### Disallowing Unexpected Calls - -If a mock method shouldn't be called at all, explicitly say so: - -```cpp -using ::testing::_; -... - EXPECT_CALL(foo, Bar(_)) - .Times(0); -``` - -If some calls to the method are allowed, but the rest are not, just list all the -expected calls: - -```cpp -using ::testing::AnyNumber; -using ::testing::Gt; -... - EXPECT_CALL(foo, Bar(5)); - EXPECT_CALL(foo, Bar(Gt(10))) - .Times(AnyNumber()); -``` - -A call to `foo.Bar()` that doesn't match any of the `EXPECT_CALL()` statements -will be an error. - -### Understanding Uninteresting vs Unexpected Calls {#uninteresting-vs-unexpected} - -*Uninteresting* calls and *unexpected* calls are different concepts in gMock. -*Very* different. - -A call `x.Y(...)` is **uninteresting** if there's *not even a single* -`EXPECT_CALL(x, Y(...))` set. In other words, the test isn't interested in the -`x.Y()` method at all, as evident in that the test doesn't care to say anything -about it. - -A call `x.Y(...)` is **unexpected** if there are *some* `EXPECT_CALL(x, -Y(...))`s set, but none of them matches the call. Put another way, the test is -interested in the `x.Y()` method (therefore it explicitly sets some -`EXPECT_CALL` to verify how it's called); however, the verification fails as the -test doesn't expect this particular call to happen. - -**An unexpected call is always an error,** as the code under test doesn't behave -the way the test expects it to behave. - -**By default, an uninteresting call is not an error,** as it violates no -constraint specified by the test. (gMock's philosophy is that saying nothing -means there is no constraint.) However, it leads to a warning, as it *might* -indicate a problem (e.g. the test author might have forgotten to specify a -constraint). - -In gMock, `NiceMock` and `StrictMock` can be used to make a mock class "nice" or -"strict". How does this affect uninteresting calls and unexpected calls? - -A **nice mock** suppresses uninteresting call *warnings*. It is less chatty than -the default mock, but otherwise is the same. If a test fails with a default -mock, it will also fail using a nice mock instead. And vice versa. Don't expect -making a mock nice to change the test's result. - -A **strict mock** turns uninteresting call warnings into errors. So making a -mock strict may change the test's result. - -Let's look at an example: - -```cpp -TEST(...) { - NiceMock mock_registry; - EXPECT_CALL(mock_registry, GetDomainOwner("google.com")) - .WillRepeatedly(Return("Larry Page")); - - // Use mock_registry in code under test. - ... &mock_registry ... -} -``` - -The sole `EXPECT_CALL` here says that all calls to `GetDomainOwner()` must have -`"google.com"` as the argument. If `GetDomainOwner("yahoo.com")` is called, it -will be an unexpected call, and thus an error. *Having a nice mock doesn't -change the severity of an unexpected call.* - -So how do we tell gMock that `GetDomainOwner()` can be called with some other -arguments as well? The standard technique is to add a "catch all" `EXPECT_CALL`: - -```cpp - EXPECT_CALL(mock_registry, GetDomainOwner(_)) - .Times(AnyNumber()); // catches all other calls to this method. - EXPECT_CALL(mock_registry, GetDomainOwner("google.com")) - .WillRepeatedly(Return("Larry Page")); -``` - -Remember that `_` is the wildcard matcher that matches anything. With this, if -`GetDomainOwner("google.com")` is called, it will do what the second -`EXPECT_CALL` says; if it is called with a different argument, it will do what -the first `EXPECT_CALL` says. - -Note that the order of the two `EXPECT_CALL`s is important, as a newer -`EXPECT_CALL` takes precedence over an older one. - -For more on uninteresting calls, nice mocks, and strict mocks, read -["The Nice, the Strict, and the Naggy"](#NiceStrictNaggy). - -### Ignoring Uninteresting Arguments {#ParameterlessExpectations} - -If your test doesn't care about the parameters (it only cares about the number -or order of calls), you can often simply omit the parameter list: - -```cpp - // Expect foo.Bar( ... ) twice with any arguments. - EXPECT_CALL(foo, Bar).Times(2); - - // Delegate to the given method whenever the factory is invoked. - ON_CALL(foo_factory, MakeFoo) - .WillByDefault(&BuildFooForTest); -``` - -This functionality is only available when a method is not overloaded; to prevent -unexpected behavior it is a compilation error to try to set an expectation on a -method where the specific overload is ambiguous. You can work around this by -supplying a [simpler mock interface](#SimplerInterfaces) than the mocked class -provides. - -This pattern is also useful when the arguments are interesting, but match logic -is substantially complex. You can leave the argument list unspecified and use -SaveArg actions to [save the values for later verification](#SaveArgVerify). If -you do that, you can easily differentiate calling the method the wrong number of -times from calling it with the wrong arguments. - -### Expecting Ordered Calls {#OrderedCalls} - -Although an `EXPECT_CALL()` statement defined later takes precedence when gMock -tries to match a function call with an expectation, by default calls don't have -to happen in the order `EXPECT_CALL()` statements are written. For example, if -the arguments match the matchers in the second `EXPECT_CALL()`, but not those in -the first and third, then the second expectation will be used. - -If you would rather have all calls occur in the order of the expectations, put -the `EXPECT_CALL()` statements in a block where you define a variable of type -`InSequence`: - -```cpp -using ::testing::_; -using ::testing::InSequence; - - { - InSequence s; - - EXPECT_CALL(foo, DoThis(5)); - EXPECT_CALL(bar, DoThat(_)) - .Times(2); - EXPECT_CALL(foo, DoThis(6)); - } -``` - -In this example, we expect a call to `foo.DoThis(5)`, followed by two calls to -`bar.DoThat()` where the argument can be anything, which are in turn followed by -a call to `foo.DoThis(6)`. If a call occurred out-of-order, gMock will report an -error. - -### Expecting Partially Ordered Calls {#PartialOrder} - -Sometimes requiring everything to occur in a predetermined order can lead to -brittle tests. For example, we may care about `A` occurring before both `B` and -`C`, but aren't interested in the relative order of `B` and `C`. In this case, -the test should reflect our real intent, instead of being overly constraining. - -gMock allows you to impose an arbitrary DAG (directed acyclic graph) on the -calls. One way to express the DAG is to use the -[`After` clause](reference/mocking.md#EXPECT_CALL.After) of `EXPECT_CALL`. - -Another way is via the `InSequence()` clause (not the same as the `InSequence` -class), which we borrowed from jMock 2. It's less flexible than `After()`, but -more convenient when you have long chains of sequential calls, as it doesn't -require you to come up with different names for the expectations in the chains. -Here's how it works: - -If we view `EXPECT_CALL()` statements as nodes in a graph, and add an edge from -node A to node B wherever A must occur before B, we can get a DAG. We use the -term "sequence" to mean a directed path in this DAG. Now, if we decompose the -DAG into sequences, we just need to know which sequences each `EXPECT_CALL()` -belongs to in order to be able to reconstruct the original DAG. - -So, to specify the partial order on the expectations we need to do two things: -first to define some `Sequence` objects, and then for each `EXPECT_CALL()` say -which `Sequence` objects it is part of. - -Expectations in the same sequence must occur in the order they are written. For -example, - -```cpp -using ::testing::Sequence; -... - Sequence s1, s2; - - EXPECT_CALL(foo, A()) - .InSequence(s1, s2); - EXPECT_CALL(bar, B()) - .InSequence(s1); - EXPECT_CALL(bar, C()) - .InSequence(s2); - EXPECT_CALL(foo, D()) - .InSequence(s2); -``` - -specifies the following DAG (where `s1` is `A -> B`, and `s2` is `A -> C -> D`): - -```text - +---> B - | - A ---| - | - +---> C ---> D -``` - -This means that A must occur before B and C, and C must occur before D. There's -no restriction about the order other than these. - -### Controlling When an Expectation Retires - -When a mock method is called, gMock only considers expectations that are still -active. An expectation is active when created, and becomes inactive (aka -*retires*) when a call that has to occur later has occurred. For example, in - -```cpp -using ::testing::_; -using ::testing::Sequence; -... - Sequence s1, s2; - - EXPECT_CALL(log, Log(WARNING, _, "File too large.")) // #1 - .Times(AnyNumber()) - .InSequence(s1, s2); - EXPECT_CALL(log, Log(WARNING, _, "Data set is empty.")) // #2 - .InSequence(s1); - EXPECT_CALL(log, Log(WARNING, _, "User not found.")) // #3 - .InSequence(s2); -``` - -as soon as either #2 or #3 is matched, #1 will retire. If a warning `"File too -large."` is logged after this, it will be an error. - -Note that an expectation doesn't retire automatically when it's saturated. For -example, - -```cpp -using ::testing::_; -... - EXPECT_CALL(log, Log(WARNING, _, _)); // #1 - EXPECT_CALL(log, Log(WARNING, _, "File too large.")); // #2 -``` - -says that there will be exactly one warning with the message `"File too -large."`. If the second warning contains this message too, #2 will match again -and result in an upper-bound-violated error. - -If this is not what you want, you can ask an expectation to retire as soon as it -becomes saturated: - -```cpp -using ::testing::_; -... - EXPECT_CALL(log, Log(WARNING, _, _)); // #1 - EXPECT_CALL(log, Log(WARNING, _, "File too large.")) // #2 - .RetiresOnSaturation(); -``` - -Here #2 can be used only once, so if you have two warnings with the message -`"File too large."`, the first will match #2 and the second will match #1 - -there will be no error. - -## Using Actions - -### Returning References from Mock Methods - -If a mock function's return type is a reference, you need to use `ReturnRef()` -instead of `Return()` to return a result: - -```cpp -using ::testing::ReturnRef; - -class MockFoo : public Foo { - public: - MOCK_METHOD(Bar&, GetBar, (), (override)); -}; -... - MockFoo foo; - Bar bar; - EXPECT_CALL(foo, GetBar()) - .WillOnce(ReturnRef(bar)); -... -``` - -### Returning Live Values from Mock Methods - -The `Return(x)` action saves a copy of `x` when the action is created, and -always returns the same value whenever it's executed. Sometimes you may want to -instead return the *live* value of `x` (i.e. its value at the time when the -action is *executed*.). Use either `ReturnRef()` or `ReturnPointee()` for this -purpose. - -If the mock function's return type is a reference, you can do it using -`ReturnRef(x)`, as shown in the previous recipe ("Returning References from Mock -Methods"). However, gMock doesn't let you use `ReturnRef()` in a mock function -whose return type is not a reference, as doing that usually indicates a user -error. So, what shall you do? - -Though you may be tempted, DO NOT use `std::ref()`: - -```cpp -using ::testing::Return; - -class MockFoo : public Foo { - public: - MOCK_METHOD(int, GetValue, (), (override)); -}; -... - int x = 0; - MockFoo foo; - EXPECT_CALL(foo, GetValue()) - .WillRepeatedly(Return(std::ref(x))); // Wrong! - x = 42; - EXPECT_EQ(foo.GetValue(), 42); -``` - -Unfortunately, it doesn't work here. The above code will fail with error: - -```text -Value of: foo.GetValue() - Actual: 0 -Expected: 42 -``` - -The reason is that `Return(*value*)` converts `value` to the actual return type -of the mock function at the time when the action is *created*, not when it is -*executed*. (This behavior was chosen for the action to be safe when `value` is -a proxy object that references some temporary objects.) As a result, -`std::ref(x)` is converted to an `int` value (instead of a `const int&`) when -the expectation is set, and `Return(std::ref(x))` will always return 0. - -`ReturnPointee(pointer)` was provided to solve this problem specifically. It -returns the value pointed to by `pointer` at the time the action is *executed*: - -```cpp -using ::testing::ReturnPointee; -... - int x = 0; - MockFoo foo; - EXPECT_CALL(foo, GetValue()) - .WillRepeatedly(ReturnPointee(&x)); // Note the & here. - x = 42; - EXPECT_EQ(foo.GetValue(), 42); // This will succeed now. -``` - -### Combining Actions - -Want to do more than one thing when a function is called? That's fine. `DoAll()` -allows you to do a sequence of actions every time. Only the return value of the -last action in the sequence will be used. - -```cpp -using ::testing::_; -using ::testing::DoAll; - -class MockFoo : public Foo { - public: - MOCK_METHOD(bool, Bar, (int n), (override)); -}; -... - EXPECT_CALL(foo, Bar(_)) - .WillOnce(DoAll(action_1, - action_2, - ... - action_n)); -``` - -The return value of the last action **must** match the return type of the mocked -method. In the example above, `action_n` could be `Return(true)`, or a lambda -that returns a `bool`, but not `SaveArg`, which returns `void`. Otherwise the -signature of `DoAll` would not match the signature expected by `WillOnce`, which -is the signature of the mocked method, and it wouldn't compile. - -### Verifying Complex Arguments {#SaveArgVerify} - -If you want to verify that a method is called with a particular argument but the -match criteria is complex, it can be difficult to distinguish between -cardinality failures (calling the method the wrong number of times) and argument -match failures. Similarly, if you are matching multiple parameters, it may not -be easy to distinguishing which argument failed to match. For example: - -```cpp - // Not ideal: this could fail because of a problem with arg1 or arg2, or maybe - // just the method wasn't called. - EXPECT_CALL(foo, SendValues(_, ElementsAre(1, 4, 4, 7), EqualsProto( ... ))); -``` - -You can instead save the arguments and test them individually: - -```cpp - EXPECT_CALL(foo, SendValues) - .WillOnce(DoAll(SaveArg<1>(&actual_array), SaveArg<2>(&actual_proto))); - ... run the test - EXPECT_THAT(actual_array, ElementsAre(1, 4, 4, 7)); - EXPECT_THAT(actual_proto, EqualsProto( ... )); -``` - -### Mocking Side Effects {#MockingSideEffects} - -Sometimes a method exhibits its effect not via returning a value but via side -effects. For example, it may change some global state or modify an output -argument. To mock side effects, in general you can define your own action by -implementing `::testing::ActionInterface`. - -If all you need to do is to change an output argument, the built-in -`SetArgPointee()` action is convenient: - -```cpp -using ::testing::_; -using ::testing::SetArgPointee; - -class MockMutator : public Mutator { - public: - MOCK_METHOD(void, Mutate, (bool mutate, int* value), (override)); - ... -} -... - MockMutator mutator; - EXPECT_CALL(mutator, Mutate(true, _)) - .WillOnce(SetArgPointee<1>(5)); -``` - -In this example, when `mutator.Mutate()` is called, we will assign 5 to the -`int` variable pointed to by argument #1 (0-based). - -`SetArgPointee()` conveniently makes an internal copy of the value you pass to -it, removing the need to keep the value in scope and alive. The implication -however is that the value must have a copy constructor and assignment operator. - -If the mock method also needs to return a value as well, you can chain -`SetArgPointee()` with `Return()` using `DoAll()`, remembering to put the -`Return()` statement last: - -```cpp -using ::testing::_; -using ::testing::DoAll; -using ::testing::Return; -using ::testing::SetArgPointee; - -class MockMutator : public Mutator { - public: - ... - MOCK_METHOD(bool, MutateInt, (int* value), (override)); -} -... - MockMutator mutator; - EXPECT_CALL(mutator, MutateInt(_)) - .WillOnce(DoAll(SetArgPointee<0>(5), - Return(true))); -``` - -Note, however, that if you use the `ReturnOKWith()` method, it will override the -values provided by `SetArgPointee()` in the response parameters of your function -call. - -If the output argument is an array, use the `SetArrayArgument(first, last)` -action instead. It copies the elements in source range `[first, last)` to the -array pointed to by the `N`-th (0-based) argument: - -```cpp -using ::testing::NotNull; -using ::testing::SetArrayArgument; - -class MockArrayMutator : public ArrayMutator { - public: - MOCK_METHOD(void, Mutate, (int* values, int num_values), (override)); - ... -} -... - MockArrayMutator mutator; - int values[5] = {1, 2, 3, 4, 5}; - EXPECT_CALL(mutator, Mutate(NotNull(), 5)) - .WillOnce(SetArrayArgument<0>(values, values + 5)); -``` - -This also works when the argument is an output iterator: - -```cpp -using ::testing::_; -using ::testing::SetArrayArgument; - -class MockRolodex : public Rolodex { - public: - MOCK_METHOD(void, GetNames, (std::back_insert_iterator>), - (override)); - ... -} -... - MockRolodex rolodex; - vector names = {"George", "John", "Thomas"}; - EXPECT_CALL(rolodex, GetNames(_)) - .WillOnce(SetArrayArgument<0>(names.begin(), names.end())); -``` - -### Changing a Mock Object's Behavior Based on the State - -If you expect a call to change the behavior of a mock object, you can use -`::testing::InSequence` to specify different behaviors before and after the -call: - -```cpp -using ::testing::InSequence; -using ::testing::Return; - -... - { - InSequence seq; - EXPECT_CALL(my_mock, IsDirty()) - .WillRepeatedly(Return(true)); - EXPECT_CALL(my_mock, Flush()); - EXPECT_CALL(my_mock, IsDirty()) - .WillRepeatedly(Return(false)); - } - my_mock.FlushIfDirty(); -``` - -This makes `my_mock.IsDirty()` return `true` before `my_mock.Flush()` is called -and return `false` afterwards. - -If the behavior change is more complex, you can store the effects in a variable -and make a mock method get its return value from that variable: - -```cpp -using ::testing::_; -using ::testing::SaveArg; -using ::testing::Return; - -ACTION_P(ReturnPointee, p) { return *p; } -... - int previous_value = 0; - EXPECT_CALL(my_mock, GetPrevValue) - .WillRepeatedly(ReturnPointee(&previous_value)); - EXPECT_CALL(my_mock, UpdateValue) - .WillRepeatedly(SaveArg<0>(&previous_value)); - my_mock.DoSomethingToUpdateValue(); -``` - -Here `my_mock.GetPrevValue()` will always return the argument of the last -`UpdateValue()` call. - -### Setting the Default Value for a Return Type {#DefaultValue} - -If a mock method's return type is a built-in C++ type or pointer, by default it -will return 0 when invoked. Also, in C++ 11 and above, a mock method whose -return type has a default constructor will return a default-constructed value by -default. You only need to specify an action if this default value doesn't work -for you. - -Sometimes, you may want to change this default value, or you may want to specify -a default value for types gMock doesn't know about. You can do this using the -`::testing::DefaultValue` class template: - -```cpp -using ::testing::DefaultValue; - -class MockFoo : public Foo { - public: - MOCK_METHOD(Bar, CalculateBar, (), (override)); -}; - - -... - Bar default_bar; - // Sets the default return value for type Bar. - DefaultValue::Set(default_bar); - - MockFoo foo; - - // We don't need to specify an action here, as the default - // return value works for us. - EXPECT_CALL(foo, CalculateBar()); - - foo.CalculateBar(); // This should return default_bar. - - // Unsets the default return value. - DefaultValue::Clear(); -``` - -Please note that changing the default value for a type can make your tests hard -to understand. We recommend you to use this feature judiciously. For example, -you may want to make sure the `Set()` and `Clear()` calls are right next to the -code that uses your mock. - -### Setting the Default Actions for a Mock Method - -You've learned how to change the default value of a given type. However, this -may be too coarse for your purpose: perhaps you have two mock methods with the -same return type and you want them to have different behaviors. The `ON_CALL()` -macro allows you to customize your mock's behavior at the method level: - -```cpp -using ::testing::_; -using ::testing::AnyNumber; -using ::testing::Gt; -using ::testing::Return; -... - ON_CALL(foo, Sign(_)) - .WillByDefault(Return(-1)); - ON_CALL(foo, Sign(0)) - .WillByDefault(Return(0)); - ON_CALL(foo, Sign(Gt(0))) - .WillByDefault(Return(1)); - - EXPECT_CALL(foo, Sign(_)) - .Times(AnyNumber()); - - foo.Sign(5); // This should return 1. - foo.Sign(-9); // This should return -1. - foo.Sign(0); // This should return 0. -``` - -As you may have guessed, when there are more than one `ON_CALL()` statements, -the newer ones in the order take precedence over the older ones. In other words, -the **last** one that matches the function arguments will be used. This matching -order allows you to set up the common behavior in a mock object's constructor or -the test fixture's set-up phase and specialize the mock's behavior later. - -Note that both `ON_CALL` and `EXPECT_CALL` have the same "later statements take -precedence" rule, but they don't interact. That is, `EXPECT_CALL`s have their -own precedence order distinct from the `ON_CALL` precedence order. - -### Using Functions/Methods/Functors/Lambdas as Actions {#FunctionsAsActions} - -If the built-in actions don't suit you, you can use an existing callable -(function, `std::function`, method, functor, lambda) as an action. - -```cpp -using ::testing::_; using ::testing::Invoke; - -class MockFoo : public Foo { - public: - MOCK_METHOD(int, Sum, (int x, int y), (override)); - MOCK_METHOD(bool, ComplexJob, (int x), (override)); -}; - -int CalculateSum(int x, int y) { return x + y; } -int Sum3(int x, int y, int z) { return x + y + z; } - -class Helper { - public: - bool ComplexJob(int x); -}; - -... - MockFoo foo; - Helper helper; - EXPECT_CALL(foo, Sum(_, _)) - .WillOnce(&CalculateSum) - .WillRepeatedly(Invoke(NewPermanentCallback(Sum3, 1))); - EXPECT_CALL(foo, ComplexJob(_)) - .WillOnce(Invoke(&helper, &Helper::ComplexJob)) - .WillOnce([] { return true; }) - .WillRepeatedly([](int x) { return x > 0; }); - - foo.Sum(5, 6); // Invokes CalculateSum(5, 6). - foo.Sum(2, 3); // Invokes Sum3(1, 2, 3). - foo.ComplexJob(10); // Invokes helper.ComplexJob(10). - foo.ComplexJob(-1); // Invokes the inline lambda. -``` - -The only requirement is that the type of the function, etc must be *compatible* -with the signature of the mock function, meaning that the latter's arguments (if -it takes any) can be implicitly converted to the corresponding arguments of the -former, and the former's return type can be implicitly converted to that of the -latter. So, you can invoke something whose type is *not* exactly the same as the -mock function, as long as it's safe to do so - nice, huh? - -Note that: - -* The action takes ownership of the callback and will delete it when the - action itself is destructed. -* If the type of a callback is derived from a base callback type `C`, you need - to implicitly cast it to `C` to resolve the overloading, e.g. - - ```cpp - using ::testing::Invoke; - ... - ResultCallback* is_ok = ...; - ... Invoke(is_ok) ...; // This works. - - BlockingClosure* done = new BlockingClosure; - ... Invoke(implicit_cast(done)) ...; // The cast is necessary. - ``` - -### Using Functions with Extra Info as Actions - -The function or functor you call using `Invoke()` must have the same number of -arguments as the mock function you use it for. Sometimes you may have a function -that takes more arguments, and you are willing to pass in the extra arguments -yourself to fill the gap. You can do this in gMock using callbacks with -pre-bound arguments. Here's an example: - -```cpp -using ::testing::Invoke; - -class MockFoo : public Foo { - public: - MOCK_METHOD(char, DoThis, (int n), (override)); -}; - -char SignOfSum(int x, int y) { - const int sum = x + y; - return (sum > 0) ? '+' : (sum < 0) ? '-' : '0'; -} - -TEST_F(FooTest, Test) { - MockFoo foo; - - EXPECT_CALL(foo, DoThis(2)) - .WillOnce(Invoke(NewPermanentCallback(SignOfSum, 5))); - EXPECT_EQ(foo.DoThis(2), '+'); // Invokes SignOfSum(5, 2). -} -``` - -### Invoking a Function/Method/Functor/Lambda/Callback Without Arguments - -`Invoke()` passes the mock function's arguments to the function, etc being -invoked such that the callee has the full context of the call to work with. If -the invoked function is not interested in some or all of the arguments, it can -simply ignore them. - -Yet, a common pattern is that a test author wants to invoke a function without -the arguments of the mock function. She could do that using a wrapper function -that throws away the arguments before invoking an underlining nullary function. -Needless to say, this can be tedious and obscures the intent of the test. - -There are two solutions to this problem. First, you can pass any callable of -zero args as an action. Alternatively, use `InvokeWithoutArgs()`, which is like -`Invoke()` except that it doesn't pass the mock function's arguments to the -callee. Here's an example of each: - -```cpp -using ::testing::_; -using ::testing::InvokeWithoutArgs; - -class MockFoo : public Foo { - public: - MOCK_METHOD(bool, ComplexJob, (int n), (override)); -}; - -bool Job1() { ... } -bool Job2(int n, char c) { ... } - -... - MockFoo foo; - EXPECT_CALL(foo, ComplexJob(_)) - .WillOnce([] { Job1(); }); - .WillOnce(InvokeWithoutArgs(NewPermanentCallback(Job2, 5, 'a'))); - - foo.ComplexJob(10); // Invokes Job1(). - foo.ComplexJob(20); // Invokes Job2(5, 'a'). -``` - -Note that: - -* The action takes ownership of the callback and will delete it when the - action itself is destructed. -* If the type of a callback is derived from a base callback type `C`, you need - to implicitly cast it to `C` to resolve the overloading, e.g. - - ```cpp - using ::testing::InvokeWithoutArgs; - ... - ResultCallback* is_ok = ...; - ... InvokeWithoutArgs(is_ok) ...; // This works. - - BlockingClosure* done = ...; - ... InvokeWithoutArgs(implicit_cast(done)) ...; - // The cast is necessary. - ``` - -### Invoking an Argument of the Mock Function - -Sometimes a mock function will receive a function pointer, a functor (in other -words, a "callable") as an argument, e.g. - -```cpp -class MockFoo : public Foo { - public: - MOCK_METHOD(bool, DoThis, (int n, (ResultCallback1* callback)), - (override)); -}; -``` - -and you may want to invoke this callable argument: - -```cpp -using ::testing::_; -... - MockFoo foo; - EXPECT_CALL(foo, DoThis(_, _)) - .WillOnce(...); - // Will execute callback->Run(5), where callback is the - // second argument DoThis() receives. -``` - -{: .callout .note} -NOTE: The section below is legacy documentation from before C++ had lambdas: - -Arghh, you need to refer to a mock function argument but C++ has no lambda -(yet), so you have to define your own action. :-( Or do you really? - -Well, gMock has an action to solve *exactly* this problem: - -```cpp -InvokeArgument(arg_1, arg_2, ..., arg_m) -``` - -will invoke the `N`-th (0-based) argument the mock function receives, with -`arg_1`, `arg_2`, ..., and `arg_m`. No matter if the argument is a function -pointer, a functor, or a callback. gMock handles them all. - -With that, you could write: - -```cpp -using ::testing::_; -using ::testing::InvokeArgument; -... - EXPECT_CALL(foo, DoThis(_, _)) - .WillOnce(InvokeArgument<1>(5)); - // Will execute callback->Run(5), where callback is the - // second argument DoThis() receives. -``` - -What if the callable takes an argument by reference? No problem - just wrap it -inside `std::ref()`: - -```cpp - ... - MOCK_METHOD(bool, Bar, - ((ResultCallback2* callback)), - (override)); - ... - using ::testing::_; - using ::testing::InvokeArgument; - ... - MockFoo foo; - Helper helper; - ... - EXPECT_CALL(foo, Bar(_)) - .WillOnce(InvokeArgument<0>(5, std::ref(helper))); - // std::ref(helper) guarantees that a reference to helper, not a copy of - // it, will be passed to the callback. -``` - -What if the callable takes an argument by reference and we do **not** wrap the -argument in `std::ref()`? Then `InvokeArgument()` will *make a copy* of the -argument, and pass a *reference to the copy*, instead of a reference to the -original value, to the callable. This is especially handy when the argument is a -temporary value: - -```cpp - ... - MOCK_METHOD(bool, DoThat, (bool (*f)(const double& x, const string& s)), - (override)); - ... - using ::testing::_; - using ::testing::InvokeArgument; - ... - MockFoo foo; - ... - EXPECT_CALL(foo, DoThat(_)) - .WillOnce(InvokeArgument<0>(5.0, string("Hi"))); - // Will execute (*f)(5.0, string("Hi")), where f is the function pointer - // DoThat() receives. Note that the values 5.0 and string("Hi") are - // temporary and dead once the EXPECT_CALL() statement finishes. Yet - // it's fine to perform this action later, since a copy of the values - // are kept inside the InvokeArgument action. -``` - -### Ignoring an Action's Result - -Sometimes you have an action that returns *something*, but you need an action -that returns `void` (perhaps you want to use it in a mock function that returns -`void`, or perhaps it needs to be used in `DoAll()` and it's not the last in the -list). `IgnoreResult()` lets you do that. For example: - -```cpp -using ::testing::_; -using ::testing::DoAll; -using ::testing::IgnoreResult; -using ::testing::Return; - -int Process(const MyData& data); -string DoSomething(); - -class MockFoo : public Foo { - public: - MOCK_METHOD(void, Abc, (const MyData& data), (override)); - MOCK_METHOD(bool, Xyz, (), (override)); -}; - - ... - MockFoo foo; - EXPECT_CALL(foo, Abc(_)) - // .WillOnce(Invoke(Process)); - // The above line won't compile as Process() returns int but Abc() needs - // to return void. - .WillOnce(IgnoreResult(Process)); - EXPECT_CALL(foo, Xyz()) - .WillOnce(DoAll(IgnoreResult(DoSomething), - // Ignores the string DoSomething() returns. - Return(true))); -``` - -Note that you **cannot** use `IgnoreResult()` on an action that already returns -`void`. Doing so will lead to ugly compiler errors. - -### Selecting an Action's Arguments {#SelectingArgs} - -Say you have a mock function `Foo()` that takes seven arguments, and you have a -custom action that you want to invoke when `Foo()` is called. Trouble is, the -custom action only wants three arguments: - -```cpp -using ::testing::_; -using ::testing::Invoke; -... - MOCK_METHOD(bool, Foo, - (bool visible, const string& name, int x, int y, - (const map>), double& weight, double min_weight, - double max_wight)); -... -bool IsVisibleInQuadrant1(bool visible, int x, int y) { - return visible && x >= 0 && y >= 0; -} -... - EXPECT_CALL(mock, Foo) - .WillOnce(Invoke(IsVisibleInQuadrant1)); // Uh, won't compile. :-( -``` - -To please the compiler God, you need to define an "adaptor" that has the same -signature as `Foo()` and calls the custom action with the right arguments: - -```cpp -using ::testing::_; -using ::testing::Invoke; -... -bool MyIsVisibleInQuadrant1(bool visible, const string& name, int x, int y, - const map, double>& weight, - double min_weight, double max_wight) { - return IsVisibleInQuadrant1(visible, x, y); -} -... - EXPECT_CALL(mock, Foo) - .WillOnce(Invoke(MyIsVisibleInQuadrant1)); // Now it works. -``` - -But isn't this awkward? - -gMock provides a generic *action adaptor*, so you can spend your time minding -more important business than writing your own adaptors. Here's the syntax: - -```cpp -WithArgs(action) -``` - -creates an action that passes the arguments of the mock function at the given -indices (0-based) to the inner `action` and performs it. Using `WithArgs`, our -original example can be written as: - -```cpp -using ::testing::_; -using ::testing::Invoke; -using ::testing::WithArgs; -... - EXPECT_CALL(mock, Foo) - .WillOnce(WithArgs<0, 2, 3>(Invoke(IsVisibleInQuadrant1))); // No need to define your own adaptor. -``` - -For better readability, gMock also gives you: - -* `WithoutArgs(action)` when the inner `action` takes *no* argument, and -* `WithArg(action)` (no `s` after `Arg`) when the inner `action` takes - *one* argument. - -As you may have realized, `InvokeWithoutArgs(...)` is just syntactic sugar for -`WithoutArgs(Invoke(...))`. - -Here are more tips: - -* The inner action used in `WithArgs` and friends does not have to be - `Invoke()` -- it can be anything. -* You can repeat an argument in the argument list if necessary, e.g. - `WithArgs<2, 3, 3, 5>(...)`. -* You can change the order of the arguments, e.g. `WithArgs<3, 2, 1>(...)`. -* The types of the selected arguments do *not* have to match the signature of - the inner action exactly. It works as long as they can be implicitly - converted to the corresponding arguments of the inner action. For example, - if the 4-th argument of the mock function is an `int` and `my_action` takes - a `double`, `WithArg<4>(my_action)` will work. - -### Ignoring Arguments in Action Functions - -The [selecting-an-action's-arguments](#SelectingArgs) recipe showed us one way -to make a mock function and an action with incompatible argument lists fit -together. The downside is that wrapping the action in `WithArgs<...>()` can get -tedious for people writing the tests. - -If you are defining a function (or method, functor, lambda, callback) to be used -with `Invoke*()`, and you are not interested in some of its arguments, an -alternative to `WithArgs` is to declare the uninteresting arguments as `Unused`. -This makes the definition less cluttered and less fragile in case the types of -the uninteresting arguments change. It could also increase the chance the action -function can be reused. For example, given - -```cpp - public: - MOCK_METHOD(double, Foo, double(const string& label, double x, double y), - (override)); - MOCK_METHOD(double, Bar, (int index, double x, double y), (override)); -``` - -instead of - -```cpp -using ::testing::_; -using ::testing::Invoke; - -double DistanceToOriginWithLabel(const string& label, double x, double y) { - return sqrt(x*x + y*y); -} -double DistanceToOriginWithIndex(int index, double x, double y) { - return sqrt(x*x + y*y); -} -... - EXPECT_CALL(mock, Foo("abc", _, _)) - .WillOnce(Invoke(DistanceToOriginWithLabel)); - EXPECT_CALL(mock, Bar(5, _, _)) - .WillOnce(Invoke(DistanceToOriginWithIndex)); -``` - -you could write - -```cpp -using ::testing::_; -using ::testing::Invoke; -using ::testing::Unused; - -double DistanceToOrigin(Unused, double x, double y) { - return sqrt(x*x + y*y); -} -... - EXPECT_CALL(mock, Foo("abc", _, _)) - .WillOnce(Invoke(DistanceToOrigin)); - EXPECT_CALL(mock, Bar(5, _, _)) - .WillOnce(Invoke(DistanceToOrigin)); -``` - -### Sharing Actions - -Just like matchers, a gMock action object consists of a pointer to a ref-counted -implementation object. Therefore copying actions is also allowed and very -efficient. When the last action that references the implementation object dies, -the implementation object will be deleted. - -If you have some complex action that you want to use again and again, you may -not have to build it from scratch every time. If the action doesn't have an -internal state (i.e. if it always does the same thing no matter how many times -it has been called), you can assign it to an action variable and use that -variable repeatedly. For example: - -```cpp -using ::testing::Action; -using ::testing::DoAll; -using ::testing::Return; -using ::testing::SetArgPointee; -... - Action set_flag = DoAll(SetArgPointee<0>(5), - Return(true)); - ... use set_flag in .WillOnce() and .WillRepeatedly() ... -``` - -However, if the action has its own state, you may be surprised if you share the -action object. Suppose you have an action factory `IncrementCounter(init)` which -creates an action that increments and returns a counter whose initial value is -`init`, using two actions created from the same expression and using a shared -action will exhibit different behaviors. Example: - -```cpp - EXPECT_CALL(foo, DoThis()) - .WillRepeatedly(IncrementCounter(0)); - EXPECT_CALL(foo, DoThat()) - .WillRepeatedly(IncrementCounter(0)); - foo.DoThis(); // Returns 1. - foo.DoThis(); // Returns 2. - foo.DoThat(); // Returns 1 - DoThat() uses a different - // counter than DoThis()'s. -``` - -versus - -```cpp -using ::testing::Action; -... - Action increment = IncrementCounter(0); - EXPECT_CALL(foo, DoThis()) - .WillRepeatedly(increment); - EXPECT_CALL(foo, DoThat()) - .WillRepeatedly(increment); - foo.DoThis(); // Returns 1. - foo.DoThis(); // Returns 2. - foo.DoThat(); // Returns 3 - the counter is shared. -``` - -### Testing Asynchronous Behavior - -One oft-encountered problem with gMock is that it can be hard to test -asynchronous behavior. Suppose you had a `EventQueue` class that you wanted to -test, and you created a separate `EventDispatcher` interface so that you could -easily mock it out. However, the implementation of the class fired all the -events on a background thread, which made test timings difficult. You could just -insert `sleep()` statements and hope for the best, but that makes your test -behavior nondeterministic. A better way is to use gMock actions and -`Notification` objects to force your asynchronous test to behave synchronously. - -```cpp -class MockEventDispatcher : public EventDispatcher { - MOCK_METHOD(bool, DispatchEvent, (int32), (override)); -}; - -TEST(EventQueueTest, EnqueueEventTest) { - MockEventDispatcher mock_event_dispatcher; - EventQueue event_queue(&mock_event_dispatcher); - - const int32 kEventId = 321; - absl::Notification done; - EXPECT_CALL(mock_event_dispatcher, DispatchEvent(kEventId)) - .WillOnce([&done] { done.Notify(); }); - - event_queue.EnqueueEvent(kEventId); - done.WaitForNotification(); -} -``` - -In the example above, we set our normal gMock expectations, but then add an -additional action to notify the `Notification` object. Now we can just call -`Notification::WaitForNotification()` in the main thread to wait for the -asynchronous call to finish. After that, our test suite is complete and we can -safely exit. - -{: .callout .note} -Note: this example has a downside: namely, if the expectation is not satisfied, -our test will run forever. It will eventually time-out and fail, but it will -take longer and be slightly harder to debug. To alleviate this problem, you can -use `WaitForNotificationWithTimeout(ms)` instead of `WaitForNotification()`. - -## Misc Recipes on Using gMock - -### Mocking Methods That Use Move-Only Types - -C++11 introduced *move-only types*. A move-only-typed value can be moved from -one object to another, but cannot be copied. `std::unique_ptr` is probably -the most commonly used move-only type. - -Mocking a method that takes and/or returns move-only types presents some -challenges, but nothing insurmountable. This recipe shows you how you can do it. -Note that the support for move-only method arguments was only introduced to -gMock in April 2017; in older code, you may find more complex -[workarounds](#LegacyMoveOnly) for lack of this feature. - -Let’s say we are working on a fictional project that lets one post and share -snippets called “buzzes”. Your code uses these types: - -```cpp -enum class AccessLevel { kInternal, kPublic }; - -class Buzz { - public: - explicit Buzz(AccessLevel access) { ... } - ... -}; - -class Buzzer { - public: - virtual ~Buzzer() {} - virtual std::unique_ptr MakeBuzz(StringPiece text) = 0; - virtual bool ShareBuzz(std::unique_ptr buzz, int64_t timestamp) = 0; - ... -}; -``` - -A `Buzz` object represents a snippet being posted. A class that implements the -`Buzzer` interface is capable of creating and sharing `Buzz`es. Methods in -`Buzzer` may return a `unique_ptr` or take a `unique_ptr`. Now we -need to mock `Buzzer` in our tests. - -To mock a method that accepts or returns move-only types, you just use the -familiar `MOCK_METHOD` syntax as usual: - -```cpp -class MockBuzzer : public Buzzer { - public: - MOCK_METHOD(std::unique_ptr, MakeBuzz, (StringPiece text), (override)); - MOCK_METHOD(bool, ShareBuzz, (std::unique_ptr buzz, int64_t timestamp), - (override)); -}; -``` - -Now that we have the mock class defined, we can use it in tests. In the -following code examples, we assume that we have defined a `MockBuzzer` object -named `mock_buzzer_`: - -```cpp - MockBuzzer mock_buzzer_; -``` - -First let’s see how we can set expectations on the `MakeBuzz()` method, which -returns a `unique_ptr`. - -As usual, if you set an expectation without an action (i.e. the `.WillOnce()` or -`.WillRepeatedly()` clause), when that expectation fires, the default action for -that method will be taken. Since `unique_ptr<>` has a default constructor that -returns a null `unique_ptr`, that’s what you’ll get if you don’t specify an -action: - -```cpp -using ::testing::IsNull; -... - // Use the default action. - EXPECT_CALL(mock_buzzer_, MakeBuzz("hello")); - - // Triggers the previous EXPECT_CALL. - EXPECT_THAT(mock_buzzer_.MakeBuzz("hello"), IsNull()); -``` - -If you are not happy with the default action, you can tweak it as usual; see -[Setting Default Actions](#OnCall). - -If you just need to return a move-only value, you can use it in combination with -`WillOnce`. For example: - -```cpp - EXPECT_CALL(mock_buzzer_, MakeBuzz("hello")) - .WillOnce(Return(std::make_unique(AccessLevel::kInternal))); - EXPECT_NE(nullptr, mock_buzzer_.MakeBuzz("hello")); -``` - -Quiz time! What do you think will happen if a `Return` action is performed more -than once (e.g. you write `... .WillRepeatedly(Return(std::move(...)));`)? Come -think of it, after the first time the action runs, the source value will be -consumed (since it’s a move-only value), so the next time around, there’s no -value to move from -- you’ll get a run-time error that `Return(std::move(...))` -can only be run once. - -If you need your mock method to do more than just moving a pre-defined value, -remember that you can always use a lambda or a callable object, which can do -pretty much anything you want: - -```cpp - EXPECT_CALL(mock_buzzer_, MakeBuzz("x")) - .WillRepeatedly([](StringPiece text) { - return std::make_unique(AccessLevel::kInternal); - }); - - EXPECT_NE(nullptr, mock_buzzer_.MakeBuzz("x")); - EXPECT_NE(nullptr, mock_buzzer_.MakeBuzz("x")); -``` - -Every time this `EXPECT_CALL` fires, a new `unique_ptr` will be created -and returned. You cannot do this with `Return(std::make_unique<...>(...))`. - -That covers returning move-only values; but how do we work with methods -accepting move-only arguments? The answer is that they work normally, although -some actions will not compile when any of method's arguments are move-only. You -can always use `Return`, or a [lambda or functor](#FunctionsAsActions): - -```cpp - using ::testing::Unused; - - EXPECT_CALL(mock_buzzer_, ShareBuzz(NotNull(), _)).WillOnce(Return(true)); - EXPECT_TRUE(mock_buzzer_.ShareBuzz(std::make_unique(AccessLevel::kInternal)), - 0); - - EXPECT_CALL(mock_buzzer_, ShareBuzz(_, _)).WillOnce( - [](std::unique_ptr buzz, Unused) { return buzz != nullptr; }); - EXPECT_FALSE(mock_buzzer_.ShareBuzz(nullptr, 0)); -``` - -Many built-in actions (`WithArgs`, `WithoutArgs`,`DeleteArg`, `SaveArg`, ...) -could in principle support move-only arguments, but the support for this is not -implemented yet. If this is blocking you, please file a bug. - -A few actions (e.g. `DoAll`) copy their arguments internally, so they can never -work with non-copyable objects; you'll have to use functors instead. - -#### Legacy workarounds for move-only types {#LegacyMoveOnly} - -Support for move-only function arguments was only introduced to gMock in April -of 2017. In older code, you may encounter the following workaround for the lack -of this feature (it is no longer necessary - we're including it just for -reference): - -```cpp -class MockBuzzer : public Buzzer { - public: - MOCK_METHOD(bool, DoShareBuzz, (Buzz* buzz, Time timestamp)); - bool ShareBuzz(std::unique_ptr buzz, Time timestamp) override { - return DoShareBuzz(buzz.get(), timestamp); - } -}; -``` - -The trick is to delegate the `ShareBuzz()` method to a mock method (let’s call -it `DoShareBuzz()`) that does not take move-only parameters. Then, instead of -setting expectations on `ShareBuzz()`, you set them on the `DoShareBuzz()` mock -method: - -```cpp - MockBuzzer mock_buzzer_; - EXPECT_CALL(mock_buzzer_, DoShareBuzz(NotNull(), _)); - - // When one calls ShareBuzz() on the MockBuzzer like this, the call is - // forwarded to DoShareBuzz(), which is mocked. Therefore this statement - // will trigger the above EXPECT_CALL. - mock_buzzer_.ShareBuzz(std::make_unique(AccessLevel::kInternal), 0); -``` - -### Making the Compilation Faster - -Believe it or not, the *vast majority* of the time spent on compiling a mock -class is in generating its constructor and destructor, as they perform -non-trivial tasks (e.g. verification of the expectations). What's more, mock -methods with different signatures have different types and thus their -constructors/destructors need to be generated by the compiler separately. As a -result, if you mock many different types of methods, compiling your mock class -can get really slow. - -If you are experiencing slow compilation, you can move the definition of your -mock class' constructor and destructor out of the class body and into a `.cc` -file. This way, even if you `#include` your mock class in N files, the compiler -only needs to generate its constructor and destructor once, resulting in a much -faster compilation. - -Let's illustrate the idea using an example. Here's the definition of a mock -class before applying this recipe: - -```cpp -// File mock_foo.h. -... -class MockFoo : public Foo { - public: - // Since we don't declare the constructor or the destructor, - // the compiler will generate them in every translation unit - // where this mock class is used. - - MOCK_METHOD(int, DoThis, (), (override)); - MOCK_METHOD(bool, DoThat, (const char* str), (override)); - ... more mock methods ... -}; -``` - -After the change, it would look like: - -```cpp -// File mock_foo.h. -... -class MockFoo : public Foo { - public: - // The constructor and destructor are declared, but not defined, here. - MockFoo(); - virtual ~MockFoo(); - - MOCK_METHOD(int, DoThis, (), (override)); - MOCK_METHOD(bool, DoThat, (const char* str), (override)); - ... more mock methods ... -}; -``` - -and - -```cpp -// File mock_foo.cc. -#include "path/to/mock_foo.h" - -// The definitions may appear trivial, but the functions actually do a -// lot of things through the constructors/destructors of the member -// variables used to implement the mock methods. -MockFoo::MockFoo() {} -MockFoo::~MockFoo() {} -``` - -### Forcing a Verification - -When it's being destroyed, your friendly mock object will automatically verify -that all expectations on it have been satisfied, and will generate googletest -failures if not. This is convenient as it leaves you with one less thing to -worry about. That is, unless you are not sure if your mock object will be -destroyed. - -How could it be that your mock object won't eventually be destroyed? Well, it -might be created on the heap and owned by the code you are testing. Suppose -there's a bug in that code and it doesn't delete the mock object properly - you -could end up with a passing test when there's actually a bug. - -Using a heap checker is a good idea and can alleviate the concern, but its -implementation is not 100% reliable. So, sometimes you do want to *force* gMock -to verify a mock object before it is (hopefully) destructed. You can do this -with `Mock::VerifyAndClearExpectations(&mock_object)`: - -```cpp -TEST(MyServerTest, ProcessesRequest) { - using ::testing::Mock; - - MockFoo* const foo = new MockFoo; - EXPECT_CALL(*foo, ...)...; - // ... other expectations ... - - // server now owns foo. - MyServer server(foo); - server.ProcessRequest(...); - - // In case that server's destructor will forget to delete foo, - // this will verify the expectations anyway. - Mock::VerifyAndClearExpectations(foo); -} // server is destroyed when it goes out of scope here. -``` - -{: .callout .tip} -**Tip:** The `Mock::VerifyAndClearExpectations()` function returns a `bool` to -indicate whether the verification was successful (`true` for yes), so you can -wrap that function call inside a `ASSERT_TRUE()` if there is no point going -further when the verification has failed. - -Do not set new expectations after verifying and clearing a mock after its use. -Setting expectations after code that exercises the mock has undefined behavior. -See [Using Mocks in Tests](gmock_for_dummies.md#using-mocks-in-tests) for more -information. - -### Using Checkpoints {#UsingCheckPoints} - -Sometimes you might want to test a mock object's behavior in phases whose sizes -are each manageable, or you might want to set more detailed expectations about -which API calls invoke which mock functions. - -A technique you can use is to put the expectations in a sequence and insert -calls to a dummy "checkpoint" function at specific places. Then you can verify -that the mock function calls do happen at the right time. For example, if you -are exercising the code: - -```cpp - Foo(1); - Foo(2); - Foo(3); -``` - -and want to verify that `Foo(1)` and `Foo(3)` both invoke `mock.Bar("a")`, but -`Foo(2)` doesn't invoke anything, you can write: - -```cpp -using ::testing::MockFunction; - -TEST(FooTest, InvokesBarCorrectly) { - MyMock mock; - // Class MockFunction has exactly one mock method. It is named - // Call() and has type F. - MockFunction check; - { - InSequence s; - - EXPECT_CALL(mock, Bar("a")); - EXPECT_CALL(check, Call("1")); - EXPECT_CALL(check, Call("2")); - EXPECT_CALL(mock, Bar("a")); - } - Foo(1); - check.Call("1"); - Foo(2); - check.Call("2"); - Foo(3); -} -``` - -The expectation spec says that the first `Bar("a")` call must happen before -checkpoint "1", the second `Bar("a")` call must happen after checkpoint "2", and -nothing should happen between the two checkpoints. The explicit checkpoints make -it clear which `Bar("a")` is called by which call to `Foo()`. - -### Mocking Destructors - -Sometimes you want to make sure a mock object is destructed at the right time, -e.g. after `bar->A()` is called but before `bar->B()` is called. We already know -that you can specify constraints on the [order](#OrderedCalls) of mock function -calls, so all we need to do is to mock the destructor of the mock function. - -This sounds simple, except for one problem: a destructor is a special function -with special syntax and special semantics, and the `MOCK_METHOD` macro doesn't -work for it: - -```cpp -MOCK_METHOD(void, ~MockFoo, ()); // Won't compile! -``` - -The good news is that you can use a simple pattern to achieve the same effect. -First, add a mock function `Die()` to your mock class and call it in the -destructor, like this: - -```cpp -class MockFoo : public Foo { - ... - // Add the following two lines to the mock class. - MOCK_METHOD(void, Die, ()); - ~MockFoo() override { Die(); } -}; -``` - -(If the name `Die()` clashes with an existing symbol, choose another name.) Now, -we have translated the problem of testing when a `MockFoo` object dies to -testing when its `Die()` method is called: - -```cpp - MockFoo* foo = new MockFoo; - MockBar* bar = new MockBar; - ... - { - InSequence s; - - // Expects *foo to die after bar->A() and before bar->B(). - EXPECT_CALL(*bar, A()); - EXPECT_CALL(*foo, Die()); - EXPECT_CALL(*bar, B()); - } -``` - -And that's that. - -### Using gMock and Threads {#UsingThreads} - -In a **unit** test, it's best if you could isolate and test a piece of code in a -single-threaded context. That avoids race conditions and dead locks, and makes -debugging your test much easier. - -Yet most programs are multi-threaded, and sometimes to test something we need to -pound on it from more than one thread. gMock works for this purpose too. - -Remember the steps for using a mock: - -1. Create a mock object `foo`. -2. Set its default actions and expectations using `ON_CALL()` and - `EXPECT_CALL()`. -3. The code under test calls methods of `foo`. -4. Optionally, verify and reset the mock. -5. Destroy the mock yourself, or let the code under test destroy it. The - destructor will automatically verify it. - -If you follow the following simple rules, your mocks and threads can live -happily together: - -* Execute your *test code* (as opposed to the code being tested) in *one* - thread. This makes your test easy to follow. -* Obviously, you can do step #1 without locking. -* When doing step #2 and #5, make sure no other thread is accessing `foo`. - Obvious too, huh? -* #3 and #4 can be done either in one thread or in multiple threads - anyway - you want. gMock takes care of the locking, so you don't have to do any - - unless required by your test logic. - -If you violate the rules (for example, if you set expectations on a mock while -another thread is calling its methods), you get undefined behavior. That's not -fun, so don't do it. - -gMock guarantees that the action for a mock function is done in the same thread -that called the mock function. For example, in - -```cpp - EXPECT_CALL(mock, Foo(1)) - .WillOnce(action1); - EXPECT_CALL(mock, Foo(2)) - .WillOnce(action2); -``` - -if `Foo(1)` is called in thread 1 and `Foo(2)` is called in thread 2, gMock will -execute `action1` in thread 1 and `action2` in thread 2. - -gMock does *not* impose a sequence on actions performed in different threads -(doing so may create deadlocks as the actions may need to cooperate). This means -that the execution of `action1` and `action2` in the above example *may* -interleave. If this is a problem, you should add proper synchronization logic to -`action1` and `action2` to make the test thread-safe. - -Also, remember that `DefaultValue` is a global resource that potentially -affects *all* living mock objects in your program. Naturally, you won't want to -mess with it from multiple threads or when there still are mocks in action. - -### Controlling How Much Information gMock Prints - -When gMock sees something that has the potential of being an error (e.g. a mock -function with no expectation is called, a.k.a. an uninteresting call, which is -allowed but perhaps you forgot to explicitly ban the call), it prints some -warning messages, including the arguments of the function, the return value, and -the stack trace. Hopefully this will remind you to take a look and see if there -is indeed a problem. - -Sometimes you are confident that your tests are correct and may not appreciate -such friendly messages. Some other times, you are debugging your tests or -learning about the behavior of the code you are testing, and wish you could -observe every mock call that happens (including argument values, the return -value, and the stack trace). Clearly, one size doesn't fit all. - -You can control how much gMock tells you using the `--gmock_verbose=LEVEL` -command-line flag, where `LEVEL` is a string with three possible values: - -* `info`: gMock will print all informational messages, warnings, and errors - (most verbose). At this setting, gMock will also log any calls to the - `ON_CALL/EXPECT_CALL` macros. It will include a stack trace in - "uninteresting call" warnings. -* `warning`: gMock will print both warnings and errors (less verbose); it will - omit the stack traces in "uninteresting call" warnings. This is the default. -* `error`: gMock will print errors only (least verbose). - -Alternatively, you can adjust the value of that flag from within your tests like -so: - -```cpp - ::testing::FLAGS_gmock_verbose = "error"; -``` - -If you find gMock printing too many stack frames with its informational or -warning messages, remember that you can control their amount with the -`--gtest_stack_trace_depth=max_depth` flag. - -Now, judiciously use the right flag to enable gMock serve you better! - -### Gaining Super Vision into Mock Calls - -You have a test using gMock. It fails: gMock tells you some expectations aren't -satisfied. However, you aren't sure why: Is there a typo somewhere in the -matchers? Did you mess up the order of the `EXPECT_CALL`s? Or is the code under -test doing something wrong? How can you find out the cause? - -Won't it be nice if you have X-ray vision and can actually see the trace of all -`EXPECT_CALL`s and mock method calls as they are made? For each call, would you -like to see its actual argument values and which `EXPECT_CALL` gMock thinks it -matches? If you still need some help to figure out who made these calls, how -about being able to see the complete stack trace at each mock call? - -You can unlock this power by running your test with the `--gmock_verbose=info` -flag. For example, given the test program: - -```cpp -#include - -using ::testing::_; -using ::testing::HasSubstr; -using ::testing::Return; - -class MockFoo { - public: - MOCK_METHOD(void, F, (const string& x, const string& y)); -}; - -TEST(Foo, Bar) { - MockFoo mock; - EXPECT_CALL(mock, F(_, _)).WillRepeatedly(Return()); - EXPECT_CALL(mock, F("a", "b")); - EXPECT_CALL(mock, F("c", HasSubstr("d"))); - - mock.F("a", "good"); - mock.F("a", "b"); -} -``` - -if you run it with `--gmock_verbose=info`, you will see this output: - -```shell -[ RUN ] Foo.Bar - -foo_test.cc:14: EXPECT_CALL(mock, F(_, _)) invoked -Stack trace: ... - -foo_test.cc:15: EXPECT_CALL(mock, F("a", "b")) invoked -Stack trace: ... - -foo_test.cc:16: EXPECT_CALL(mock, F("c", HasSubstr("d"))) invoked -Stack trace: ... - -foo_test.cc:14: Mock function call matches EXPECT_CALL(mock, F(_, _))... - Function call: F(@0x7fff7c8dad40"a",@0x7fff7c8dad10"good") -Stack trace: ... - -foo_test.cc:15: Mock function call matches EXPECT_CALL(mock, F("a", "b"))... - Function call: F(@0x7fff7c8dada0"a",@0x7fff7c8dad70"b") -Stack trace: ... - -foo_test.cc:16: Failure -Actual function call count doesn't match EXPECT_CALL(mock, F("c", HasSubstr("d")))... - Expected: to be called once - Actual: never called - unsatisfied and active -[ FAILED ] Foo.Bar -``` - -Suppose the bug is that the `"c"` in the third `EXPECT_CALL` is a typo and -should actually be `"a"`. With the above message, you should see that the actual -`F("a", "good")` call is matched by the first `EXPECT_CALL`, not the third as -you thought. From that it should be obvious that the third `EXPECT_CALL` is -written wrong. Case solved. - -If you are interested in the mock call trace but not the stack traces, you can -combine `--gmock_verbose=info` with `--gtest_stack_trace_depth=0` on the test -command line. - -### Running Tests in Emacs - -If you build and run your tests in Emacs using the `M-x google-compile` command -(as many googletest users do), the source file locations of gMock and googletest -errors will be highlighted. Just press `` on one of them and you'll be -taken to the offending line. Or, you can just type `C-x`` to jump to the next -error. - -To make it even easier, you can add the following lines to your `~/.emacs` file: - -```text -(global-set-key "\M-m" 'google-compile) ; m is for make -(global-set-key [M-down] 'next-error) -(global-set-key [M-up] '(lambda () (interactive) (next-error -1))) -``` - -Then you can type `M-m` to start a build (if you want to run the test as well, -just make sure `foo_test.run` or `runtests` is in the build command you supply -after typing `M-m`), or `M-up`/`M-down` to move back and forth between errors. - -## Extending gMock - -### Writing New Matchers Quickly {#NewMatchers} - -{: .callout .warning} -WARNING: gMock does not guarantee when or how many times a matcher will be -invoked. Therefore, all matchers must be functionally pure. See -[this section](#PureMatchers) for more details. - -The `MATCHER*` family of macros can be used to define custom matchers easily. -The syntax: - -```cpp -MATCHER(name, description_string_expression) { statements; } -``` - -will define a matcher with the given name that executes the statements, which -must return a `bool` to indicate if the match succeeds. Inside the statements, -you can refer to the value being matched by `arg`, and refer to its type by -`arg_type`. - -The *description string* is a `string`-typed expression that documents what the -matcher does, and is used to generate the failure message when the match fails. -It can (and should) reference the special `bool` variable `negation`, and should -evaluate to the description of the matcher when `negation` is `false`, or that -of the matcher's negation when `negation` is `true`. - -For convenience, we allow the description string to be empty (`""`), in which -case gMock will use the sequence of words in the matcher name as the -description. - -#### Basic Example - -```cpp -MATCHER(IsDivisibleBy7, "") { return (arg % 7) == 0; } -``` - -allows you to write - -```cpp - // Expects mock_foo.Bar(n) to be called where n is divisible by 7. - EXPECT_CALL(mock_foo, Bar(IsDivisibleBy7())); -``` - -or, - -```cpp - using ::testing::Not; - ... - // Verifies that a value is divisible by 7 and the other is not. - EXPECT_THAT(some_expression, IsDivisibleBy7()); - EXPECT_THAT(some_other_expression, Not(IsDivisibleBy7())); -``` - -If the above assertions fail, they will print something like: - -```shell - Value of: some_expression - Expected: is divisible by 7 - Actual: 27 - ... - Value of: some_other_expression - Expected: not (is divisible by 7) - Actual: 21 -``` - -where the descriptions `"is divisible by 7"` and `"not (is divisible by 7)"` are -automatically calculated from the matcher name `IsDivisibleBy7`. - -#### Adding Custom Failure Messages - -As you may have noticed, the auto-generated descriptions (especially those for -the negation) may not be so great. You can always override them with a `string` -expression of your own: - -```cpp -MATCHER(IsDivisibleBy7, - absl::StrCat(negation ? "isn't" : "is", " divisible by 7")) { - return (arg % 7) == 0; -} -``` - -Optionally, you can stream additional information to a hidden argument named -`result_listener` to explain the match result. For example, a better definition -of `IsDivisibleBy7` is: - -```cpp -MATCHER(IsDivisibleBy7, "") { - if ((arg % 7) == 0) - return true; - - *result_listener << "the remainder is " << (arg % 7); - return false; -} -``` - -With this definition, the above assertion will give a better message: - -```shell - Value of: some_expression - Expected: is divisible by 7 - Actual: 27 (the remainder is 6) -``` - -#### Using EXPECT_ Statements in Matchers - -You can also use `EXPECT_...` (and `ASSERT_...`) statements inside custom -matcher definitions. In many cases, this allows you to write your matcher more -concisely while still providing an informative error message. For example: - -```cpp -MATCHER(IsDivisibleBy7, "") { - const auto remainder = arg % 7; - EXPECT_EQ(remainder, 0); - return true; -} -``` - -If you write a test that includes the line `EXPECT_THAT(27, IsDivisibleBy7());`, -you will get an error something like the following: - -```shell -Expected equality of these values: - remainder - Which is: 6 - 0 -``` - -#### `MatchAndExplain` - -You should let `MatchAndExplain()` print *any additional information* that can -help a user understand the match result. Note that it should explain why the -match succeeds in case of a success (unless it's obvious) - this is useful when -the matcher is used inside `Not()`. There is no need to print the argument value -itself, as gMock already prints it for you. - -#### Argument Types - -The type of the value being matched (`arg_type`) is determined by the -context in which you use the matcher and is supplied to you by the compiler, so -you don't need to worry about declaring it (nor can you). This allows the -matcher to be polymorphic. For example, `IsDivisibleBy7()` can be used to match -any type where the value of `(arg % 7) == 0` can be implicitly converted to a -`bool`. In the `Bar(IsDivisibleBy7())` example above, if method `Bar()` takes an -`int`, `arg_type` will be `int`; if it takes an `unsigned long`, `arg_type` will -be `unsigned long`; and so on. - -### Writing New Parameterized Matchers Quickly - -Sometimes you'll want to define a matcher that has parameters. For that you can -use the macro: - -```cpp -MATCHER_P(name, param_name, description_string) { statements; } -``` - -where the description string can be either `""` or a `string` expression that -references `negation` and `param_name`. - -For example: - -```cpp -MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; } -``` - -will allow you to write: - -```cpp - EXPECT_THAT(Blah("a"), HasAbsoluteValue(n)); -``` - -which may lead to this message (assuming `n` is 10): - -```shell - Value of: Blah("a") - Expected: has absolute value 10 - Actual: -9 -``` - -Note that both the matcher description and its parameter are printed, making the -message human-friendly. - -In the matcher definition body, you can write `foo_type` to reference the type -of a parameter named `foo`. For example, in the body of -`MATCHER_P(HasAbsoluteValue, value)` above, you can write `value_type` to refer -to the type of `value`. - -gMock also provides `MATCHER_P2`, `MATCHER_P3`, ..., up to `MATCHER_P10` to -support multi-parameter matchers: - -```cpp -MATCHER_Pk(name, param_1, ..., param_k, description_string) { statements; } -``` - -Please note that the custom description string is for a particular *instance* of -the matcher, where the parameters have been bound to actual values. Therefore -usually you'll want the parameter values to be part of the description. gMock -lets you do that by referencing the matcher parameters in the description string -expression. - -For example, - -```cpp -using ::testing::PrintToString; -MATCHER_P2(InClosedRange, low, hi, - absl::StrFormat("%s in range [%s, %s]", negation ? "isn't" : "is", - PrintToString(low), PrintToString(hi))) { - return low <= arg && arg <= hi; -} -... -EXPECT_THAT(3, InClosedRange(4, 6)); -``` - -would generate a failure that contains the message: - -```shell - Expected: is in range [4, 6] -``` - -If you specify `""` as the description, the failure message will contain the -sequence of words in the matcher name followed by the parameter values printed -as a tuple. For example, - -```cpp - MATCHER_P2(InClosedRange, low, hi, "") { ... } - ... - EXPECT_THAT(3, InClosedRange(4, 6)); -``` - -would generate a failure that contains the text: - -```shell - Expected: in closed range (4, 6) -``` - -For the purpose of typing, you can view - -```cpp -MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... } -``` - -as shorthand for - -```cpp -template -FooMatcherPk -Foo(p1_type p1, ..., pk_type pk) { ... } -``` - -When you write `Foo(v1, ..., vk)`, the compiler infers the types of the -parameters `v1`, ..., and `vk` for you. If you are not happy with the result of -the type inference, you can specify the types by explicitly instantiating the -template, as in `Foo(5, false)`. As said earlier, you don't get to -(or need to) specify `arg_type` as that's determined by the context in which the -matcher is used. - -You can assign the result of expression `Foo(p1, ..., pk)` to a variable of type -`FooMatcherPk`. This can be useful when composing -matchers. Matchers that don't have a parameter or have only one parameter have -special types: you can assign `Foo()` to a `FooMatcher`-typed variable, and -assign `Foo(p)` to a `FooMatcherP`-typed variable. - -While you can instantiate a matcher template with reference types, passing the -parameters by pointer usually makes your code more readable. If, however, you -still want to pass a parameter by reference, be aware that in the failure -message generated by the matcher you will see the value of the referenced object -but not its address. - -You can overload matchers with different numbers of parameters: - -```cpp -MATCHER_P(Blah, a, description_string_1) { ... } -MATCHER_P2(Blah, a, b, description_string_2) { ... } -``` - -While it's tempting to always use the `MATCHER*` macros when defining a new -matcher, you should also consider implementing the matcher interface directly -instead (see the recipes that follow), especially if you need to use the matcher -a lot. While these approaches require more work, they give you more control on -the types of the value being matched and the matcher parameters, which in -general leads to better compiler error messages that pay off in the long run. -They also allow overloading matchers based on parameter types (as opposed to -just based on the number of parameters). - -### Writing New Monomorphic Matchers - -A matcher of argument type `T` implements the matcher interface for `T` and does -two things: it tests whether a value of type `T` matches the matcher, and can -describe what kind of values it matches. The latter ability is used for -generating readable error messages when expectations are violated. - -A matcher of `T` must declare a typedef like: - -```cpp -using is_gtest_matcher = void; -``` - -and supports the following operations: - -```cpp -// Match a value and optionally explain into an ostream. -bool matched = matcher.MatchAndExplain(value, maybe_os); -// where `value` is of type `T` and -// `maybe_os` is of type `std::ostream*`, where it can be null if the caller -// is not interested in there textual explanation. - -matcher.DescribeTo(os); -matcher.DescribeNegationTo(os); -// where `os` is of type `std::ostream*`. -``` - -If you need a custom matcher but `Truly()` is not a good option (for example, -you may not be happy with the way `Truly(predicate)` describes itself, or you -may want your matcher to be polymorphic as `Eq(value)` is), you can define a -matcher to do whatever you want in two steps: first implement the matcher -interface, and then define a factory function to create a matcher instance. The -second step is not strictly needed but it makes the syntax of using the matcher -nicer. - -For example, you can define a matcher to test whether an `int` is divisible by 7 -and then use it like this: - -```cpp -using ::testing::Matcher; - -class DivisibleBy7Matcher { - public: - using is_gtest_matcher = void; - - bool MatchAndExplain(int n, std::ostream*) const { - return (n % 7) == 0; - } - - void DescribeTo(std::ostream* os) const { - *os << "is divisible by 7"; - } - - void DescribeNegationTo(std::ostream* os) const { - *os << "is not divisible by 7"; - } -}; - -Matcher DivisibleBy7() { - return DivisibleBy7Matcher(); -} - -... - EXPECT_CALL(foo, Bar(DivisibleBy7())); -``` - -You may improve the matcher message by streaming additional information to the -`os` argument in `MatchAndExplain()`: - -```cpp -class DivisibleBy7Matcher { - public: - bool MatchAndExplain(int n, std::ostream* os) const { - const int remainder = n % 7; - if (remainder != 0 && os != nullptr) { - *os << "the remainder is " << remainder; - } - return remainder == 0; - } - ... -}; -``` - -Then, `EXPECT_THAT(x, DivisibleBy7());` may generate a message like this: - -```shell -Value of: x -Expected: is divisible by 7 - Actual: 23 (the remainder is 2) -``` - -{: .callout .tip} -Tip: for convenience, `MatchAndExplain()` can take a `MatchResultListener*` -instead of `std::ostream*`. - -### Writing New Polymorphic Matchers - -Expanding what we learned above to *polymorphic* matchers is now just as simple -as adding templates in the right place. - -```cpp - -class NotNullMatcher { - public: - using is_gtest_matcher = void; - - // To implement a polymorphic matcher, we just need to make MatchAndExplain a - // template on its first argument. - - // In this example, we want to use NotNull() with any pointer, so - // MatchAndExplain() accepts a pointer of any type as its first argument. - // In general, you can define MatchAndExplain() as an ordinary method or - // a method template, or even overload it. - template - bool MatchAndExplain(T* p, std::ostream*) const { - return p != nullptr; - } - - // Describes the property of a value matching this matcher. - void DescribeTo(std::ostream* os) const { *os << "is not NULL"; } - - // Describes the property of a value NOT matching this matcher. - void DescribeNegationTo(std::ostream* os) const { *os << "is NULL"; } -}; - -NotNullMatcher NotNull() { - return NotNullMatcher(); -} - -... - - EXPECT_CALL(foo, Bar(NotNull())); // The argument must be a non-NULL pointer. -``` - -### Legacy Matcher Implementation - -Defining matchers used to be somewhat more complicated, in which it required -several supporting classes and virtual functions. To implement a matcher for -type `T` using the legacy API you have to derive from `MatcherInterface` and -call `MakeMatcher` to construct the object. - -The interface looks like this: - -```cpp -class MatchResultListener { - public: - ... - // Streams x to the underlying ostream; does nothing if the ostream - // is NULL. - template - MatchResultListener& operator<<(const T& x); - - // Returns the underlying ostream. - std::ostream* stream(); -}; - -template -class MatcherInterface { - public: - virtual ~MatcherInterface(); - - // Returns true if and only if the matcher matches x; also explains the match - // result to 'listener'. - virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0; - - // Describes this matcher to an ostream. - virtual void DescribeTo(std::ostream* os) const = 0; - - // Describes the negation of this matcher to an ostream. - virtual void DescribeNegationTo(std::ostream* os) const; -}; -``` - -Fortunately, most of the time you can define a polymorphic matcher easily with -the help of `MakePolymorphicMatcher()`. Here's how you can define `NotNull()` as -an example: - -```cpp -using ::testing::MakePolymorphicMatcher; -using ::testing::MatchResultListener; -using ::testing::PolymorphicMatcher; - -class NotNullMatcher { - public: - // To implement a polymorphic matcher, first define a COPYABLE class - // that has three members MatchAndExplain(), DescribeTo(), and - // DescribeNegationTo(), like the following. - - // In this example, we want to use NotNull() with any pointer, so - // MatchAndExplain() accepts a pointer of any type as its first argument. - // In general, you can define MatchAndExplain() as an ordinary method or - // a method template, or even overload it. - template - bool MatchAndExplain(T* p, - MatchResultListener* /* listener */) const { - return p != NULL; - } - - // Describes the property of a value matching this matcher. - void DescribeTo(std::ostream* os) const { *os << "is not NULL"; } - - // Describes the property of a value NOT matching this matcher. - void DescribeNegationTo(std::ostream* os) const { *os << "is NULL"; } -}; - -// To construct a polymorphic matcher, pass an instance of the class -// to MakePolymorphicMatcher(). Note the return type. -PolymorphicMatcher NotNull() { - return MakePolymorphicMatcher(NotNullMatcher()); -} - -... - - EXPECT_CALL(foo, Bar(NotNull())); // The argument must be a non-NULL pointer. -``` - -{: .callout .note} -**Note:** Your polymorphic matcher class does **not** need to inherit from -`MatcherInterface` or any other class, and its methods do **not** need to be -virtual. - -Like in a monomorphic matcher, you may explain the match result by streaming -additional information to the `listener` argument in `MatchAndExplain()`. - -### Writing New Cardinalities - -A cardinality is used in `Times()` to tell gMock how many times you expect a -call to occur. It doesn't have to be exact. For example, you can say -`AtLeast(5)` or `Between(2, 4)`. - -If the [built-in set](gmock_cheat_sheet.md#CardinalityList) of cardinalities -doesn't suit you, you are free to define your own by implementing the following -interface (in namespace `testing`): - -```cpp -class CardinalityInterface { - public: - virtual ~CardinalityInterface(); - - // Returns true if and only if call_count calls will satisfy this cardinality. - virtual bool IsSatisfiedByCallCount(int call_count) const = 0; - - // Returns true if and only if call_count calls will saturate this - // cardinality. - virtual bool IsSaturatedByCallCount(int call_count) const = 0; - - // Describes self to an ostream. - virtual void DescribeTo(std::ostream* os) const = 0; -}; -``` - -For example, to specify that a call must occur even number of times, you can -write - -```cpp -using ::testing::Cardinality; -using ::testing::CardinalityInterface; -using ::testing::MakeCardinality; - -class EvenNumberCardinality : public CardinalityInterface { - public: - bool IsSatisfiedByCallCount(int call_count) const override { - return (call_count % 2) == 0; - } - - bool IsSaturatedByCallCount(int call_count) const override { - return false; - } - - void DescribeTo(std::ostream* os) const { - *os << "called even number of times"; - } -}; - -Cardinality EvenNumber() { - return MakeCardinality(new EvenNumberCardinality); -} - -... - EXPECT_CALL(foo, Bar(3)) - .Times(EvenNumber()); -``` - -### Writing New Actions {#QuickNewActions} - -If the built-in actions don't work for you, you can easily define your own one. -All you need is a call operator with a signature compatible with the mocked -function. So you can use a lambda: - -```cpp -MockFunction mock; -EXPECT_CALL(mock, Call).WillOnce([](const int input) { return input * 7; }); -EXPECT_EQ(mock.AsStdFunction()(2), 14); -``` - -Or a struct with a call operator (even a templated one): - -```cpp -struct MultiplyBy { - template - T operator()(T arg) { return arg * multiplier; } - - int multiplier; -}; - -// Then use: -// EXPECT_CALL(...).WillOnce(MultiplyBy{7}); -``` - -It's also fine for the callable to take no arguments, ignoring the arguments -supplied to the mock function: - -```cpp -MockFunction mock; -EXPECT_CALL(mock, Call).WillOnce([] { return 17; }); -EXPECT_EQ(mock.AsStdFunction()(0), 17); -``` - -When used with `WillOnce`, the callable can assume it will be called at most -once and is allowed to be a move-only type: - -```cpp -// An action that contains move-only types and has an &&-qualified operator, -// demanding in the type system that it be called at most once. This can be -// used with WillOnce, but the compiler will reject it if handed to -// WillRepeatedly. -struct MoveOnlyAction { - std::unique_ptr move_only_state; - std::unique_ptr operator()() && { return std::move(move_only_state); } -}; - -MockFunction()> mock; -EXPECT_CALL(mock, Call).WillOnce(MoveOnlyAction{std::make_unique(17)}); -EXPECT_THAT(mock.AsStdFunction()(), Pointee(Eq(17))); -``` - -More generally, to use with a mock function whose signature is `R(Args...)` the -object can be anything convertible to `OnceAction` or -`Action. The difference between the two is that `OnceAction` has -weaker requirements (`Action` requires a copy-constructible input that can be -called repeatedly whereas `OnceAction` requires only move-constructible and -supports `&&`-qualified call operators), but can be used only with `WillOnce`. -`OnceAction` is typically relevant only when supporting move-only types or -actions that want a type-system guarantee that they will be called at most once. - -Typically the `OnceAction` and `Action` templates need not be referenced -directly in your actions: a struct or class with a call operator is sufficient, -as in the examples above. But fancier polymorphic actions that need to know the -specific return type of the mock function can define templated conversion -operators to make that possible. See `gmock-actions.h` for examples. - -#### Legacy macro-based Actions - -Before C++11, the functor-based actions were not supported; the old way of -writing actions was through a set of `ACTION*` macros. We suggest to avoid them -in new code; they hide a lot of logic behind the macro, potentially leading to -harder-to-understand compiler errors. Nevertheless, we cover them here for -completeness. - -By writing - -```cpp -ACTION(name) { statements; } -``` - -in a namespace scope (i.e. not inside a class or function), you will define an -action with the given name that executes the statements. The value returned by -`statements` will be used as the return value of the action. Inside the -statements, you can refer to the K-th (0-based) argument of the mock function as -`argK`. For example: - -```cpp -ACTION(IncrementArg1) { return ++(*arg1); } -``` - -allows you to write - -```cpp -... WillOnce(IncrementArg1()); -``` - -Note that you don't need to specify the types of the mock function arguments. -Rest assured that your code is type-safe though: you'll get a compiler error if -`*arg1` doesn't support the `++` operator, or if the type of `++(*arg1)` isn't -compatible with the mock function's return type. - -Another example: - -```cpp -ACTION(Foo) { - (*arg2)(5); - Blah(); - *arg1 = 0; - return arg0; -} -``` - -defines an action `Foo()` that invokes argument #2 (a function pointer) with 5, -calls function `Blah()`, sets the value pointed to by argument #1 to 0, and -returns argument #0. - -For more convenience and flexibility, you can also use the following pre-defined -symbols in the body of `ACTION`: - -`argK_type` | The type of the K-th (0-based) argument of the mock function -:-------------- | :----------------------------------------------------------- -`args` | All arguments of the mock function as a tuple -`args_type` | The type of all arguments of the mock function as a tuple -`return_type` | The return type of the mock function -`function_type` | The type of the mock function - -For example, when using an `ACTION` as a stub action for mock function: - -```cpp -int DoSomething(bool flag, int* ptr); -``` - -we have: - -Pre-defined Symbol | Is Bound To ------------------- | --------------------------------- -`arg0` | the value of `flag` -`arg0_type` | the type `bool` -`arg1` | the value of `ptr` -`arg1_type` | the type `int*` -`args` | the tuple `(flag, ptr)` -`args_type` | the type `std::tuple` -`return_type` | the type `int` -`function_type` | the type `int(bool, int*)` - -#### Legacy macro-based parameterized Actions - -Sometimes you'll want to parameterize an action you define. For that we have -another macro - -```cpp -ACTION_P(name, param) { statements; } -``` - -For example, - -```cpp -ACTION_P(Add, n) { return arg0 + n; } -``` - -will allow you to write - -```cpp -// Returns argument #0 + 5. -... WillOnce(Add(5)); -``` - -For convenience, we use the term *arguments* for the values used to invoke the -mock function, and the term *parameters* for the values used to instantiate an -action. - -Note that you don't need to provide the type of the parameter either. Suppose -the parameter is named `param`, you can also use the gMock-defined symbol -`param_type` to refer to the type of the parameter as inferred by the compiler. -For example, in the body of `ACTION_P(Add, n)` above, you can write `n_type` for -the type of `n`. - -gMock also provides `ACTION_P2`, `ACTION_P3`, and etc to support multi-parameter -actions. For example, - -```cpp -ACTION_P2(ReturnDistanceTo, x, y) { - double dx = arg0 - x; - double dy = arg1 - y; - return sqrt(dx*dx + dy*dy); -} -``` - -lets you write - -```cpp -... WillOnce(ReturnDistanceTo(5.0, 26.5)); -``` - -You can view `ACTION` as a degenerated parameterized action where the number of -parameters is 0. - -You can also easily define actions overloaded on the number of parameters: - -```cpp -ACTION_P(Plus, a) { ... } -ACTION_P2(Plus, a, b) { ... } -``` - -### Restricting the Type of an Argument or Parameter in an ACTION - -For maximum brevity and reusability, the `ACTION*` macros don't ask you to -provide the types of the mock function arguments and the action parameters. -Instead, we let the compiler infer the types for us. - -Sometimes, however, we may want to be more explicit about the types. There are -several tricks to do that. For example: - -```cpp -ACTION(Foo) { - // Makes sure arg0 can be converted to int. - int n = arg0; - ... use n instead of arg0 here ... -} - -ACTION_P(Bar, param) { - // Makes sure the type of arg1 is const char*. - ::testing::StaticAssertTypeEq(); - - // Makes sure param can be converted to bool. - bool flag = param; -} -``` - -where `StaticAssertTypeEq` is a compile-time assertion in googletest that -verifies two types are the same. - -### Writing New Action Templates Quickly - -Sometimes you want to give an action explicit template parameters that cannot be -inferred from its value parameters. `ACTION_TEMPLATE()` supports that and can be -viewed as an extension to `ACTION()` and `ACTION_P*()`. - -The syntax: - -```cpp -ACTION_TEMPLATE(ActionName, - HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m), - AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; } -``` - -defines an action template that takes *m* explicit template parameters and *n* -value parameters, where *m* is in [1, 10] and *n* is in [0, 10]. `name_i` is the -name of the *i*-th template parameter, and `kind_i` specifies whether it's a -`typename`, an integral constant, or a template. `p_i` is the name of the *i*-th -value parameter. - -Example: - -```cpp -// DuplicateArg(output) converts the k-th argument of the mock -// function to type T and copies it to *output. -ACTION_TEMPLATE(DuplicateArg, - // Note the comma between int and k: - HAS_2_TEMPLATE_PARAMS(int, k, typename, T), - AND_1_VALUE_PARAMS(output)) { - *output = T(std::get(args)); -} -``` - -To create an instance of an action template, write: - -```cpp -ActionName(v1, ..., v_n) -``` - -where the `t`s are the template arguments and the `v`s are the value arguments. -The value argument types are inferred by the compiler. For example: - -```cpp -using ::testing::_; -... - int n; - EXPECT_CALL(mock, Foo).WillOnce(DuplicateArg<1, unsigned char>(&n)); -``` - -If you want to explicitly specify the value argument types, you can provide -additional template arguments: - -```cpp -ActionName(v1, ..., v_n) -``` - -where `u_i` is the desired type of `v_i`. - -`ACTION_TEMPLATE` and `ACTION`/`ACTION_P*` can be overloaded on the number of -value parameters, but not on the number of template parameters. Without the -restriction, the meaning of the following is unclear: - -```cpp - OverloadedAction(x); -``` - -Are we using a single-template-parameter action where `bool` refers to the type -of `x`, or a two-template-parameter action where the compiler is asked to infer -the type of `x`? - -### Using the ACTION Object's Type - -If you are writing a function that returns an `ACTION` object, you'll need to -know its type. The type depends on the macro used to define the action and the -parameter types. The rule is relatively simple: - - -| Given Definition | Expression | Has Type | -| ----------------------------- | ------------------- | --------------------- | -| `ACTION(Foo)` | `Foo()` | `FooAction` | -| `ACTION_TEMPLATE(Foo, HAS_m_TEMPLATE_PARAMS(...), AND_0_VALUE_PARAMS())` | `Foo()` | `FooAction` | -| `ACTION_P(Bar, param)` | `Bar(int_value)` | `BarActionP` | -| `ACTION_TEMPLATE(Bar, HAS_m_TEMPLATE_PARAMS(...), AND_1_VALUE_PARAMS(p1))` | `Bar(int_value)` | `BarActionP` | -| `ACTION_P2(Baz, p1, p2)` | `Baz(bool_value, int_value)` | `BazActionP2` | -| `ACTION_TEMPLATE(Baz, HAS_m_TEMPLATE_PARAMS(...), AND_2_VALUE_PARAMS(p1, p2))` | `Baz(bool_value, int_value)` | `BazActionP2` | -| ... | ... | ... | - - -Note that we have to pick different suffixes (`Action`, `ActionP`, `ActionP2`, -and etc) for actions with different numbers of value parameters, or the action -definitions cannot be overloaded on the number of them. - -### Writing New Monomorphic Actions {#NewMonoActions} - -While the `ACTION*` macros are very convenient, sometimes they are -inappropriate. For example, despite the tricks shown in the previous recipes, -they don't let you directly specify the types of the mock function arguments and -the action parameters, which in general leads to unoptimized compiler error -messages that can baffle unfamiliar users. They also don't allow overloading -actions based on parameter types without jumping through some hoops. - -An alternative to the `ACTION*` macros is to implement -`::testing::ActionInterface`, where `F` is the type of the mock function in -which the action will be used. For example: - -```cpp -template -class ActionInterface { - public: - virtual ~ActionInterface(); - - // Performs the action. Result is the return type of function type - // F, and ArgumentTuple is the tuple of arguments of F. - // - - // For example, if F is int(bool, const string&), then Result would - // be int, and ArgumentTuple would be std::tuple. - virtual Result Perform(const ArgumentTuple& args) = 0; -}; -``` - -```cpp -using ::testing::_; -using ::testing::Action; -using ::testing::ActionInterface; -using ::testing::MakeAction; - -typedef int IncrementMethod(int*); - -class IncrementArgumentAction : public ActionInterface { - public: - int Perform(const std::tuple& args) override { - int* p = std::get<0>(args); // Grabs the first argument. - return *p++; - } -}; - -Action IncrementArgument() { - return MakeAction(new IncrementArgumentAction); -} - -... - EXPECT_CALL(foo, Baz(_)) - .WillOnce(IncrementArgument()); - - int n = 5; - foo.Baz(&n); // Should return 5 and change n to 6. -``` - -### Writing New Polymorphic Actions {#NewPolyActions} - -The previous recipe showed you how to define your own action. This is all good, -except that you need to know the type of the function in which the action will -be used. Sometimes that can be a problem. For example, if you want to use the -action in functions with *different* types (e.g. like `Return()` and -`SetArgPointee()`). - -If an action can be used in several types of mock functions, we say it's -*polymorphic*. The `MakePolymorphicAction()` function template makes it easy to -define such an action: - -```cpp -namespace testing { -template -PolymorphicAction MakePolymorphicAction(const Impl& impl); -} // namespace testing -``` - -As an example, let's define an action that returns the second argument in the -mock function's argument list. The first step is to define an implementation -class: - -```cpp -class ReturnSecondArgumentAction { - public: - template - Result Perform(const ArgumentTuple& args) const { - // To get the i-th (0-based) argument, use std::get(args). - return std::get<1>(args); - } -}; -``` - -This implementation class does *not* need to inherit from any particular class. -What matters is that it must have a `Perform()` method template. This method -template takes the mock function's arguments as a tuple in a **single** -argument, and returns the result of the action. It can be either `const` or not, -but must be invocable with exactly one template argument, which is the result -type. In other words, you must be able to call `Perform(args)` where `R` is -the mock function's return type and `args` is its arguments in a tuple. - -Next, we use `MakePolymorphicAction()` to turn an instance of the implementation -class into the polymorphic action we need. It will be convenient to have a -wrapper for this: - -```cpp -using ::testing::MakePolymorphicAction; -using ::testing::PolymorphicAction; - -PolymorphicAction ReturnSecondArgument() { - return MakePolymorphicAction(ReturnSecondArgumentAction()); -} -``` - -Now, you can use this polymorphic action the same way you use the built-in ones: - -```cpp -using ::testing::_; - -class MockFoo : public Foo { - public: - MOCK_METHOD(int, DoThis, (bool flag, int n), (override)); - MOCK_METHOD(string, DoThat, (int x, const char* str1, const char* str2), - (override)); -}; - - ... - MockFoo foo; - EXPECT_CALL(foo, DoThis).WillOnce(ReturnSecondArgument()); - EXPECT_CALL(foo, DoThat).WillOnce(ReturnSecondArgument()); - ... - foo.DoThis(true, 5); // Will return 5. - foo.DoThat(1, "Hi", "Bye"); // Will return "Hi". -``` - -### Teaching gMock How to Print Your Values - -When an uninteresting or unexpected call occurs, gMock prints the argument -values and the stack trace to help you debug. Assertion macros like -`EXPECT_THAT` and `EXPECT_EQ` also print the values in question when the -assertion fails. gMock and googletest do this using googletest's user-extensible -value printer. - -This printer knows how to print built-in C++ types, native arrays, STL -containers, and any type that supports the `<<` operator. For other types, it -prints the raw bytes in the value and hopes that you the user can figure it out. -[The GoogleTest advanced guide](advanced.md#teaching-googletest-how-to-print-your-values) -explains how to extend the printer to do a better job at printing your -particular type than to dump the bytes. - -## Useful Mocks Created Using gMock - - - - -### Mock std::function {#MockFunction} - -`std::function` is a general function type introduced in C++11. It is a -preferred way of passing callbacks to new interfaces. Functions are copyable, -and are not usually passed around by pointer, which makes them tricky to mock. -But fear not - `MockFunction` can help you with that. - -`MockFunction` has a mock method `Call()` with the signature: - -```cpp - R Call(T1, ..., Tn); -``` - -It also has a `AsStdFunction()` method, which creates a `std::function` proxy -forwarding to Call: - -```cpp - std::function AsStdFunction(); -``` - -To use `MockFunction`, first create `MockFunction` object and set up -expectations on its `Call` method. Then pass proxy obtained from -`AsStdFunction()` to the code you are testing. For example: - -```cpp -TEST(FooTest, RunsCallbackWithBarArgument) { - // 1. Create a mock object. - MockFunction mock_function; - - // 2. Set expectations on Call() method. - EXPECT_CALL(mock_function, Call("bar")).WillOnce(Return(1)); - - // 3. Exercise code that uses std::function. - Foo(mock_function.AsStdFunction()); - // Foo's signature can be either of: - // void Foo(const std::function& fun); - // void Foo(std::function fun); - - // 4. All expectations will be verified when mock_function - // goes out of scope and is destroyed. -} -``` - -Remember that function objects created with `AsStdFunction()` are just -forwarders. If you create multiple of them, they will share the same set of -expectations. - -Although `std::function` supports unlimited number of arguments, `MockFunction` -implementation is limited to ten. If you ever hit that limit... well, your -callback has bigger problems than being mockable. :-) diff --git a/test/unit/googletest/docs/gmock_faq.md b/test/unit/googletest/docs/gmock_faq.md deleted file mode 100644 index 8f220bf..0000000 --- a/test/unit/googletest/docs/gmock_faq.md +++ /dev/null @@ -1,390 +0,0 @@ -# Legacy gMock FAQ - -### When I call a method on my mock object, the method for the real object is invoked instead. What's the problem? - -In order for a method to be mocked, it must be *virtual*, unless you use the -[high-perf dependency injection technique](gmock_cook_book.md#MockingNonVirtualMethods). - -### Can I mock a variadic function? - -You cannot mock a variadic function (i.e. a function taking ellipsis (`...`) -arguments) directly in gMock. - -The problem is that in general, there is *no way* for a mock object to know how -many arguments are passed to the variadic method, and what the arguments' types -are. Only the *author of the base class* knows the protocol, and we cannot look -into his or her head. - -Therefore, to mock such a function, the *user* must teach the mock object how to -figure out the number of arguments and their types. One way to do it is to -provide overloaded versions of the function. - -Ellipsis arguments are inherited from C and not really a C++ feature. They are -unsafe to use and don't work with arguments that have constructors or -destructors. Therefore we recommend to avoid them in C++ as much as possible. - -### MSVC gives me warning C4301 or C4373 when I define a mock method with a const parameter. Why? - -If you compile this using Microsoft Visual C++ 2005 SP1: - -```cpp -class Foo { - ... - virtual void Bar(const int i) = 0; -}; - -class MockFoo : public Foo { - ... - MOCK_METHOD(void, Bar, (const int i), (override)); -}; -``` - -You may get the following warning: - -```shell -warning C4301: 'MockFoo::Bar': overriding virtual function only differs from 'Foo::Bar' by const/volatile qualifier -``` - -This is a MSVC bug. The same code compiles fine with gcc, for example. If you -use Visual C++ 2008 SP1, you would get the warning: - -```shell -warning C4373: 'MockFoo::Bar': virtual function overrides 'Foo::Bar', previous versions of the compiler did not override when parameters only differed by const/volatile qualifiers -``` - -In C++, if you *declare* a function with a `const` parameter, the `const` -modifier is ignored. Therefore, the `Foo` base class above is equivalent to: - -```cpp -class Foo { - ... - virtual void Bar(int i) = 0; // int or const int? Makes no difference. -}; -``` - -In fact, you can *declare* `Bar()` with an `int` parameter, and define it with a -`const int` parameter. The compiler will still match them up. - -Since making a parameter `const` is meaningless in the method declaration, we -recommend to remove it in both `Foo` and `MockFoo`. That should workaround the -VC bug. - -Note that we are talking about the *top-level* `const` modifier here. If the -function parameter is passed by pointer or reference, declaring the pointee or -referee as `const` is still meaningful. For example, the following two -declarations are *not* equivalent: - -```cpp -void Bar(int* p); // Neither p nor *p is const. -void Bar(const int* p); // p is not const, but *p is. -``` - -### I can't figure out why gMock thinks my expectations are not satisfied. What should I do? - -You might want to run your test with `--gmock_verbose=info`. This flag lets -gMock print a trace of every mock function call it receives. By studying the -trace, you'll gain insights on why the expectations you set are not met. - -If you see the message "The mock function has no default action set, and its -return type has no default value set.", then try -[adding a default action](gmock_cheat_sheet.md#OnCall). Due to a known issue, -unexpected calls on mocks without default actions don't print out a detailed -comparison between the actual arguments and the expected arguments. - -### My program crashed and `ScopedMockLog` spit out tons of messages. Is it a gMock bug? - -gMock and `ScopedMockLog` are likely doing the right thing here. - -When a test crashes, the failure signal handler will try to log a lot of -information (the stack trace, and the address map, for example). The messages -are compounded if you have many threads with depth stacks. When `ScopedMockLog` -intercepts these messages and finds that they don't match any expectations, it -prints an error for each of them. - -You can learn to ignore the errors, or you can rewrite your expectations to make -your test more robust, for example, by adding something like: - -```cpp -using ::testing::AnyNumber; -using ::testing::Not; -... - // Ignores any log not done by us. - EXPECT_CALL(log, Log(_, Not(EndsWith("/my_file.cc")), _)) - .Times(AnyNumber()); -``` - -### How can I assert that a function is NEVER called? - -```cpp -using ::testing::_; -... - EXPECT_CALL(foo, Bar(_)) - .Times(0); -``` - -### I have a failed test where gMock tells me TWICE that a particular expectation is not satisfied. Isn't this redundant? - -When gMock detects a failure, it prints relevant information (the mock function -arguments, the state of relevant expectations, and etc) to help the user debug. -If another failure is detected, gMock will do the same, including printing the -state of relevant expectations. - -Sometimes an expectation's state didn't change between two failures, and you'll -see the same description of the state twice. They are however *not* redundant, -as they refer to *different points in time*. The fact they are the same *is* -interesting information. - -### I get a heapcheck failure when using a mock object, but using a real object is fine. What can be wrong? - -Does the class (hopefully a pure interface) you are mocking have a virtual -destructor? - -Whenever you derive from a base class, make sure its destructor is virtual. -Otherwise Bad Things will happen. Consider the following code: - -```cpp -class Base { - public: - // Not virtual, but should be. - ~Base() { ... } - ... -}; - -class Derived : public Base { - public: - ... - private: - std::string value_; -}; - -... - Base* p = new Derived; - ... - delete p; // Surprise! ~Base() will be called, but ~Derived() will not - // - value_ is leaked. -``` - -By changing `~Base()` to virtual, `~Derived()` will be correctly called when -`delete p` is executed, and the heap checker will be happy. - -### The "newer expectations override older ones" rule makes writing expectations awkward. Why does gMock do that? - -When people complain about this, often they are referring to code like: - -```cpp -using ::testing::Return; -... - // foo.Bar() should be called twice, return 1 the first time, and return - // 2 the second time. However, I have to write the expectations in the - // reverse order. This sucks big time!!! - EXPECT_CALL(foo, Bar()) - .WillOnce(Return(2)) - .RetiresOnSaturation(); - EXPECT_CALL(foo, Bar()) - .WillOnce(Return(1)) - .RetiresOnSaturation(); -``` - -The problem, is that they didn't pick the **best** way to express the test's -intent. - -By default, expectations don't have to be matched in *any* particular order. If -you want them to match in a certain order, you need to be explicit. This is -gMock's (and jMock's) fundamental philosophy: it's easy to accidentally -over-specify your tests, and we want to make it harder to do so. - -There are two better ways to write the test spec. You could either put the -expectations in sequence: - -```cpp -using ::testing::Return; -... - // foo.Bar() should be called twice, return 1 the first time, and return - // 2 the second time. Using a sequence, we can write the expectations - // in their natural order. - { - InSequence s; - EXPECT_CALL(foo, Bar()) - .WillOnce(Return(1)) - .RetiresOnSaturation(); - EXPECT_CALL(foo, Bar()) - .WillOnce(Return(2)) - .RetiresOnSaturation(); - } -``` - -or you can put the sequence of actions in the same expectation: - -```cpp -using ::testing::Return; -... - // foo.Bar() should be called twice, return 1 the first time, and return - // 2 the second time. - EXPECT_CALL(foo, Bar()) - .WillOnce(Return(1)) - .WillOnce(Return(2)) - .RetiresOnSaturation(); -``` - -Back to the original questions: why does gMock search the expectations (and -`ON_CALL`s) from back to front? Because this allows a user to set up a mock's -behavior for the common case early (e.g. in the mock's constructor or the test -fixture's set-up phase) and customize it with more specific rules later. If -gMock searches from front to back, this very useful pattern won't be possible. - -### gMock prints a warning when a function without EXPECT_CALL is called, even if I have set its behavior using ON_CALL. Would it be reasonable not to show the warning in this case? - -When choosing between being neat and being safe, we lean toward the latter. So -the answer is that we think it's better to show the warning. - -Often people write `ON_CALL`s in the mock object's constructor or `SetUp()`, as -the default behavior rarely changes from test to test. Then in the test body -they set the expectations, which are often different for each test. Having an -`ON_CALL` in the set-up part of a test doesn't mean that the calls are expected. -If there's no `EXPECT_CALL` and the method is called, it's possibly an error. If -we quietly let the call go through without notifying the user, bugs may creep in -unnoticed. - -If, however, you are sure that the calls are OK, you can write - -```cpp -using ::testing::_; -... - EXPECT_CALL(foo, Bar(_)) - .WillRepeatedly(...); -``` - -instead of - -```cpp -using ::testing::_; -... - ON_CALL(foo, Bar(_)) - .WillByDefault(...); -``` - -This tells gMock that you do expect the calls and no warning should be printed. - -Also, you can control the verbosity by specifying `--gmock_verbose=error`. Other -values are `info` and `warning`. If you find the output too noisy when -debugging, just choose a less verbose level. - -### How can I delete the mock function's argument in an action? - -If your mock function takes a pointer argument and you want to delete that -argument, you can use testing::DeleteArg() to delete the N'th (zero-indexed) -argument: - -```cpp -using ::testing::_; - ... - MOCK_METHOD(void, Bar, (X* x, const Y& y)); - ... - EXPECT_CALL(mock_foo_, Bar(_, _)) - .WillOnce(testing::DeleteArg<0>())); -``` - -### How can I perform an arbitrary action on a mock function's argument? - -If you find yourself needing to perform some action that's not supported by -gMock directly, remember that you can define your own actions using -[`MakeAction()`](#NewMonoActions) or -[`MakePolymorphicAction()`](#NewPolyActions), or you can write a stub function -and invoke it using [`Invoke()`](#FunctionsAsActions). - -```cpp -using ::testing::_; -using ::testing::Invoke; - ... - MOCK_METHOD(void, Bar, (X* p)); - ... - EXPECT_CALL(mock_foo_, Bar(_)) - .WillOnce(Invoke(MyAction(...))); -``` - -### My code calls a static/global function. Can I mock it? - -You can, but you need to make some changes. - -In general, if you find yourself needing to mock a static function, it's a sign -that your modules are too tightly coupled (and less flexible, less reusable, -less testable, etc). You are probably better off defining a small interface and -call the function through that interface, which then can be easily mocked. It's -a bit of work initially, but usually pays for itself quickly. - -This Google Testing Blog -[post](https://testing.googleblog.com/2008/06/defeat-static-cling.html) says it -excellently. Check it out. - -### My mock object needs to do complex stuff. It's a lot of pain to specify the actions. gMock sucks! - -I know it's not a question, but you get an answer for free any way. :-) - -With gMock, you can create mocks in C++ easily. And people might be tempted to -use them everywhere. Sometimes they work great, and sometimes you may find them, -well, a pain to use. So, what's wrong in the latter case? - -When you write a test without using mocks, you exercise the code and assert that -it returns the correct value or that the system is in an expected state. This is -sometimes called "state-based testing". - -Mocks are great for what some call "interaction-based" testing: instead of -checking the system state at the very end, mock objects verify that they are -invoked the right way and report an error as soon as it arises, giving you a -handle on the precise context in which the error was triggered. This is often -more effective and economical to do than state-based testing. - -If you are doing state-based testing and using a test double just to simulate -the real object, you are probably better off using a fake. Using a mock in this -case causes pain, as it's not a strong point for mocks to perform complex -actions. If you experience this and think that mocks suck, you are just not -using the right tool for your problem. Or, you might be trying to solve the -wrong problem. :-) - -### I got a warning "Uninteresting function call encountered - default action taken.." Should I panic? - -By all means, NO! It's just an FYI. :-) - -What it means is that you have a mock function, you haven't set any expectations -on it (by gMock's rule this means that you are not interested in calls to this -function and therefore it can be called any number of times), and it is called. -That's OK - you didn't say it's not OK to call the function! - -What if you actually meant to disallow this function to be called, but forgot to -write `EXPECT_CALL(foo, Bar()).Times(0)`? While one can argue that it's the -user's fault, gMock tries to be nice and prints you a note. - -So, when you see the message and believe that there shouldn't be any -uninteresting calls, you should investigate what's going on. To make your life -easier, gMock dumps the stack trace when an uninteresting call is encountered. -From that you can figure out which mock function it is, and how it is called. - -### I want to define a custom action. Should I use Invoke() or implement the ActionInterface interface? - -Either way is fine - you want to choose the one that's more convenient for your -circumstance. - -Usually, if your action is for a particular function type, defining it using -`Invoke()` should be easier; if your action can be used in functions of -different types (e.g. if you are defining `Return(*value*)`), -`MakePolymorphicAction()` is easiest. Sometimes you want precise control on what -types of functions the action can be used in, and implementing `ActionInterface` -is the way to go here. See the implementation of `Return()` in `gmock-actions.h` -for an example. - -### I use SetArgPointee() in WillOnce(), but gcc complains about "conflicting return type specified". What does it mean? - -You got this error as gMock has no idea what value it should return when the -mock method is called. `SetArgPointee()` says what the side effect is, but -doesn't say what the return value should be. You need `DoAll()` to chain a -`SetArgPointee()` with a `Return()` that provides a value appropriate to the API -being mocked. - -See this [recipe](gmock_cook_book.md#mocking-side-effects) for more details and -an example. - -### I have a huge mock class, and Microsoft Visual C++ runs out of memory when compiling it. What can I do? - -We've noticed that when the `/clr` compiler flag is used, Visual C++ uses 5~6 -times as much memory when compiling a mock class. We suggest to avoid `/clr` -when compiling native C++ mocks. diff --git a/test/unit/googletest/docs/gmock_for_dummies.md b/test/unit/googletest/docs/gmock_for_dummies.md deleted file mode 100644 index ed2297c..0000000 --- a/test/unit/googletest/docs/gmock_for_dummies.md +++ /dev/null @@ -1,702 +0,0 @@ -# gMock for Dummies - -## What Is gMock? - -When you write a prototype or test, often it's not feasible or wise to rely on -real objects entirely. A **mock object** implements the same interface as a real -object (so it can be used as one), but lets you specify at run time how it will -be used and what it should do (which methods will be called? in which order? how -many times? with what arguments? what will they return? etc). - -It is easy to confuse the term *fake objects* with mock objects. Fakes and mocks -actually mean very different things in the Test-Driven Development (TDD) -community: - -* **Fake** objects have working implementations, but usually take some - shortcut (perhaps to make the operations less expensive), which makes them - not suitable for production. An in-memory file system would be an example of - a fake. -* **Mocks** are objects pre-programmed with *expectations*, which form a - specification of the calls they are expected to receive. - -If all this seems too abstract for you, don't worry - the most important thing -to remember is that a mock allows you to check the *interaction* between itself -and code that uses it. The difference between fakes and mocks shall become much -clearer once you start to use mocks. - -**gMock** is a library (sometimes we also call it a "framework" to make it sound -cool) for creating mock classes and using them. It does to C++ what -jMock/EasyMock does to Java (well, more or less). - -When using gMock, - -1. first, you use some simple macros to describe the interface you want to - mock, and they will expand to the implementation of your mock class; -2. next, you create some mock objects and specify its expectations and behavior - using an intuitive syntax; -3. then you exercise code that uses the mock objects. gMock will catch any - violation to the expectations as soon as it arises. - -## Why gMock? - -While mock objects help you remove unnecessary dependencies in tests and make -them fast and reliable, using mocks manually in C++ is *hard*: - -* Someone has to implement the mocks. The job is usually tedious and - error-prone. No wonder people go great distance to avoid it. -* The quality of those manually written mocks is a bit, uh, unpredictable. You - may see some really polished ones, but you may also see some that were - hacked up in a hurry and have all sorts of ad hoc restrictions. -* The knowledge you gained from using one mock doesn't transfer to the next - one. - -In contrast, Java and Python programmers have some fine mock frameworks (jMock, -EasyMock, etc), which automate the creation of mocks. As a result, mocking is a -proven effective technique and widely adopted practice in those communities. -Having the right tool absolutely makes the difference. - -gMock was built to help C++ programmers. It was inspired by jMock and EasyMock, -but designed with C++'s specifics in mind. It is your friend if any of the -following problems is bothering you: - -* You are stuck with a sub-optimal design and wish you had done more - prototyping before it was too late, but prototyping in C++ is by no means - "rapid". -* Your tests are slow as they depend on too many libraries or use expensive - resources (e.g. a database). -* Your tests are brittle as some resources they use are unreliable (e.g. the - network). -* You want to test how your code handles a failure (e.g. a file checksum - error), but it's not easy to cause one. -* You need to make sure that your module interacts with other modules in the - right way, but it's hard to observe the interaction; therefore you resort to - observing the side effects at the end of the action, but it's awkward at - best. -* You want to "mock out" your dependencies, except that they don't have mock - implementations yet; and, frankly, you aren't thrilled by some of those - hand-written mocks. - -We encourage you to use gMock as - -* a *design* tool, for it lets you experiment with your interface design early - and often. More iterations lead to better designs! -* a *testing* tool to cut your tests' outbound dependencies and probe the - interaction between your module and its collaborators. - -## Getting Started - -gMock is bundled with googletest. - -## A Case for Mock Turtles - -Let's look at an example. Suppose you are developing a graphics program that -relies on a [LOGO](https://en.wikipedia.org/wiki/Logo_programming_language)-like -API for drawing. How would you test that it does the right thing? Well, you can -run it and compare the screen with a golden screen snapshot, but let's admit it: -tests like this are expensive to run and fragile (What if you just upgraded to a -shiny new graphics card that has better anti-aliasing? Suddenly you have to -update all your golden images.). It would be too painful if all your tests are -like this. Fortunately, you learned about -[Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection) and know the right thing -to do: instead of having your application talk to the system API directly, wrap -the API in an interface (say, `Turtle`) and code to that interface: - -```cpp -class Turtle { - ... - virtual ~Turtle() {} - virtual void PenUp() = 0; - virtual void PenDown() = 0; - virtual void Forward(int distance) = 0; - virtual void Turn(int degrees) = 0; - virtual void GoTo(int x, int y) = 0; - virtual int GetX() const = 0; - virtual int GetY() const = 0; -}; -``` - -(Note that the destructor of `Turtle` **must** be virtual, as is the case for -**all** classes you intend to inherit from - otherwise the destructor of the -derived class will not be called when you delete an object through a base -pointer, and you'll get corrupted program states like memory leaks.) - -You can control whether the turtle's movement will leave a trace using `PenUp()` -and `PenDown()`, and control its movement using `Forward()`, `Turn()`, and -`GoTo()`. Finally, `GetX()` and `GetY()` tell you the current position of the -turtle. - -Your program will normally use a real implementation of this interface. In -tests, you can use a mock implementation instead. This allows you to easily -check what drawing primitives your program is calling, with what arguments, and -in which order. Tests written this way are much more robust (they won't break -because your new machine does anti-aliasing differently), easier to read and -maintain (the intent of a test is expressed in the code, not in some binary -images), and run *much, much faster*. - -## Writing the Mock Class - -If you are lucky, the mocks you need to use have already been implemented by -some nice people. If, however, you find yourself in the position to write a mock -class, relax - gMock turns this task into a fun game! (Well, almost.) - -### How to Define It - -Using the `Turtle` interface as example, here are the simple steps you need to -follow: - -* Derive a class `MockTurtle` from `Turtle`. -* Take a *virtual* function of `Turtle` (while it's possible to - [mock non-virtual methods using templates](gmock_cook_book.md#MockingNonVirtualMethods), - it's much more involved). -* In the `public:` section of the child class, write `MOCK_METHOD();` -* Now comes the fun part: you take the function signature, cut-and-paste it - into the macro, and add two commas - one between the return type and the - name, another between the name and the argument list. -* If you're mocking a const method, add a 4th parameter containing `(const)` - (the parentheses are required). -* Since you're overriding a virtual method, we suggest adding the `override` - keyword. For const methods the 4th parameter becomes `(const, override)`, - for non-const methods just `(override)`. This isn't mandatory. -* Repeat until all virtual functions you want to mock are done. (It goes - without saying that *all* pure virtual methods in your abstract class must - be either mocked or overridden.) - -After the process, you should have something like: - -```cpp -#include // Brings in gMock. - -class MockTurtle : public Turtle { - public: - ... - MOCK_METHOD(void, PenUp, (), (override)); - MOCK_METHOD(void, PenDown, (), (override)); - MOCK_METHOD(void, Forward, (int distance), (override)); - MOCK_METHOD(void, Turn, (int degrees), (override)); - MOCK_METHOD(void, GoTo, (int x, int y), (override)); - MOCK_METHOD(int, GetX, (), (const, override)); - MOCK_METHOD(int, GetY, (), (const, override)); -}; -``` - -You don't need to define these mock methods somewhere else - the `MOCK_METHOD` -macro will generate the definitions for you. It's that simple! - -### Where to Put It - -When you define a mock class, you need to decide where to put its definition. -Some people put it in a `_test.cc`. This is fine when the interface being mocked -(say, `Foo`) is owned by the same person or team. Otherwise, when the owner of -`Foo` changes it, your test could break. (You can't really expect `Foo`'s -maintainer to fix every test that uses `Foo`, can you?) - -Generally, you should not mock classes you don't own. If you must mock such a -class owned by others, define the mock class in `Foo`'s Bazel package (usually -the same directory or a `testing` sub-directory), and put it in a `.h` and a -`cc_library` with `testonly=True`. Then everyone can reference them from their -tests. If `Foo` ever changes, there is only one copy of `MockFoo` to change, and -only tests that depend on the changed methods need to be fixed. - -Another way to do it: you can introduce a thin layer `FooAdaptor` on top of -`Foo` and code to this new interface. Since you own `FooAdaptor`, you can absorb -changes in `Foo` much more easily. While this is more work initially, carefully -choosing the adaptor interface can make your code easier to write and more -readable (a net win in the long run), as you can choose `FooAdaptor` to fit your -specific domain much better than `Foo` does. - -## Using Mocks in Tests - -Once you have a mock class, using it is easy. The typical work flow is: - -1. Import the gMock names from the `testing` namespace such that you can use - them unqualified (You only have to do it once per file). Remember that - namespaces are a good idea. -2. Create some mock objects. -3. Specify your expectations on them (How many times will a method be called? - With what arguments? What should it do? etc.). -4. Exercise some code that uses the mocks; optionally, check the result using - googletest assertions. If a mock method is called more than expected or with - wrong arguments, you'll get an error immediately. -5. When a mock is destructed, gMock will automatically check whether all - expectations on it have been satisfied. - -Here's an example: - -```cpp -#include "path/to/mock-turtle.h" -#include -#include - -using ::testing::AtLeast; // #1 - -TEST(PainterTest, CanDrawSomething) { - MockTurtle turtle; // #2 - EXPECT_CALL(turtle, PenDown()) // #3 - .Times(AtLeast(1)); - - Painter painter(&turtle); // #4 - - EXPECT_TRUE(painter.DrawCircle(0, 0, 10)); // #5 -} -``` - -As you might have guessed, this test checks that `PenDown()` is called at least -once. If the `painter` object didn't call this method, your test will fail with -a message like this: - -```text -path/to/my_test.cc:119: Failure -Actual function call count doesn't match this expectation: -Actually: never called; -Expected: called at least once. -Stack trace: -... -``` - -**Tip 1:** If you run the test from an Emacs buffer, you can hit `` on -the line number to jump right to the failed expectation. - -**Tip 2:** If your mock objects are never deleted, the final verification won't -happen. Therefore it's a good idea to turn on the heap checker in your tests -when you allocate mocks on the heap. You get that automatically if you use the -`gtest_main` library already. - -###### Expectation Ordering - -**Important note:** gMock requires expectations to be set **before** the mock -functions are called, otherwise the behavior is **undefined**. Do not alternate -between calls to `EXPECT_CALL()` and calls to the mock functions, and do not set -any expectations on a mock after passing the mock to an API. - -This means `EXPECT_CALL()` should be read as expecting that a call will occur -*in the future*, not that a call has occurred. Why does gMock work like that? -Well, specifying the expectation beforehand allows gMock to report a violation -as soon as it rises, when the context (stack trace, etc) is still available. -This makes debugging much easier. - -Admittedly, this test is contrived and doesn't do much. You can easily achieve -the same effect without using gMock. However, as we shall reveal soon, gMock -allows you to do *so much more* with the mocks. - -## Setting Expectations - -The key to using a mock object successfully is to set the *right expectations* -on it. If you set the expectations too strict, your test will fail as the result -of unrelated changes. If you set them too loose, bugs can slip through. You want -to do it just right such that your test can catch exactly the kind of bugs you -intend it to catch. gMock provides the necessary means for you to do it "just -right." - -### General Syntax - -In gMock we use the `EXPECT_CALL()` macro to set an expectation on a mock -method. The general syntax is: - -```cpp -EXPECT_CALL(mock_object, method(matchers)) - .Times(cardinality) - .WillOnce(action) - .WillRepeatedly(action); -``` - -The macro has two arguments: first the mock object, and then the method and its -arguments. Note that the two are separated by a comma (`,`), not a period (`.`). -(Why using a comma? The answer is that it was necessary for technical reasons.) -If the method is not overloaded, the macro can also be called without matchers: - -```cpp -EXPECT_CALL(mock_object, non-overloaded-method) - .Times(cardinality) - .WillOnce(action) - .WillRepeatedly(action); -``` - -This syntax allows the test writer to specify "called with any arguments" -without explicitly specifying the number or types of arguments. To avoid -unintended ambiguity, this syntax may only be used for methods that are not -overloaded. - -Either form of the macro can be followed by some optional *clauses* that provide -more information about the expectation. We'll discuss how each clause works in -the coming sections. - -This syntax is designed to make an expectation read like English. For example, -you can probably guess that - -```cpp -using ::testing::Return; -... -EXPECT_CALL(turtle, GetX()) - .Times(5) - .WillOnce(Return(100)) - .WillOnce(Return(150)) - .WillRepeatedly(Return(200)); -``` - -says that the `turtle` object's `GetX()` method will be called five times, it -will return 100 the first time, 150 the second time, and then 200 every time. -Some people like to call this style of syntax a Domain-Specific Language (DSL). - -{: .callout .note} -**Note:** Why do we use a macro to do this? Well it serves two purposes: first -it makes expectations easily identifiable (either by `grep` or by a human -reader), and second it allows gMock to include the source file location of a -failed expectation in messages, making debugging easier. - -### Matchers: What Arguments Do We Expect? - -When a mock function takes arguments, we may specify what arguments we are -expecting, for example: - -```cpp -// Expects the turtle to move forward by 100 units. -EXPECT_CALL(turtle, Forward(100)); -``` - -Oftentimes you do not want to be too specific. Remember that talk about tests -being too rigid? Over specification leads to brittle tests and obscures the -intent of tests. Therefore we encourage you to specify only what's necessary—no -more, no less. If you aren't interested in the value of an argument, write `_` -as the argument, which means "anything goes": - -```cpp -using ::testing::_; -... -// Expects that the turtle jumps to somewhere on the x=50 line. -EXPECT_CALL(turtle, GoTo(50, _)); -``` - -`_` is an instance of what we call **matchers**. A matcher is like a predicate -and can test whether an argument is what we'd expect. You can use a matcher -inside `EXPECT_CALL()` wherever a function argument is expected. `_` is a -convenient way of saying "any value". - -In the above examples, `100` and `50` are also matchers; implicitly, they are -the same as `Eq(100)` and `Eq(50)`, which specify that the argument must be -equal (using `operator==`) to the matcher argument. There are many -[built-in matchers](reference/matchers.md) for common types (as well as -[custom matchers](gmock_cook_book.md#NewMatchers)); for example: - -```cpp -using ::testing::Ge; -... -// Expects the turtle moves forward by at least 100. -EXPECT_CALL(turtle, Forward(Ge(100))); -``` - -If you don't care about *any* arguments, rather than specify `_` for each of -them you may instead omit the parameter list: - -```cpp -// Expects the turtle to move forward. -EXPECT_CALL(turtle, Forward); -// Expects the turtle to jump somewhere. -EXPECT_CALL(turtle, GoTo); -``` - -This works for all non-overloaded methods; if a method is overloaded, you need -to help gMock resolve which overload is expected by specifying the number of -arguments and possibly also the -[types of the arguments](gmock_cook_book.md#SelectOverload). - -### Cardinalities: How Many Times Will It Be Called? - -The first clause we can specify following an `EXPECT_CALL()` is `Times()`. We -call its argument a **cardinality** as it tells *how many times* the call should -occur. It allows us to repeat an expectation many times without actually writing -it as many times. More importantly, a cardinality can be "fuzzy", just like a -matcher can be. This allows a user to express the intent of a test exactly. - -An interesting special case is when we say `Times(0)`. You may have guessed - it -means that the function shouldn't be called with the given arguments at all, and -gMock will report a googletest failure whenever the function is (wrongfully) -called. - -We've seen `AtLeast(n)` as an example of fuzzy cardinalities earlier. For the -list of built-in cardinalities you can use, see -[here](gmock_cheat_sheet.md#CardinalityList). - -The `Times()` clause can be omitted. **If you omit `Times()`, gMock will infer -the cardinality for you.** The rules are easy to remember: - -* If **neither** `WillOnce()` **nor** `WillRepeatedly()` is in the - `EXPECT_CALL()`, the inferred cardinality is `Times(1)`. -* If there are *n* `WillOnce()`'s but **no** `WillRepeatedly()`, where *n* >= - 1, the cardinality is `Times(n)`. -* If there are *n* `WillOnce()`'s and **one** `WillRepeatedly()`, where *n* >= - 0, the cardinality is `Times(AtLeast(n))`. - -**Quick quiz:** what do you think will happen if a function is expected to be -called twice but actually called four times? - -### Actions: What Should It Do? - -Remember that a mock object doesn't really have a working implementation? We as -users have to tell it what to do when a method is invoked. This is easy in -gMock. - -First, if the return type of a mock function is a built-in type or a pointer, -the function has a **default action** (a `void` function will just return, a -`bool` function will return `false`, and other functions will return 0). In -addition, in C++ 11 and above, a mock function whose return type is -default-constructible (i.e. has a default constructor) has a default action of -returning a default-constructed value. If you don't say anything, this behavior -will be used. - -Second, if a mock function doesn't have a default action, or the default action -doesn't suit you, you can specify the action to be taken each time the -expectation matches using a series of `WillOnce()` clauses followed by an -optional `WillRepeatedly()`. For example, - -```cpp -using ::testing::Return; -... -EXPECT_CALL(turtle, GetX()) - .WillOnce(Return(100)) - .WillOnce(Return(200)) - .WillOnce(Return(300)); -``` - -says that `turtle.GetX()` will be called *exactly three times* (gMock inferred -this from how many `WillOnce()` clauses we've written, since we didn't -explicitly write `Times()`), and will return 100, 200, and 300 respectively. - -```cpp -using ::testing::Return; -... -EXPECT_CALL(turtle, GetY()) - .WillOnce(Return(100)) - .WillOnce(Return(200)) - .WillRepeatedly(Return(300)); -``` - -says that `turtle.GetY()` will be called *at least twice* (gMock knows this as -we've written two `WillOnce()` clauses and a `WillRepeatedly()` while having no -explicit `Times()`), will return 100 and 200 respectively the first two times, -and 300 from the third time on. - -Of course, if you explicitly write a `Times()`, gMock will not try to infer the -cardinality itself. What if the number you specified is larger than there are -`WillOnce()` clauses? Well, after all `WillOnce()`s are used up, gMock will do -the *default* action for the function every time (unless, of course, you have a -`WillRepeatedly()`.). - -What can we do inside `WillOnce()` besides `Return()`? You can return a -reference using `ReturnRef(`*`variable`*`)`, or invoke a pre-defined function, -among [others](gmock_cook_book.md#using-actions). - -**Important note:** The `EXPECT_CALL()` statement evaluates the action clause -only once, even though the action may be performed many times. Therefore you -must be careful about side effects. The following may not do what you want: - -```cpp -using ::testing::Return; -... -int n = 100; -EXPECT_CALL(turtle, GetX()) - .Times(4) - .WillRepeatedly(Return(n++)); -``` - -Instead of returning 100, 101, 102, ..., consecutively, this mock function will -always return 100 as `n++` is only evaluated once. Similarly, `Return(new Foo)` -will create a new `Foo` object when the `EXPECT_CALL()` is executed, and will -return the same pointer every time. If you want the side effect to happen every -time, you need to define a custom action, which we'll teach in the -[cook book](gmock_cook_book.md). - -Time for another quiz! What do you think the following means? - -```cpp -using ::testing::Return; -... -EXPECT_CALL(turtle, GetY()) - .Times(4) - .WillOnce(Return(100)); -``` - -Obviously `turtle.GetY()` is expected to be called four times. But if you think -it will return 100 every time, think twice! Remember that one `WillOnce()` -clause will be consumed each time the function is invoked and the default action -will be taken afterwards. So the right answer is that `turtle.GetY()` will -return 100 the first time, but **return 0 from the second time on**, as -returning 0 is the default action for `int` functions. - -### Using Multiple Expectations {#MultiExpectations} - -So far we've only shown examples where you have a single expectation. More -realistically, you'll specify expectations on multiple mock methods which may be -from multiple mock objects. - -By default, when a mock method is invoked, gMock will search the expectations in -the **reverse order** they are defined, and stop when an active expectation that -matches the arguments is found (you can think of it as "newer rules override -older ones."). If the matching expectation cannot take any more calls, you will -get an upper-bound-violated failure. Here's an example: - -```cpp -using ::testing::_; -... -EXPECT_CALL(turtle, Forward(_)); // #1 -EXPECT_CALL(turtle, Forward(10)) // #2 - .Times(2); -``` - -If `Forward(10)` is called three times in a row, the third time it will be an -error, as the last matching expectation (#2) has been saturated. If, however, -the third `Forward(10)` call is replaced by `Forward(20)`, then it would be OK, -as now #1 will be the matching expectation. - -{: .callout .note} -**Note:** Why does gMock search for a match in the *reverse* order of the -expectations? The reason is that this allows a user to set up the default -expectations in a mock object's constructor or the test fixture's set-up phase -and then customize the mock by writing more specific expectations in the test -body. So, if you have two expectations on the same method, you want to put the -one with more specific matchers **after** the other, or the more specific rule -would be shadowed by the more general one that comes after it. - -{: .callout .tip} -**Tip:** It is very common to start with a catch-all expectation for a method -and `Times(AnyNumber())` (omitting arguments, or with `_` for all arguments, if -overloaded). This makes any calls to the method expected. This is not necessary -for methods that are not mentioned at all (these are "uninteresting"), but is -useful for methods that have some expectations, but for which other calls are -ok. See -[Understanding Uninteresting vs Unexpected Calls](gmock_cook_book.md#uninteresting-vs-unexpected). - -### Ordered vs Unordered Calls {#OrderedCalls} - -By default, an expectation can match a call even though an earlier expectation -hasn't been satisfied. In other words, the calls don't have to occur in the -order the expectations are specified. - -Sometimes, you may want all the expected calls to occur in a strict order. To -say this in gMock is easy: - -```cpp -using ::testing::InSequence; -... -TEST(FooTest, DrawsLineSegment) { - ... - { - InSequence seq; - - EXPECT_CALL(turtle, PenDown()); - EXPECT_CALL(turtle, Forward(100)); - EXPECT_CALL(turtle, PenUp()); - } - Foo(); -} -``` - -By creating an object of type `InSequence`, all expectations in its scope are -put into a *sequence* and have to occur *sequentially*. Since we are just -relying on the constructor and destructor of this object to do the actual work, -its name is really irrelevant. - -In this example, we test that `Foo()` calls the three expected functions in the -order as written. If a call is made out-of-order, it will be an error. - -(What if you care about the relative order of some of the calls, but not all of -them? Can you specify an arbitrary partial order? The answer is ... yes! The -details can be found [here](gmock_cook_book.md#OrderedCalls).) - -### All Expectations Are Sticky (Unless Said Otherwise) {#StickyExpectations} - -Now let's do a quick quiz to see how well you can use this mock stuff already. -How would you test that the turtle is asked to go to the origin *exactly twice* -(you want to ignore any other instructions it receives)? - -After you've come up with your answer, take a look at ours and compare notes -(solve it yourself first - don't cheat!): - -```cpp -using ::testing::_; -using ::testing::AnyNumber; -... -EXPECT_CALL(turtle, GoTo(_, _)) // #1 - .Times(AnyNumber()); -EXPECT_CALL(turtle, GoTo(0, 0)) // #2 - .Times(2); -``` - -Suppose `turtle.GoTo(0, 0)` is called three times. In the third time, gMock will -see that the arguments match expectation #2 (remember that we always pick the -last matching expectation). Now, since we said that there should be only two -such calls, gMock will report an error immediately. This is basically what we've -told you in the [Using Multiple Expectations](#MultiExpectations) section above. - -This example shows that **expectations in gMock are "sticky" by default**, in -the sense that they remain active even after we have reached their invocation -upper bounds. This is an important rule to remember, as it affects the meaning -of the spec, and is **different** to how it's done in many other mocking -frameworks (Why'd we do that? Because we think our rule makes the common cases -easier to express and understand.). - -Simple? Let's see if you've really understood it: what does the following code -say? - -```cpp -using ::testing::Return; -... -for (int i = n; i > 0; i--) { - EXPECT_CALL(turtle, GetX()) - .WillOnce(Return(10*i)); -} -``` - -If you think it says that `turtle.GetX()` will be called `n` times and will -return 10, 20, 30, ..., consecutively, think twice! The problem is that, as we -said, expectations are sticky. So, the second time `turtle.GetX()` is called, -the last (latest) `EXPECT_CALL()` statement will match, and will immediately -lead to an "upper bound violated" error - this piece of code is not very useful! - -One correct way of saying that `turtle.GetX()` will return 10, 20, 30, ..., is -to explicitly say that the expectations are *not* sticky. In other words, they -should *retire* as soon as they are saturated: - -```cpp -using ::testing::Return; -... -for (int i = n; i > 0; i--) { - EXPECT_CALL(turtle, GetX()) - .WillOnce(Return(10*i)) - .RetiresOnSaturation(); -} -``` - -And, there's a better way to do it: in this case, we expect the calls to occur -in a specific order, and we line up the actions to match the order. Since the -order is important here, we should make it explicit using a sequence: - -```cpp -using ::testing::InSequence; -using ::testing::Return; -... -{ - InSequence s; - - for (int i = 1; i <= n; i++) { - EXPECT_CALL(turtle, GetX()) - .WillOnce(Return(10*i)) - .RetiresOnSaturation(); - } -} -``` - -By the way, the other situation where an expectation may *not* be sticky is when -it's in a sequence - as soon as another expectation that comes after it in the -sequence has been used, it automatically retires (and will never be used to -match any call). - -### Uninteresting Calls - -A mock object may have many methods, and not all of them are that interesting. -For example, in some tests we may not care about how many times `GetX()` and -`GetY()` get called. - -In gMock, if you are not interested in a method, just don't say anything about -it. If a call to this method occurs, you'll see a warning in the test output, -but it won't be a failure. This is called "naggy" behavior; to change, see -[The Nice, the Strict, and the Naggy](gmock_cook_book.md#NiceStrictNaggy). diff --git a/test/unit/googletest/docs/index.md b/test/unit/googletest/docs/index.md deleted file mode 100644 index b162c74..0000000 --- a/test/unit/googletest/docs/index.md +++ /dev/null @@ -1,22 +0,0 @@ -# GoogleTest User's Guide - -## Welcome to GoogleTest! - -GoogleTest is Google's C++ testing and mocking framework. This user's guide has -the following contents: - -* [GoogleTest Primer](primer.md) - Teaches you how to write simple tests using - GoogleTest. Read this first if you are new to GoogleTest. -* [GoogleTest Advanced](advanced.md) - Read this when you've finished the - Primer and want to utilize GoogleTest to its full potential. -* [GoogleTest Samples](samples.md) - Describes some GoogleTest samples. -* [GoogleTest FAQ](faq.md) - Have a question? Want some tips? Check here - first. -* [Mocking for Dummies](gmock_for_dummies.md) - Teaches you how to create mock - objects and use them in tests. -* [Mocking Cookbook](gmock_cook_book.md) - Includes tips and approaches to - common mocking use cases. -* [Mocking Cheat Sheet](gmock_cheat_sheet.md) - A handy reference for - matchers, actions, invariants, and more. -* [Mocking FAQ](gmock_faq.md) - Contains answers to some mocking-specific - questions. diff --git a/test/unit/googletest/docs/pkgconfig.md b/test/unit/googletest/docs/pkgconfig.md deleted file mode 100644 index bf05d59..0000000 --- a/test/unit/googletest/docs/pkgconfig.md +++ /dev/null @@ -1,144 +0,0 @@ -## Using GoogleTest from various build systems - -GoogleTest comes with pkg-config files that can be used to determine all -necessary flags for compiling and linking to GoogleTest (and GoogleMock). -Pkg-config is a standardised plain-text format containing - -* the includedir (-I) path -* necessary macro (-D) definitions -* further required flags (-pthread) -* the library (-L) path -* the library (-l) to link to - -All current build systems support pkg-config in one way or another. For all -examples here we assume you want to compile the sample -`samples/sample3_unittest.cc`. - -### CMake - -Using `pkg-config` in CMake is fairly easy: - -```cmake -find_package(PkgConfig) -pkg_search_module(GTEST REQUIRED gtest_main) - -add_executable(testapp) -target_sources(testapp PRIVATE samples/sample3_unittest.cc) -target_link_libraries(testapp PRIVATE ${GTEST_LDFLAGS}) -target_compile_options(testapp PRIVATE ${GTEST_CFLAGS}) - -enable_testing() -add_test(first_and_only_test testapp) -``` - -It is generally recommended that you use `target_compile_options` + `_CFLAGS` -over `target_include_directories` + `_INCLUDE_DIRS` as the former includes not -just -I flags (GoogleTest might require a macro indicating to internal headers -that all libraries have been compiled with threading enabled. In addition, -GoogleTest might also require `-pthread` in the compiling step, and as such -splitting the pkg-config `Cflags` variable into include dirs and macros for -`target_compile_definitions()` might still miss this). The same recommendation -goes for using `_LDFLAGS` over the more commonplace `_LIBRARIES`, which happens -to discard `-L` flags and `-pthread`. - -### Help! pkg-config can't find GoogleTest! - -Let's say you have a `CMakeLists.txt` along the lines of the one in this -tutorial and you try to run `cmake`. It is very possible that you get a failure -along the lines of: - -``` --- Checking for one of the modules 'gtest_main' -CMake Error at /usr/share/cmake/Modules/FindPkgConfig.cmake:640 (message): - None of the required 'gtest_main' found -``` - -These failures are common if you installed GoogleTest yourself and have not -sourced it from a distro or other package manager. If so, you need to tell -pkg-config where it can find the `.pc` files containing the information. Say you -installed GoogleTest to `/usr/local`, then it might be that the `.pc` files are -installed under `/usr/local/lib64/pkgconfig`. If you set - -``` -export PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig -``` - -pkg-config will also try to look in `PKG_CONFIG_PATH` to find `gtest_main.pc`. - -### Using pkg-config in a cross-compilation setting - -Pkg-config can be used in a cross-compilation setting too. To do this, let's -assume the final prefix of the cross-compiled installation will be `/usr`, and -your sysroot is `/home/MYUSER/sysroot`. Configure and install GTest using - -``` -mkdir build && cmake -DCMAKE_INSTALL_PREFIX=/usr .. -``` - -Install into the sysroot using `DESTDIR`: - -``` -make -j install DESTDIR=/home/MYUSER/sysroot -``` - -Before we continue, it is recommended to **always** define the following two -variables for pkg-config in a cross-compilation setting: - -``` -export PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=yes -export PKG_CONFIG_ALLOW_SYSTEM_LIBS=yes -``` - -otherwise `pkg-config` will filter `-I` and `-L` flags against standard prefixes -such as `/usr` (see https://bugs.freedesktop.org/show_bug.cgi?id=28264#c3 for -reasons why this stripping needs to occur usually). - -If you look at the generated pkg-config file, it will look something like - -``` -libdir=/usr/lib64 -includedir=/usr/include - -Name: gtest -Description: GoogleTest (without main() function) -Version: 1.11.0 -URL: https://github.com/google/googletest -Libs: -L${libdir} -lgtest -lpthread -Cflags: -I${includedir} -DGTEST_HAS_PTHREAD=1 -lpthread -``` - -Notice that the sysroot is not included in `libdir` and `includedir`! If you try -to run `pkg-config` with the correct -`PKG_CONFIG_LIBDIR=/home/MYUSER/sysroot/usr/lib64/pkgconfig` against this `.pc` -file, you will get - -``` -$ pkg-config --cflags gtest --DGTEST_HAS_PTHREAD=1 -lpthread -I/usr/include -$ pkg-config --libs gtest --L/usr/lib64 -lgtest -lpthread -``` - -which is obviously wrong and points to the `CBUILD` and not `CHOST` root. In -order to use this in a cross-compilation setting, we need to tell pkg-config to -inject the actual sysroot into `-I` and `-L` variables. Let us now tell -pkg-config about the actual sysroot - -``` -export PKG_CONFIG_DIR= -export PKG_CONFIG_SYSROOT_DIR=/home/MYUSER/sysroot -export PKG_CONFIG_LIBDIR=${PKG_CONFIG_SYSROOT_DIR}/usr/lib64/pkgconfig -``` - -and running `pkg-config` again we get - -``` -$ pkg-config --cflags gtest --DGTEST_HAS_PTHREAD=1 -lpthread -I/home/MYUSER/sysroot/usr/include -$ pkg-config --libs gtest --L/home/MYUSER/sysroot/usr/lib64 -lgtest -lpthread -``` - -which contains the correct sysroot now. For a more comprehensive guide to also -including `${CHOST}` in build system calls, see the excellent tutorial by Diego -Elio Pettenò: diff --git a/test/unit/googletest/docs/platforms.md b/test/unit/googletest/docs/platforms.md deleted file mode 100644 index d35a7be..0000000 --- a/test/unit/googletest/docs/platforms.md +++ /dev/null @@ -1,8 +0,0 @@ -# Supported Platforms - -GoogleTest follows Google's -[Foundational C++ Support Policy](https://opensource.google/documentation/policies/cplusplus-support). -See -[this table](https://github.com/google/oss-policies-info/blob/main/foundational-cxx-support-matrix.md) -for a list of currently supported versions compilers, platforms, and build -tools. diff --git a/test/unit/googletest/docs/primer.md b/test/unit/googletest/docs/primer.md deleted file mode 100644 index 61806be..0000000 --- a/test/unit/googletest/docs/primer.md +++ /dev/null @@ -1,482 +0,0 @@ -# GoogleTest Primer - -## Introduction: Why GoogleTest? - -*GoogleTest* helps you write better C++ tests. - -GoogleTest is a testing framework developed by the Testing Technology team with -Google's specific requirements and constraints in mind. Whether you work on -Linux, Windows, or a Mac, if you write C++ code, GoogleTest can help you. And it -supports *any* kind of tests, not just unit tests. - -So what makes a good test, and how does GoogleTest fit in? We believe: - -1. Tests should be *independent* and *repeatable*. It's a pain to debug a test - that succeeds or fails as a result of other tests. GoogleTest isolates the - tests by running each of them on a different object. When a test fails, - GoogleTest allows you to run it in isolation for quick debugging. -2. Tests should be well *organized* and reflect the structure of the tested - code. GoogleTest groups related tests into test suites that can share data - and subroutines. This common pattern is easy to recognize and makes tests - easy to maintain. Such consistency is especially helpful when people switch - projects and start to work on a new code base. -3. Tests should be *portable* and *reusable*. Google has a lot of code that is - platform-neutral; its tests should also be platform-neutral. GoogleTest - works on different OSes, with different compilers, with or without - exceptions, so GoogleTest tests can work with a variety of configurations. -4. When tests fail, they should provide as much *information* about the problem - as possible. GoogleTest doesn't stop at the first test failure. Instead, it - only stops the current test and continues with the next. You can also set up - tests that report non-fatal failures after which the current test continues. - Thus, you can detect and fix multiple bugs in a single run-edit-compile - cycle. -5. The testing framework should liberate test writers from housekeeping chores - and let them focus on the test *content*. GoogleTest automatically keeps - track of all tests defined, and doesn't require the user to enumerate them - in order to run them. -6. Tests should be *fast*. With GoogleTest, you can reuse shared resources - across tests and pay for the set-up/tear-down only once, without making - tests depend on each other. - -Since GoogleTest is based on the popular xUnit architecture, you'll feel right -at home if you've used JUnit or PyUnit before. If not, it will take you about 10 -minutes to learn the basics and get started. So let's go! - -## Beware of the Nomenclature - -{: .callout .note} -*Note:* There might be some confusion arising from different definitions of the -terms *Test*, *Test Case* and *Test Suite*, so beware of misunderstanding these. - -Historically, GoogleTest started to use the term *Test Case* for grouping -related tests, whereas current publications, including International Software -Testing Qualifications Board ([ISTQB](https://www.istqb.org/)) materials and -various textbooks on software quality, use the term -*[Test Suite][istqb test suite]* for this. - -The related term *Test*, as it is used in GoogleTest, corresponds to the term -*[Test Case][istqb test case]* of ISTQB and others. - -The term *Test* is commonly of broad enough sense, including ISTQB's definition -of *Test Case*, so it's not much of a problem here. But the term *Test Case* as -was used in Google Test is of contradictory sense and thus confusing. - -GoogleTest recently started replacing the term *Test Case* with *Test Suite*. -The preferred API is *TestSuite*. The older TestCase API is being slowly -deprecated and refactored away. - -So please be aware of the different definitions of the terms: - - -Meaning | GoogleTest Term | [ISTQB](https://www.istqb.org/) Term -:----------------------------------------------------------------------------------- | :---------------------- | :---------------------------------- -Exercise a particular program path with specific input values and verify the results | [TEST()](#simple-tests) | [Test Case][istqb test case] - - -[istqb test case]: https://glossary.istqb.org/en_US/term/test-case-2 -[istqb test suite]: https://glossary.istqb.org/en_US/term/test-suite-1-3 - -## Basic Concepts - -When using GoogleTest, you start by writing *assertions*, which are statements -that check whether a condition is true. An assertion's result can be *success*, -*nonfatal failure*, or *fatal failure*. If a fatal failure occurs, it aborts the -current function; otherwise the program continues normally. - -*Tests* use assertions to verify the tested code's behavior. If a test crashes -or has a failed assertion, then it *fails*; otherwise it *succeeds*. - -A *test suite* contains one or many tests. You should group your tests into test -suites that reflect the structure of the tested code. When multiple tests in a -test suite need to share common objects and subroutines, you can put them into a -*test fixture* class. - -A *test program* can contain multiple test suites. - -We'll now explain how to write a test program, starting at the individual -assertion level and building up to tests and test suites. - -## Assertions - -GoogleTest assertions are macros that resemble function calls. You test a class -or function by making assertions about its behavior. When an assertion fails, -GoogleTest prints the assertion's source file and line number location, along -with a failure message. You may also supply a custom failure message which will -be appended to GoogleTest's message. - -The assertions come in pairs that test the same thing but have different effects -on the current function. `ASSERT_*` versions generate fatal failures when they -fail, and **abort the current function**. `EXPECT_*` versions generate nonfatal -failures, which don't abort the current function. Usually `EXPECT_*` are -preferred, as they allow more than one failure to be reported in a test. -However, you should use `ASSERT_*` if it doesn't make sense to continue when the -assertion in question fails. - -Since a failed `ASSERT_*` returns from the current function immediately, -possibly skipping clean-up code that comes after it, it may cause a space leak. -Depending on the nature of the leak, it may or may not be worth fixing - so keep -this in mind if you get a heap checker error in addition to assertion errors. - -To provide a custom failure message, simply stream it into the macro using the -`<<` operator or a sequence of such operators. See the following example, using -the [`ASSERT_EQ` and `EXPECT_EQ`](reference/assertions.md#EXPECT_EQ) macros to -verify value equality: - -```c++ -ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length"; - -for (int i = 0; i < x.size(); ++i) { - EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i; -} -``` - -Anything that can be streamed to an `ostream` can be streamed to an assertion -macro--in particular, C strings and `string` objects. If a wide string -(`wchar_t*`, `TCHAR*` in `UNICODE` mode on Windows, or `std::wstring`) is -streamed to an assertion, it will be translated to UTF-8 when printed. - -GoogleTest provides a collection of assertions for verifying the behavior of -your code in various ways. You can check Boolean conditions, compare values -based on relational operators, verify string values, floating-point values, and -much more. There are even assertions that enable you to verify more complex -states by providing custom predicates. For the complete list of assertions -provided by GoogleTest, see the [Assertions Reference](reference/assertions.md). - -## Simple Tests - -To create a test: - -1. Use the `TEST()` macro to define and name a test function. These are - ordinary C++ functions that don't return a value. -2. In this function, along with any valid C++ statements you want to include, - use the various GoogleTest assertions to check values. -3. The test's result is determined by the assertions; if any assertion in the - test fails (either fatally or non-fatally), or if the test crashes, the - entire test fails. Otherwise, it succeeds. - -```c++ -TEST(TestSuiteName, TestName) { - ... test body ... -} -``` - -`TEST()` arguments go from general to specific. The *first* argument is the name -of the test suite, and the *second* argument is the test's name within the test -suite. Both names must be valid C++ identifiers, and they should not contain any -underscores (`_`). A test's *full name* consists of its containing test suite -and its individual name. Tests from different test suites can have the same -individual name. - -For example, let's take a simple integer function: - -```c++ -int Factorial(int n); // Returns the factorial of n -``` - -A test suite for this function might look like: - -```c++ -// Tests factorial of 0. -TEST(FactorialTest, HandlesZeroInput) { - EXPECT_EQ(Factorial(0), 1); -} - -// Tests factorial of positive numbers. -TEST(FactorialTest, HandlesPositiveInput) { - EXPECT_EQ(Factorial(1), 1); - EXPECT_EQ(Factorial(2), 2); - EXPECT_EQ(Factorial(3), 6); - EXPECT_EQ(Factorial(8), 40320); -} -``` - -GoogleTest groups the test results by test suites, so logically related tests -should be in the same test suite; in other words, the first argument to their -`TEST()` should be the same. In the above example, we have two tests, -`HandlesZeroInput` and `HandlesPositiveInput`, that belong to the same test -suite `FactorialTest`. - -When naming your test suites and tests, you should follow the same convention as -for -[naming functions and classes](https://google.github.io/styleguide/cppguide.html#Function_Names). - -**Availability**: Linux, Windows, Mac. - -## Test Fixtures: Using the Same Data Configuration for Multiple Tests {#same-data-multiple-tests} - -If you find yourself writing two or more tests that operate on similar data, you -can use a *test fixture*. This allows you to reuse the same configuration of -objects for several different tests. - -To create a fixture: - -1. Derive a class from `testing::Test` . Start its body with `protected:`, as - we'll want to access fixture members from sub-classes. -2. Inside the class, declare any objects you plan to use. -3. If necessary, write a default constructor or `SetUp()` function to prepare - the objects for each test. A common mistake is to spell `SetUp()` as - **`Setup()`** with a small `u` - Use `override` in C++11 to make sure you - spelled it correctly. -4. If necessary, write a destructor or `TearDown()` function to release any - resources you allocated in `SetUp()` . To learn when you should use the - constructor/destructor and when you should use `SetUp()/TearDown()`, read - the [FAQ](faq.md#CtorVsSetUp). -5. If needed, define subroutines for your tests to share. - -When using a fixture, use `TEST_F()` instead of `TEST()` as it allows you to -access objects and subroutines in the test fixture: - -```c++ -TEST_F(TestFixtureClassName, TestName) { - ... test body ... -} -``` - -Unlike `TEST()`, in `TEST_F()` the first argument must be the name of the test -fixture class. (`_F` stands for "Fixture"). No test suite name is specified for -this macro. - -Unfortunately, the C++ macro system does not allow us to create a single macro -that can handle both types of tests. Using the wrong macro causes a compiler -error. - -Also, you must first define a test fixture class before using it in a -`TEST_F()`, or you'll get the compiler error "`virtual outside class -declaration`". - -For each test defined with `TEST_F()`, GoogleTest will create a *fresh* test -fixture at runtime, immediately initialize it via `SetUp()`, run the test, clean -up by calling `TearDown()`, and then delete the test fixture. Note that -different tests in the same test suite have different test fixture objects, and -GoogleTest always deletes a test fixture before it creates the next one. -GoogleTest does **not** reuse the same test fixture for multiple tests. Any -changes one test makes to the fixture do not affect other tests. - -As an example, let's write tests for a FIFO queue class named `Queue`, which has -the following interface: - -```c++ -template // E is the element type. -class Queue { - public: - Queue(); - void Enqueue(const E& element); - E* Dequeue(); // Returns NULL if the queue is empty. - size_t size() const; - ... -}; -``` - -First, define a fixture class. By convention, you should give it the name -`FooTest` where `Foo` is the class being tested. - -```c++ -class QueueTest : public testing::Test { - protected: - QueueTest() { - // q0_ remains empty - q1_.Enqueue(1); - q2_.Enqueue(2); - q2_.Enqueue(3); - } - - // ~QueueTest() override = default; - - Queue q0_; - Queue q1_; - Queue q2_; -}; -``` - -In this case, we don't need to define a destructor or a `TearDown()` method, -because the implicit destructor generated by the compiler will perform all of -the necessary cleanup. - -Now we'll write tests using `TEST_F()` and this fixture. - -```c++ -TEST_F(QueueTest, IsEmptyInitially) { - EXPECT_EQ(q0_.size(), 0); -} - -TEST_F(QueueTest, DequeueWorks) { - int* n = q0_.Dequeue(); - EXPECT_EQ(n, nullptr); - - n = q1_.Dequeue(); - ASSERT_NE(n, nullptr); - EXPECT_EQ(*n, 1); - EXPECT_EQ(q1_.size(), 0); - delete n; - - n = q2_.Dequeue(); - ASSERT_NE(n, nullptr); - EXPECT_EQ(*n, 2); - EXPECT_EQ(q2_.size(), 1); - delete n; -} -``` - -The above uses both `ASSERT_*` and `EXPECT_*` assertions. The rule of thumb is -to use `EXPECT_*` when you want the test to continue to reveal more errors after -the assertion failure, and use `ASSERT_*` when continuing after failure doesn't -make sense. For example, the second assertion in the `Dequeue` test is -`ASSERT_NE(n, nullptr)`, as we need to dereference the pointer `n` later, which -would lead to a segfault when `n` is `NULL`. - -When these tests run, the following happens: - -1. GoogleTest constructs a `QueueTest` object (let's call it `t1`). -2. The first test (`IsEmptyInitially`) runs on `t1`. -3. `t1` is destructed. -4. The above steps are repeated on another `QueueTest` object, this time - running the `DequeueWorks` test. - -**Availability**: Linux, Windows, Mac. - -## Invoking the Tests - -`TEST()` and `TEST_F()` implicitly register their tests with GoogleTest. So, -unlike with many other C++ testing frameworks, you don't have to re-list all -your defined tests in order to run them. - -After defining your tests, you can run them with `RUN_ALL_TESTS()`, which -returns `0` if all the tests are successful, or `1` otherwise. Note that -`RUN_ALL_TESTS()` runs *all tests* in your link unit--they can be from different -test suites, or even different source files. - -When invoked, the `RUN_ALL_TESTS()` macro: - -* Saves the state of all GoogleTest flags. - -* Creates a test fixture object for the first test. - -* Initializes it via `SetUp()`. - -* Runs the test on the fixture object. - -* Cleans up the fixture via `TearDown()`. - -* Deletes the fixture. - -* Restores the state of all GoogleTest flags. - -* Repeats the above steps for the next test, until all tests have run. - -If a fatal failure happens the subsequent steps will be skipped. - -{: .callout .important} -> IMPORTANT: You must **not** ignore the return value of `RUN_ALL_TESTS()`, or -> you will get a compiler error. The rationale for this design is that the -> automated testing service determines whether a test has passed based on its -> exit code, not on its stdout/stderr output; thus your `main()` function must -> return the value of `RUN_ALL_TESTS()`. -> -> Also, you should call `RUN_ALL_TESTS()` only **once**. Calling it more than -> once conflicts with some advanced GoogleTest features (e.g., thread-safe -> [death tests](advanced.md#death-tests)) and thus is not supported. - -**Availability**: Linux, Windows, Mac. - -## Writing the main() Function - -Most users should *not* need to write their own `main` function and instead link -with `gtest_main` (as opposed to with `gtest`), which defines a suitable entry -point. See the end of this section for details. The remainder of this section -should only apply when you need to do something custom before the tests run that -cannot be expressed within the framework of fixtures and test suites. - -If you write your own `main` function, it should return the value of -`RUN_ALL_TESTS()`. - -You can start from this boilerplate: - -```c++ -#include "this/package/foo.h" - -#include - -namespace my { -namespace project { -namespace { - -// The fixture for testing class Foo. -class FooTest : public testing::Test { - protected: - // You can remove any or all of the following functions if their bodies would - // be empty. - - FooTest() { - // You can do set-up work for each test here. - } - - ~FooTest() override { - // You can do clean-up work that doesn't throw exceptions here. - } - - // If the constructor and destructor are not enough for setting up - // and cleaning up each test, you can define the following methods: - - void SetUp() override { - // Code here will be called immediately after the constructor (right - // before each test). - } - - void TearDown() override { - // Code here will be called immediately after each test (right - // before the destructor). - } - - // Class members declared here can be used by all tests in the test suite - // for Foo. -}; - -// Tests that the Foo::Bar() method does Abc. -TEST_F(FooTest, MethodBarDoesAbc) { - const std::string input_filepath = "this/package/testdata/myinputfile.dat"; - const std::string output_filepath = "this/package/testdata/myoutputfile.dat"; - Foo f; - EXPECT_EQ(f.Bar(input_filepath, output_filepath), 0); -} - -// Tests that Foo does Xyz. -TEST_F(FooTest, DoesXyz) { - // Exercises the Xyz feature of Foo. -} - -} // namespace -} // namespace project -} // namespace my - -int main(int argc, char **argv) { - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} -``` - -The `testing::InitGoogleTest()` function parses the command line for GoogleTest -flags, and removes all recognized flags. This allows the user to control a test -program's behavior via various flags, which we'll cover in the -[AdvancedGuide](advanced.md). You **must** call this function before calling -`RUN_ALL_TESTS()`, or the flags won't be properly initialized. - -On Windows, `InitGoogleTest()` also works with wide strings, so it can be used -in programs compiled in `UNICODE` mode as well. - -But maybe you think that writing all those `main` functions is too much work? We -agree with you completely, and that's why Google Test provides a basic -implementation of main(). If it fits your needs, then just link your test with -the `gtest_main` library and you are good to go. - -{: .callout .note} -NOTE: `ParseGUnitFlags()` is deprecated in favor of `InitGoogleTest()`. - -## Known Limitations - -* Google Test is designed to be thread-safe. The implementation is thread-safe - on systems where the `pthreads` library is available. It is currently - *unsafe* to use Google Test assertions from two threads concurrently on - other systems (e.g. Windows). In most tests this is not an issue as usually - the assertions are done in the main thread. If you want to help, you can - volunteer to implement the necessary synchronization primitives in - `gtest-port.h` for your platform. diff --git a/test/unit/googletest/docs/quickstart-bazel.md b/test/unit/googletest/docs/quickstart-bazel.md deleted file mode 100644 index 5750f02..0000000 --- a/test/unit/googletest/docs/quickstart-bazel.md +++ /dev/null @@ -1,146 +0,0 @@ -# Quickstart: Building with Bazel - -This tutorial aims to get you up and running with GoogleTest using the Bazel -build system. If you're using GoogleTest for the first time or need a refresher, -we recommend this tutorial as a starting point. - -## Prerequisites - -To complete this tutorial, you'll need: - -* A compatible operating system (e.g. Linux, macOS, Windows). -* A compatible C++ compiler that supports at least C++14. -* [Bazel](https://bazel.build/) 7.0 or higher, the preferred build system used - by the GoogleTest team. - -See [Supported Platforms](platforms.md) for more information about platforms -compatible with GoogleTest. - -If you don't already have Bazel installed, see the -[Bazel installation guide](https://bazel.build/install). - -{: .callout .note} Note: The terminal commands in this tutorial show a Unix -shell prompt, but the commands work on the Windows command line as well. - -## Set up a Bazel workspace - -A -[Bazel workspace](https://docs.bazel.build/versions/main/build-ref.html#workspace) -is a directory on your filesystem that you use to manage source files for the -software you want to build. Each workspace directory has a text file named -`MODULE.bazel` which may be empty, or may contain references to external -dependencies required to build the outputs. - -First, create a directory for your workspace: - -``` -$ mkdir my_workspace && cd my_workspace -``` - -Next, you’ll create the `MODULE.bazel` file to specify dependencies. As of Bazel -7.0, the recommended way to consume GoogleTest is through the -[Bazel Central Registry](https://registry.bazel.build/modules/googletest). To do -this, create a `MODULE.bazel` file in the root directory of your Bazel workspace -with the following content: - -``` -# MODULE.bazel - -# Choose the most recent version available at -# https://registry.bazel.build/modules/googletest -bazel_dep(name = "googletest", version = "1.15.2") -``` - -Now you're ready to build C++ code that uses GoogleTest. - -## Create and run a binary - -With your Bazel workspace set up, you can now use GoogleTest code within your -own project. - -As an example, create a file named `hello_test.cc` in your `my_workspace` -directory with the following contents: - -```cpp -#include - -// Demonstrate some basic assertions. -TEST(HelloTest, BasicAssertions) { - // Expect two strings not to be equal. - EXPECT_STRNE("hello", "world"); - // Expect equality. - EXPECT_EQ(7 * 6, 42); -} -``` - -GoogleTest provides [assertions](primer.md#assertions) that you use to test the -behavior of your code. The above sample includes the main GoogleTest header file -and demonstrates some basic assertions. - -To build the code, create a file named `BUILD` in the same directory with the -following contents: - -``` -cc_test( - name = "hello_test", - size = "small", - srcs = ["hello_test.cc"], - deps = [ - "@googletest//:gtest", - "@googletest//:gtest_main", - ], -) -``` - -This `cc_test` rule declares the C++ test binary you want to build, and links to -the GoogleTest library (`@googletest//:gtest"`) and the GoogleTest `main()` -function (`@googletest//:gtest_main`). For more information about Bazel `BUILD` -files, see the -[Bazel C++ Tutorial](https://docs.bazel.build/versions/main/tutorial/cpp.html). - -{: .callout .note} -NOTE: In the example below, we assume Clang or GCC and set `--cxxopt=-std=c++14` -to ensure that GoogleTest is compiled as C++14 instead of the compiler's default -setting (which could be C++11). For MSVC, the equivalent would be -`--cxxopt=/std:c++14`. See [Supported Platforms](platforms.md) for more details -on supported language versions. - -Now you can build and run your test: - -
-$ bazel test --cxxopt=-std=c++14 --test_output=all //:hello_test
-INFO: Analyzed target //:hello_test (26 packages loaded, 362 targets configured).
-INFO: Found 1 test target...
-INFO: From Testing //:hello_test:
-==================== Test output for //:hello_test:
-Running main() from gmock_main.cc
-[==========] Running 1 test from 1 test suite.
-[----------] Global test environment set-up.
-[----------] 1 test from HelloTest
-[ RUN      ] HelloTest.BasicAssertions
-[       OK ] HelloTest.BasicAssertions (0 ms)
-[----------] 1 test from HelloTest (0 ms total)
-
-[----------] Global test environment tear-down
-[==========] 1 test from 1 test suite ran. (0 ms total)
-[  PASSED  ] 1 test.
-================================================================================
-Target //:hello_test up-to-date:
-  bazel-bin/hello_test
-INFO: Elapsed time: 4.190s, Critical Path: 3.05s
-INFO: 27 processes: 8 internal, 19 linux-sandbox.
-INFO: Build completed successfully, 27 total actions
-//:hello_test                                                     PASSED in 0.1s
-
-INFO: Build completed successfully, 27 total actions
-
- -Congratulations! You've successfully built and run a test binary using -GoogleTest. - -## Next steps - -* [Check out the Primer](primer.md) to start learning how to write simple - tests. -* [See the code samples](samples.md) for more examples showing how to use a - variety of GoogleTest features. diff --git a/test/unit/googletest/docs/quickstart-cmake.md b/test/unit/googletest/docs/quickstart-cmake.md deleted file mode 100644 index 4e422b7..0000000 --- a/test/unit/googletest/docs/quickstart-cmake.md +++ /dev/null @@ -1,157 +0,0 @@ -# Quickstart: Building with CMake - -This tutorial aims to get you up and running with GoogleTest using CMake. If -you're using GoogleTest for the first time or need a refresher, we recommend -this tutorial as a starting point. If your project uses Bazel, see the -[Quickstart for Bazel](quickstart-bazel.md) instead. - -## Prerequisites - -To complete this tutorial, you'll need: - -* A compatible operating system (e.g. Linux, macOS, Windows). -* A compatible C++ compiler that supports at least C++14. -* [CMake](https://cmake.org/) and a compatible build tool for building the - project. - * Compatible build tools include - [Make](https://www.gnu.org/software/make/), - [Ninja](https://ninja-build.org/), and others - see - [CMake Generators](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html) - for more information. - -See [Supported Platforms](platforms.md) for more information about platforms -compatible with GoogleTest. - -If you don't already have CMake installed, see the -[CMake installation guide](https://cmake.org/install). - -{: .callout .note} -Note: The terminal commands in this tutorial show a Unix shell prompt, but the -commands work on the Windows command line as well. - -## Set up a project - -CMake uses a file named `CMakeLists.txt` to configure the build system for a -project. You'll use this file to set up your project and declare a dependency on -GoogleTest. - -First, create a directory for your project: - -``` -$ mkdir my_project && cd my_project -``` - -Next, you'll create the `CMakeLists.txt` file and declare a dependency on -GoogleTest. There are many ways to express dependencies in the CMake ecosystem; -in this quickstart, you'll use the -[`FetchContent` CMake module](https://cmake.org/cmake/help/latest/module/FetchContent.html). -To do this, in your project directory (`my_project`), create a file named -`CMakeLists.txt` with the following contents: - -```cmake -cmake_minimum_required(VERSION 3.14) -project(my_project) - -# GoogleTest requires at least C++14 -set(CMAKE_CXX_STANDARD 14) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -include(FetchContent) -FetchContent_Declare( - googletest - URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip -) -# For Windows: Prevent overriding the parent project's compiler/linker settings -set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) -FetchContent_MakeAvailable(googletest) -``` - -The above configuration declares a dependency on GoogleTest which is downloaded -from GitHub. In the above example, `03597a01ee50ed33e9dfd640b249b4be3799d395` is -the Git commit hash of the GoogleTest version to use; we recommend updating the -hash often to point to the latest version. - -For more information about how to create `CMakeLists.txt` files, see the -[CMake Tutorial](https://cmake.org/cmake/help/latest/guide/tutorial/index.html). - -## Create and run a binary - -With GoogleTest declared as a dependency, you can use GoogleTest code within -your own project. - -As an example, create a file named `hello_test.cc` in your `my_project` -directory with the following contents: - -```cpp -#include - -// Demonstrate some basic assertions. -TEST(HelloTest, BasicAssertions) { - // Expect two strings not to be equal. - EXPECT_STRNE("hello", "world"); - // Expect equality. - EXPECT_EQ(7 * 6, 42); -} -``` - -GoogleTest provides [assertions](primer.md#assertions) that you use to test the -behavior of your code. The above sample includes the main GoogleTest header file -and demonstrates some basic assertions. - -To build the code, add the following to the end of your `CMakeLists.txt` file: - -```cmake -enable_testing() - -add_executable( - hello_test - hello_test.cc -) -target_link_libraries( - hello_test - GTest::gtest_main -) - -include(GoogleTest) -gtest_discover_tests(hello_test) -``` - -The above configuration enables testing in CMake, declares the C++ test binary -you want to build (`hello_test`), and links it to GoogleTest (`gtest_main`). The -last two lines enable CMake's test runner to discover the tests included in the -binary, using the -[`GoogleTest` CMake module](https://cmake.org/cmake/help/git-stage/module/GoogleTest.html). - -Now you can build and run your test: - -
-my_project$ cmake -S . -B build
--- The C compiler identification is GNU 10.2.1
--- The CXX compiler identification is GNU 10.2.1
-...
--- Build files have been written to: .../my_project/build
-
-my_project$ cmake --build build
-Scanning dependencies of target gtest
-...
-[100%] Built target gmock_main
-
-my_project$ cd build && ctest
-Test project .../my_project/build
-    Start 1: HelloTest.BasicAssertions
-1/1 Test #1: HelloTest.BasicAssertions ........   Passed    0.00 sec
-
-100% tests passed, 0 tests failed out of 1
-
-Total Test time (real) =   0.01 sec
-
- -Congratulations! You've successfully built and run a test binary using -GoogleTest. - -## Next steps - -* [Check out the Primer](primer.md) to start learning how to write simple - tests. -* [See the code samples](samples.md) for more examples showing how to use a - variety of GoogleTest features. diff --git a/test/unit/googletest/docs/reference/actions.md b/test/unit/googletest/docs/reference/actions.md deleted file mode 100644 index ab81a12..0000000 --- a/test/unit/googletest/docs/reference/actions.md +++ /dev/null @@ -1,115 +0,0 @@ -# Actions Reference - -[**Actions**](../gmock_for_dummies.md#actions-what-should-it-do) specify what a -mock function should do when invoked. This page lists the built-in actions -provided by GoogleTest. All actions are defined in the `::testing` namespace. - -## Returning a Value - -| Action | Description | -| :-------------------------------- | :-------------------------------------------- | -| `Return()` | Return from a `void` mock function. | -| `Return(value)` | Return `value`. If the type of `value` is different to the mock function's return type, `value` is converted to the latter type at the time the expectation is set, not when the action is executed. | -| `ReturnArg()` | Return the `N`-th (0-based) argument. | -| `ReturnNew(a1, ..., ak)` | Return `new T(a1, ..., ak)`; a different object is created each time. | -| `ReturnNull()` | Return a null pointer. | -| `ReturnPointee(ptr)` | Return the value pointed to by `ptr`. | -| `ReturnRef(variable)` | Return a reference to `variable`. | -| `ReturnRefOfCopy(value)` | Return a reference to a copy of `value`; the copy lives as long as the action. | -| `ReturnRoundRobin({a1, ..., ak})` | Each call will return the next `ai` in the list, starting at the beginning when the end of the list is reached. | - -## Side Effects - -| Action | Description | -| :--------------------------------- | :-------------------------------------- | -| `Assign(&variable, value)` | Assign `value` to variable. | -| `DeleteArg()` | Delete the `N`-th (0-based) argument, which must be a pointer. | -| `SaveArg(pointer)` | Save the `N`-th (0-based) argument to `*pointer`. | -| `SaveArgPointee(pointer)` | Save the value pointed to by the `N`-th (0-based) argument to `*pointer`. | -| `SetArgReferee(value)` | Assign `value` to the variable referenced by the `N`-th (0-based) argument. | -| `SetArgPointee(value)` | Assign `value` to the variable pointed by the `N`-th (0-based) argument. | -| `SetArgumentPointee(value)` | Same as `SetArgPointee(value)`. Deprecated. Will be removed in v1.7.0. | -| `SetArrayArgument(first, last)` | Copies the elements in source range [`first`, `last`) to the array pointed to by the `N`-th (0-based) argument, which can be either a pointer or an iterator. The action does not take ownership of the elements in the source range. | -| `SetErrnoAndReturn(error, value)` | Set `errno` to `error` and return `value`. | -| `Throw(exception)` | Throws the given exception, which can be any copyable value. Available since v1.1.0. | - -## Using a Function, Functor, or Lambda as an Action - -In the following, by "callable" we mean a free function, `std::function`, -functor, or lambda. - -| Action | Description | -| :---------------------------------- | :------------------------------------- | -| `f` | Invoke `f` with the arguments passed to the mock function, where `f` is a callable. | -| `Invoke(f)` | Invoke `f` with the arguments passed to the mock function, where `f` can be a global/static function or a functor. | -| `Invoke(object_pointer, &class::method)` | Invoke the method on the object with the arguments passed to the mock function. | -| `InvokeWithoutArgs(f)` | Invoke `f`, which can be a global/static function or a functor. `f` must take no arguments. | -| `InvokeWithoutArgs(object_pointer, &class::method)` | Invoke the method on the object, which takes no arguments. | -| `InvokeArgument(arg1, arg2, ..., argk)` | Invoke the mock function's `N`-th (0-based) argument, which must be a function or a functor, with the `k` arguments. | - -The return value of the invoked function is used as the return value of the -action. - -When defining a callable to be used with `Invoke*()`, you can declare any unused -parameters as `Unused`: - -```cpp -using ::testing::Invoke; -double Distance(Unused, double x, double y) { return sqrt(x*x + y*y); } -... -EXPECT_CALL(mock, Foo("Hi", _, _)).WillOnce(Invoke(Distance)); -``` - -`Invoke(callback)` and `InvokeWithoutArgs(callback)` take ownership of -`callback`, which must be permanent. The type of `callback` must be a base -callback type instead of a derived one, e.g. - -```cpp - BlockingClosure* done = new BlockingClosure; - ... Invoke(done) ...; // This won't compile! - - Closure* done2 = new BlockingClosure; - ... Invoke(done2) ...; // This works. -``` - -In `InvokeArgument(...)`, if an argument needs to be passed by reference, -wrap it inside `std::ref()`. For example, - -```cpp -using ::testing::InvokeArgument; -... -InvokeArgument<2>(5, string("Hi"), std::ref(foo)) -``` - -calls the mock function's #2 argument, passing to it `5` and `string("Hi")` by -value, and `foo` by reference. - -## Default Action - -| Action | Description | -| :------------ | :----------------------------------------------------- | -| `DoDefault()` | Do the default action (specified by `ON_CALL()` or the built-in one). | - -{: .callout .note} -**Note:** due to technical reasons, `DoDefault()` cannot be used inside a -composite action - trying to do so will result in a run-time error. - -## Composite Actions - -| Action | Description | -| :----------------------------- | :------------------------------------------ | -| `DoAll(a1, a2, ..., an)` | Do all actions `a1` to `an` and return the result of `an` in each invocation. The first `n - 1` sub-actions must return void and will receive a readonly view of the arguments. | -| `IgnoreResult(a)` | Perform action `a` and ignore its result. `a` must not return void. | -| `WithArg(a)` | Pass the `N`-th (0-based) argument of the mock function to action `a` and perform it. | -| `WithArgs(a)` | Pass the selected (0-based) arguments of the mock function to action `a` and perform it. | -| `WithoutArgs(a)` | Perform action `a` without any arguments. | - -## Defining Actions - -| Macro | Description | -| :--------------------------------- | :-------------------------------------- | -| `ACTION(Sum) { return arg0 + arg1; }` | Defines an action `Sum()` to return the sum of the mock function's argument #0 and #1. | -| `ACTION_P(Plus, n) { return arg0 + n; }` | Defines an action `Plus(n)` to return the sum of the mock function's argument #0 and `n`. | -| `ACTION_Pk(Foo, p1, ..., pk) { statements; }` | Defines a parameterized action `Foo(p1, ..., pk)` to execute the given `statements`. | - -The `ACTION*` macros cannot be used inside a function or class. diff --git a/test/unit/googletest/docs/reference/assertions.md b/test/unit/googletest/docs/reference/assertions.md deleted file mode 100644 index eeec4a0..0000000 --- a/test/unit/googletest/docs/reference/assertions.md +++ /dev/null @@ -1,640 +0,0 @@ -# Assertions Reference - -This page lists the assertion macros provided by GoogleTest for verifying code -behavior. To use them, add `#include `. - -The majority of the macros listed below come as a pair with an `EXPECT_` variant -and an `ASSERT_` variant. Upon failure, `EXPECT_` macros generate nonfatal -failures and allow the current function to continue running, while `ASSERT_` -macros generate fatal failures and abort the current function. - -All assertion macros support streaming a custom failure message into them with -the `<<` operator, for example: - -```cpp -EXPECT_TRUE(my_condition) << "My condition is not true"; -``` - -Anything that can be streamed to an `ostream` can be streamed to an assertion -macro—in particular, C strings and string objects. If a wide string (`wchar_t*`, -`TCHAR*` in `UNICODE` mode on Windows, or `std::wstring`) is streamed to an -assertion, it will be translated to UTF-8 when printed. - -## Explicit Success and Failure {#success-failure} - -The assertions in this section generate a success or failure directly instead of -testing a value or expression. These are useful when control flow, rather than a -Boolean expression, determines the test's success or failure, as shown by the -following example: - -```c++ -switch(expression) { - case 1: - ... some checks ... - case 2: - ... some other checks ... - default: - FAIL() << "We shouldn't get here."; -} -``` - -### SUCCEED {#SUCCEED} - -`SUCCEED()` - -Generates a success. This *does not* make the overall test succeed. A test is -considered successful only if none of its assertions fail during its execution. - -The `SUCCEED` assertion is purely documentary and currently doesn't generate any -user-visible output. However, we may add `SUCCEED` messages to GoogleTest output -in the future. - -### FAIL {#FAIL} - -`FAIL()` - -Generates a fatal failure, which returns from the current function. - -Can only be used in functions that return `void`. See -[Assertion Placement](../advanced.md#assertion-placement) for more information. - -### ADD_FAILURE {#ADD_FAILURE} - -`ADD_FAILURE()` - -Generates a nonfatal failure, which allows the current function to continue -running. - -### ADD_FAILURE_AT {#ADD_FAILURE_AT} - -`ADD_FAILURE_AT(`*`file_path`*`,`*`line_number`*`)` - -Generates a nonfatal failure at the file and line number specified. - -## Generalized Assertion {#generalized} - -The following assertion allows [matchers](matchers.md) to be used to verify -values. - -### EXPECT_THAT {#EXPECT_THAT} - -`EXPECT_THAT(`*`value`*`,`*`matcher`*`)` \ -`ASSERT_THAT(`*`value`*`,`*`matcher`*`)` - -Verifies that *`value`* matches the [matcher](matchers.md) *`matcher`*. - -For example, the following code verifies that the string `value1` starts with -`"Hello"`, `value2` matches a regular expression, and `value3` is between 5 and -10: - -```cpp -#include - -using ::testing::AllOf; -using ::testing::Gt; -using ::testing::Lt; -using ::testing::MatchesRegex; -using ::testing::StartsWith; - -... -EXPECT_THAT(value1, StartsWith("Hello")); -EXPECT_THAT(value2, MatchesRegex("Line \\d+")); -ASSERT_THAT(value3, AllOf(Gt(5), Lt(10))); -``` - -Matchers enable assertions of this form to read like English and generate -informative failure messages. For example, if the above assertion on `value1` -fails, the resulting message will be similar to the following: - -``` -Value of: value1 - Actual: "Hi, world!" -Expected: starts with "Hello" -``` - -GoogleTest provides a built-in library of matchers—see the -[Matchers Reference](matchers.md). It is also possible to write your own -matchers—see [Writing New Matchers Quickly](../gmock_cook_book.md#NewMatchers). -The use of matchers makes `EXPECT_THAT` a powerful, extensible assertion. - -*The idea for this assertion was borrowed from Joe Walnes' Hamcrest project, -which adds `assertThat()` to JUnit.* - -## Boolean Conditions {#boolean} - -The following assertions test Boolean conditions. - -### EXPECT_TRUE {#EXPECT_TRUE} - -`EXPECT_TRUE(`*`condition`*`)` \ -`ASSERT_TRUE(`*`condition`*`)` - -Verifies that *`condition`* is true. - -### EXPECT_FALSE {#EXPECT_FALSE} - -`EXPECT_FALSE(`*`condition`*`)` \ -`ASSERT_FALSE(`*`condition`*`)` - -Verifies that *`condition`* is false. - -## Binary Comparison {#binary-comparison} - -The following assertions compare two values. The value arguments must be -comparable by the assertion's comparison operator, otherwise a compiler error -will result. - -If an argument supports the `<<` operator, it will be called to print the -argument when the assertion fails. Otherwise, GoogleTest will attempt to print -them in the best way it can—see -[Teaching GoogleTest How to Print Your Values](../advanced.md#teaching-googletest-how-to-print-your-values). - -Arguments are always evaluated exactly once, so it's OK for the arguments to -have side effects. However, the argument evaluation order is undefined and -programs should not depend on any particular argument evaluation order. - -These assertions work with both narrow and wide string objects (`string` and -`wstring`). - -See also the [Floating-Point Comparison](#floating-point) assertions to compare -floating-point numbers and avoid problems caused by rounding. - -### EXPECT_EQ {#EXPECT_EQ} - -`EXPECT_EQ(`*`val1`*`,`*`val2`*`)` \ -`ASSERT_EQ(`*`val1`*`,`*`val2`*`)` - -Verifies that *`val1`*`==`*`val2`*. - -Does pointer equality on pointers. If used on two C strings, it tests if they -are in the same memory location, not if they have the same value. Use -[`EXPECT_STREQ`](#EXPECT_STREQ) to compare C strings (e.g. `const char*`) by -value. - -When comparing a pointer to `NULL`, use `EXPECT_EQ(`*`ptr`*`, nullptr)` instead -of `EXPECT_EQ(`*`ptr`*`, NULL)`. - -### EXPECT_NE {#EXPECT_NE} - -`EXPECT_NE(`*`val1`*`,`*`val2`*`)` \ -`ASSERT_NE(`*`val1`*`,`*`val2`*`)` - -Verifies that *`val1`*`!=`*`val2`*. - -Does pointer equality on pointers. If used on two C strings, it tests if they -are in different memory locations, not if they have different values. Use -[`EXPECT_STRNE`](#EXPECT_STRNE) to compare C strings (e.g. `const char*`) by -value. - -When comparing a pointer to `NULL`, use `EXPECT_NE(`*`ptr`*`, nullptr)` instead -of `EXPECT_NE(`*`ptr`*`, NULL)`. - -### EXPECT_LT {#EXPECT_LT} - -`EXPECT_LT(`*`val1`*`,`*`val2`*`)` \ -`ASSERT_LT(`*`val1`*`,`*`val2`*`)` - -Verifies that *`val1`*`<`*`val2`*. - -### EXPECT_LE {#EXPECT_LE} - -`EXPECT_LE(`*`val1`*`,`*`val2`*`)` \ -`ASSERT_LE(`*`val1`*`,`*`val2`*`)` - -Verifies that *`val1`*`<=`*`val2`*. - -### EXPECT_GT {#EXPECT_GT} - -`EXPECT_GT(`*`val1`*`,`*`val2`*`)` \ -`ASSERT_GT(`*`val1`*`,`*`val2`*`)` - -Verifies that *`val1`*`>`*`val2`*. - -### EXPECT_GE {#EXPECT_GE} - -`EXPECT_GE(`*`val1`*`,`*`val2`*`)` \ -`ASSERT_GE(`*`val1`*`,`*`val2`*`)` - -Verifies that *`val1`*`>=`*`val2`*. - -## String Comparison {#c-strings} - -The following assertions compare two **C strings**. To compare two `string` -objects, use [`EXPECT_EQ`](#EXPECT_EQ) or [`EXPECT_NE`](#EXPECT_NE) instead. - -These assertions also accept wide C strings (`wchar_t*`). If a comparison of two -wide strings fails, their values will be printed as UTF-8 narrow strings. - -To compare a C string with `NULL`, use `EXPECT_EQ(`*`c_string`*`, nullptr)` or -`EXPECT_NE(`*`c_string`*`, nullptr)`. - -### EXPECT_STREQ {#EXPECT_STREQ} - -`EXPECT_STREQ(`*`str1`*`,`*`str2`*`)` \ -`ASSERT_STREQ(`*`str1`*`,`*`str2`*`)` - -Verifies that the two C strings *`str1`* and *`str2`* have the same contents. - -### EXPECT_STRNE {#EXPECT_STRNE} - -`EXPECT_STRNE(`*`str1`*`,`*`str2`*`)` \ -`ASSERT_STRNE(`*`str1`*`,`*`str2`*`)` - -Verifies that the two C strings *`str1`* and *`str2`* have different contents. - -### EXPECT_STRCASEEQ {#EXPECT_STRCASEEQ} - -`EXPECT_STRCASEEQ(`*`str1`*`,`*`str2`*`)` \ -`ASSERT_STRCASEEQ(`*`str1`*`,`*`str2`*`)` - -Verifies that the two C strings *`str1`* and *`str2`* have the same contents, -ignoring case. - -### EXPECT_STRCASENE {#EXPECT_STRCASENE} - -`EXPECT_STRCASENE(`*`str1`*`,`*`str2`*`)` \ -`ASSERT_STRCASENE(`*`str1`*`,`*`str2`*`)` - -Verifies that the two C strings *`str1`* and *`str2`* have different contents, -ignoring case. - -## Floating-Point Comparison {#floating-point} - -The following assertions compare two floating-point values. - -Due to rounding errors, it is very unlikely that two floating-point values will -match exactly, so `EXPECT_EQ` is not suitable. In general, for floating-point -comparison to make sense, the user needs to carefully choose the error bound. - -GoogleTest also provides assertions that use a default error bound based on -Units in the Last Place (ULPs). To learn more about ULPs, see the article -[Comparing Floating Point Numbers](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/). - -### EXPECT_FLOAT_EQ {#EXPECT_FLOAT_EQ} - -`EXPECT_FLOAT_EQ(`*`val1`*`,`*`val2`*`)` \ -`ASSERT_FLOAT_EQ(`*`val1`*`,`*`val2`*`)` - -Verifies that the two `float` values *`val1`* and *`val2`* are approximately -equal, to within 4 ULPs from each other. Infinity and the largest finite float -value are considered to be one ULP apart. - -### EXPECT_DOUBLE_EQ {#EXPECT_DOUBLE_EQ} - -`EXPECT_DOUBLE_EQ(`*`val1`*`,`*`val2`*`)` \ -`ASSERT_DOUBLE_EQ(`*`val1`*`,`*`val2`*`)` - -Verifies that the two `double` values *`val1`* and *`val2`* are approximately -equal, to within 4 ULPs from each other. Infinity and the largest finite double -value are considered to be one ULP apart. - -### EXPECT_NEAR {#EXPECT_NEAR} - -`EXPECT_NEAR(`*`val1`*`,`*`val2`*`,`*`abs_error`*`)` \ -`ASSERT_NEAR(`*`val1`*`,`*`val2`*`,`*`abs_error`*`)` - -Verifies that the difference between *`val1`* and *`val2`* does not exceed the -absolute error bound *`abs_error`*. - -If *`val`* and *`val2`* are both infinity of the same sign, the difference is -considered to be 0. Otherwise, if either value is infinity, the difference is -considered to be infinity. All non-NaN values (including infinity) are -considered to not exceed an *`abs_error`* of infinity. - -## Exception Assertions {#exceptions} - -The following assertions verify that a piece of code throws, or does not throw, -an exception. Usage requires exceptions to be enabled in the build environment. - -Note that the piece of code under test can be a compound statement, for example: - -```cpp -EXPECT_NO_THROW({ - int n = 5; - DoSomething(&n); -}); -``` - -### EXPECT_THROW {#EXPECT_THROW} - -`EXPECT_THROW(`*`statement`*`,`*`exception_type`*`)` \ -`ASSERT_THROW(`*`statement`*`,`*`exception_type`*`)` - -Verifies that *`statement`* throws an exception of type *`exception_type`*. - -### EXPECT_ANY_THROW {#EXPECT_ANY_THROW} - -`EXPECT_ANY_THROW(`*`statement`*`)` \ -`ASSERT_ANY_THROW(`*`statement`*`)` - -Verifies that *`statement`* throws an exception of any type. - -### EXPECT_NO_THROW {#EXPECT_NO_THROW} - -`EXPECT_NO_THROW(`*`statement`*`)` \ -`ASSERT_NO_THROW(`*`statement`*`)` - -Verifies that *`statement`* does not throw any exception. - -## Predicate Assertions {#predicates} - -The following assertions enable more complex predicates to be verified while -printing a more clear failure message than if `EXPECT_TRUE` were used alone. - -### EXPECT_PRED* {#EXPECT_PRED} - -`EXPECT_PRED1(`*`pred`*`,`*`val1`*`)` \ -`EXPECT_PRED2(`*`pred`*`,`*`val1`*`,`*`val2`*`)` \ -`EXPECT_PRED3(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`)` \ -`EXPECT_PRED4(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`)` \ -`EXPECT_PRED5(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`,`*`val5`*`)` - -`ASSERT_PRED1(`*`pred`*`,`*`val1`*`)` \ -`ASSERT_PRED2(`*`pred`*`,`*`val1`*`,`*`val2`*`)` \ -`ASSERT_PRED3(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`)` \ -`ASSERT_PRED4(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`)` \ -`ASSERT_PRED5(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`,`*`val5`*`)` - -Verifies that the predicate *`pred`* returns `true` when passed the given values -as arguments. - -The parameter *`pred`* is a function or functor that accepts as many arguments -as the corresponding macro accepts values. If *`pred`* returns `true` for the -given arguments, the assertion succeeds, otherwise the assertion fails. - -When the assertion fails, it prints the value of each argument. Arguments are -always evaluated exactly once. - -As an example, see the following code: - -```cpp -// Returns true if m and n have no common divisors except 1. -bool MutuallyPrime(int m, int n) { ... } -... -const int a = 3; -const int b = 4; -const int c = 10; -... -EXPECT_PRED2(MutuallyPrime, a, b); // Succeeds -EXPECT_PRED2(MutuallyPrime, b, c); // Fails -``` - -In the above example, the first assertion succeeds, and the second fails with -the following message: - -``` -MutuallyPrime(b, c) is false, where -b is 4 -c is 10 -``` - -Note that if the given predicate is an overloaded function or a function -template, the assertion macro might not be able to determine which version to -use, and it might be necessary to explicitly specify the type of the function. -For example, for a Boolean function `IsPositive()` overloaded to take either a -single `int` or `double` argument, it would be necessary to write one of the -following: - -```cpp -EXPECT_PRED1(static_cast(IsPositive), 5); -EXPECT_PRED1(static_cast(IsPositive), 3.14); -``` - -Writing simply `EXPECT_PRED1(IsPositive, 5);` would result in a compiler error. -Similarly, to use a template function, specify the template arguments: - -```cpp -template -bool IsNegative(T x) { - return x < 0; -} -... -EXPECT_PRED1(IsNegative, -5); // Must specify type for IsNegative -``` - -If a template has multiple parameters, wrap the predicate in parentheses so the -macro arguments are parsed correctly: - -```cpp -ASSERT_PRED2((MyPredicate), 5, 0); -``` - -### EXPECT_PRED_FORMAT* {#EXPECT_PRED_FORMAT} - -`EXPECT_PRED_FORMAT1(`*`pred_formatter`*`,`*`val1`*`)` \ -`EXPECT_PRED_FORMAT2(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`)` \ -`EXPECT_PRED_FORMAT3(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`)` \ -`EXPECT_PRED_FORMAT4(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`)` -\ -`EXPECT_PRED_FORMAT5(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`,`*`val5`*`)` - -`ASSERT_PRED_FORMAT1(`*`pred_formatter`*`,`*`val1`*`)` \ -`ASSERT_PRED_FORMAT2(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`)` \ -`ASSERT_PRED_FORMAT3(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`)` \ -`ASSERT_PRED_FORMAT4(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`)` -\ -`ASSERT_PRED_FORMAT5(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`,`*`val5`*`)` - -Verifies that the predicate *`pred_formatter`* succeeds when passed the given -values as arguments. - -The parameter *`pred_formatter`* is a *predicate-formatter*, which is a function -or functor with the signature: - -```cpp -testing::AssertionResult PredicateFormatter(const char* expr1, - const char* expr2, - ... - const char* exprn, - T1 val1, - T2 val2, - ... - Tn valn); -``` - -where *`val1`*, *`val2`*, ..., *`valn`* are the values of the predicate -arguments, and *`expr1`*, *`expr2`*, ..., *`exprn`* are the corresponding -expressions as they appear in the source code. The types `T1`, `T2`, ..., `Tn` -can be either value types or reference types; if an argument has type `T`, it -can be declared as either `T` or `const T&`, whichever is appropriate. For more -about the return type `testing::AssertionResult`, see -[Using a Function That Returns an AssertionResult](../advanced.md#using-a-function-that-returns-an-assertionresult). - -As an example, see the following code: - -```cpp -// Returns the smallest prime common divisor of m and n, -// or 1 when m and n are mutually prime. -int SmallestPrimeCommonDivisor(int m, int n) { ... } - -// Returns true if m and n have no common divisors except 1. -bool MutuallyPrime(int m, int n) { ... } - -// A predicate-formatter for asserting that two integers are mutually prime. -testing::AssertionResult AssertMutuallyPrime(const char* m_expr, - const char* n_expr, - int m, - int n) { - if (MutuallyPrime(m, n)) return testing::AssertionSuccess(); - - return testing::AssertionFailure() << m_expr << " and " << n_expr - << " (" << m << " and " << n << ") are not mutually prime, " - << "as they have a common divisor " << SmallestPrimeCommonDivisor(m, n); -} - -... -const int a = 3; -const int b = 4; -const int c = 10; -... -EXPECT_PRED_FORMAT2(AssertMutuallyPrime, a, b); // Succeeds -EXPECT_PRED_FORMAT2(AssertMutuallyPrime, b, c); // Fails -``` - -In the above example, the final assertion fails and the predicate-formatter -produces the following failure message: - -``` -b and c (4 and 10) are not mutually prime, as they have a common divisor 2 -``` - -## Windows HRESULT Assertions {#HRESULT} - -The following assertions test for `HRESULT` success or failure. For example: - -```cpp -CComPtr shell; -ASSERT_HRESULT_SUCCEEDED(shell.CoCreateInstance(L"Shell.Application")); -CComVariant empty; -ASSERT_HRESULT_SUCCEEDED(shell->ShellExecute(CComBSTR(url), empty, empty, empty, empty)); -``` - -The generated output contains the human-readable error message associated with -the returned `HRESULT` code. - -### EXPECT_HRESULT_SUCCEEDED {#EXPECT_HRESULT_SUCCEEDED} - -`EXPECT_HRESULT_SUCCEEDED(`*`expression`*`)` \ -`ASSERT_HRESULT_SUCCEEDED(`*`expression`*`)` - -Verifies that *`expression`* is a success `HRESULT`. - -### EXPECT_HRESULT_FAILED {#EXPECT_HRESULT_FAILED} - -`EXPECT_HRESULT_FAILED(`*`expression`*`)` \ -`ASSERT_HRESULT_FAILED(`*`expression`*`)` - -Verifies that *`expression`* is a failure `HRESULT`. - -## Death Assertions {#death} - -The following assertions verify that a piece of code causes the process to -terminate. For context, see [Death Tests](../advanced.md#death-tests). - -These assertions spawn a new process and execute the code under test in that -process. How that happens depends on the platform and the variable -`::testing::GTEST_FLAG(death_test_style)`, which is initialized from the -command-line flag `--gtest_death_test_style`. - -* On POSIX systems, `fork()` (or `clone()` on Linux) is used to spawn the - child, after which: - * If the variable's value is `"fast"`, the death test statement is - immediately executed. - * If the variable's value is `"threadsafe"`, the child process re-executes - the unit test binary just as it was originally invoked, but with some - extra flags to cause just the single death test under consideration to - be run. -* On Windows, the child is spawned using the `CreateProcess()` API, and - re-executes the binary to cause just the single death test under - consideration to be run - much like the `"threadsafe"` mode on POSIX. - -Other values for the variable are illegal and will cause the death test to fail. -Currently, the flag's default value is -**`"fast"`**. - -If the death test statement runs to completion without dying, the child process -will nonetheless terminate, and the assertion fails. - -Note that the piece of code under test can be a compound statement, for example: - -```cpp -EXPECT_DEATH({ - int n = 5; - DoSomething(&n); -}, "Error on line .* of DoSomething()"); -``` - -### EXPECT_DEATH {#EXPECT_DEATH} - -`EXPECT_DEATH(`*`statement`*`,`*`matcher`*`)` \ -`ASSERT_DEATH(`*`statement`*`,`*`matcher`*`)` - -Verifies that *`statement`* causes the process to terminate with a nonzero exit -status and produces `stderr` output that matches *`matcher`*. - -The parameter *`matcher`* is either a [matcher](matchers.md) for a `const -std::string&`, or a regular expression (see -[Regular Expression Syntax](../advanced.md#regular-expression-syntax))—a bare -string *`s`* (with no matcher) is treated as -[`ContainsRegex(s)`](matchers.md#string-matchers), **not** -[`Eq(s)`](matchers.md#generic-comparison). - -For example, the following code verifies that calling `DoSomething(42)` causes -the process to die with an error message that contains the text `My error`: - -```cpp -EXPECT_DEATH(DoSomething(42), "My error"); -``` - -### EXPECT_DEATH_IF_SUPPORTED {#EXPECT_DEATH_IF_SUPPORTED} - -`EXPECT_DEATH_IF_SUPPORTED(`*`statement`*`,`*`matcher`*`)` \ -`ASSERT_DEATH_IF_SUPPORTED(`*`statement`*`,`*`matcher`*`)` - -If death tests are supported, behaves the same as -[`EXPECT_DEATH`](#EXPECT_DEATH). Otherwise, verifies nothing. - -### EXPECT_DEBUG_DEATH {#EXPECT_DEBUG_DEATH} - -`EXPECT_DEBUG_DEATH(`*`statement`*`,`*`matcher`*`)` \ -`ASSERT_DEBUG_DEATH(`*`statement`*`,`*`matcher`*`)` - -In debug mode, behaves the same as [`EXPECT_DEATH`](#EXPECT_DEATH). When not in -debug mode (i.e. `NDEBUG` is defined), just executes *`statement`*. - -### EXPECT_EXIT {#EXPECT_EXIT} - -`EXPECT_EXIT(`*`statement`*`,`*`predicate`*`,`*`matcher`*`)` \ -`ASSERT_EXIT(`*`statement`*`,`*`predicate`*`,`*`matcher`*`)` - -Verifies that *`statement`* causes the process to terminate with an exit status -that satisfies *`predicate`*, and produces `stderr` output that matches -*`matcher`*. - -The parameter *`predicate`* is a function or functor that accepts an `int` exit -status and returns a `bool`. GoogleTest provides two predicates to handle common -cases: - -```cpp -// Returns true if the program exited normally with the given exit status code. -::testing::ExitedWithCode(exit_code); - -// Returns true if the program was killed by the given signal. -// Not available on Windows. -::testing::KilledBySignal(signal_number); -``` - -The parameter *`matcher`* is either a [matcher](matchers.md) for a `const -std::string&`, or a regular expression (see -[Regular Expression Syntax](../advanced.md#regular-expression-syntax))—a bare -string *`s`* (with no matcher) is treated as -[`ContainsRegex(s)`](matchers.md#string-matchers), **not** -[`Eq(s)`](matchers.md#generic-comparison). - -For example, the following code verifies that calling `NormalExit()` causes the -process to print a message containing the text `Success` to `stderr` and exit -with exit status code 0: - -```cpp -EXPECT_EXIT(NormalExit(), testing::ExitedWithCode(0), "Success"); -``` diff --git a/test/unit/googletest/docs/reference/matchers.md b/test/unit/googletest/docs/reference/matchers.md deleted file mode 100644 index 243e3f9..0000000 --- a/test/unit/googletest/docs/reference/matchers.md +++ /dev/null @@ -1,302 +0,0 @@ -# Matchers Reference - -A **matcher** matches a *single* argument. You can use it inside `ON_CALL()` or -`EXPECT_CALL()`, or use it to validate a value directly using two macros: - -| Macro | Description | -| :----------------------------------- | :------------------------------------ | -| `EXPECT_THAT(actual_value, matcher)` | Asserts that `actual_value` matches `matcher`. | -| `ASSERT_THAT(actual_value, matcher)` | The same as `EXPECT_THAT(actual_value, matcher)`, except that it generates a **fatal** failure. | - -{: .callout .warning} -**WARNING:** Equality matching via `EXPECT_THAT(actual_value, expected_value)` -is supported, however note that implicit conversions can cause surprising -results. For example, `EXPECT_THAT(some_bool, "some string")` will compile and -may pass unintentionally. - -**BEST PRACTICE:** Prefer to make the comparison explicit via -`EXPECT_THAT(actual_value, Eq(expected_value))` or `EXPECT_EQ(actual_value, -expected_value)`. - -Built-in matchers (where `argument` is the function argument, e.g. -`actual_value` in the example above, or when used in the context of -`EXPECT_CALL(mock_object, method(matchers))`, the arguments of `method`) are -divided into several categories. All matchers are defined in the `::testing` -namespace unless otherwise noted. - -## Wildcard - -Matcher | Description -:-------------------------- | :----------------------------------------------- -`_` | `argument` can be any value of the correct type. -`A()` or `An()` | `argument` can be any value of type `type`. - -## Generic Comparison - -| Matcher | Description | -| :--------------------- | :-------------------------------------------------- | -| `Eq(value)` or `value` | `argument == value` | -| `Ge(value)` | `argument >= value` | -| `Gt(value)` | `argument > value` | -| `Le(value)` | `argument <= value` | -| `Lt(value)` | `argument < value` | -| `Ne(value)` | `argument != value` | -| `IsFalse()` | `argument` evaluates to `false` in a Boolean context. | -| `IsTrue()` | `argument` evaluates to `true` in a Boolean context. | -| `IsNull()` | `argument` is a `NULL` pointer (raw or smart). | -| `NotNull()` | `argument` is a non-null pointer (raw or smart). | -| `Optional(m)` | `argument` is `optional<>` that contains a value matching `m`. (For testing whether an `optional<>` is set, check for equality with `nullopt`. You may need to use `Eq(nullopt)` if the inner type doesn't have `==`.)| -| `VariantWith(m)` | `argument` is `variant<>` that holds the alternative of type T with a value matching `m`. | -| `Ref(variable)` | `argument` is a reference to `variable`. | -| `TypedEq(value)` | `argument` has type `type` and is equal to `value`. You may need to use this instead of `Eq(value)` when the mock function is overloaded. | - -Except `Ref()`, these matchers make a *copy* of `value` in case it's modified or -destructed later. If the compiler complains that `value` doesn't have a public -copy constructor, try wrap it in `std::ref()`, e.g. -`Eq(std::ref(non_copyable_value))`. If you do that, make sure -`non_copyable_value` is not changed afterwards, or the meaning of your matcher -will be changed. - -`IsTrue` and `IsFalse` are useful when you need to use a matcher, or for types -that can be explicitly converted to Boolean, but are not implicitly converted to -Boolean. In other cases, you can use the basic -[`EXPECT_TRUE` and `EXPECT_FALSE`](assertions.md#boolean) assertions. - -## Floating-Point Matchers {#FpMatchers} - -| Matcher | Description | -| :------------------------------- | :--------------------------------- | -| `DoubleEq(a_double)` | `argument` is a `double` value approximately equal to `a_double`, treating two NaNs as unequal. | -| `FloatEq(a_float)` | `argument` is a `float` value approximately equal to `a_float`, treating two NaNs as unequal. | -| `NanSensitiveDoubleEq(a_double)` | `argument` is a `double` value approximately equal to `a_double`, treating two NaNs as equal. | -| `NanSensitiveFloatEq(a_float)` | `argument` is a `float` value approximately equal to `a_float`, treating two NaNs as equal. | -| `IsNan()` | `argument` is any floating-point type with a NaN value. | - -The above matchers use ULP-based comparison (the same as used in googletest). -They automatically pick a reasonable error bound based on the absolute value of -the expected value. `DoubleEq()` and `FloatEq()` conform to the IEEE standard, -which requires comparing two NaNs for equality to return false. The -`NanSensitive*` version instead treats two NaNs as equal, which is often what a -user wants. - -| Matcher | Description | -| :------------------------------------------------ | :----------------------- | -| `DoubleNear(a_double, max_abs_error)` | `argument` is a `double` value close to `a_double` (absolute error <= `max_abs_error`), treating two NaNs as unequal. | -| `FloatNear(a_float, max_abs_error)` | `argument` is a `float` value close to `a_float` (absolute error <= `max_abs_error`), treating two NaNs as unequal. | -| `NanSensitiveDoubleNear(a_double, max_abs_error)` | `argument` is a `double` value close to `a_double` (absolute error <= `max_abs_error`), treating two NaNs as equal. | -| `NanSensitiveFloatNear(a_float, max_abs_error)` | `argument` is a `float` value close to `a_float` (absolute error <= `max_abs_error`), treating two NaNs as equal. | - -## String Matchers - -The `argument` can be either a C string or a C++ string object: - -| Matcher | Description | -| :---------------------- | :------------------------------------------------- | -| `ContainsRegex(string)` | `argument` matches the given regular expression. | -| `EndsWith(suffix)` | `argument` ends with string `suffix`. | -| `HasSubstr(string)` | `argument` contains `string` as a sub-string. | -| `IsEmpty()` | `argument` is an empty string. | -| `MatchesRegex(string)` | `argument` matches the given regular expression with the match starting at the first character and ending at the last character. | -| `StartsWith(prefix)` | `argument` starts with string `prefix`. | -| `StrCaseEq(string)` | `argument` is equal to `string`, ignoring case. | -| `StrCaseNe(string)` | `argument` is not equal to `string`, ignoring case. | -| `StrEq(string)` | `argument` is equal to `string`. | -| `StrNe(string)` | `argument` is not equal to `string`. | -| `WhenBase64Unescaped(m)` | `argument` is a base-64 escaped string whose unescaped string matches `m`. The web-safe format from [RFC 4648](https://www.rfc-editor.org/rfc/rfc4648#section-5) is supported. | - -`ContainsRegex()` and `MatchesRegex()` take ownership of the `RE` object. They -use the regular expression syntax defined -[here](../advanced.md#regular-expression-syntax). All of these matchers, except -`ContainsRegex()` and `MatchesRegex()` work for wide strings as well. - -## Container Matchers - -Most STL-style containers support `==`, so you can use `Eq(expected_container)` -or simply `expected_container` to match a container exactly. If you want to -write the elements in-line, match them more flexibly, or get more informative -messages, you can use: - -| Matcher | Description | -| :---------------------------------------- | :------------------------------- | -| `BeginEndDistanceIs(m)` | `argument` is a container whose `begin()` and `end()` iterators are separated by a number of increments matching `m`. E.g. `BeginEndDistanceIs(2)` or `BeginEndDistanceIs(Lt(2))`. For containers that define a `size()` method, `SizeIs(m)` may be more efficient. | -| `ContainerEq(container)` | The same as `Eq(container)` except that the failure message also includes which elements are in one container but not the other. | -| `Contains(e)` | `argument` contains an element that matches `e`, which can be either a value or a matcher. | -| `Contains(e).Times(n)` | `argument` contains elements that match `e`, which can be either a value or a matcher, and the number of matches is `n`, which can be either a value or a matcher. Unlike the plain `Contains` and `Each` this allows to check for arbitrary occurrences including testing for absence with `Contains(e).Times(0)`. | -| `Each(e)` | `argument` is a container where *every* element matches `e`, which can be either a value or a matcher. | -| `ElementsAre(e0, e1, ..., en)` | `argument` has `n + 1` elements, where the *i*-th element matches `ei`, which can be a value or a matcher. | -| `ElementsAreArray({e0, e1, ..., en})`, `ElementsAreArray(a_container)`, `ElementsAreArray(begin, end)`, `ElementsAreArray(array)`, or `ElementsAreArray(array, count)` | The same as `ElementsAre()` except that the expected element values/matchers come from an initializer list, STL-style container, iterator range, or C-style array. | -| `IsEmpty()` | `argument` is an empty container (`container.empty()`). | -| `IsSubsetOf({e0, e1, ..., en})`, `IsSubsetOf(a_container)`, `IsSubsetOf(begin, end)`, `IsSubsetOf(array)`, or `IsSubsetOf(array, count)` | `argument` matches `UnorderedElementsAre(x0, x1, ..., xk)` for some subset `{x0, x1, ..., xk}` of the expected matchers. | -| `IsSupersetOf({e0, e1, ..., en})`, `IsSupersetOf(a_container)`, `IsSupersetOf(begin, end)`, `IsSupersetOf(array)`, or `IsSupersetOf(array, count)` | Some subset of `argument` matches `UnorderedElementsAre(`expected matchers`)`. | -| `Pointwise(m, container)`, `Pointwise(m, {e0, e1, ..., en})` | `argument` contains the same number of elements as in `container`, and for all i, (the i-th element in `argument`, the i-th element in `container`) match `m`, which is a matcher on 2-tuples. E.g. `Pointwise(Le(), upper_bounds)` verifies that each element in `argument` doesn't exceed the corresponding element in `upper_bounds`. See more detail below. | -| `SizeIs(m)` | `argument` is a container whose size matches `m`. E.g. `SizeIs(2)` or `SizeIs(Lt(2))`. | -| `UnorderedElementsAre(e0, e1, ..., en)` | `argument` has `n + 1` elements, and under *some* permutation of the elements, each element matches an `ei` (for a different `i`), which can be a value or a matcher. | -| `UnorderedElementsAreArray({e0, e1, ..., en})`, `UnorderedElementsAreArray(a_container)`, `UnorderedElementsAreArray(begin, end)`, `UnorderedElementsAreArray(array)`, or `UnorderedElementsAreArray(array, count)` | The same as `UnorderedElementsAre()` except that the expected element values/matchers come from an initializer list, STL-style container, iterator range, or C-style array. | -| `UnorderedPointwise(m, container)`, `UnorderedPointwise(m, {e0, e1, ..., en})` | Like `Pointwise(m, container)`, but ignores the order of elements. | -| `WhenSorted(m)` | When `argument` is sorted using the `<` operator, it matches container matcher `m`. E.g. `WhenSorted(ElementsAre(1, 2, 3))` verifies that `argument` contains elements 1, 2, and 3, ignoring order. | -| `WhenSortedBy(comparator, m)` | The same as `WhenSorted(m)`, except that the given comparator instead of `<` is used to sort `argument`. E.g. `WhenSortedBy(std::greater(), ElementsAre(3, 2, 1))`. | - -**Notes:** - -* These matchers can also match: - 1. a native array passed by reference (e.g. in `Foo(const int (&a)[5])`), - and - 2. an array passed as a pointer and a count (e.g. in `Bar(const T* buffer, - int len)` -- see [Multi-argument Matchers](#MultiArgMatchers)). -* The array being matched may be multi-dimensional (i.e. its elements can be - arrays). -* `m` in `Pointwise(m, ...)` and `UnorderedPointwise(m, ...)` should be a - matcher for `::std::tuple` where `T` and `U` are the element type of - the actual container and the expected container, respectively. For example, - to compare two `Foo` containers where `Foo` doesn't support `operator==`, - one might write: - - ```cpp - MATCHER(FooEq, "") { - return std::get<0>(arg).Equals(std::get<1>(arg)); - } - ... - EXPECT_THAT(actual_foos, Pointwise(FooEq(), expected_foos)); - ``` - -## Member Matchers - -| Matcher | Description | -| :------------------------------ | :----------------------------------------- | -| `Field(&class::field, m)` | `argument.field` (or `argument->field` when `argument` is a plain pointer) matches matcher `m`, where `argument` is an object of type _class_. | -| `Field(field_name, &class::field, m)` | The same as the two-parameter version, but provides a better error message. | -| `Key(e)` | `argument.first` matches `e`, which can be either a value or a matcher. E.g. `Contains(Key(Le(5)))` can verify that a `map` contains a key `<= 5`. | -| `Pair(m1, m2)` | `argument` is an `std::pair` whose `first` field matches `m1` and `second` field matches `m2`. | -| `FieldsAre(m...)` | `argument` is a compatible object where each field matches piecewise with the matchers `m...`. A compatible object is any that supports the `std::tuple_size`+`get(obj)` protocol. In C++17 and up this also supports types compatible with structured bindings, like aggregates. | -| `Property(&class::property, m)` | `argument.property()` (or `argument->property()` when `argument` is a plain pointer) matches matcher `m`, where `argument` is an object of type _class_. The method `property()` must take no argument and be declared as `const`. | -| `Property(property_name, &class::property, m)` | The same as the two-parameter version, but provides a better error message. - -**Notes:** - -* You can use `FieldsAre()` to match any type that supports structured - bindings, such as `std::tuple`, `std::pair`, `std::array`, and aggregate - types. For example: - - ```cpp - std::tuple my_tuple{7, "hello world"}; - EXPECT_THAT(my_tuple, FieldsAre(Ge(0), HasSubstr("hello"))); - - struct MyStruct { - int value = 42; - std::string greeting = "aloha"; - }; - MyStruct s; - EXPECT_THAT(s, FieldsAre(42, "aloha")); - ``` - -* Don't use `Property()` against member functions that you do not own, because - taking addresses of functions is fragile and generally not part of the - contract of the function. - -## Matching the Result of a Function, Functor, or Callback - -| Matcher | Description | -| :--------------- | :------------------------------------------------ | -| `ResultOf(f, m)` | `f(argument)` matches matcher `m`, where `f` is a function or functor. | -| `ResultOf(result_description, f, m)` | The same as the two-parameter version, but provides a better error message. - -## Pointer Matchers - -| Matcher | Description | -| :------------------------ | :---------------------------------------------- | -| `Address(m)` | the result of `std::addressof(argument)` matches `m`. | -| `Pointee(m)` | `argument` (either a smart pointer or a raw pointer) points to a value that matches matcher `m`. | -| `Pointer(m)` | `argument` (either a smart pointer or a raw pointer) contains a pointer that matches `m`. `m` will match against the raw pointer regardless of the type of `argument`. | -| `WhenDynamicCastTo(m)` | when `argument` is passed through `dynamic_cast()`, it matches matcher `m`. | - -## Multi-argument Matchers {#MultiArgMatchers} - -Technically, all matchers match a *single* value. A "multi-argument" matcher is -just one that matches a *tuple*. The following matchers can be used to match a -tuple `(x, y)`: - -Matcher | Description -:------ | :---------- -`Eq()` | `x == y` -`Ge()` | `x >= y` -`Gt()` | `x > y` -`Le()` | `x <= y` -`Lt()` | `x < y` -`Ne()` | `x != y` - -You can use the following selectors to pick a subset of the arguments (or -reorder them) to participate in the matching: - -| Matcher | Description | -| :------------------------- | :---------------------------------------------- | -| `AllArgs(m)` | Equivalent to `m`. Useful as syntactic sugar in `.With(AllArgs(m))`. | -| `Args(m)` | The tuple of the `k` selected (using 0-based indices) arguments matches `m`, e.g. `Args<1, 2>(Eq())`. | - -## Composite Matchers - -You can make a matcher from one or more other matchers: - -| Matcher | Description | -| :------------------------------- | :-------------------------------------- | -| `AllOf(m1, m2, ..., mn)` | `argument` matches all of the matchers `m1` to `mn`. | -| `AllOfArray({m0, m1, ..., mn})`, `AllOfArray(a_container)`, `AllOfArray(begin, end)`, `AllOfArray(array)`, or `AllOfArray(array, count)` | The same as `AllOf()` except that the matchers come from an initializer list, STL-style container, iterator range, or C-style array. | -| `AnyOf(m1, m2, ..., mn)` | `argument` matches at least one of the matchers `m1` to `mn`. | -| `AnyOfArray({m0, m1, ..., mn})`, `AnyOfArray(a_container)`, `AnyOfArray(begin, end)`, `AnyOfArray(array)`, or `AnyOfArray(array, count)` | The same as `AnyOf()` except that the matchers come from an initializer list, STL-style container, iterator range, or C-style array. | -| `Not(m)` | `argument` doesn't match matcher `m`. | -| `Conditional(cond, m1, m2)` | Matches matcher `m1` if `cond` evaluates to true, else matches `m2`.| - -## Adapters for Matchers - -| Matcher | Description | -| :---------------------- | :------------------------------------ | -| `MatcherCast(m)` | casts matcher `m` to type `Matcher`. | -| `SafeMatcherCast(m)` | [safely casts](../gmock_cook_book.md#SafeMatcherCast) matcher `m` to type `Matcher`. | -| `Truly(predicate)` | `predicate(argument)` returns something considered by C++ to be true, where `predicate` is a function or functor. | - -`AddressSatisfies(callback)` and `Truly(callback)` take ownership of `callback`, -which must be a permanent callback. - -## Using Matchers as Predicates {#MatchersAsPredicatesCheat} - -| Matcher | Description | -| :---------------------------- | :------------------------------------------ | -| `Matches(m)(value)` | evaluates to `true` if `value` matches `m`. You can use `Matches(m)` alone as a unary functor. | -| `ExplainMatchResult(m, value, result_listener)` | evaluates to `true` if `value` matches `m`, explaining the result to `result_listener`. | -| `Value(value, m)` | evaluates to `true` if `value` matches `m`. | - -## Defining Matchers - -| Macro | Description | -| :----------------------------------- | :------------------------------------ | -| `MATCHER(IsEven, "") { return (arg % 2) == 0; }` | Defines a matcher `IsEven()` to match an even number. | -| `MATCHER_P(IsDivisibleBy, n, "") { *result_listener << "where the remainder is " << (arg % n); return (arg % n) == 0; }` | Defines a matcher `IsDivisibleBy(n)` to match a number divisible by `n`. | -| `MATCHER_P2(IsBetween, a, b, absl::StrCat(negation ? "isn't" : "is", " between ", PrintToString(a), " and ", PrintToString(b))) { return a <= arg && arg <= b; }` | Defines a matcher `IsBetween(a, b)` to match a value in the range [`a`, `b`]. | - -**Notes:** - -1. The `MATCHER*` macros cannot be used inside a function or class. -2. The matcher body must be *purely functional* (i.e. it cannot have any side - effect, and the result must not depend on anything other than the value - being matched and the matcher parameters). -3. You can use `PrintToString(x)` to convert a value `x` of any type to a - string. -4. You can use `ExplainMatchResult()` in a custom matcher to wrap another - matcher, for example: - - ```cpp - MATCHER_P(NestedPropertyMatches, matcher, "") { - return ExplainMatchResult(matcher, arg.nested().property(), result_listener); - } - ``` - -5. You can use `DescribeMatcher<>` to describe another matcher. For example: - - ```cpp - MATCHER_P(XAndYThat, matcher, - "X that " + DescribeMatcher(matcher, negation) + - (negation ? " or" : " and") + " Y that " + - DescribeMatcher(matcher, negation)) { - return ExplainMatchResult(matcher, arg.x(), result_listener) && - ExplainMatchResult(matcher, arg.y(), result_listener); - } - ``` diff --git a/test/unit/googletest/docs/reference/mocking.md b/test/unit/googletest/docs/reference/mocking.md deleted file mode 100644 index ab37ebf..0000000 --- a/test/unit/googletest/docs/reference/mocking.md +++ /dev/null @@ -1,588 +0,0 @@ -# Mocking Reference - -This page lists the facilities provided by GoogleTest for creating and working -with mock objects. To use them, add `#include `. - -## Macros {#macros} - -GoogleTest defines the following macros for working with mocks. - -### MOCK_METHOD {#MOCK_METHOD} - -`MOCK_METHOD(`*`return_type`*`,`*`method_name`*`, (`*`args...`*`));` \ -`MOCK_METHOD(`*`return_type`*`,`*`method_name`*`, (`*`args...`*`), -(`*`specs...`*`));` - -Defines a mock method *`method_name`* with arguments `(`*`args...`*`)` and -return type *`return_type`* within a mock class. - -The parameters of `MOCK_METHOD` mirror the method declaration. The optional -fourth parameter *`specs...`* is a comma-separated list of qualifiers. The -following qualifiers are accepted: - -| Qualifier | Meaning | -| -------------------------- | -------------------------------------------- | -| `const` | Makes the mocked method a `const` method. Required if overriding a `const` method. | -| `override` | Marks the method with `override`. Recommended if overriding a `virtual` method. | -| `noexcept` | Marks the method with `noexcept`. Required if overriding a `noexcept` method. | -| `Calltype(`*`calltype`*`)` | Sets the call type for the method, for example `Calltype(STDMETHODCALLTYPE)`. Useful on Windows. | -| `ref(`*`qualifier`*`)` | Marks the method with the given reference qualifier, for example `ref(&)` or `ref(&&)`. Required if overriding a method that has a reference qualifier. | - -Note that commas in arguments prevent `MOCK_METHOD` from parsing the arguments -correctly if they are not appropriately surrounded by parentheses. See the -following example: - -```cpp -class MyMock { - public: - // The following 2 lines will not compile due to commas in the arguments: - MOCK_METHOD(std::pair, GetPair, ()); // Error! - MOCK_METHOD(bool, CheckMap, (std::map, bool)); // Error! - - // One solution - wrap arguments that contain commas in parentheses: - MOCK_METHOD((std::pair), GetPair, ()); - MOCK_METHOD(bool, CheckMap, ((std::map), bool)); - - // Another solution - use type aliases: - using BoolAndInt = std::pair; - MOCK_METHOD(BoolAndInt, GetPair, ()); - using MapIntDouble = std::map; - MOCK_METHOD(bool, CheckMap, (MapIntDouble, bool)); -}; -``` - -`MOCK_METHOD` must be used in the `public:` section of a mock class definition, -regardless of whether the method being mocked is `public`, `protected`, or -`private` in the base class. - -### EXPECT_CALL {#EXPECT_CALL} - -`EXPECT_CALL(`*`mock_object`*`,`*`method_name`*`(`*`matchers...`*`))` - -Creates an [expectation](../gmock_for_dummies.md#setting-expectations) that the -method *`method_name`* of the object *`mock_object`* is called with arguments -that match the given matchers *`matchers...`*. `EXPECT_CALL` must precede any -code that exercises the mock object. - -The parameter *`matchers...`* is a comma-separated list of -[matchers](../gmock_for_dummies.md#matchers-what-arguments-do-we-expect) that -correspond to each argument of the method *`method_name`*. The expectation will -apply only to calls of *`method_name`* whose arguments match all of the -matchers. If `(`*`matchers...`*`)` is omitted, the expectation behaves as if -each argument's matcher were a [wildcard matcher (`_`)](matchers.md#wildcard). -See the [Matchers Reference](matchers.md) for a list of all built-in matchers. - -The following chainable clauses can be used to modify the expectation, and they -must be used in the following order: - -```cpp -EXPECT_CALL(mock_object, method_name(matchers...)) - .With(multi_argument_matcher) // Can be used at most once - .Times(cardinality) // Can be used at most once - .InSequence(sequences...) // Can be used any number of times - .After(expectations...) // Can be used any number of times - .WillOnce(action) // Can be used any number of times - .WillRepeatedly(action) // Can be used at most once - .RetiresOnSaturation(); // Can be used at most once -``` - -See details for each modifier clause below. - -#### With {#EXPECT_CALL.With} - -`.With(`*`multi_argument_matcher`*`)` - -Restricts the expectation to apply only to mock function calls whose arguments -as a whole match the multi-argument matcher *`multi_argument_matcher`*. - -GoogleTest passes all of the arguments as one tuple into the matcher. The -parameter *`multi_argument_matcher`* must thus be a matcher of type -`Matcher>`, where `A1, ..., An` are the types of the -function arguments. - -For example, the following code sets the expectation that -`my_mock.SetPosition()` is called with any two arguments, the first argument -being less than the second: - -```cpp -using ::testing::_; -using ::testing::Lt; -... -EXPECT_CALL(my_mock, SetPosition(_, _)) - .With(Lt()); -``` - -GoogleTest provides some built-in matchers for 2-tuples, including the `Lt()` -matcher above. See [Multi-argument Matchers](matchers.md#MultiArgMatchers). - -The `With` clause can be used at most once on an expectation and must be the -first clause. - -#### Times {#EXPECT_CALL.Times} - -`.Times(`*`cardinality`*`)` - -Specifies how many times the mock function call is expected. - -The parameter *`cardinality`* represents the number of expected calls and can be -one of the following, all defined in the `::testing` namespace: - -| Cardinality | Meaning | -| ------------------- | --------------------------------------------------- | -| `AnyNumber()` | The function can be called any number of times. | -| `AtLeast(n)` | The function call is expected at least *n* times. | -| `AtMost(n)` | The function call is expected at most *n* times. | -| `Between(m, n)` | The function call is expected between *m* and *n* times, inclusive. | -| `Exactly(n)` or `n` | The function call is expected exactly *n* times. If *n* is 0, the call should never happen. | - -If the `Times` clause is omitted, GoogleTest infers the cardinality as follows: - -* If neither [`WillOnce`](#EXPECT_CALL.WillOnce) nor - [`WillRepeatedly`](#EXPECT_CALL.WillRepeatedly) are specified, the inferred - cardinality is `Times(1)`. -* If there are *n* `WillOnce` clauses and no `WillRepeatedly` clause, where - *n* >= 1, the inferred cardinality is `Times(n)`. -* If there are *n* `WillOnce` clauses and one `WillRepeatedly` clause, where - *n* >= 0, the inferred cardinality is `Times(AtLeast(n))`. - -The `Times` clause can be used at most once on an expectation. - -#### InSequence {#EXPECT_CALL.InSequence} - -`.InSequence(`*`sequences...`*`)` - -Specifies that the mock function call is expected in a certain sequence. - -The parameter *`sequences...`* is any number of [`Sequence`](#Sequence) objects. -Expected calls assigned to the same sequence are expected to occur in the order -the expectations are declared. - -For example, the following code sets the expectation that the `Reset()` method -of `my_mock` is called before both `GetSize()` and `Describe()`, and `GetSize()` -and `Describe()` can occur in any order relative to each other: - -```cpp -using ::testing::Sequence; -Sequence s1, s2; -... -EXPECT_CALL(my_mock, Reset()) - .InSequence(s1, s2); -EXPECT_CALL(my_mock, GetSize()) - .InSequence(s1); -EXPECT_CALL(my_mock, Describe()) - .InSequence(s2); -``` - -The `InSequence` clause can be used any number of times on an expectation. - -See also the [`InSequence` class](#InSequence). - -#### After {#EXPECT_CALL.After} - -`.After(`*`expectations...`*`)` - -Specifies that the mock function call is expected to occur after one or more -other calls. - -The parameter *`expectations...`* can be up to five -[`Expectation`](#Expectation) or [`ExpectationSet`](#ExpectationSet) objects. -The mock function call is expected to occur after all of the given expectations. - -For example, the following code sets the expectation that the `Describe()` -method of `my_mock` is called only after both `InitX()` and `InitY()` have been -called. - -```cpp -using ::testing::Expectation; -... -Expectation init_x = EXPECT_CALL(my_mock, InitX()); -Expectation init_y = EXPECT_CALL(my_mock, InitY()); -EXPECT_CALL(my_mock, Describe()) - .After(init_x, init_y); -``` - -The `ExpectationSet` object is helpful when the number of prerequisites for an -expectation is large or variable, for example: - -```cpp -using ::testing::ExpectationSet; -... -ExpectationSet all_inits; -// Collect all expectations of InitElement() calls -for (int i = 0; i < element_count; i++) { - all_inits += EXPECT_CALL(my_mock, InitElement(i)); -} -EXPECT_CALL(my_mock, Describe()) - .After(all_inits); // Expect Describe() call after all InitElement() calls -``` - -The `After` clause can be used any number of times on an expectation. - -#### WillOnce {#EXPECT_CALL.WillOnce} - -`.WillOnce(`*`action`*`)` - -Specifies the mock function's actual behavior when invoked, for a single -matching function call. - -The parameter *`action`* represents the -[action](../gmock_for_dummies.md#actions-what-should-it-do) that the function -call will perform. See the [Actions Reference](actions.md) for a list of -built-in actions. - -The use of `WillOnce` implicitly sets a cardinality on the expectation when -`Times` is not specified. See [`Times`](#EXPECT_CALL.Times). - -Each matching function call will perform the next action in the order declared. -For example, the following code specifies that `my_mock.GetNumber()` is expected -to be called exactly 3 times and will return `1`, `2`, and `3` respectively on -the first, second, and third calls: - -```cpp -using ::testing::Return; -... -EXPECT_CALL(my_mock, GetNumber()) - .WillOnce(Return(1)) - .WillOnce(Return(2)) - .WillOnce(Return(3)); -``` - -The `WillOnce` clause can be used any number of times on an expectation. Unlike -`WillRepeatedly`, the action fed to each `WillOnce` call will be called at most -once, so may be a move-only type and/or have an `&&`-qualified call operator. - -#### WillRepeatedly {#EXPECT_CALL.WillRepeatedly} - -`.WillRepeatedly(`*`action`*`)` - -Specifies the mock function's actual behavior when invoked, for all subsequent -matching function calls. Takes effect after the actions specified in the -[`WillOnce`](#EXPECT_CALL.WillOnce) clauses, if any, have been performed. - -The parameter *`action`* represents the -[action](../gmock_for_dummies.md#actions-what-should-it-do) that the function -call will perform. See the [Actions Reference](actions.md) for a list of -built-in actions. - -The use of `WillRepeatedly` implicitly sets a cardinality on the expectation -when `Times` is not specified. See [`Times`](#EXPECT_CALL.Times). - -If any `WillOnce` clauses have been specified, matching function calls will -perform those actions before the action specified by `WillRepeatedly`. See the -following example: - -```cpp -using ::testing::Return; -... -EXPECT_CALL(my_mock, GetName()) - .WillRepeatedly(Return("John Doe")); // Return "John Doe" on all calls - -EXPECT_CALL(my_mock, GetNumber()) - .WillOnce(Return(42)) // Return 42 on the first call - .WillRepeatedly(Return(7)); // Return 7 on all subsequent calls -``` - -The `WillRepeatedly` clause can be used at most once on an expectation. - -#### RetiresOnSaturation {#EXPECT_CALL.RetiresOnSaturation} - -`.RetiresOnSaturation()` - -Indicates that the expectation will no longer be active after the expected -number of matching function calls has been reached. - -The `RetiresOnSaturation` clause is only meaningful for expectations with an -upper-bounded cardinality. The expectation will *retire* (no longer match any -function calls) after it has been *saturated* (the upper bound has been -reached). See the following example: - -```cpp -using ::testing::_; -using ::testing::AnyNumber; -... -EXPECT_CALL(my_mock, SetNumber(_)) // Expectation 1 - .Times(AnyNumber()); -EXPECT_CALL(my_mock, SetNumber(7)) // Expectation 2 - .Times(2) - .RetiresOnSaturation(); -``` - -In the above example, the first two calls to `my_mock.SetNumber(7)` match -expectation 2, which then becomes inactive and no longer matches any calls. A -third call to `my_mock.SetNumber(7)` would then match expectation 1. Without -`RetiresOnSaturation()` on expectation 2, a third call to `my_mock.SetNumber(7)` -would match expectation 2 again, producing a failure since the limit of 2 calls -was exceeded. - -The `RetiresOnSaturation` clause can be used at most once on an expectation and -must be the last clause. - -### ON_CALL {#ON_CALL} - -`ON_CALL(`*`mock_object`*`,`*`method_name`*`(`*`matchers...`*`))` - -Defines what happens when the method *`method_name`* of the object -*`mock_object`* is called with arguments that match the given matchers -*`matchers...`*. Requires a modifier clause to specify the method's behavior. -*Does not* set any expectations that the method will be called. - -The parameter *`matchers...`* is a comma-separated list of -[matchers](../gmock_for_dummies.md#matchers-what-arguments-do-we-expect) that -correspond to each argument of the method *`method_name`*. The `ON_CALL` -specification will apply only to calls of *`method_name`* whose arguments match -all of the matchers. If `(`*`matchers...`*`)` is omitted, the behavior is as if -each argument's matcher were a [wildcard matcher (`_`)](matchers.md#wildcard). -See the [Matchers Reference](matchers.md) for a list of all built-in matchers. - -The following chainable clauses can be used to set the method's behavior, and -they must be used in the following order: - -```cpp -ON_CALL(mock_object, method_name(matchers...)) - .With(multi_argument_matcher) // Can be used at most once - .WillByDefault(action); // Required -``` - -See details for each modifier clause below. - -#### With {#ON_CALL.With} - -`.With(`*`multi_argument_matcher`*`)` - -Restricts the specification to only mock function calls whose arguments as a -whole match the multi-argument matcher *`multi_argument_matcher`*. - -GoogleTest passes all of the arguments as one tuple into the matcher. The -parameter *`multi_argument_matcher`* must thus be a matcher of type -`Matcher>`, where `A1, ..., An` are the types of the -function arguments. - -For example, the following code sets the default behavior when -`my_mock.SetPosition()` is called with any two arguments, the first argument -being less than the second: - -```cpp -using ::testing::_; -using ::testing::Lt; -using ::testing::Return; -... -ON_CALL(my_mock, SetPosition(_, _)) - .With(Lt()) - .WillByDefault(Return(true)); -``` - -GoogleTest provides some built-in matchers for 2-tuples, including the `Lt()` -matcher above. See [Multi-argument Matchers](matchers.md#MultiArgMatchers). - -The `With` clause can be used at most once with each `ON_CALL` statement. - -#### WillByDefault {#ON_CALL.WillByDefault} - -`.WillByDefault(`*`action`*`)` - -Specifies the default behavior of a matching mock function call. - -The parameter *`action`* represents the -[action](../gmock_for_dummies.md#actions-what-should-it-do) that the function -call will perform. See the [Actions Reference](actions.md) for a list of -built-in actions. - -For example, the following code specifies that by default, a call to -`my_mock.Greet()` will return `"hello"`: - -```cpp -using ::testing::Return; -... -ON_CALL(my_mock, Greet()) - .WillByDefault(Return("hello")); -``` - -The action specified by `WillByDefault` is superseded by the actions specified -on a matching `EXPECT_CALL` statement, if any. See the -[`WillOnce`](#EXPECT_CALL.WillOnce) and -[`WillRepeatedly`](#EXPECT_CALL.WillRepeatedly) clauses of `EXPECT_CALL`. - -The `WillByDefault` clause must be used exactly once with each `ON_CALL` -statement. - -## Classes {#classes} - -GoogleTest defines the following classes for working with mocks. - -### DefaultValue {#DefaultValue} - -`::testing::DefaultValue` - -Allows a user to specify the default value for a type `T` that is both copyable -and publicly destructible (i.e. anything that can be used as a function return -type). For mock functions with a return type of `T`, this default value is -returned from function calls that do not specify an action. - -Provides the static methods `Set()`, `SetFactory()`, and `Clear()` to manage the -default value: - -```cpp -// Sets the default value to be returned. T must be copy constructible. -DefaultValue::Set(value); - -// Sets a factory. Will be invoked on demand. T must be move constructible. -T MakeT(); -DefaultValue::SetFactory(&MakeT); - -// Unsets the default value. -DefaultValue::Clear(); -``` - -### NiceMock {#NiceMock} - -`::testing::NiceMock` - -Represents a mock object that suppresses warnings on -[uninteresting calls](../gmock_cook_book.md#uninteresting-vs-unexpected). The -template parameter `T` is any mock class, except for another `NiceMock`, -`NaggyMock`, or `StrictMock`. - -Usage of `NiceMock` is analogous to usage of `T`. `NiceMock` is a subclass -of `T`, so it can be used wherever an object of type `T` is accepted. In -addition, `NiceMock` can be constructed with any arguments that a constructor -of `T` accepts. - -For example, the following code suppresses warnings on the mock `my_mock` of -type `MockClass` if a method other than `DoSomething()` is called: - -```cpp -using ::testing::NiceMock; -... -NiceMock my_mock("some", "args"); -EXPECT_CALL(my_mock, DoSomething()); -... code that uses my_mock ... -``` - -`NiceMock` only works for mock methods defined using the `MOCK_METHOD` macro -directly in the definition of class `T`. If a mock method is defined in a base -class of `T`, a warning might still be generated. - -`NiceMock` might not work correctly if the destructor of `T` is not virtual. - -### NaggyMock {#NaggyMock} - -`::testing::NaggyMock` - -Represents a mock object that generates warnings on -[uninteresting calls](../gmock_cook_book.md#uninteresting-vs-unexpected). The -template parameter `T` is any mock class, except for another `NiceMock`, -`NaggyMock`, or `StrictMock`. - -Usage of `NaggyMock` is analogous to usage of `T`. `NaggyMock` is a -subclass of `T`, so it can be used wherever an object of type `T` is accepted. -In addition, `NaggyMock` can be constructed with any arguments that a -constructor of `T` accepts. - -For example, the following code generates warnings on the mock `my_mock` of type -`MockClass` if a method other than `DoSomething()` is called: - -```cpp -using ::testing::NaggyMock; -... -NaggyMock my_mock("some", "args"); -EXPECT_CALL(my_mock, DoSomething()); -... code that uses my_mock ... -``` - -Mock objects of type `T` by default behave the same way as `NaggyMock`. - -### StrictMock {#StrictMock} - -`::testing::StrictMock` - -Represents a mock object that generates test failures on -[uninteresting calls](../gmock_cook_book.md#uninteresting-vs-unexpected). The -template parameter `T` is any mock class, except for another `NiceMock`, -`NaggyMock`, or `StrictMock`. - -Usage of `StrictMock` is analogous to usage of `T`. `StrictMock` is a -subclass of `T`, so it can be used wherever an object of type `T` is accepted. -In addition, `StrictMock` can be constructed with any arguments that a -constructor of `T` accepts. - -For example, the following code generates a test failure on the mock `my_mock` -of type `MockClass` if a method other than `DoSomething()` is called: - -```cpp -using ::testing::StrictMock; -... -StrictMock my_mock("some", "args"); -EXPECT_CALL(my_mock, DoSomething()); -... code that uses my_mock ... -``` - -`StrictMock` only works for mock methods defined using the `MOCK_METHOD` -macro directly in the definition of class `T`. If a mock method is defined in a -base class of `T`, a failure might not be generated. - -`StrictMock` might not work correctly if the destructor of `T` is not -virtual. - -### Sequence {#Sequence} - -`::testing::Sequence` - -Represents a chronological sequence of expectations. See the -[`InSequence`](#EXPECT_CALL.InSequence) clause of `EXPECT_CALL` for usage. - -### InSequence {#InSequence} - -`::testing::InSequence` - -An object of this type causes all expectations encountered in its scope to be -put in an anonymous sequence. - -This allows more convenient expression of multiple expectations in a single -sequence: - -```cpp -using ::testing::InSequence; -{ - InSequence seq; - - // The following are expected to occur in the order declared. - EXPECT_CALL(...); - EXPECT_CALL(...); - ... - EXPECT_CALL(...); -} -``` - -The name of the `InSequence` object does not matter. - -### Expectation {#Expectation} - -`::testing::Expectation` - -Represents a mock function call expectation as created by -[`EXPECT_CALL`](#EXPECT_CALL): - -```cpp -using ::testing::Expectation; -Expectation my_expectation = EXPECT_CALL(...); -``` - -Useful for specifying sequences of expectations; see the -[`After`](#EXPECT_CALL.After) clause of `EXPECT_CALL`. - -### ExpectationSet {#ExpectationSet} - -`::testing::ExpectationSet` - -Represents a set of mock function call expectations. - -Use the `+=` operator to add [`Expectation`](#Expectation) objects to the set: - -```cpp -using ::testing::ExpectationSet; -ExpectationSet my_expectations; -my_expectations += EXPECT_CALL(...); -``` - -Useful for specifying sequences of expectations; see the -[`After`](#EXPECT_CALL.After) clause of `EXPECT_CALL`. diff --git a/test/unit/googletest/docs/reference/testing.md b/test/unit/googletest/docs/reference/testing.md deleted file mode 100644 index 3ed5211..0000000 --- a/test/unit/googletest/docs/reference/testing.md +++ /dev/null @@ -1,1453 +0,0 @@ -# Testing Reference - - - -This page lists the facilities provided by GoogleTest for writing test programs. -To use them, add `#include `. - -## Macros - -GoogleTest defines the following macros for writing tests. - -### TEST {#TEST} - -
-TEST(TestSuiteName, TestName) {
-  ... statements ...
-}
-
- -Defines an individual test named *`TestName`* in the test suite -*`TestSuiteName`*, consisting of the given statements. - -Both arguments *`TestSuiteName`* and *`TestName`* must be valid C++ identifiers -and must not contain underscores (`_`). Tests in different test suites can have -the same individual name. - -The statements within the test body can be any code under test. -[Assertions](assertions.md) used within the test body determine the outcome of -the test. - -### TEST_F {#TEST_F} - -
-TEST_F(TestFixtureName, TestName) {
-  ... statements ...
-}
-
- -Defines an individual test named *`TestName`* that uses the test fixture class -*`TestFixtureName`*. The test suite name is *`TestFixtureName`*. - -Both arguments *`TestFixtureName`* and *`TestName`* must be valid C++ -identifiers and must not contain underscores (`_`). *`TestFixtureName`* must be -the name of a test fixture class—see -[Test Fixtures](../primer.md#same-data-multiple-tests). - -The statements within the test body can be any code under test. -[Assertions](assertions.md) used within the test body determine the outcome of -the test. - -### TEST_P {#TEST_P} - -
-TEST_P(TestFixtureName, TestName) {
-  ... statements ...
-}
-
- -Defines an individual value-parameterized test named *`TestName`* that uses the -test fixture class *`TestFixtureName`*. The test suite name is -*`TestFixtureName`*. - -Both arguments *`TestFixtureName`* and *`TestName`* must be valid C++ -identifiers and must not contain underscores (`_`). *`TestFixtureName`* must be -the name of a value-parameterized test fixture class—see -[Value-Parameterized Tests](../advanced.md#value-parameterized-tests). - -The statements within the test body can be any code under test. Within the test -body, the test parameter can be accessed with the `GetParam()` function (see -[`WithParamInterface`](#WithParamInterface)). For example: - -```cpp -TEST_P(MyTestSuite, DoesSomething) { - ... - EXPECT_TRUE(DoSomething(GetParam())); - ... -} -``` - -[Assertions](assertions.md) used within the test body determine the outcome of -the test. - -See also [`INSTANTIATE_TEST_SUITE_P`](#INSTANTIATE_TEST_SUITE_P). - -### INSTANTIATE_TEST_SUITE_P {#INSTANTIATE_TEST_SUITE_P} - -`INSTANTIATE_TEST_SUITE_P(`*`InstantiationName`*`,`*`TestSuiteName`*`,`*`param_generator`*`)` -\ -`INSTANTIATE_TEST_SUITE_P(`*`InstantiationName`*`,`*`TestSuiteName`*`,`*`param_generator`*`,`*`name_generator`*`)` - -Instantiates the value-parameterized test suite *`TestSuiteName`* (defined with -[`TEST_P`](#TEST_P)). - -The argument *`InstantiationName`* is a unique name for the instantiation of the -test suite, to distinguish between multiple instantiations. In test output, the -instantiation name is added as a prefix to the test suite name -*`TestSuiteName`*. If *`InstantiationName`* is empty -(`INSTANTIATE_TEST_SUITE_P(, ...)`), no prefix is added. - -The argument *`param_generator`* is one of the following GoogleTest-provided -functions that generate the test parameters, all defined in the `::testing` -namespace: - - - -| Parameter Generator | Behavior | -| ------------------- | ---------------------------------------------------- | -| `Range(begin, end [, step])` | Yields values `{begin, begin+step, begin+step+step, ...}`. The values do not include `end`. `step` defaults to 1. | -| `Values(v1, v2, ..., vN)` | Yields values `{v1, v2, ..., vN}`. | -| `ValuesIn(container)` or `ValuesIn(begin,end)` | Yields values from a C-style array, an STL-style container, or an iterator range `[begin, end)`. | -| `Bool()` | Yields sequence `{false, true}`. | -| `Combine(g1, g2, ..., gN)` | Yields as `std::tuple` *n*-tuples all combinations (Cartesian product) of the values generated by the given *n* generators `g1`, `g2`, ..., `gN`. | -| `ConvertGenerator(g)` | Yields values generated by generator `g`, `static_cast` to `T`. | - -The optional last argument *`name_generator`* is a function or functor that -generates custom test name suffixes based on the test parameters. The function -must accept an argument of type -[`TestParamInfo`](#TestParamInfo) and return a `std::string`. -The test name suffix can only contain alphanumeric characters and underscores. -GoogleTest provides [`PrintToStringParamName`](#PrintToStringParamName), or a -custom function can be used for more control: - -```cpp -INSTANTIATE_TEST_SUITE_P( - MyInstantiation, MyTestSuite, - testing::Values(...), - [](const testing::TestParamInfo& info) { - // Can use info.param here to generate the test suffix - std::string name = ... - return name; - }); -``` - -For more information, see -[Value-Parameterized Tests](../advanced.md#value-parameterized-tests). - -See also -[`GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST`](#GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST). - -### TYPED_TEST_SUITE {#TYPED_TEST_SUITE} - -`TYPED_TEST_SUITE(`*`TestFixtureName`*`,`*`Types`*`)` -`TYPED_TEST_SUITE(`*`TestFixtureName`*`,`*`Types`*`,`*`NameGenerator`*`)` - -Defines a typed test suite based on the test fixture *`TestFixtureName`*. The -test suite name is *`TestFixtureName`*. - -The argument *`TestFixtureName`* is a fixture class template, parameterized by a -type, for example: - -```cpp -template -class MyFixture : public testing::Test { - public: - ... - using List = std::list; - static T shared_; - T value_; -}; -``` - -The argument *`Types`* is a [`Types`](#Types) object representing the list of -types to run the tests on, for example: - -```cpp -using MyTypes = ::testing::Types; -TYPED_TEST_SUITE(MyFixture, MyTypes); -``` - -The type alias (`using` or `typedef`) is necessary for the `TYPED_TEST_SUITE` -macro to parse correctly. - -The optional third argument *`NameGenerator`* allows specifying a class that -exposes a templated static function `GetName(int)`. For example: - -```cpp -class NameGenerator { - public: - template - static std::string GetName(int) { - if constexpr (std::is_same_v) return "char"; - if constexpr (std::is_same_v) return "int"; - if constexpr (std::is_same_v) return "unsignedInt"; - } -}; -TYPED_TEST_SUITE(MyFixture, MyTypes, NameGenerator); -``` - -See also [`TYPED_TEST`](#TYPED_TEST) and -[Typed Tests](../advanced.md#typed-tests) for more information. - -### TYPED_TEST {#TYPED_TEST} - -
-TYPED_TEST(TestSuiteName, TestName) {
-  ... statements ...
-}
-
- -Defines an individual typed test named *`TestName`* in the typed test suite -*`TestSuiteName`*. The test suite must be defined with -[`TYPED_TEST_SUITE`](#TYPED_TEST_SUITE). - -Within the test body, the special name `TypeParam` refers to the type parameter, -and `TestFixture` refers to the fixture class. See the following example: - -```cpp -TYPED_TEST(MyFixture, Example) { - // Inside a test, refer to the special name TypeParam to get the type - // parameter. Since we are inside a derived class template, C++ requires - // us to visit the members of MyFixture via 'this'. - TypeParam n = this->value_; - - // To visit static members of the fixture, add the 'TestFixture::' - // prefix. - n += TestFixture::shared_; - - // To refer to typedefs in the fixture, add the 'typename TestFixture::' - // prefix. The 'typename' is required to satisfy the compiler. - typename TestFixture::List values; - - values.push_back(n); - ... -} -``` - -For more information, see [Typed Tests](../advanced.md#typed-tests). - -### TYPED_TEST_SUITE_P {#TYPED_TEST_SUITE_P} - -`TYPED_TEST_SUITE_P(`*`TestFixtureName`*`)` - -Defines a type-parameterized test suite based on the test fixture -*`TestFixtureName`*. The test suite name is *`TestFixtureName`*. - -The argument *`TestFixtureName`* is a fixture class template, parameterized by a -type. See [`TYPED_TEST_SUITE`](#TYPED_TEST_SUITE) for an example. - -See also [`TYPED_TEST_P`](#TYPED_TEST_P) and -[Type-Parameterized Tests](../advanced.md#type-parameterized-tests) for more -information. - -### TYPED_TEST_P {#TYPED_TEST_P} - -
-TYPED_TEST_P(TestSuiteName, TestName) {
-  ... statements ...
-}
-
- -Defines an individual type-parameterized test named *`TestName`* in the -type-parameterized test suite *`TestSuiteName`*. The test suite must be defined -with [`TYPED_TEST_SUITE_P`](#TYPED_TEST_SUITE_P). - -Within the test body, the special name `TypeParam` refers to the type parameter, -and `TestFixture` refers to the fixture class. See [`TYPED_TEST`](#TYPED_TEST) -for an example. - -See also [`REGISTER_TYPED_TEST_SUITE_P`](#REGISTER_TYPED_TEST_SUITE_P) and -[Type-Parameterized Tests](../advanced.md#type-parameterized-tests) for more -information. - -### REGISTER_TYPED_TEST_SUITE_P {#REGISTER_TYPED_TEST_SUITE_P} - -`REGISTER_TYPED_TEST_SUITE_P(`*`TestSuiteName`*`,`*`TestNames...`*`)` - -Registers the type-parameterized tests *`TestNames...`* of the test suite -*`TestSuiteName`*. The test suite and tests must be defined with -[`TYPED_TEST_SUITE_P`](#TYPED_TEST_SUITE_P) and [`TYPED_TEST_P`](#TYPED_TEST_P). - -For example: - -```cpp -// Define the test suite and tests. -TYPED_TEST_SUITE_P(MyFixture); -TYPED_TEST_P(MyFixture, HasPropertyA) { ... } -TYPED_TEST_P(MyFixture, HasPropertyB) { ... } - -// Register the tests in the test suite. -REGISTER_TYPED_TEST_SUITE_P(MyFixture, HasPropertyA, HasPropertyB); -``` - -See also [`INSTANTIATE_TYPED_TEST_SUITE_P`](#INSTANTIATE_TYPED_TEST_SUITE_P) and -[Type-Parameterized Tests](../advanced.md#type-parameterized-tests) for more -information. - -### INSTANTIATE_TYPED_TEST_SUITE_P {#INSTANTIATE_TYPED_TEST_SUITE_P} - -`INSTANTIATE_TYPED_TEST_SUITE_P(`*`InstantiationName`*`,`*`TestSuiteName`*`,`*`Types`*`)` - -Instantiates the type-parameterized test suite *`TestSuiteName`*. The test suite -must be registered with -[`REGISTER_TYPED_TEST_SUITE_P`](#REGISTER_TYPED_TEST_SUITE_P). - -The argument *`InstantiationName`* is a unique name for the instantiation of the -test suite, to distinguish between multiple instantiations. In test output, the -instantiation name is added as a prefix to the test suite name -*`TestSuiteName`*. If *`InstantiationName`* is empty -(`INSTANTIATE_TYPED_TEST_SUITE_P(, ...)`), no prefix is added. - -The argument *`Types`* is a [`Types`](#Types) object representing the list of -types to run the tests on, for example: - -```cpp -using MyTypes = ::testing::Types; -INSTANTIATE_TYPED_TEST_SUITE_P(MyInstantiation, MyFixture, MyTypes); -``` - -The type alias (`using` or `typedef`) is necessary for the -`INSTANTIATE_TYPED_TEST_SUITE_P` macro to parse correctly. - -For more information, see -[Type-Parameterized Tests](../advanced.md#type-parameterized-tests). - -### FRIEND_TEST {#FRIEND_TEST} - -`FRIEND_TEST(`*`TestSuiteName`*`,`*`TestName`*`)` - -Within a class body, declares an individual test as a friend of the class, -enabling the test to access private class members. - -If the class is defined in a namespace, then in order to be friends of the -class, test fixtures and tests must be defined in the exact same namespace, -without inline or anonymous namespaces. - -For example, if the class definition looks like the following: - -```cpp -namespace my_namespace { - -class MyClass { - friend class MyClassTest; - FRIEND_TEST(MyClassTest, HasPropertyA); - FRIEND_TEST(MyClassTest, HasPropertyB); - ... definition of class MyClass ... -}; - -} // namespace my_namespace -``` - -Then the test code should look like: - -```cpp -namespace my_namespace { - -class MyClassTest : public testing::Test { - ... -}; - -TEST_F(MyClassTest, HasPropertyA) { ... } -TEST_F(MyClassTest, HasPropertyB) { ... } - -} // namespace my_namespace -``` - -See [Testing Private Code](../advanced.md#testing-private-code) for more -information. - -### SCOPED_TRACE {#SCOPED_TRACE} - -`SCOPED_TRACE(`*`message`*`)` - -Causes the current file name, line number, and the given message *`message`* to -be added to the failure message for each assertion failure that occurs in the -scope. - -For more information, see -[Adding Traces to Assertions](../advanced.md#adding-traces-to-assertions). - -See also the [`ScopedTrace` class](#ScopedTrace). - -### GTEST_SKIP {#GTEST_SKIP} - -`GTEST_SKIP()` - -Prevents further test execution at runtime. - -Can be used in individual test cases or in the `SetUp()` methods of test -environments or test fixtures (classes derived from the -[`Environment`](#Environment) or [`Test`](#Test) classes). If used in a global -test environment `SetUp()` method, it skips all tests in the test program. If -used in a test fixture `SetUp()` method, it skips all tests in the corresponding -test suite. - -Similar to assertions, `GTEST_SKIP` allows streaming a custom message into it. - -See [Skipping Test Execution](../advanced.md#skipping-test-execution) for more -information. - -### GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST {#GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST} - -`GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(`*`TestSuiteName`*`)` - -Allows the value-parameterized test suite *`TestSuiteName`* to be -uninstantiated. - -By default, every [`TEST_P`](#TEST_P) call without a corresponding -[`INSTANTIATE_TEST_SUITE_P`](#INSTANTIATE_TEST_SUITE_P) call causes a failing -test in the test suite `GoogleTestVerification`. -`GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST` suppresses this failure for the -given test suite. - -## Classes and types - -GoogleTest defines the following classes and types to help with writing tests. - -### AssertionResult {#AssertionResult} - -`testing::AssertionResult` - -A class for indicating whether an assertion was successful. - -When the assertion wasn't successful, the `AssertionResult` object stores a -non-empty failure message that can be retrieved with the object's `message()` -method. - -To create an instance of this class, use one of the factory functions -[`AssertionSuccess()`](#AssertionSuccess) or -[`AssertionFailure()`](#AssertionFailure). - -### AssertionException {#AssertionException} - -`testing::AssertionException` - -Exception which can be thrown from -[`TestEventListener::OnTestPartResult`](#TestEventListener::OnTestPartResult). - -### EmptyTestEventListener {#EmptyTestEventListener} - -`testing::EmptyTestEventListener` - -Provides an empty implementation of all methods in the -[`TestEventListener`](#TestEventListener) interface, such that a subclass only -needs to override the methods it cares about. - -### Environment {#Environment} - -`testing::Environment` - -Represents a global test environment. See -[Global Set-Up and Tear-Down](../advanced.md#global-set-up-and-tear-down). - -#### Protected Methods {#Environment-protected} - -##### SetUp {#Environment::SetUp} - -`virtual void Environment::SetUp()` - -Override this to define how to set up the environment. - -##### TearDown {#Environment::TearDown} - -`virtual void Environment::TearDown()` - -Override this to define how to tear down the environment. - -### ScopedTrace {#ScopedTrace} - -`testing::ScopedTrace` - -An instance of this class causes a trace to be included in every test failure -message generated by code in the scope of the lifetime of the `ScopedTrace` -instance. The effect is undone with the destruction of the instance. - -The `ScopedTrace` constructor has the following form: - -```cpp -template -ScopedTrace(const char* file, int line, const T& message) -``` - -Example usage: - -```cpp -testing::ScopedTrace trace("file.cc", 123, "message"); -``` - -The resulting trace includes the given source file path and line number, and the -given message. The `message` argument can be anything streamable to -`std::ostream`. - -See also [`SCOPED_TRACE`](#SCOPED_TRACE). - -### Test {#Test} - -`testing::Test` - -The abstract class that all tests inherit from. `Test` is not copyable. - -#### Public Methods {#Test-public} - -##### SetUpTestSuite {#Test::SetUpTestSuite} - -`static void Test::SetUpTestSuite()` - -Performs shared setup for all tests in the test suite. GoogleTest calls -`SetUpTestSuite()` before running the first test in the test suite. - -##### TearDownTestSuite {#Test::TearDownTestSuite} - -`static void Test::TearDownTestSuite()` - -Performs shared teardown for all tests in the test suite. GoogleTest calls -`TearDownTestSuite()` after running the last test in the test suite. - -##### HasFatalFailure {#Test::HasFatalFailure} - -`static bool Test::HasFatalFailure()` - -Returns true if and only if the current test has a fatal failure. - -##### HasNonfatalFailure {#Test::HasNonfatalFailure} - -`static bool Test::HasNonfatalFailure()` - -Returns true if and only if the current test has a nonfatal failure. - -##### HasFailure {#Test::HasFailure} - -`static bool Test::HasFailure()` - -Returns true if and only if the current test has any failure, either fatal or -nonfatal. - -##### IsSkipped {#Test::IsSkipped} - -`static bool Test::IsSkipped()` - -Returns true if and only if the current test was skipped. - -##### RecordProperty {#Test::RecordProperty} - -`static void Test::RecordProperty(const std::string& key, const std::string& -value)` \ -`static void Test::RecordProperty(const std::string& key, int value)` - -Logs a property for the current test, test suite, or entire invocation of the -test program. Only the last value for a given key is logged. - -The key must be a valid XML attribute name, and cannot conflict with the ones -already used by GoogleTest (`name`, `file`, `line`, `status`, `time`, -`classname`, `type_param`, and `value_param`). - -`RecordProperty` is `public static` so it can be called from utility functions -that are not members of the test fixture. - -Calls to `RecordProperty` made during the lifespan of the test (from the moment -its constructor starts to the moment its destructor finishes) are output in XML -as attributes of the `` element. Properties recorded from a fixture's -`SetUpTestSuite` or `TearDownTestSuite` methods are logged as attributes of the -corresponding `` element. Calls to `RecordProperty` made in the -global context (before or after invocation of `RUN_ALL_TESTS` or from the -`SetUp`/`TearDown` methods of registered `Environment` objects) are output as -attributes of the `` element. - -#### Protected Methods {#Test-protected} - -##### SetUp {#Test::SetUp} - -`virtual void Test::SetUp()` - -Override this to perform test fixture setup. GoogleTest calls `SetUp()` before -running each individual test. - -##### TearDown {#Test::TearDown} - -`virtual void Test::TearDown()` - -Override this to perform test fixture teardown. GoogleTest calls `TearDown()` -after running each individual test. - -### TestWithParam {#TestWithParam} - -`testing::TestWithParam` - -A convenience class which inherits from both [`Test`](#Test) and -[`WithParamInterface`](#WithParamInterface). - -### TestSuite {#TestSuite} - -Represents a test suite. `TestSuite` is not copyable. - -#### Public Methods {#TestSuite-public} - -##### name {#TestSuite::name} - -`const char* TestSuite::name() const` - -Gets the name of the test suite. - -##### type_param {#TestSuite::type_param} - -`const char* TestSuite::type_param() const` - -Returns the name of the parameter type, or `NULL` if this is not a typed or -type-parameterized test suite. See [Typed Tests](../advanced.md#typed-tests) and -[Type-Parameterized Tests](../advanced.md#type-parameterized-tests). - -##### should_run {#TestSuite::should_run} - -`bool TestSuite::should_run() const` - -Returns true if any test in this test suite should run. - -##### successful_test_count {#TestSuite::successful_test_count} - -`int TestSuite::successful_test_count() const` - -Gets the number of successful tests in this test suite. - -##### skipped_test_count {#TestSuite::skipped_test_count} - -`int TestSuite::skipped_test_count() const` - -Gets the number of skipped tests in this test suite. - -##### failed_test_count {#TestSuite::failed_test_count} - -`int TestSuite::failed_test_count() const` - -Gets the number of failed tests in this test suite. - -##### reportable_disabled_test_count {#TestSuite::reportable_disabled_test_count} - -`int TestSuite::reportable_disabled_test_count() const` - -Gets the number of disabled tests that will be reported in the XML report. - -##### disabled_test_count {#TestSuite::disabled_test_count} - -`int TestSuite::disabled_test_count() const` - -Gets the number of disabled tests in this test suite. - -##### reportable_test_count {#TestSuite::reportable_test_count} - -`int TestSuite::reportable_test_count() const` - -Gets the number of tests to be printed in the XML report. - -##### test_to_run_count {#TestSuite::test_to_run_count} - -`int TestSuite::test_to_run_count() const` - -Get the number of tests in this test suite that should run. - -##### total_test_count {#TestSuite::total_test_count} - -`int TestSuite::total_test_count() const` - -Gets the number of all tests in this test suite. - -##### Passed {#TestSuite::Passed} - -`bool TestSuite::Passed() const` - -Returns true if and only if the test suite passed. - -##### Failed {#TestSuite::Failed} - -`bool TestSuite::Failed() const` - -Returns true if and only if the test suite failed. - -##### elapsed_time {#TestSuite::elapsed_time} - -`TimeInMillis TestSuite::elapsed_time() const` - -Returns the elapsed time, in milliseconds. - -##### start_timestamp {#TestSuite::start_timestamp} - -`TimeInMillis TestSuite::start_timestamp() const` - -Gets the time of the test suite start, in ms from the start of the UNIX epoch. - -##### GetTestInfo {#TestSuite::GetTestInfo} - -`const TestInfo* TestSuite::GetTestInfo(int i) const` - -Returns the [`TestInfo`](#TestInfo) for the `i`-th test among all the tests. `i` -can range from 0 to `total_test_count() - 1`. If `i` is not in that range, -returns `NULL`. - -##### ad_hoc_test_result {#TestSuite::ad_hoc_test_result} - -`const TestResult& TestSuite::ad_hoc_test_result() const` - -Returns the [`TestResult`](#TestResult) that holds test properties recorded -during execution of `SetUpTestSuite` and `TearDownTestSuite`. - -### TestInfo {#TestInfo} - -`testing::TestInfo` - -Stores information about a test. - -#### Public Methods {#TestInfo-public} - -##### test_suite_name {#TestInfo::test_suite_name} - -`const char* TestInfo::test_suite_name() const` - -Returns the test suite name. - -##### name {#TestInfo::name} - -`const char* TestInfo::name() const` - -Returns the test name. - -##### type_param {#TestInfo::type_param} - -`const char* TestInfo::type_param() const` - -Returns the name of the parameter type, or `NULL` if this is not a typed or -type-parameterized test. See [Typed Tests](../advanced.md#typed-tests) and -[Type-Parameterized Tests](../advanced.md#type-parameterized-tests). - -##### value_param {#TestInfo::value_param} - -`const char* TestInfo::value_param() const` - -Returns the text representation of the value parameter, or `NULL` if this is not -a value-parameterized test. See -[Value-Parameterized Tests](../advanced.md#value-parameterized-tests). - -##### file {#TestInfo::file} - -`const char* TestInfo::file() const` - -Returns the file name where this test is defined. - -##### line {#TestInfo::line} - -`int TestInfo::line() const` - -Returns the line where this test is defined. - -##### is_in_another_shard {#TestInfo::is_in_another_shard} - -`bool TestInfo::is_in_another_shard() const` - -Returns true if this test should not be run because it's in another shard. - -##### should_run {#TestInfo::should_run} - -`bool TestInfo::should_run() const` - -Returns true if this test should run, that is if the test is not disabled (or it -is disabled but the `also_run_disabled_tests` flag has been specified) and its -full name matches the user-specified filter. - -GoogleTest allows the user to filter the tests by their full names. Only the -tests that match the filter will run. See -[Running a Subset of the Tests](../advanced.md#running-a-subset-of-the-tests) -for more information. - -##### is_reportable {#TestInfo::is_reportable} - -`bool TestInfo::is_reportable() const` - -Returns true if and only if this test will appear in the XML report. - -##### result {#TestInfo::result} - -`const TestResult* TestInfo::result() const` - -Returns the result of the test. See [`TestResult`](#TestResult). - -### TestParamInfo {#TestParamInfo} - -`testing::TestParamInfo` - -Describes a parameter to a value-parameterized test. The type `T` is the type of -the parameter. - -Contains the fields `param` and `index` which hold the value of the parameter -and its integer index respectively. - -### UnitTest {#UnitTest} - -`testing::UnitTest` - -This class contains information about the test program. - -`UnitTest` is a singleton class. The only instance is created when -`UnitTest::GetInstance()` is first called. This instance is never deleted. - -`UnitTest` is not copyable. - -#### Public Methods {#UnitTest-public} - -##### GetInstance {#UnitTest::GetInstance} - -`static UnitTest* UnitTest::GetInstance()` - -Gets the singleton `UnitTest` object. The first time this method is called, a -`UnitTest` object is constructed and returned. Consecutive calls will return the -same object. - -##### original_working_dir {#UnitTest::original_working_dir} - -`const char* UnitTest::original_working_dir() const` - -Returns the working directory when the first [`TEST()`](#TEST) or -[`TEST_F()`](#TEST_F) was executed. The `UnitTest` object owns the string. - -##### current_test_suite {#UnitTest::current_test_suite} - -`const TestSuite* UnitTest::current_test_suite() const` - -Returns the [`TestSuite`](#TestSuite) object for the test that's currently -running, or `NULL` if no test is running. - -##### current_test_info {#UnitTest::current_test_info} - -`const TestInfo* UnitTest::current_test_info() const` - -Returns the [`TestInfo`](#TestInfo) object for the test that's currently -running, or `NULL` if no test is running. - -##### random_seed {#UnitTest::random_seed} - -`int UnitTest::random_seed() const` - -Returns the random seed used at the start of the current test run. - -##### successful_test_suite_count {#UnitTest::successful_test_suite_count} - -`int UnitTest::successful_test_suite_count() const` - -Gets the number of successful test suites. - -##### failed_test_suite_count {#UnitTest::failed_test_suite_count} - -`int UnitTest::failed_test_suite_count() const` - -Gets the number of failed test suites. - -##### total_test_suite_count {#UnitTest::total_test_suite_count} - -`int UnitTest::total_test_suite_count() const` - -Gets the number of all test suites. - -##### test_suite_to_run_count {#UnitTest::test_suite_to_run_count} - -`int UnitTest::test_suite_to_run_count() const` - -Gets the number of all test suites that contain at least one test that should -run. - -##### successful_test_count {#UnitTest::successful_test_count} - -`int UnitTest::successful_test_count() const` - -Gets the number of successful tests. - -##### skipped_test_count {#UnitTest::skipped_test_count} - -`int UnitTest::skipped_test_count() const` - -Gets the number of skipped tests. - -##### failed_test_count {#UnitTest::failed_test_count} - -`int UnitTest::failed_test_count() const` - -Gets the number of failed tests. - -##### reportable_disabled_test_count {#UnitTest::reportable_disabled_test_count} - -`int UnitTest::reportable_disabled_test_count() const` - -Gets the number of disabled tests that will be reported in the XML report. - -##### disabled_test_count {#UnitTest::disabled_test_count} - -`int UnitTest::disabled_test_count() const` - -Gets the number of disabled tests. - -##### reportable_test_count {#UnitTest::reportable_test_count} - -`int UnitTest::reportable_test_count() const` - -Gets the number of tests to be printed in the XML report. - -##### total_test_count {#UnitTest::total_test_count} - -`int UnitTest::total_test_count() const` - -Gets the number of all tests. - -##### test_to_run_count {#UnitTest::test_to_run_count} - -`int UnitTest::test_to_run_count() const` - -Gets the number of tests that should run. - -##### start_timestamp {#UnitTest::start_timestamp} - -`TimeInMillis UnitTest::start_timestamp() const` - -Gets the time of the test program start, in ms from the start of the UNIX epoch. - -##### elapsed_time {#UnitTest::elapsed_time} - -`TimeInMillis UnitTest::elapsed_time() const` - -Gets the elapsed time, in milliseconds. - -##### Passed {#UnitTest::Passed} - -`bool UnitTest::Passed() const` - -Returns true if and only if the unit test passed (i.e. all test suites passed). - -##### Failed {#UnitTest::Failed} - -`bool UnitTest::Failed() const` - -Returns true if and only if the unit test failed (i.e. some test suite failed or -something outside of all tests failed). - -##### GetTestSuite {#UnitTest::GetTestSuite} - -`const TestSuite* UnitTest::GetTestSuite(int i) const` - -Gets the [`TestSuite`](#TestSuite) object for the `i`-th test suite among all -the test suites. `i` can range from 0 to `total_test_suite_count() - 1`. If `i` -is not in that range, returns `NULL`. - -##### ad_hoc_test_result {#UnitTest::ad_hoc_test_result} - -`const TestResult& UnitTest::ad_hoc_test_result() const` - -Returns the [`TestResult`](#TestResult) containing information on test failures -and properties logged outside of individual test suites. - -##### listeners {#UnitTest::listeners} - -`TestEventListeners& UnitTest::listeners()` - -Returns the list of event listeners that can be used to track events inside -GoogleTest. See [`TestEventListeners`](#TestEventListeners). - -### TestEventListener {#TestEventListener} - -`testing::TestEventListener` - -The interface for tracing execution of tests. The methods below are listed in -the order the corresponding events are fired. - -#### Public Methods {#TestEventListener-public} - -##### OnTestProgramStart {#TestEventListener::OnTestProgramStart} - -`virtual void TestEventListener::OnTestProgramStart(const UnitTest& unit_test)` - -Fired before any test activity starts. - -##### OnTestIterationStart {#TestEventListener::OnTestIterationStart} - -`virtual void TestEventListener::OnTestIterationStart(const UnitTest& unit_test, -int iteration)` - -Fired before each iteration of tests starts. There may be more than one -iteration if `GTEST_FLAG(repeat)` is set. `iteration` is the iteration index, -starting from 0. - -##### OnEnvironmentsSetUpStart {#TestEventListener::OnEnvironmentsSetUpStart} - -`virtual void TestEventListener::OnEnvironmentsSetUpStart(const UnitTest& -unit_test)` - -Fired before environment set-up for each iteration of tests starts. - -##### OnEnvironmentsSetUpEnd {#TestEventListener::OnEnvironmentsSetUpEnd} - -`virtual void TestEventListener::OnEnvironmentsSetUpEnd(const UnitTest& -unit_test)` - -Fired after environment set-up for each iteration of tests ends. - -##### OnTestSuiteStart {#TestEventListener::OnTestSuiteStart} - -`virtual void TestEventListener::OnTestSuiteStart(const TestSuite& test_suite)` - -Fired before the test suite starts. - -##### OnTestStart {#TestEventListener::OnTestStart} - -`virtual void TestEventListener::OnTestStart(const TestInfo& test_info)` - -Fired before the test starts. - -##### OnTestPartResult {#TestEventListener::OnTestPartResult} - -`virtual void TestEventListener::OnTestPartResult(const TestPartResult& -test_part_result)` - -Fired after a failed assertion or a `SUCCEED()` invocation. If you want to throw -an exception from this function to skip to the next test, it must be an -[`AssertionException`](#AssertionException) or inherited from it. - -##### OnTestEnd {#TestEventListener::OnTestEnd} - -`virtual void TestEventListener::OnTestEnd(const TestInfo& test_info)` - -Fired after the test ends. - -##### OnTestSuiteEnd {#TestEventListener::OnTestSuiteEnd} - -`virtual void TestEventListener::OnTestSuiteEnd(const TestSuite& test_suite)` - -Fired after the test suite ends. - -##### OnEnvironmentsTearDownStart {#TestEventListener::OnEnvironmentsTearDownStart} - -`virtual void TestEventListener::OnEnvironmentsTearDownStart(const UnitTest& -unit_test)` - -Fired before environment tear-down for each iteration of tests starts. - -##### OnEnvironmentsTearDownEnd {#TestEventListener::OnEnvironmentsTearDownEnd} - -`virtual void TestEventListener::OnEnvironmentsTearDownEnd(const UnitTest& -unit_test)` - -Fired after environment tear-down for each iteration of tests ends. - -##### OnTestIterationEnd {#TestEventListener::OnTestIterationEnd} - -`virtual void TestEventListener::OnTestIterationEnd(const UnitTest& unit_test, -int iteration)` - -Fired after each iteration of tests finishes. - -##### OnTestProgramEnd {#TestEventListener::OnTestProgramEnd} - -`virtual void TestEventListener::OnTestProgramEnd(const UnitTest& unit_test)` - -Fired after all test activities have ended. - -### TestEventListeners {#TestEventListeners} - -`testing::TestEventListeners` - -Lets users add listeners to track events in GoogleTest. - -#### Public Methods {#TestEventListeners-public} - -##### Append {#TestEventListeners::Append} - -`void TestEventListeners::Append(TestEventListener* listener)` - -Appends an event listener to the end of the list. GoogleTest assumes ownership -of the listener (i.e. it will delete the listener when the test program -finishes). - -##### Release {#TestEventListeners::Release} - -`TestEventListener* TestEventListeners::Release(TestEventListener* listener)` - -Removes the given event listener from the list and returns it. It then becomes -the caller's responsibility to delete the listener. Returns `NULL` if the -listener is not found in the list. - -##### default_result_printer {#TestEventListeners::default_result_printer} - -`TestEventListener* TestEventListeners::default_result_printer() const` - -Returns the standard listener responsible for the default console output. Can be -removed from the listeners list to shut down default console output. Note that -removing this object from the listener list with -[`Release()`](#TestEventListeners::Release) transfers its ownership to the -caller and makes this function return `NULL` the next time. - -##### default_xml_generator {#TestEventListeners::default_xml_generator} - -`TestEventListener* TestEventListeners::default_xml_generator() const` - -Returns the standard listener responsible for the default XML output controlled -by the `--gtest_output=xml` flag. Can be removed from the listeners list by -users who want to shut down the default XML output controlled by this flag and -substitute it with custom one. Note that removing this object from the listener -list with [`Release()`](#TestEventListeners::Release) transfers its ownership to -the caller and makes this function return `NULL` the next time. - -### TestPartResult {#TestPartResult} - -`testing::TestPartResult` - -A copyable object representing the result of a test part (i.e. an assertion or -an explicit `FAIL()`, `ADD_FAILURE()`, or `SUCCESS()`). - -#### Public Methods {#TestPartResult-public} - -##### type {#TestPartResult::type} - -`Type TestPartResult::type() const` - -Gets the outcome of the test part. - -The return type `Type` is an enum defined as follows: - -```cpp -enum Type { - kSuccess, // Succeeded. - kNonFatalFailure, // Failed but the test can continue. - kFatalFailure, // Failed and the test should be terminated. - kSkip // Skipped. -}; -``` - -##### file_name {#TestPartResult::file_name} - -`const char* TestPartResult::file_name() const` - -Gets the name of the source file where the test part took place, or `NULL` if -it's unknown. - -##### line_number {#TestPartResult::line_number} - -`int TestPartResult::line_number() const` - -Gets the line in the source file where the test part took place, or `-1` if it's -unknown. - -##### summary {#TestPartResult::summary} - -`const char* TestPartResult::summary() const` - -Gets the summary of the failure message. - -##### message {#TestPartResult::message} - -`const char* TestPartResult::message() const` - -Gets the message associated with the test part. - -##### skipped {#TestPartResult::skipped} - -`bool TestPartResult::skipped() const` - -Returns true if and only if the test part was skipped. - -##### passed {#TestPartResult::passed} - -`bool TestPartResult::passed() const` - -Returns true if and only if the test part passed. - -##### nonfatally_failed {#TestPartResult::nonfatally_failed} - -`bool TestPartResult::nonfatally_failed() const` - -Returns true if and only if the test part non-fatally failed. - -##### fatally_failed {#TestPartResult::fatally_failed} - -`bool TestPartResult::fatally_failed() const` - -Returns true if and only if the test part fatally failed. - -##### failed {#TestPartResult::failed} - -`bool TestPartResult::failed() const` - -Returns true if and only if the test part failed. - -### TestProperty {#TestProperty} - -`testing::TestProperty` - -A copyable object representing a user-specified test property which can be -output as a key/value string pair. - -#### Public Methods {#TestProperty-public} - -##### key {#key} - -`const char* key() const` - -Gets the user-supplied key. - -##### value {#value} - -`const char* value() const` - -Gets the user-supplied value. - -##### SetValue {#SetValue} - -`void SetValue(const std::string& new_value)` - -Sets a new value, overriding the previous one. - -### TestResult {#TestResult} - -`testing::TestResult` - -Contains information about the result of a single test. - -`TestResult` is not copyable. - -#### Public Methods {#TestResult-public} - -##### total_part_count {#TestResult::total_part_count} - -`int TestResult::total_part_count() const` - -Gets the number of all test parts. This is the sum of the number of successful -test parts and the number of failed test parts. - -##### test_property_count {#TestResult::test_property_count} - -`int TestResult::test_property_count() const` - -Returns the number of test properties. - -##### Passed {#TestResult::Passed} - -`bool TestResult::Passed() const` - -Returns true if and only if the test passed (i.e. no test part failed). - -##### Skipped {#TestResult::Skipped} - -`bool TestResult::Skipped() const` - -Returns true if and only if the test was skipped. - -##### Failed {#TestResult::Failed} - -`bool TestResult::Failed() const` - -Returns true if and only if the test failed. - -##### HasFatalFailure {#TestResult::HasFatalFailure} - -`bool TestResult::HasFatalFailure() const` - -Returns true if and only if the test fatally failed. - -##### HasNonfatalFailure {#TestResult::HasNonfatalFailure} - -`bool TestResult::HasNonfatalFailure() const` - -Returns true if and only if the test has a non-fatal failure. - -##### elapsed_time {#TestResult::elapsed_time} - -`TimeInMillis TestResult::elapsed_time() const` - -Returns the elapsed time, in milliseconds. - -##### start_timestamp {#TestResult::start_timestamp} - -`TimeInMillis TestResult::start_timestamp() const` - -Gets the time of the test case start, in ms from the start of the UNIX epoch. - -##### GetTestPartResult {#TestResult::GetTestPartResult} - -`const TestPartResult& TestResult::GetTestPartResult(int i) const` - -Returns the [`TestPartResult`](#TestPartResult) for the `i`-th test part result -among all the results. `i` can range from 0 to `total_part_count() - 1`. If `i` -is not in that range, aborts the program. - -##### GetTestProperty {#TestResult::GetTestProperty} - -`const TestProperty& TestResult::GetTestProperty(int i) const` - -Returns the [`TestProperty`](#TestProperty) object for the `i`-th test property. -`i` can range from 0 to `test_property_count() - 1`. If `i` is not in that -range, aborts the program. - -### TimeInMillis {#TimeInMillis} - -`testing::TimeInMillis` - -An integer type representing time in milliseconds. - -### Types {#Types} - -`testing::Types` - -Represents a list of types for use in typed tests and type-parameterized tests. - -The template argument `T...` can be any number of types, for example: - -``` -testing::Types -``` - -See [Typed Tests](../advanced.md#typed-tests) and -[Type-Parameterized Tests](../advanced.md#type-parameterized-tests) for more -information. - -### WithParamInterface {#WithParamInterface} - -`testing::WithParamInterface` - -The pure interface class that all value-parameterized tests inherit from. - -A value-parameterized test fixture class must inherit from both [`Test`](#Test) -and `WithParamInterface`. In most cases that just means inheriting from -[`TestWithParam`](#TestWithParam), but more complicated test hierarchies may -need to inherit from `Test` and `WithParamInterface` at different levels. - -This interface defines the type alias `ParamType` for the parameter type `T` and -has support for accessing the test parameter value via the `GetParam()` method: - -``` -static const ParamType& GetParam() -``` - -For more information, see -[Value-Parameterized Tests](../advanced.md#value-parameterized-tests). - -## Functions - -GoogleTest defines the following functions to help with writing and running -tests. - -### InitGoogleTest {#InitGoogleTest} - -`void testing::InitGoogleTest(int* argc, char** argv)` \ -`void testing::InitGoogleTest(int* argc, wchar_t** argv)` \ -`void testing::InitGoogleTest()` - -Initializes GoogleTest. This must be called before calling -[`RUN_ALL_TESTS()`](#RUN_ALL_TESTS). In particular, it parses the command line -for the flags that GoogleTest recognizes. Whenever a GoogleTest flag is seen, it -is removed from `argv`, and `*argc` is decremented. Keep in mind that `argv` -must terminate with a `NULL` pointer (i.e. `argv[argc]` is `NULL`), which is -already the case with the default `argv` passed to `main`. - -No value is returned. Instead, the GoogleTest flag variables are updated. - -The `InitGoogleTest(int* argc, wchar_t** argv)` overload can be used in Windows -programs compiled in `UNICODE` mode. - -The argument-less `InitGoogleTest()` overload can be used on Arduino/embedded -platforms where there is no `argc`/`argv`. - -### AddGlobalTestEnvironment {#AddGlobalTestEnvironment} - -`Environment* testing::AddGlobalTestEnvironment(Environment* env)` - -Adds a test environment to the test program. Must be called before -[`RUN_ALL_TESTS()`](#RUN_ALL_TESTS) is called. See -[Global Set-Up and Tear-Down](../advanced.md#global-set-up-and-tear-down) for -more information. - -See also [`Environment`](#Environment). - -### RegisterTest {#RegisterTest} - -```cpp -template -TestInfo* testing::RegisterTest(const char* test_suite_name, const char* test_name, - const char* type_param, const char* value_param, - const char* file, int line, Factory factory) -``` - -Dynamically registers a test with the framework. - -The `factory` argument is a factory callable (move-constructible) object or -function pointer that creates a new instance of the `Test` object. It handles -ownership to the caller. The signature of the callable is `Fixture*()`, where -`Fixture` is the test fixture class for the test. All tests registered with the -same `test_suite_name` must return the same fixture type. This is checked at -runtime. - -The framework will infer the fixture class from the factory and will call the -`SetUpTestSuite` and `TearDownTestSuite` methods for it. - -Must be called before [`RUN_ALL_TESTS()`](#RUN_ALL_TESTS) is invoked, otherwise -behavior is undefined. - -See -[Registering tests programmatically](../advanced.md#registering-tests-programmatically) -for more information. - -### RUN_ALL_TESTS {#RUN_ALL_TESTS} - -`int RUN_ALL_TESTS()` - -Use this function in `main()` to run all tests. It returns `0` if all tests are -successful, or `1` otherwise. - -`RUN_ALL_TESTS()` should be invoked after the command line has been parsed by -[`InitGoogleTest()`](#InitGoogleTest). - -This function was formerly a macro; thus, it is in the global namespace and has -an all-caps name. - -### AssertionSuccess {#AssertionSuccess} - -`AssertionResult testing::AssertionSuccess()` - -Creates a successful assertion result. See -[`AssertionResult`](#AssertionResult). - -### AssertionFailure {#AssertionFailure} - -`AssertionResult testing::AssertionFailure()` - -Creates a failed assertion result. Use the `<<` operator to store a failure -message: - -```cpp -testing::AssertionFailure() << "My failure message"; -``` - -See [`AssertionResult`](#AssertionResult). - -### StaticAssertTypeEq {#StaticAssertTypeEq} - -`testing::StaticAssertTypeEq()` - -Compile-time assertion for type equality. Compiles if and only if `T1` and `T2` -are the same type. The value it returns is irrelevant. - -See [Type Assertions](../advanced.md#type-assertions) for more information. - -### PrintToString {#PrintToString} - -`std::string testing::PrintToString(x)` - -Prints any value `x` using GoogleTest's value printer. - -See -[Teaching GoogleTest How to Print Your Values](../advanced.md#teaching-googletest-how-to-print-your-values) -for more information. - -### PrintToStringParamName {#PrintToStringParamName} - -`std::string testing::PrintToStringParamName(TestParamInfo& info)` - -A built-in parameterized test name generator which returns the result of -[`PrintToString`](#PrintToString) called on `info.param`. Does not work when the -test parameter is a `std::string` or C string. See -[Specifying Names for Value-Parameterized Test Parameters](../advanced.md#specifying-names-for-value-parameterized-test-parameters) -for more information. - -See also [`TestParamInfo`](#TestParamInfo) and -[`INSTANTIATE_TEST_SUITE_P`](#INSTANTIATE_TEST_SUITE_P). diff --git a/test/unit/googletest/docs/samples.md b/test/unit/googletest/docs/samples.md deleted file mode 100644 index dedc590..0000000 --- a/test/unit/googletest/docs/samples.md +++ /dev/null @@ -1,22 +0,0 @@ -# Googletest Samples - -If you're like us, you'd like to look at -[googletest samples.](https://github.com/google/googletest/blob/main/googletest/samples) -The sample directory has a number of well-commented samples showing how to use a -variety of googletest features. - -* Sample #1 shows the basic steps of using googletest to test C++ functions. -* Sample #2 shows a more complex unit test for a class with multiple member - functions. -* Sample #3 uses a test fixture. -* Sample #4 teaches you how to use googletest and `googletest.h` together to - get the best of both libraries. -* Sample #5 puts shared testing logic in a base test fixture, and reuses it in - derived fixtures. -* Sample #6 demonstrates type-parameterized tests. -* Sample #7 teaches the basics of value-parameterized tests. -* Sample #8 shows using `Combine()` in value-parameterized tests. -* Sample #9 shows use of the listener API to modify Google Test's console - output and the use of its reflection API to inspect test results. -* Sample #10 shows use of the listener API to implement a primitive memory - leak checker. diff --git a/test/unit/googletest/fake_fuchsia_sdk.bzl b/test/unit/googletest/fake_fuchsia_sdk.bzl deleted file mode 100644 index 2024dc6..0000000 --- a/test/unit/googletest/fake_fuchsia_sdk.bzl +++ /dev/null @@ -1,33 +0,0 @@ -"""Provides a fake @fuchsia_sdk implementation that's used when the real one isn't available. - -This is needed since bazel queries on targets that depend on //:gtest (eg: -`bazel query "deps(set(//googletest/test:gtest_all_test))"`) will fail if @fuchsia_sdk is not -defined when bazel is evaluating the transitive closure of the query target. - -See https://github.com/google/googletest/issues/4472. -""" - -def _fake_fuchsia_sdk_impl(repo_ctx): - for stub_target in repo_ctx.attr._stub_build_targets: - stub_package = stub_target - stub_target_name = stub_target.split("/")[-1] - repo_ctx.file("%s/BUILD.bazel" % stub_package, """ -filegroup( - name = "%s", -) -""" % stub_target_name) - -fake_fuchsia_sdk = repository_rule( - doc = "Used to create a fake @fuchsia_sdk repository with stub build targets.", - implementation = _fake_fuchsia_sdk_impl, - attrs = { - "_stub_build_targets": attr.string_list( - doc = "The stub build targets to initialize.", - default = [ - "pkg/fdio", - "pkg/syslog", - "pkg/zx", - ], - ), - }, -) diff --git a/test/unit/googletest/googlemock/CMakeLists.txt b/test/unit/googletest/googlemock/CMakeLists.txt deleted file mode 100644 index 99b2411..0000000 --- a/test/unit/googletest/googlemock/CMakeLists.txt +++ /dev/null @@ -1,210 +0,0 @@ -######################################################################## -# Note: CMake support is community-based. The maintainers do not use CMake -# internally. -# -# CMake build script for Google Mock. -# -# To run the tests for Google Mock itself on Linux, use 'make test' or -# ctest. You can select which tests to run using 'ctest -R regex'. -# For more options, run 'ctest --help'. - -option(gmock_build_tests "Build all of Google Mock's own tests." OFF) - -# A directory to find Google Test sources. -if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/gtest/CMakeLists.txt") - set(gtest_dir gtest) -else() - set(gtest_dir ../googletest) -endif() - -# Defines pre_project_set_up_hermetic_build() and set_up_hermetic_build(). -include("${gtest_dir}/cmake/hermetic_build.cmake" OPTIONAL) - -if (COMMAND pre_project_set_up_hermetic_build) - # Google Test also calls hermetic setup functions from add_subdirectory, - # although its changes will not affect things at the current scope. - pre_project_set_up_hermetic_build() -endif() - -######################################################################## -# -# Project-wide settings - -# Name of the project. -# -# CMake files in this project can refer to the root source directory -# as ${gmock_SOURCE_DIR} and to the root binary directory as -# ${gmock_BINARY_DIR}. -# Language "C" is required for find_package(Threads). -cmake_minimum_required(VERSION 3.13) -project(gmock VERSION ${GOOGLETEST_VERSION} LANGUAGES CXX C) - -if (COMMAND set_up_hermetic_build) - set_up_hermetic_build() -endif() - -# Instructs CMake to process Google Test's CMakeLists.txt and add its -# targets to the current scope. We are placing Google Test's binary -# directory in a subdirectory of our own as VC compilation may break -# if they are the same (the default). -add_subdirectory("${gtest_dir}" "${gmock_BINARY_DIR}/${gtest_dir}") - - -# These commands only run if this is the main project -if(CMAKE_PROJECT_NAME STREQUAL "gmock" OR CMAKE_PROJECT_NAME STREQUAL "googletest-distribution") - # BUILD_SHARED_LIBS is a standard CMake variable, but we declare it here to - # make it prominent in the GUI. - option(BUILD_SHARED_LIBS "Build shared libraries (DLLs)." OFF) -else() - mark_as_advanced(gmock_build_tests) -endif() - -# Although Google Test's CMakeLists.txt calls this function, the -# changes there don't affect the current scope. Therefore we have to -# call it again here. -config_compiler_and_linker() # from ${gtest_dir}/cmake/internal_utils.cmake - -# Adds Google Mock's and Google Test's header directories to the search path. -# Get Google Test's include dirs from the target, gtest_SOURCE_DIR is broken -# when using fetch-content with the name "GTest". -get_target_property(gtest_include_dirs gtest INCLUDE_DIRECTORIES) -set(gmock_build_include_dirs - "${gmock_SOURCE_DIR}/include" - "${gmock_SOURCE_DIR}" - "${gtest_include_dirs}") -include_directories(${gmock_build_include_dirs}) - -######################################################################## -# -# Defines the gmock & gmock_main libraries. User tests should link -# with one of them. - -# Google Mock libraries. We build them using more strict warnings than what -# are used for other targets, to ensure that Google Mock can be compiled by -# a user aggressive about warnings. -if (MSVC) - cxx_library(gmock - "${cxx_strict}" - "${gtest_dir}/src/gtest-all.cc" - src/gmock-all.cc) - - cxx_library(gmock_main - "${cxx_strict}" - "${gtest_dir}/src/gtest-all.cc" - src/gmock-all.cc - src/gmock_main.cc) -else() - cxx_library(gmock "${cxx_strict}" src/gmock-all.cc) - target_link_libraries(gmock PUBLIC gtest) - set_target_properties(gmock PROPERTIES VERSION ${GOOGLETEST_VERSION}) - cxx_library(gmock_main "${cxx_strict}" src/gmock_main.cc) - target_link_libraries(gmock_main PUBLIC gmock) - set_target_properties(gmock_main PROPERTIES VERSION ${GOOGLETEST_VERSION}) -endif() - -string(REPLACE ";" "$" dirs "${gmock_build_include_dirs}") -target_include_directories(gmock SYSTEM INTERFACE - "$" - "$/${CMAKE_INSTALL_INCLUDEDIR}>") -target_include_directories(gmock_main SYSTEM INTERFACE - "$" - "$/${CMAKE_INSTALL_INCLUDEDIR}>") - -######################################################################## -# -# Install rules. -install_project(gmock gmock_main) - -######################################################################## -# -# Google Mock's own tests. -# -# You can skip this section if you aren't interested in testing -# Google Mock itself. -# -# The tests are not built by default. To build them, set the -# gmock_build_tests option to ON. You can do it by running ccmake -# or specifying the -Dgmock_build_tests=ON flag when running cmake. - -if (gmock_build_tests) - # This must be set in the root directory for the tests to be run by - # 'make test' or ctest. - enable_testing() - - if (MINGW OR CYGWIN) - add_compile_options("-Wa,-mbig-obj") - endif() - - ############################################################ - # C++ tests built with standard compiler flags. - - cxx_test(gmock-actions_test gmock_main) - cxx_test(gmock-cardinalities_test gmock_main) - cxx_test(gmock_ex_test gmock_main) - cxx_test(gmock-function-mocker_test gmock_main) - cxx_test(gmock-internal-utils_test gmock_main) - cxx_test(gmock-matchers-arithmetic_test gmock_main) - cxx_test(gmock-matchers-comparisons_test gmock_main) - cxx_test(gmock-matchers-containers_test gmock_main) - cxx_test(gmock-matchers-misc_test gmock_main) - cxx_test(gmock-more-actions_test gmock_main) - cxx_test(gmock-nice-strict_test gmock_main) - cxx_test(gmock-port_test gmock_main) - cxx_test(gmock-spec-builders_test gmock_main) - cxx_test(gmock_link_test gmock_main test/gmock_link2_test.cc) - cxx_test(gmock_test gmock_main) - - if (DEFINED GTEST_HAS_PTHREAD) - cxx_test(gmock_stress_test gmock) - endif() - - # gmock_all_test is commented to save time building and running tests. - # Uncomment if necessary. - # cxx_test(gmock_all_test gmock_main) - - ############################################################ - # C++ tests built with non-standard compiler flags. - - if (MSVC) - cxx_library(gmock_main_no_exception "${cxx_no_exception}" - "${gtest_dir}/src/gtest-all.cc" src/gmock-all.cc src/gmock_main.cc) - - cxx_library(gmock_main_no_rtti "${cxx_no_rtti}" - "${gtest_dir}/src/gtest-all.cc" src/gmock-all.cc src/gmock_main.cc) - - else() - cxx_library(gmock_main_no_exception "${cxx_no_exception}" src/gmock_main.cc) - target_link_libraries(gmock_main_no_exception PUBLIC gmock) - - cxx_library(gmock_main_no_rtti "${cxx_no_rtti}" src/gmock_main.cc) - target_link_libraries(gmock_main_no_rtti PUBLIC gmock) - endif() - cxx_test_with_flags(gmock-more-actions_no_exception_test "${cxx_no_exception}" - gmock_main_no_exception test/gmock-more-actions_test.cc) - - cxx_test_with_flags(gmock_no_rtti_test "${cxx_no_rtti}" - gmock_main_no_rtti test/gmock-spec-builders_test.cc) - - cxx_shared_library(shared_gmock_main "${cxx_default}" - "${gtest_dir}/src/gtest-all.cc" src/gmock-all.cc src/gmock_main.cc) - - # Tests that a binary can be built with Google Mock as a shared library. On - # some system configurations, it may not possible to run the binary without - # knowing more details about the system configurations. We do not try to run - # this binary. To get a more robust shared library coverage, configure with - # -DBUILD_SHARED_LIBS=ON. - cxx_executable_with_flags(shared_gmock_test_ "${cxx_default}" - shared_gmock_main test/gmock-spec-builders_test.cc) - set_target_properties(shared_gmock_test_ - PROPERTIES - COMPILE_DEFINITIONS "GTEST_LINKED_AS_SHARED_LIBRARY=1") - - ############################################################ - # Python tests. - - cxx_executable(gmock_leak_test_ test gmock_main) - py_test(gmock_leak_test) - - cxx_executable(gmock_output_test_ test gmock) - py_test(gmock_output_test) -endif() diff --git a/test/unit/googletest/googlemock/README.md b/test/unit/googletest/googlemock/README.md deleted file mode 100644 index e1103b1..0000000 --- a/test/unit/googletest/googlemock/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# Googletest Mocking (gMock) Framework - -### Overview - -Google's framework for writing and using C++ mock classes. It can help you -derive better designs of your system and write better tests. - -It is inspired by: - -* [jMock](http://www.jmock.org/) -* [EasyMock](https://easymock.org/) -* [Hamcrest](https://code.google.com/p/hamcrest/) - -It is designed with C++'s specifics in mind. - -gMock: - -- Provides a declarative syntax for defining mocks. -- Can define partial (hybrid) mocks, which are a cross of real and mock - objects. -- Handles functions of arbitrary types and overloaded functions. -- Comes with a rich set of matchers for validating function arguments. -- Uses an intuitive syntax for controlling the behavior of a mock. -- Does automatic verification of expectations (no record-and-replay needed). -- Allows arbitrary (partial) ordering constraints on function calls to be - expressed. -- Lets a user extend it by defining new matchers and actions. -- Does not use exceptions. -- Is easy to learn and use. - -Details and examples can be found here: - -* [gMock for Dummies](https://google.github.io/googletest/gmock_for_dummies.html) -* [Legacy gMock FAQ](https://google.github.io/googletest/gmock_faq.html) -* [gMock Cookbook](https://google.github.io/googletest/gmock_cook_book.html) -* [gMock Cheat Sheet](https://google.github.io/googletest/gmock_cheat_sheet.html) - -GoogleMock is a part of -[GoogleTest C++ testing framework](https://github.com/google/googletest/) and a -subject to the same requirements. diff --git a/test/unit/googletest/googlemock/cmake/gmock.pc.in b/test/unit/googletest/googlemock/cmake/gmock.pc.in deleted file mode 100644 index 23c67b5..0000000 --- a/test/unit/googletest/googlemock/cmake/gmock.pc.in +++ /dev/null @@ -1,10 +0,0 @@ -libdir=@CMAKE_INSTALL_FULL_LIBDIR@ -includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ - -Name: gmock -Description: GoogleMock (without main() function) -Version: @PROJECT_VERSION@ -URL: https://github.com/google/googletest -Requires: gtest = @PROJECT_VERSION@ -Libs: -L${libdir} -lgmock @CMAKE_THREAD_LIBS_INIT@ -Cflags: -I${includedir} @GTEST_HAS_PTHREAD_MACRO@ diff --git a/test/unit/googletest/googlemock/cmake/gmock_main.pc.in b/test/unit/googletest/googlemock/cmake/gmock_main.pc.in deleted file mode 100644 index 66ffea7..0000000 --- a/test/unit/googletest/googlemock/cmake/gmock_main.pc.in +++ /dev/null @@ -1,10 +0,0 @@ -libdir=@CMAKE_INSTALL_FULL_LIBDIR@ -includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ - -Name: gmock_main -Description: GoogleMock (with main() function) -Version: @PROJECT_VERSION@ -URL: https://github.com/google/googletest -Requires: gmock = @PROJECT_VERSION@ -Libs: -L${libdir} -lgmock_main @CMAKE_THREAD_LIBS_INIT@ -Cflags: -I${includedir} @GTEST_HAS_PTHREAD_MACRO@ diff --git a/test/unit/googletest/googlemock/docs/README.md b/test/unit/googletest/googlemock/docs/README.md deleted file mode 100644 index 1bc57b7..0000000 --- a/test/unit/googletest/googlemock/docs/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Content Moved - -We are working on updates to the GoogleTest documentation, which has moved to -the top-level [docs](../../docs) directory. diff --git a/test/unit/googletest/googlemock/include/gmock/gmock-actions.h b/test/unit/googletest/googlemock/include/gmock/gmock-actions.h deleted file mode 100644 index aa47079..0000000 --- a/test/unit/googletest/googlemock/include/gmock/gmock-actions.h +++ /dev/null @@ -1,2360 +0,0 @@ -// Copyright 2007, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Google Mock - a framework for writing C++ mock classes. -// -// The ACTION* family of macros can be used in a namespace scope to -// define custom actions easily. The syntax: -// -// ACTION(name) { statements; } -// -// will define an action with the given name that executes the -// statements. The value returned by the statements will be used as -// the return value of the action. Inside the statements, you can -// refer to the K-th (0-based) argument of the mock function by -// 'argK', and refer to its type by 'argK_type'. For example: -// -// ACTION(IncrementArg1) { -// arg1_type temp = arg1; -// return ++(*temp); -// } -// -// allows you to write -// -// ...WillOnce(IncrementArg1()); -// -// You can also refer to the entire argument tuple and its type by -// 'args' and 'args_type', and refer to the mock function type and its -// return type by 'function_type' and 'return_type'. -// -// Note that you don't need to specify the types of the mock function -// arguments. However rest assured that your code is still type-safe: -// you'll get a compiler error if *arg1 doesn't support the ++ -// operator, or if the type of ++(*arg1) isn't compatible with the -// mock function's return type, for example. -// -// Sometimes you'll want to parameterize the action. For that you can use -// another macro: -// -// ACTION_P(name, param_name) { statements; } -// -// For example: -// -// ACTION_P(Add, n) { return arg0 + n; } -// -// will allow you to write: -// -// ...WillOnce(Add(5)); -// -// Note that you don't need to provide the type of the parameter -// either. If you need to reference the type of a parameter named -// 'foo', you can write 'foo_type'. For example, in the body of -// ACTION_P(Add, n) above, you can write 'n_type' to refer to the type -// of 'n'. -// -// We also provide ACTION_P2, ACTION_P3, ..., up to ACTION_P10 to support -// multi-parameter actions. -// -// For the purpose of typing, you can view -// -// ACTION_Pk(Foo, p1, ..., pk) { ... } -// -// as shorthand for -// -// template -// FooActionPk Foo(p1_type p1, ..., pk_type pk) { ... } -// -// In particular, you can provide the template type arguments -// explicitly when invoking Foo(), as in Foo(5, false); -// although usually you can rely on the compiler to infer the types -// for you automatically. You can assign the result of expression -// Foo(p1, ..., pk) to a variable of type FooActionPk. This can be useful when composing actions. -// -// You can also overload actions with different numbers of parameters: -// -// ACTION_P(Plus, a) { ... } -// ACTION_P2(Plus, a, b) { ... } -// -// While it's tempting to always use the ACTION* macros when defining -// a new action, you should also consider implementing ActionInterface -// or using MakePolymorphicAction() instead, especially if you need to -// use the action a lot. While these approaches require more work, -// they give you more control on the types of the mock function -// arguments and the action parameters, which in general leads to -// better compiler error messages that pay off in the long run. They -// also allow overloading actions based on parameter types (as opposed -// to just based on the number of parameters). -// -// CAVEAT: -// -// ACTION*() can only be used in a namespace scope as templates cannot be -// declared inside of a local class. -// Users can, however, define any local functors (e.g. a lambda) that -// can be used as actions. -// -// MORE INFORMATION: -// -// To learn more about using these macros, please search for 'ACTION' on -// https://github.com/google/googletest/blob/main/docs/gmock_cook_book.md - -// IWYU pragma: private, include "gmock/gmock.h" -// IWYU pragma: friend gmock/.* - -#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_ -#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_ - -#ifndef _WIN32_WCE -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gmock/internal/gmock-internal-utils.h" -#include "gmock/internal/gmock-port.h" -#include "gmock/internal/gmock-pp.h" - -GTEST_DISABLE_MSC_WARNINGS_PUSH_(4100) - -namespace testing { - -// To implement an action Foo, define: -// 1. a class FooAction that implements the ActionInterface interface, and -// 2. a factory function that creates an Action object from a -// const FooAction*. -// -// The two-level delegation design follows that of Matcher, providing -// consistency for extension developers. It also eases ownership -// management as Action objects can now be copied like plain values. - -namespace internal { - -// BuiltInDefaultValueGetter::Get() returns a -// default-constructed T value. BuiltInDefaultValueGetter::Get() crashes with an error. -// -// This primary template is used when kDefaultConstructible is true. -template -struct BuiltInDefaultValueGetter { - static T Get() { return T(); } -}; -template -struct BuiltInDefaultValueGetter { - static T Get() { - Assert(false, __FILE__, __LINE__, - "Default action undefined for the function return type."); -#if defined(__GNUC__) || defined(__clang__) - __builtin_unreachable(); -#elif defined(_MSC_VER) - __assume(0); -#else - return Invalid(); - // The above statement will never be reached, but is required in - // order for this function to compile. -#endif - } -}; - -// BuiltInDefaultValue::Get() returns the "built-in" default value -// for type T, which is NULL when T is a raw pointer type, 0 when T is -// a numeric type, false when T is bool, or "" when T is string or -// std::string. In addition, in C++11 and above, it turns a -// default-constructed T value if T is default constructible. For any -// other type T, the built-in default T value is undefined, and the -// function will abort the process. -template -class BuiltInDefaultValue { - public: - // This function returns true if and only if type T has a built-in default - // value. - static bool Exists() { return ::std::is_default_constructible::value; } - - static T Get() { - return BuiltInDefaultValueGetter< - T, ::std::is_default_constructible::value>::Get(); - } -}; - -// This partial specialization says that we use the same built-in -// default value for T and const T. -template -class BuiltInDefaultValue { - public: - static bool Exists() { return BuiltInDefaultValue::Exists(); } - static T Get() { return BuiltInDefaultValue::Get(); } -}; - -// This partial specialization defines the default values for pointer -// types. -template -class BuiltInDefaultValue { - public: - static bool Exists() { return true; } - static T* Get() { return nullptr; } -}; - -// The following specializations define the default values for -// specific types we care about. -#define GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(type, value) \ - template <> \ - class BuiltInDefaultValue { \ - public: \ - static bool Exists() { return true; } \ - static type Get() { return value; } \ - } - -GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(void, ); // NOLINT -GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(::std::string, ""); -GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(bool, false); -GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned char, '\0'); -GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed char, '\0'); -GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(char, '\0'); - -// There's no need for a default action for signed wchar_t, as that -// type is the same as wchar_t for gcc, and invalid for MSVC. -// -// There's also no need for a default action for unsigned wchar_t, as -// that type is the same as unsigned int for gcc, and invalid for -// MSVC. -#if GMOCK_WCHAR_T_IS_NATIVE_ -GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(wchar_t, 0U); // NOLINT -#endif - -GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned short, 0U); // NOLINT -GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed short, 0); // NOLINT -GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned int, 0U); -GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed int, 0); -GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned long, 0UL); // NOLINT -GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed long, 0L); // NOLINT -GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned long long, 0); // NOLINT -GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed long long, 0); // NOLINT -GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(float, 0); -GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(double, 0); - -#undef GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_ - -// Partial implementations of metaprogramming types from the standard library -// not available in C++11. - -template -struct negation - // NOLINTNEXTLINE - : std::integral_constant {}; - -// Base case: with zero predicates the answer is always true. -template -struct conjunction : std::true_type {}; - -// With a single predicate, the answer is that predicate. -template -struct conjunction : P1 {}; - -// With multiple predicates the answer is the first predicate if that is false, -// and we recurse otherwise. -template -struct conjunction - : std::conditional, P1>::type {}; - -template -struct disjunction : std::false_type {}; - -template -struct disjunction : P1 {}; - -template -struct disjunction - // NOLINTNEXTLINE - : std::conditional, P1>::type {}; - -template -using void_t = void; - -// Detects whether an expression of type `From` can be implicitly converted to -// `To` according to [conv]. In C++17, [conv]/3 defines this as follows: -// -// An expression e can be implicitly converted to a type T if and only if -// the declaration T t=e; is well-formed, for some invented temporary -// variable t ([dcl.init]). -// -// [conv]/2 implies we can use function argument passing to detect whether this -// initialization is valid. -// -// Note that this is distinct from is_convertible, which requires this be valid: -// -// To test() { -// return declval(); -// } -// -// In particular, is_convertible doesn't give the correct answer when `To` and -// `From` are the same non-moveable type since `declval` will be an rvalue -// reference, defeating the guaranteed copy elision that would otherwise make -// this function work. -// -// REQUIRES: `From` is not cv void. -template -struct is_implicitly_convertible { - private: - // A function that accepts a parameter of type T. This can be called with type - // U successfully only if U is implicitly convertible to T. - template - static void Accept(T); - - // A function that creates a value of type T. - template - static T Make(); - - // An overload be selected when implicit conversion from T to To is possible. - template (Make()))> - static std::true_type TestImplicitConversion(int); - - // A fallback overload selected in all other cases. - template - static std::false_type TestImplicitConversion(...); - - public: - using type = decltype(TestImplicitConversion(0)); - static constexpr bool value = type::value; -}; - -// Like std::invoke_result_t from C++17, but works only for objects with call -// operators (not e.g. member function pointers, which we don't need specific -// support for in OnceAction because std::function deals with them). -template -using call_result_t = decltype(std::declval()(std::declval()...)); - -template -struct is_callable_r_impl : std::false_type {}; - -// Specialize the struct for those template arguments where call_result_t is -// well-formed. When it's not, the generic template above is chosen, resulting -// in std::false_type. -template -struct is_callable_r_impl>, R, F, Args...> - : std::conditional< - std::is_void::value, // - std::true_type, // - is_implicitly_convertible, R>>::type {}; - -// Like std::is_invocable_r from C++17, but works only for objects with call -// operators. See the note on call_result_t. -template -using is_callable_r = is_callable_r_impl; - -// Like std::as_const from C++17. -template -typename std::add_const::type& as_const(T& t) { - return t; -} - -} // namespace internal - -// Specialized for function types below. -template -class OnceAction; - -// An action that can only be used once. -// -// This is accepted by WillOnce, which doesn't require the underlying action to -// be copy-constructible (only move-constructible), and promises to invoke it as -// an rvalue reference. This allows the action to work with move-only types like -// std::move_only_function in a type-safe manner. -// -// For example: -// -// // Assume we have some API that needs to accept a unique pointer to some -// // non-copyable object Foo. -// void AcceptUniquePointer(std::unique_ptr foo); -// -// // We can define an action that provides a Foo to that API. Because It -// // has to give away its unique pointer, it must not be called more than -// // once, so its call operator is &&-qualified. -// struct ProvideFoo { -// std::unique_ptr foo; -// -// void operator()() && { -// AcceptUniquePointer(std::move(Foo)); -// } -// }; -// -// // This action can be used with WillOnce. -// EXPECT_CALL(mock, Call) -// .WillOnce(ProvideFoo{std::make_unique(...)}); -// -// // But a call to WillRepeatedly will fail to compile. This is correct, -// // since the action cannot correctly be used repeatedly. -// EXPECT_CALL(mock, Call) -// .WillRepeatedly(ProvideFoo{std::make_unique(...)}); -// -// A less-contrived example would be an action that returns an arbitrary type, -// whose &&-qualified call operator is capable of dealing with move-only types. -template -class OnceAction final { - private: - // True iff we can use the given callable type (or lvalue reference) directly - // via StdFunctionAdaptor. - template - using IsDirectlyCompatible = internal::conjunction< - // It must be possible to capture the callable in StdFunctionAdaptor. - std::is_constructible::type, Callable>, - // The callable must be compatible with our signature. - internal::is_callable_r::type, - Args...>>; - - // True iff we can use the given callable type via StdFunctionAdaptor once we - // ignore incoming arguments. - template - using IsCompatibleAfterIgnoringArguments = internal::conjunction< - // It must be possible to capture the callable in a lambda. - std::is_constructible::type, Callable>, - // The callable must be invocable with zero arguments, returning something - // convertible to Result. - internal::is_callable_r::type>>; - - public: - // Construct from a callable that is directly compatible with our mocked - // signature: it accepts our function type's arguments and returns something - // convertible to our result type. - template ::type>>, - IsDirectlyCompatible> // - ::value, - int>::type = 0> - OnceAction(Callable&& callable) // NOLINT - : function_(StdFunctionAdaptor::type>( - {}, std::forward(callable))) {} - - // As above, but for a callable that ignores the mocked function's arguments. - template ::type>>, - // Exclude callables for which the overload above works. - // We'd rather provide the arguments if possible. - internal::negation>, - IsCompatibleAfterIgnoringArguments>::value, - int>::type = 0> - OnceAction(Callable&& callable) // NOLINT - // Call the constructor above with a callable - // that ignores the input arguments. - : OnceAction(IgnoreIncomingArguments::type>{ - std::forward(callable)}) {} - - // We are naturally copyable because we store only an std::function, but - // semantically we should not be copyable. - OnceAction(const OnceAction&) = delete; - OnceAction& operator=(const OnceAction&) = delete; - OnceAction(OnceAction&&) = default; - - // Invoke the underlying action callable with which we were constructed, - // handing it the supplied arguments. - Result Call(Args... args) && { - return function_(std::forward(args)...); - } - - private: - // An adaptor that wraps a callable that is compatible with our signature and - // being invoked as an rvalue reference so that it can be used as an - // StdFunctionAdaptor. This throws away type safety, but that's fine because - // this is only used by WillOnce, which we know calls at most once. - // - // Once we have something like std::move_only_function from C++23, we can do - // away with this. - template - class StdFunctionAdaptor final { - public: - // A tag indicating that the (otherwise universal) constructor is accepting - // the callable itself, instead of e.g. stealing calls for the move - // constructor. - struct CallableTag final {}; - - template - explicit StdFunctionAdaptor(CallableTag, F&& callable) - : callable_(std::make_shared(std::forward(callable))) {} - - // Rather than explicitly returning Result, we return whatever the wrapped - // callable returns. This allows for compatibility with existing uses like - // the following, when the mocked function returns void: - // - // EXPECT_CALL(mock_fn_, Call) - // .WillOnce([&] { - // [...] - // return 0; - // }); - // - // Such a callable can be turned into std::function. If we use an - // explicit return type of Result here then it *doesn't* work with - // std::function, because we'll get a "void function should not return a - // value" error. - // - // We need not worry about incompatible result types because the SFINAE on - // OnceAction already checks this for us. std::is_invocable_r_v itself makes - // the same allowance for void result types. - template - internal::call_result_t operator()( - ArgRefs&&... args) const { - return std::move(*callable_)(std::forward(args)...); - } - - private: - // We must put the callable on the heap so that we are copyable, which - // std::function needs. - std::shared_ptr callable_; - }; - - // An adaptor that makes a callable that accepts zero arguments callable with - // our mocked arguments. - template - struct IgnoreIncomingArguments { - internal::call_result_t operator()(Args&&...) { - return std::move(callable)(); - } - - Callable callable; - }; - - std::function function_; -}; - -// When an unexpected function call is encountered, Google Mock will -// let it return a default value if the user has specified one for its -// return type, or if the return type has a built-in default value; -// otherwise Google Mock won't know what value to return and will have -// to abort the process. -// -// The DefaultValue class allows a user to specify the -// default value for a type T that is both copyable and publicly -// destructible (i.e. anything that can be used as a function return -// type). The usage is: -// -// // Sets the default value for type T to be foo. -// DefaultValue::Set(foo); -template -class DefaultValue { - public: - // Sets the default value for type T; requires T to be - // copy-constructable and have a public destructor. - static void Set(T x) { - delete producer_; - producer_ = new FixedValueProducer(x); - } - - // Provides a factory function to be called to generate the default value. - // This method can be used even if T is only move-constructible, but it is not - // limited to that case. - typedef T (*FactoryFunction)(); - static void SetFactory(FactoryFunction factory) { - delete producer_; - producer_ = new FactoryValueProducer(factory); - } - - // Unsets the default value for type T. - static void Clear() { - delete producer_; - producer_ = nullptr; - } - - // Returns true if and only if the user has set the default value for type T. - static bool IsSet() { return producer_ != nullptr; } - - // Returns true if T has a default return value set by the user or there - // exists a built-in default value. - static bool Exists() { - return IsSet() || internal::BuiltInDefaultValue::Exists(); - } - - // Returns the default value for type T if the user has set one; - // otherwise returns the built-in default value. Requires that Exists() - // is true, which ensures that the return value is well-defined. - static T Get() { - return producer_ == nullptr ? internal::BuiltInDefaultValue::Get() - : producer_->Produce(); - } - - private: - class ValueProducer { - public: - virtual ~ValueProducer() = default; - virtual T Produce() = 0; - }; - - class FixedValueProducer : public ValueProducer { - public: - explicit FixedValueProducer(T value) : value_(value) {} - T Produce() override { return value_; } - - private: - const T value_; - FixedValueProducer(const FixedValueProducer&) = delete; - FixedValueProducer& operator=(const FixedValueProducer&) = delete; - }; - - class FactoryValueProducer : public ValueProducer { - public: - explicit FactoryValueProducer(FactoryFunction factory) - : factory_(factory) {} - T Produce() override { return factory_(); } - - private: - const FactoryFunction factory_; - FactoryValueProducer(const FactoryValueProducer&) = delete; - FactoryValueProducer& operator=(const FactoryValueProducer&) = delete; - }; - - static ValueProducer* producer_; -}; - -// This partial specialization allows a user to set default values for -// reference types. -template -class DefaultValue { - public: - // Sets the default value for type T&. - static void Set(T& x) { // NOLINT - address_ = &x; - } - - // Unsets the default value for type T&. - static void Clear() { address_ = nullptr; } - - // Returns true if and only if the user has set the default value for type T&. - static bool IsSet() { return address_ != nullptr; } - - // Returns true if T has a default return value set by the user or there - // exists a built-in default value. - static bool Exists() { - return IsSet() || internal::BuiltInDefaultValue::Exists(); - } - - // Returns the default value for type T& if the user has set one; - // otherwise returns the built-in default value if there is one; - // otherwise aborts the process. - static T& Get() { - return address_ == nullptr ? internal::BuiltInDefaultValue::Get() - : *address_; - } - - private: - static T* address_; -}; - -// This specialization allows DefaultValue::Get() to -// compile. -template <> -class DefaultValue { - public: - static bool Exists() { return true; } - static void Get() {} -}; - -// Points to the user-set default value for type T. -template -typename DefaultValue::ValueProducer* DefaultValue::producer_ = nullptr; - -// Points to the user-set default value for type T&. -template -T* DefaultValue::address_ = nullptr; - -// Implement this interface to define an action for function type F. -template -class ActionInterface { - public: - typedef typename internal::Function::Result Result; - typedef typename internal::Function::ArgumentTuple ArgumentTuple; - - ActionInterface() = default; - virtual ~ActionInterface() = default; - - // Performs the action. This method is not const, as in general an - // action can have side effects and be stateful. For example, a - // get-the-next-element-from-the-collection action will need to - // remember the current element. - virtual Result Perform(const ArgumentTuple& args) = 0; - - private: - ActionInterface(const ActionInterface&) = delete; - ActionInterface& operator=(const ActionInterface&) = delete; -}; - -template -class Action; - -// An Action is a copyable and IMMUTABLE (except by assignment) -// object that represents an action to be taken when a mock function of type -// R(Args...) is called. The implementation of Action is just a -// std::shared_ptr to const ActionInterface. Don't inherit from Action! You -// can view an object implementing ActionInterface as a concrete action -// (including its current state), and an Action object as a handle to it. -template -class Action { - private: - using F = R(Args...); - - // Adapter class to allow constructing Action from a legacy ActionInterface. - // New code should create Actions from functors instead. - struct ActionAdapter { - // Adapter must be copyable to satisfy std::function requirements. - ::std::shared_ptr> impl_; - - template - typename internal::Function::Result operator()(InArgs&&... args) { - return impl_->Perform( - ::std::forward_as_tuple(::std::forward(args)...)); - } - }; - - template - using IsCompatibleFunctor = std::is_constructible, G>; - - public: - typedef typename internal::Function::Result Result; - typedef typename internal::Function::ArgumentTuple ArgumentTuple; - - // Constructs a null Action. Needed for storing Action objects in - // STL containers. - Action() = default; - - // Construct an Action from a specified callable. - // This cannot take std::function directly, because then Action would not be - // directly constructible from lambda (it would require two conversions). - template < - typename G, - typename = typename std::enable_if, std::is_constructible, - G>>::value>::type> - Action(G&& fun) { // NOLINT - Init(::std::forward(fun), IsCompatibleFunctor()); - } - - // Constructs an Action from its implementation. - explicit Action(ActionInterface* impl) - : fun_(ActionAdapter{::std::shared_ptr>(impl)}) {} - - // This constructor allows us to turn an Action object into an - // Action, as long as F's arguments can be implicitly converted - // to Func's and Func's return type can be implicitly converted to F's. - template - Action(const Action& action) // NOLINT - : fun_(action.fun_) {} - - // Returns true if and only if this is the DoDefault() action. - bool IsDoDefault() const { return fun_ == nullptr; } - - // Performs the action. Note that this method is const even though - // the corresponding method in ActionInterface is not. The reason - // is that a const Action means that it cannot be re-bound to - // another concrete action, not that the concrete action it binds to - // cannot change state. (Think of the difference between a const - // pointer and a pointer to const.) - Result Perform(ArgumentTuple args) const { - if (IsDoDefault()) { - internal::IllegalDoDefault(__FILE__, __LINE__); - } - return internal::Apply(fun_, ::std::move(args)); - } - - // An action can be used as a OnceAction, since it's obviously safe to call it - // once. - operator OnceAction() const { // NOLINT - // Return a OnceAction-compatible callable that calls Perform with the - // arguments it is provided. We could instead just return fun_, but then - // we'd need to handle the IsDoDefault() case separately. - struct OA { - Action action; - - R operator()(Args... args) && { - return action.Perform( - std::forward_as_tuple(std::forward(args)...)); - } - }; - - return OA{*this}; - } - - private: - template - friend class Action; - - template - void Init(G&& g, ::std::true_type) { - fun_ = ::std::forward(g); - } - - template - void Init(G&& g, ::std::false_type) { - fun_ = IgnoreArgs::type>{::std::forward(g)}; - } - - template - struct IgnoreArgs { - template - Result operator()(const InArgs&...) const { - return function_impl(); - } - - FunctionImpl function_impl; - }; - - // fun_ is an empty function if and only if this is the DoDefault() action. - ::std::function fun_; -}; - -// The PolymorphicAction class template makes it easy to implement a -// polymorphic action (i.e. an action that can be used in mock -// functions of than one type, e.g. Return()). -// -// To define a polymorphic action, a user first provides a COPYABLE -// implementation class that has a Perform() method template: -// -// class FooAction { -// public: -// template -// Result Perform(const ArgumentTuple& args) const { -// // Processes the arguments and returns a result, using -// // std::get(args) to get the N-th (0-based) argument in the tuple. -// } -// ... -// }; -// -// Then the user creates the polymorphic action using -// MakePolymorphicAction(object) where object has type FooAction. See -// the definition of Return(void) and SetArgumentPointee(value) for -// complete examples. -template -class PolymorphicAction { - public: - explicit PolymorphicAction(const Impl& impl) : impl_(impl) {} - - template - operator Action() const { - return Action(new MonomorphicImpl(impl_)); - } - - private: - template - class MonomorphicImpl : public ActionInterface { - public: - typedef typename internal::Function::Result Result; - typedef typename internal::Function::ArgumentTuple ArgumentTuple; - - explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {} - - Result Perform(const ArgumentTuple& args) override { - return impl_.template Perform(args); - } - - private: - Impl impl_; - }; - - Impl impl_; -}; - -// Creates an Action from its implementation and returns it. The -// created Action object owns the implementation. -template -Action MakeAction(ActionInterface* impl) { - return Action(impl); -} - -// Creates a polymorphic action from its implementation. This is -// easier to use than the PolymorphicAction constructor as it -// doesn't require you to explicitly write the template argument, e.g. -// -// MakePolymorphicAction(foo); -// vs -// PolymorphicAction(foo); -template -inline PolymorphicAction MakePolymorphicAction(const Impl& impl) { - return PolymorphicAction(impl); -} - -namespace internal { - -// Helper struct to specialize ReturnAction to execute a move instead of a copy -// on return. Useful for move-only types, but could be used on any type. -template -struct ByMoveWrapper { - explicit ByMoveWrapper(T value) : payload(std::move(value)) {} - T payload; -}; - -// The general implementation of Return(R). Specializations follow below. -template -class ReturnAction final { - public: - explicit ReturnAction(R value) : value_(std::move(value)) {} - - template >, // - negation>, // - std::is_convertible, // - std::is_move_constructible>::value>::type> - operator OnceAction() && { // NOLINT - return Impl(std::move(value_)); - } - - template >, // - negation>, // - std::is_convertible, // - std::is_copy_constructible>::value>::type> - operator Action() const { // NOLINT - return Impl(value_); - } - - private: - // Implements the Return(x) action for a mock function that returns type U. - template - class Impl final { - public: - // The constructor used when the return value is allowed to move from the - // input value (i.e. we are converting to OnceAction). - explicit Impl(R&& input_value) - : state_(new State(std::move(input_value))) {} - - // The constructor used when the return value is not allowed to move from - // the input value (i.e. we are converting to Action). - explicit Impl(const R& input_value) : state_(new State(input_value)) {} - - U operator()() && { return std::move(state_->value); } - U operator()() const& { return state_->value; } - - private: - // We put our state on the heap so that the compiler-generated copy/move - // constructors work correctly even when U is a reference-like type. This is - // necessary only because we eagerly create State::value (see the note on - // that symbol for details). If we instead had only the input value as a - // member then the default constructors would work fine. - // - // For example, when R is std::string and U is std::string_view, value is a - // reference to the string backed by input_value. The copy constructor would - // copy both, so that we wind up with a new input_value object (with the - // same contents) and a reference to the *old* input_value object rather - // than the new one. - struct State { - explicit State(const R& input_value_in) - : input_value(input_value_in), - // Make an implicit conversion to Result before initializing the U - // object we store, avoiding calling any explicit constructor of U - // from R. - // - // This simulates the language rules: a function with return type U - // that does `return R()` requires R to be implicitly convertible to - // U, and uses that path for the conversion, even U Result has an - // explicit constructor from R. - value(ImplicitCast_(internal::as_const(input_value))) {} - - // As above, but for the case where we're moving from the ReturnAction - // object because it's being used as a OnceAction. - explicit State(R&& input_value_in) - : input_value(std::move(input_value_in)), - // For the same reason as above we make an implicit conversion to U - // before initializing the value. - // - // Unlike above we provide the input value as an rvalue to the - // implicit conversion because this is a OnceAction: it's fine if it - // wants to consume the input value. - value(ImplicitCast_(std::move(input_value))) {} - - // A copy of the value originally provided by the user. We retain this in - // addition to the value of the mock function's result type below in case - // the latter is a reference-like type. See the std::string_view example - // in the documentation on Return. - R input_value; - - // The value we actually return, as the type returned by the mock function - // itself. - // - // We eagerly initialize this here, rather than lazily doing the implicit - // conversion automatically each time Perform is called, for historical - // reasons: in 2009-11, commit a070cbd91c (Google changelist 13540126) - // made the Action conversion operator eagerly convert the R value to - // U, but without keeping the R alive. This broke the use case discussed - // in the documentation for Return, making reference-like types such as - // std::string_view not safe to use as U where the input type R is a - // value-like type such as std::string. - // - // The example the commit gave was not very clear, nor was the issue - // thread (https://github.com/google/googlemock/issues/86), but it seems - // the worry was about reference-like input types R that flatten to a - // value-like type U when being implicitly converted. An example of this - // is std::vector::reference, which is often a proxy type with an - // reference to the underlying vector: - // - // // Helper method: have the mock function return bools according - // // to the supplied script. - // void SetActions(MockFunction& mock, - // const std::vector& script) { - // for (size_t i = 0; i < script.size(); ++i) { - // EXPECT_CALL(mock, Call(i)).WillOnce(Return(script[i])); - // } - // } - // - // TEST(Foo, Bar) { - // // Set actions using a temporary vector, whose operator[] - // // returns proxy objects that references that will be - // // dangling once the call to SetActions finishes and the - // // vector is destroyed. - // MockFunction mock; - // SetActions(mock, {false, true}); - // - // EXPECT_FALSE(mock.AsStdFunction()(0)); - // EXPECT_TRUE(mock.AsStdFunction()(1)); - // } - // - // This eager conversion helps with a simple case like this, but doesn't - // fully make these types work in general. For example the following still - // uses a dangling reference: - // - // TEST(Foo, Baz) { - // MockFunction()> mock; - // - // // Return the same vector twice, and then the empty vector - // // thereafter. - // auto action = Return(std::initializer_list{ - // "taco", "burrito", - // }); - // - // EXPECT_CALL(mock, Call) - // .WillOnce(action) - // .WillOnce(action) - // .WillRepeatedly(Return(std::vector{})); - // - // EXPECT_THAT(mock.AsStdFunction()(), - // ElementsAre("taco", "burrito")); - // EXPECT_THAT(mock.AsStdFunction()(), - // ElementsAre("taco", "burrito")); - // EXPECT_THAT(mock.AsStdFunction()(), IsEmpty()); - // } - // - U value; - }; - - const std::shared_ptr state_; - }; - - R value_; -}; - -// A specialization of ReturnAction when R is ByMoveWrapper for some T. -// -// This version applies the type system-defeating hack of moving from T even in -// the const call operator, checking at runtime that it isn't called more than -// once, since the user has declared their intent to do so by using ByMove. -template -class ReturnAction> final { - public: - explicit ReturnAction(ByMoveWrapper wrapper) - : state_(new State(std::move(wrapper.payload))) {} - - T operator()() const { - GTEST_CHECK_(!state_->called) - << "A ByMove() action must be performed at most once."; - - state_->called = true; - return std::move(state_->value); - } - - private: - // We store our state on the heap so that we are copyable as required by - // Action, despite the fact that we are stateful and T may not be copyable. - struct State { - explicit State(T&& value_in) : value(std::move(value_in)) {} - - T value; - bool called = false; - }; - - const std::shared_ptr state_; -}; - -// Implements the ReturnNull() action. -class ReturnNullAction { - public: - // Allows ReturnNull() to be used in any pointer-returning function. In C++11 - // this is enforced by returning nullptr, and in non-C++11 by asserting a - // pointer type on compile time. - template - static Result Perform(const ArgumentTuple&) { - return nullptr; - } -}; - -// Implements the Return() action. -class ReturnVoidAction { - public: - // Allows Return() to be used in any void-returning function. - template - static void Perform(const ArgumentTuple&) { - static_assert(std::is_void::value, "Result should be void."); - } -}; - -// Implements the polymorphic ReturnRef(x) action, which can be used -// in any function that returns a reference to the type of x, -// regardless of the argument types. -template -class ReturnRefAction { - public: - // Constructs a ReturnRefAction object from the reference to be returned. - explicit ReturnRefAction(T& ref) : ref_(ref) {} // NOLINT - - // This template type conversion operator allows ReturnRef(x) to be - // used in ANY function that returns a reference to x's type. - template - operator Action() const { - typedef typename Function::Result Result; - // Asserts that the function return type is a reference. This - // catches the user error of using ReturnRef(x) when Return(x) - // should be used, and generates some helpful error message. - static_assert(std::is_reference::value, - "use Return instead of ReturnRef to return a value"); - return Action(new Impl(ref_)); - } - - private: - // Implements the ReturnRef(x) action for a particular function type F. - template - class Impl : public ActionInterface { - public: - typedef typename Function::Result Result; - typedef typename Function::ArgumentTuple ArgumentTuple; - - explicit Impl(T& ref) : ref_(ref) {} // NOLINT - - Result Perform(const ArgumentTuple&) override { return ref_; } - - private: - T& ref_; - }; - - T& ref_; -}; - -// Implements the polymorphic ReturnRefOfCopy(x) action, which can be -// used in any function that returns a reference to the type of x, -// regardless of the argument types. -template -class ReturnRefOfCopyAction { - public: - // Constructs a ReturnRefOfCopyAction object from the reference to - // be returned. - explicit ReturnRefOfCopyAction(const T& value) : value_(value) {} // NOLINT - - // This template type conversion operator allows ReturnRefOfCopy(x) to be - // used in ANY function that returns a reference to x's type. - template - operator Action() const { - typedef typename Function::Result Result; - // Asserts that the function return type is a reference. This - // catches the user error of using ReturnRefOfCopy(x) when Return(x) - // should be used, and generates some helpful error message. - static_assert(std::is_reference::value, - "use Return instead of ReturnRefOfCopy to return a value"); - return Action(new Impl(value_)); - } - - private: - // Implements the ReturnRefOfCopy(x) action for a particular function type F. - template - class Impl : public ActionInterface { - public: - typedef typename Function::Result Result; - typedef typename Function::ArgumentTuple ArgumentTuple; - - explicit Impl(const T& value) : value_(value) {} // NOLINT - - Result Perform(const ArgumentTuple&) override { return value_; } - - private: - T value_; - }; - - const T value_; -}; - -// Implements the polymorphic ReturnRoundRobin(v) action, which can be -// used in any function that returns the element_type of v. -template -class ReturnRoundRobinAction { - public: - explicit ReturnRoundRobinAction(std::vector values) { - GTEST_CHECK_(!values.empty()) - << "ReturnRoundRobin requires at least one element."; - state_->values = std::move(values); - } - - template - T operator()(Args&&...) const { - return state_->Next(); - } - - private: - struct State { - T Next() { - T ret_val = values[i++]; - if (i == values.size()) i = 0; - return ret_val; - } - - std::vector values; - size_t i = 0; - }; - std::shared_ptr state_ = std::make_shared(); -}; - -// Implements the polymorphic DoDefault() action. -class DoDefaultAction { - public: - // This template type conversion operator allows DoDefault() to be - // used in any function. - template - operator Action() const { - return Action(); - } // NOLINT -}; - -// Implements the Assign action to set a given pointer referent to a -// particular value. -template -class AssignAction { - public: - AssignAction(T1* ptr, T2 value) : ptr_(ptr), value_(value) {} - - template - void Perform(const ArgumentTuple& /* args */) const { - *ptr_ = value_; - } - - private: - T1* const ptr_; - const T2 value_; -}; - -#ifndef GTEST_OS_WINDOWS_MOBILE - -// Implements the SetErrnoAndReturn action to simulate return from -// various system calls and libc functions. -template -class SetErrnoAndReturnAction { - public: - SetErrnoAndReturnAction(int errno_value, T result) - : errno_(errno_value), result_(result) {} - template - Result Perform(const ArgumentTuple& /* args */) const { - errno = errno_; - return result_; - } - - private: - const int errno_; - const T result_; -}; - -#endif // !GTEST_OS_WINDOWS_MOBILE - -// Implements the SetArgumentPointee(x) action for any function -// whose N-th argument (0-based) is a pointer to x's type. -template -struct SetArgumentPointeeAction { - A value; - - template - void operator()(const Args&... args) const { - *::std::get(std::tie(args...)) = value; - } -}; - -// Implements the Invoke(object_ptr, &Class::Method) action. -template -struct InvokeMethodAction { - Class* const obj_ptr; - const MethodPtr method_ptr; - - template - auto operator()(Args&&... args) const - -> decltype((obj_ptr->*method_ptr)(std::forward(args)...)) { - return (obj_ptr->*method_ptr)(std::forward(args)...); - } -}; - -// Implements the InvokeWithoutArgs(f) action. The template argument -// FunctionImpl is the implementation type of f, which can be either a -// function pointer or a functor. InvokeWithoutArgs(f) can be used as an -// Action as long as f's type is compatible with F. -template -struct InvokeWithoutArgsAction { - FunctionImpl function_impl; - - // Allows InvokeWithoutArgs(f) to be used as any action whose type is - // compatible with f. - template - auto operator()(const Args&...) -> decltype(function_impl()) { - return function_impl(); - } -}; - -// Implements the InvokeWithoutArgs(object_ptr, &Class::Method) action. -template -struct InvokeMethodWithoutArgsAction { - Class* const obj_ptr; - const MethodPtr method_ptr; - - using ReturnType = - decltype((std::declval()->*std::declval())()); - - template - ReturnType operator()(const Args&...) const { - return (obj_ptr->*method_ptr)(); - } -}; - -// Implements the IgnoreResult(action) action. -template -class IgnoreResultAction { - public: - explicit IgnoreResultAction(const A& action) : action_(action) {} - - template - operator Action() const { - // Assert statement belongs here because this is the best place to verify - // conditions on F. It produces the clearest error messages - // in most compilers. - // Impl really belongs in this scope as a local class but can't - // because MSVC produces duplicate symbols in different translation units - // in this case. Until MS fixes that bug we put Impl into the class scope - // and put the typedef both here (for use in assert statement) and - // in the Impl class. But both definitions must be the same. - typedef typename internal::Function::Result Result; - - // Asserts at compile time that F returns void. - static_assert(std::is_void::value, "Result type should be void."); - - return Action(new Impl(action_)); - } - - private: - template - class Impl : public ActionInterface { - public: - typedef typename internal::Function::Result Result; - typedef typename internal::Function::ArgumentTuple ArgumentTuple; - - explicit Impl(const A& action) : action_(action) {} - - void Perform(const ArgumentTuple& args) override { - // Performs the action and ignores its result. - action_.Perform(args); - } - - private: - // Type OriginalFunction is the same as F except that its return - // type is IgnoredValue. - typedef - typename internal::Function::MakeResultIgnoredValue OriginalFunction; - - const Action action_; - }; - - const A action_; -}; - -template -struct WithArgsAction { - InnerAction inner_action; - - // The signature of the function as seen by the inner action, given an out - // action with the given result and argument types. - template - using InnerSignature = - R(typename std::tuple_element>::type...); - - // Rather than a call operator, we must define conversion operators to - // particular action types. This is necessary for embedded actions like - // DoDefault(), which rely on an action conversion operators rather than - // providing a call operator because even with a particular set of arguments - // they don't have a fixed return type. - - template < - typename R, typename... Args, - typename std::enable_if< - std::is_convertible>...)>>::value, - int>::type = 0> - operator OnceAction() && { // NOLINT - struct OA { - OnceAction> inner_action; - - R operator()(Args&&... args) && { - return std::move(inner_action) - .Call(std::get( - std::forward_as_tuple(std::forward(args)...))...); - } - }; - - return OA{std::move(inner_action)}; - } - - template < - typename R, typename... Args, - typename std::enable_if< - std::is_convertible>...)>>::value, - int>::type = 0> - operator Action() const { // NOLINT - Action> converted(inner_action); - - return [converted](Args&&... args) -> R { - return converted.Perform(std::forward_as_tuple( - std::get(std::forward_as_tuple(std::forward(args)...))...)); - }; - } -}; - -template -class DoAllAction; - -// Base case: only a single action. -template -class DoAllAction { - public: - struct UserConstructorTag {}; - - template - explicit DoAllAction(UserConstructorTag, T&& action) - : final_action_(std::forward(action)) {} - - // Rather than a call operator, we must define conversion operators to - // particular action types. This is necessary for embedded actions like - // DoDefault(), which rely on an action conversion operators rather than - // providing a call operator because even with a particular set of arguments - // they don't have a fixed return type. - - // We support conversion to OnceAction whenever the sub-action does. - template >::value, - int>::type = 0> - operator OnceAction() && { // NOLINT - return std::move(final_action_); - } - - // We also support conversion to OnceAction whenever the sub-action supports - // conversion to Action (since any Action can also be a OnceAction). - template < - typename R, typename... Args, - typename std::enable_if< - conjunction< - negation< - std::is_convertible>>, - std::is_convertible>>::value, - int>::type = 0> - operator OnceAction() && { // NOLINT - return Action(std::move(final_action_)); - } - - // We support conversion to Action whenever the sub-action does. - template < - typename R, typename... Args, - typename std::enable_if< - std::is_convertible>::value, - int>::type = 0> - operator Action() const { // NOLINT - return final_action_; - } - - private: - FinalAction final_action_; -}; - -// Recursive case: support N actions by calling the initial action and then -// calling through to the base class containing N-1 actions. -template -class DoAllAction - : private DoAllAction { - private: - using Base = DoAllAction; - - // The type of reference that should be provided to an initial action for a - // mocked function parameter of type T. - // - // There are two quirks here: - // - // * Unlike most forwarding functions, we pass scalars through by value. - // This isn't strictly necessary because an lvalue reference would work - // fine too and be consistent with other non-reference types, but it's - // perhaps less surprising. - // - // For example if the mocked function has signature void(int), then it - // might seem surprising for the user's initial action to need to be - // convertible to Action. This is perhaps less - // surprising for a non-scalar type where there may be a performance - // impact, or it might even be impossible, to pass by value. - // - // * More surprisingly, `const T&` is often not a const reference type. - // By the reference collapsing rules in C++17 [dcl.ref]/6, if T refers to - // U& or U&& for some non-scalar type U, then InitialActionArgType is - // U&. In other words, we may hand over a non-const reference. - // - // So for example, given some non-scalar type Obj we have the following - // mappings: - // - // T InitialActionArgType - // ------- ----------------------- - // Obj const Obj& - // Obj& Obj& - // Obj&& Obj& - // const Obj const Obj& - // const Obj& const Obj& - // const Obj&& const Obj& - // - // In other words, the initial actions get a mutable view of an non-scalar - // argument if and only if the mock function itself accepts a non-const - // reference type. They are never given an rvalue reference to an - // non-scalar type. - // - // This situation makes sense if you imagine use with a matcher that is - // designed to write through a reference. For example, if the caller wants - // to fill in a reference argument and then return a canned value: - // - // EXPECT_CALL(mock, Call) - // .WillOnce(DoAll(SetArgReferee<0>(17), Return(19))); - // - template - using InitialActionArgType = - typename std::conditional::value, T, const T&>::type; - - public: - struct UserConstructorTag {}; - - template - explicit DoAllAction(UserConstructorTag, T&& initial_action, - U&&... other_actions) - : Base({}, std::forward(other_actions)...), - initial_action_(std::forward(initial_action)) {} - - // We support conversion to OnceAction whenever both the initial action and - // the rest support conversion to OnceAction. - template < - typename R, typename... Args, - typename std::enable_if< - conjunction...)>>, - std::is_convertible>>::value, - int>::type = 0> - operator OnceAction() && { // NOLINT - // Return an action that first calls the initial action with arguments - // filtered through InitialActionArgType, then forwards arguments directly - // to the base class to deal with the remaining actions. - struct OA { - OnceAction...)> initial_action; - OnceAction remaining_actions; - - R operator()(Args... args) && { - std::move(initial_action) - .Call(static_cast>(args)...); - - return std::move(remaining_actions).Call(std::forward(args)...); - } - }; - - return OA{ - std::move(initial_action_), - std::move(static_cast(*this)), - }; - } - - // We also support conversion to OnceAction whenever the initial action - // supports conversion to Action (since any Action can also be a OnceAction). - // - // The remaining sub-actions must also be compatible, but we don't need to - // special case them because the base class deals with them. - template < - typename R, typename... Args, - typename std::enable_if< - conjunction< - negation...)>>>, - std::is_convertible...)>>, - std::is_convertible>>::value, - int>::type = 0> - operator OnceAction() && { // NOLINT - return DoAll( - Action...)>(std::move(initial_action_)), - std::move(static_cast(*this))); - } - - // We support conversion to Action whenever both the initial action and the - // rest support conversion to Action. - template < - typename R, typename... Args, - typename std::enable_if< - conjunction< - std::is_convertible...)>>, - std::is_convertible>>::value, - int>::type = 0> - operator Action() const { // NOLINT - // Return an action that first calls the initial action with arguments - // filtered through InitialActionArgType, then forwards arguments directly - // to the base class to deal with the remaining actions. - struct OA { - Action...)> initial_action; - Action remaining_actions; - - R operator()(Args... args) const { - initial_action.Perform(std::forward_as_tuple( - static_cast>(args)...)); - - return remaining_actions.Perform( - std::forward_as_tuple(std::forward(args)...)); - } - }; - - return OA{ - initial_action_, - static_cast(*this), - }; - } - - private: - InitialAction initial_action_; -}; - -template -struct ReturnNewAction { - T* operator()() const { - return internal::Apply( - [](const Params&... unpacked_params) { - return new T(unpacked_params...); - }, - params); - } - std::tuple params; -}; - -template -struct ReturnArgAction { - template ::type> - auto operator()(Args&&... args) const - -> decltype(std::get( - std::forward_as_tuple(std::forward(args)...))) { - return std::get(std::forward_as_tuple(std::forward(args)...)); - } -}; - -template -struct SaveArgAction { - Ptr pointer; - - template - void operator()(const Args&... args) const { - *pointer = std::get(std::tie(args...)); - } -}; - -template -struct SaveArgPointeeAction { - Ptr pointer; - - template - void operator()(const Args&... args) const { - *pointer = *std::get(std::tie(args...)); - } -}; - -template -struct SetArgRefereeAction { - T value; - - template - void operator()(Args&&... args) const { - using argk_type = - typename ::std::tuple_element>::type; - static_assert(std::is_lvalue_reference::value, - "Argument must be a reference type."); - std::get(std::tie(args...)) = value; - } -}; - -template -struct SetArrayArgumentAction { - I1 first; - I2 last; - - template - void operator()(const Args&... args) const { - auto value = std::get(std::tie(args...)); - for (auto it = first; it != last; ++it, (void)++value) { - *value = *it; - } - } -}; - -template -struct DeleteArgAction { - template - void operator()(const Args&... args) const { - delete std::get(std::tie(args...)); - } -}; - -template -struct ReturnPointeeAction { - Ptr pointer; - template - auto operator()(const Args&...) const -> decltype(*pointer) { - return *pointer; - } -}; - -#if GTEST_HAS_EXCEPTIONS -template -struct ThrowAction { - T exception; - // We use a conversion operator to adapt to any return type. - template - operator Action() const { // NOLINT - T copy = exception; - return [copy](Args...) -> R { throw copy; }; - } -}; -struct RethrowAction { - std::exception_ptr exception; - template - operator Action() const { // NOLINT - return [ex = exception](Args...) -> R { std::rethrow_exception(ex); }; - } -}; -#endif // GTEST_HAS_EXCEPTIONS - -} // namespace internal - -// An Unused object can be implicitly constructed from ANY value. -// This is handy when defining actions that ignore some or all of the -// mock function arguments. For example, given -// -// MOCK_METHOD3(Foo, double(const string& label, double x, double y)); -// MOCK_METHOD3(Bar, double(int index, double x, double y)); -// -// instead of -// -// double DistanceToOriginWithLabel(const string& label, double x, double y) { -// return sqrt(x*x + y*y); -// } -// double DistanceToOriginWithIndex(int index, double x, double y) { -// return sqrt(x*x + y*y); -// } -// ... -// EXPECT_CALL(mock, Foo("abc", _, _)) -// .WillOnce(Invoke(DistanceToOriginWithLabel)); -// EXPECT_CALL(mock, Bar(5, _, _)) -// .WillOnce(Invoke(DistanceToOriginWithIndex)); -// -// you could write -// -// // We can declare any uninteresting argument as Unused. -// double DistanceToOrigin(Unused, double x, double y) { -// return sqrt(x*x + y*y); -// } -// ... -// EXPECT_CALL(mock, Foo("abc", _, _)).WillOnce(Invoke(DistanceToOrigin)); -// EXPECT_CALL(mock, Bar(5, _, _)).WillOnce(Invoke(DistanceToOrigin)); -typedef internal::IgnoredValue Unused; - -// Creates an action that does actions a1, a2, ..., sequentially in -// each invocation. All but the last action will have a readonly view of the -// arguments. -template -internal::DoAllAction::type...> DoAll( - Action&&... action) { - return internal::DoAllAction::type...>( - {}, std::forward(action)...); -} - -// WithArg(an_action) creates an action that passes the k-th -// (0-based) argument of the mock function to an_action and performs -// it. It adapts an action accepting one argument to one that accepts -// multiple arguments. For convenience, we also provide -// WithArgs(an_action) (defined below) as a synonym. -template -internal::WithArgsAction::type, k> WithArg( - InnerAction&& action) { - return {std::forward(action)}; -} - -// WithArgs(an_action) creates an action that passes -// the selected arguments of the mock function to an_action and -// performs it. It serves as an adaptor between actions with -// different argument lists. -template -internal::WithArgsAction::type, k, ks...> -WithArgs(InnerAction&& action) { - return {std::forward(action)}; -} - -// WithoutArgs(inner_action) can be used in a mock function with a -// non-empty argument list to perform inner_action, which takes no -// argument. In other words, it adapts an action accepting no -// argument to one that accepts (and ignores) arguments. -template -internal::WithArgsAction::type> WithoutArgs( - InnerAction&& action) { - return {std::forward(action)}; -} - -// Creates an action that returns a value. -// -// The returned type can be used with a mock function returning a non-void, -// non-reference type U as follows: -// -// * If R is convertible to U and U is move-constructible, then the action can -// be used with WillOnce. -// -// * If const R& is convertible to U and U is copy-constructible, then the -// action can be used with both WillOnce and WillRepeatedly. -// -// The mock expectation contains the R value from which the U return value is -// constructed (a move/copy of the argument to Return). This means that the R -// value will survive at least until the mock object's expectations are cleared -// or the mock object is destroyed, meaning that U can safely be a -// reference-like type such as std::string_view: -// -// // The mock function returns a view of a copy of the string fed to -// // Return. The view is valid even after the action is performed. -// MockFunction mock; -// EXPECT_CALL(mock, Call).WillOnce(Return(std::string("taco"))); -// const std::string_view result = mock.AsStdFunction()(); -// EXPECT_EQ("taco", result); -// -template -internal::ReturnAction Return(R value) { - return internal::ReturnAction(std::move(value)); -} - -// Creates an action that returns NULL. -inline PolymorphicAction ReturnNull() { - return MakePolymorphicAction(internal::ReturnNullAction()); -} - -// Creates an action that returns from a void function. -inline PolymorphicAction Return() { - return MakePolymorphicAction(internal::ReturnVoidAction()); -} - -// Creates an action that returns the reference to a variable. -template -inline internal::ReturnRefAction ReturnRef(R& x) { // NOLINT - return internal::ReturnRefAction(x); -} - -// Prevent using ReturnRef on reference to temporary. -template -internal::ReturnRefAction ReturnRef(R&&) = delete; - -// Creates an action that returns the reference to a copy of the -// argument. The copy is created when the action is constructed and -// lives as long as the action. -template -inline internal::ReturnRefOfCopyAction ReturnRefOfCopy(const R& x) { - return internal::ReturnRefOfCopyAction(x); -} - -// DEPRECATED: use Return(x) directly with WillOnce. -// -// Modifies the parent action (a Return() action) to perform a move of the -// argument instead of a copy. -// Return(ByMove()) actions can only be executed once and will assert this -// invariant. -template -internal::ByMoveWrapper ByMove(R x) { - return internal::ByMoveWrapper(std::move(x)); -} - -// Creates an action that returns an element of `vals`. Calling this action will -// repeatedly return the next value from `vals` until it reaches the end and -// will restart from the beginning. -template -internal::ReturnRoundRobinAction ReturnRoundRobin(std::vector vals) { - return internal::ReturnRoundRobinAction(std::move(vals)); -} - -// Creates an action that returns an element of `vals`. Calling this action will -// repeatedly return the next value from `vals` until it reaches the end and -// will restart from the beginning. -template -internal::ReturnRoundRobinAction ReturnRoundRobin( - std::initializer_list vals) { - return internal::ReturnRoundRobinAction(std::vector(vals)); -} - -// Creates an action that does the default action for the give mock function. -inline internal::DoDefaultAction DoDefault() { - return internal::DoDefaultAction(); -} - -// Creates an action that sets the variable pointed by the N-th -// (0-based) function argument to 'value'. -template -internal::SetArgumentPointeeAction SetArgPointee(T value) { - return {std::move(value)}; -} - -// The following version is DEPRECATED. -template -internal::SetArgumentPointeeAction SetArgumentPointee(T value) { - return {std::move(value)}; -} - -// Creates an action that sets a pointer referent to a given value. -template -PolymorphicAction> Assign(T1* ptr, T2 val) { - return MakePolymorphicAction(internal::AssignAction(ptr, val)); -} - -#ifndef GTEST_OS_WINDOWS_MOBILE - -// Creates an action that sets errno and returns the appropriate error. -template -PolymorphicAction> SetErrnoAndReturn( - int errval, T result) { - return MakePolymorphicAction( - internal::SetErrnoAndReturnAction(errval, result)); -} - -#endif // !GTEST_OS_WINDOWS_MOBILE - -// Various overloads for Invoke(). - -// Legacy function. -// Actions can now be implicitly constructed from callables. No need to create -// wrapper objects. -// This function exists for backwards compatibility. -template -typename std::decay::type Invoke(FunctionImpl&& function_impl) { - return std::forward(function_impl); -} - -// Creates an action that invokes the given method on the given object -// with the mock function's arguments. -template -internal::InvokeMethodAction Invoke(Class* obj_ptr, - MethodPtr method_ptr) { - return {obj_ptr, method_ptr}; -} - -// Creates an action that invokes 'function_impl' with no argument. -template -internal::InvokeWithoutArgsAction::type> -InvokeWithoutArgs(FunctionImpl function_impl) { - return {std::move(function_impl)}; -} - -// Creates an action that invokes the given method on the given object -// with no argument. -template -internal::InvokeMethodWithoutArgsAction InvokeWithoutArgs( - Class* obj_ptr, MethodPtr method_ptr) { - return {obj_ptr, method_ptr}; -} - -// Creates an action that performs an_action and throws away its -// result. In other words, it changes the return type of an_action to -// void. an_action MUST NOT return void, or the code won't compile. -template -inline internal::IgnoreResultAction IgnoreResult(const A& an_action) { - return internal::IgnoreResultAction(an_action); -} - -// Creates a reference wrapper for the given L-value. If necessary, -// you can explicitly specify the type of the reference. For example, -// suppose 'derived' is an object of type Derived, ByRef(derived) -// would wrap a Derived&. If you want to wrap a const Base& instead, -// where Base is a base class of Derived, just write: -// -// ByRef(derived) -// -// N.B. ByRef is redundant with std::ref, std::cref and std::reference_wrapper. -// However, it may still be used for consistency with ByMove(). -template -inline ::std::reference_wrapper ByRef(T& l_value) { // NOLINT - return ::std::reference_wrapper(l_value); -} - -// The ReturnNew(a1, a2, ..., a_k) action returns a pointer to a new -// instance of type T, constructed on the heap with constructor arguments -// a1, a2, ..., and a_k. The caller assumes ownership of the returned value. -template -internal::ReturnNewAction::type...> ReturnNew( - Params&&... params) { - return {std::forward_as_tuple(std::forward(params)...)}; -} - -// Action ReturnArg() returns the k-th argument of the mock function. -template -internal::ReturnArgAction ReturnArg() { - return {}; -} - -// Action SaveArg(pointer) saves the k-th (0-based) argument of the -// mock function to *pointer. -template -internal::SaveArgAction SaveArg(Ptr pointer) { - return {pointer}; -} - -// Action SaveArgPointee(pointer) saves the value pointed to -// by the k-th (0-based) argument of the mock function to *pointer. -template -internal::SaveArgPointeeAction SaveArgPointee(Ptr pointer) { - return {pointer}; -} - -// Action SetArgReferee(value) assigns 'value' to the variable -// referenced by the k-th (0-based) argument of the mock function. -template -internal::SetArgRefereeAction::type> SetArgReferee( - T&& value) { - return {std::forward(value)}; -} - -// Action SetArrayArgument(first, last) copies the elements in -// source range [first, last) to the array pointed to by the k-th -// (0-based) argument, which can be either a pointer or an -// iterator. The action does not take ownership of the elements in the -// source range. -template -internal::SetArrayArgumentAction SetArrayArgument(I1 first, - I2 last) { - return {first, last}; -} - -// Action DeleteArg() deletes the k-th (0-based) argument of the mock -// function. -template -internal::DeleteArgAction DeleteArg() { - return {}; -} - -// This action returns the value pointed to by 'pointer'. -template -internal::ReturnPointeeAction ReturnPointee(Ptr pointer) { - return {pointer}; -} - -#if GTEST_HAS_EXCEPTIONS -// Action Throw(exception) can be used in a mock function of any type -// to throw the given exception. Any copyable value can be thrown, -// except for std::exception_ptr, which is likely a mistake if -// thrown directly. -template -typename std::enable_if< - !std::is_base_of::type>::value, - internal::ThrowAction::type>>::type -Throw(T&& exception) { - return {std::forward(exception)}; -} -// Action Rethrow(exception_ptr) can be used in a mock function of any type -// to rethrow any exception_ptr. Note that the same object is thrown each time. -inline internal::RethrowAction Rethrow(std::exception_ptr exception) { - return {std::move(exception)}; -} -#endif // GTEST_HAS_EXCEPTIONS - -namespace internal { - -// A macro from the ACTION* family (defined later in gmock-generated-actions.h) -// defines an action that can be used in a mock function. Typically, -// these actions only care about a subset of the arguments of the mock -// function. For example, if such an action only uses the second -// argument, it can be used in any mock function that takes >= 2 -// arguments where the type of the second argument is compatible. -// -// Therefore, the action implementation must be prepared to take more -// arguments than it needs. The ExcessiveArg type is used to -// represent those excessive arguments. In order to keep the compiler -// error messages tractable, we define it in the testing namespace -// instead of testing::internal. However, this is an INTERNAL TYPE -// and subject to change without notice, so a user MUST NOT USE THIS -// TYPE DIRECTLY. -struct ExcessiveArg {}; - -// Builds an implementation of an Action<> for some particular signature, using -// a class defined by an ACTION* macro. -template -struct ActionImpl; - -template -struct ImplBase { - struct Holder { - // Allows each copy of the Action<> to get to the Impl. - explicit operator const Impl&() const { return *ptr; } - std::shared_ptr ptr; - }; - using type = typename std::conditional::value, - Impl, Holder>::type; -}; - -template -struct ActionImpl : ImplBase::type { - using Base = typename ImplBase::type; - using function_type = R(Args...); - using args_type = std::tuple; - - ActionImpl() = default; // Only defined if appropriate for Base. - explicit ActionImpl(std::shared_ptr impl) : Base{std::move(impl)} {} - - R operator()(Args&&... arg) const { - static constexpr size_t kMaxArgs = - sizeof...(Args) <= 10 ? sizeof...(Args) : 10; - return Apply(std::make_index_sequence{}, - std::make_index_sequence<10 - kMaxArgs>{}, - args_type{std::forward(arg)...}); - } - - template - R Apply(std::index_sequence, std::index_sequence, - const args_type& args) const { - // Impl need not be specific to the signature of action being implemented; - // only the implementing function body needs to have all of the specific - // types instantiated. Up to 10 of the args that are provided by the - // args_type get passed, followed by a dummy of unspecified type for the - // remainder up to 10 explicit args. - static constexpr ExcessiveArg kExcessArg{}; - return static_cast(*this) - .template gmock_PerformImpl< - /*function_type=*/function_type, /*return_type=*/R, - /*args_type=*/args_type, - /*argN_type=*/ - typename std::tuple_element::type...>( - /*args=*/args, std::get(args)..., - ((void)excess_id, kExcessArg)...); - } -}; - -// Stores a default-constructed Impl as part of the Action<>'s -// std::function<>. The Impl should be trivial to copy. -template -::testing::Action MakeAction() { - return ::testing::Action(ActionImpl()); -} - -// Stores just the one given instance of Impl. -template -::testing::Action MakeAction(std::shared_ptr impl) { - return ::testing::Action(ActionImpl(std::move(impl))); -} - -#define GMOCK_INTERNAL_ARG_UNUSED(i, data, el) \ - , GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED const arg##i##_type& arg##i -#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_ \ - GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED const args_type& args GMOCK_PP_REPEAT( \ - GMOCK_INTERNAL_ARG_UNUSED, , 10) - -#define GMOCK_INTERNAL_ARG(i, data, el) , const arg##i##_type& arg##i -#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_ \ - const args_type& args GMOCK_PP_REPEAT(GMOCK_INTERNAL_ARG, , 10) - -#define GMOCK_INTERNAL_TEMPLATE_ARG(i, data, el) , typename arg##i##_type -#define GMOCK_ACTION_TEMPLATE_ARGS_NAMES_ \ - GMOCK_PP_TAIL(GMOCK_PP_REPEAT(GMOCK_INTERNAL_TEMPLATE_ARG, , 10)) - -#define GMOCK_INTERNAL_TYPENAME_PARAM(i, data, param) , typename param##_type -#define GMOCK_ACTION_TYPENAME_PARAMS_(params) \ - GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_TYPENAME_PARAM, , params)) - -#define GMOCK_INTERNAL_TYPE_PARAM(i, data, param) , param##_type -#define GMOCK_ACTION_TYPE_PARAMS_(params) \ - GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_TYPE_PARAM, , params)) - -#define GMOCK_INTERNAL_TYPE_GVALUE_PARAM(i, data, param) \ - , param##_type gmock_p##i -#define GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params) \ - GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_TYPE_GVALUE_PARAM, , params)) - -#define GMOCK_INTERNAL_GVALUE_PARAM(i, data, param) \ - , std::forward(gmock_p##i) -#define GMOCK_ACTION_GVALUE_PARAMS_(params) \ - GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_GVALUE_PARAM, , params)) - -#define GMOCK_INTERNAL_INIT_PARAM(i, data, param) \ - , param(::std::forward(gmock_p##i)) -#define GMOCK_ACTION_INIT_PARAMS_(params) \ - GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_INIT_PARAM, , params)) - -#define GMOCK_INTERNAL_FIELD_PARAM(i, data, param) param##_type param; -#define GMOCK_ACTION_FIELD_PARAMS_(params) \ - GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_FIELD_PARAM, , params) - -#define GMOCK_INTERNAL_ACTION(name, full_name, params) \ - template \ - class full_name { \ - public: \ - explicit full_name(GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params)) \ - : impl_(std::make_shared( \ - GMOCK_ACTION_GVALUE_PARAMS_(params))) {} \ - full_name(const full_name&) = default; \ - full_name(full_name&&) noexcept = default; \ - template \ - operator ::testing::Action() const { \ - return ::testing::internal::MakeAction(impl_); \ - } \ - \ - private: \ - class gmock_Impl { \ - public: \ - explicit gmock_Impl(GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params)) \ - : GMOCK_ACTION_INIT_PARAMS_(params) {} \ - template \ - return_type gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_) const; \ - GMOCK_ACTION_FIELD_PARAMS_(params) \ - }; \ - std::shared_ptr impl_; \ - }; \ - template \ - inline full_name name( \ - GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params)) GTEST_MUST_USE_RESULT_; \ - template \ - inline full_name name( \ - GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params)) { \ - return full_name( \ - GMOCK_ACTION_GVALUE_PARAMS_(params)); \ - } \ - template \ - template \ - return_type \ - full_name::gmock_Impl::gmock_PerformImpl( \ - GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const - -} // namespace internal - -// Similar to GMOCK_INTERNAL_ACTION, but no bound parameters are stored. -#define ACTION(name) \ - class name##Action { \ - public: \ - explicit name##Action() noexcept {} \ - name##Action(const name##Action&) noexcept {} \ - template \ - operator ::testing::Action() const { \ - return ::testing::internal::MakeAction(); \ - } \ - \ - private: \ - class gmock_Impl { \ - public: \ - template \ - return_type gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_) const; \ - }; \ - }; \ - inline name##Action name() GTEST_MUST_USE_RESULT_; \ - inline name##Action name() { return name##Action(); } \ - template \ - return_type name##Action::gmock_Impl::gmock_PerformImpl( \ - GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const - -#define ACTION_P(name, ...) \ - GMOCK_INTERNAL_ACTION(name, name##ActionP, (__VA_ARGS__)) - -#define ACTION_P2(name, ...) \ - GMOCK_INTERNAL_ACTION(name, name##ActionP2, (__VA_ARGS__)) - -#define ACTION_P3(name, ...) \ - GMOCK_INTERNAL_ACTION(name, name##ActionP3, (__VA_ARGS__)) - -#define ACTION_P4(name, ...) \ - GMOCK_INTERNAL_ACTION(name, name##ActionP4, (__VA_ARGS__)) - -#define ACTION_P5(name, ...) \ - GMOCK_INTERNAL_ACTION(name, name##ActionP5, (__VA_ARGS__)) - -#define ACTION_P6(name, ...) \ - GMOCK_INTERNAL_ACTION(name, name##ActionP6, (__VA_ARGS__)) - -#define ACTION_P7(name, ...) \ - GMOCK_INTERNAL_ACTION(name, name##ActionP7, (__VA_ARGS__)) - -#define ACTION_P8(name, ...) \ - GMOCK_INTERNAL_ACTION(name, name##ActionP8, (__VA_ARGS__)) - -#define ACTION_P9(name, ...) \ - GMOCK_INTERNAL_ACTION(name, name##ActionP9, (__VA_ARGS__)) - -#define ACTION_P10(name, ...) \ - GMOCK_INTERNAL_ACTION(name, name##ActionP10, (__VA_ARGS__)) - -} // namespace testing - -GTEST_DISABLE_MSC_WARNINGS_POP_() // 4100 - -#endif // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_ diff --git a/test/unit/googletest/googlemock/include/gmock/gmock-cardinalities.h b/test/unit/googletest/googlemock/include/gmock/gmock-cardinalities.h deleted file mode 100644 index 533e604..0000000 --- a/test/unit/googletest/googlemock/include/gmock/gmock-cardinalities.h +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2007, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Google Mock - a framework for writing C++ mock classes. -// -// This file implements some commonly used cardinalities. More -// cardinalities can be defined by the user implementing the -// CardinalityInterface interface if necessary. - -// IWYU pragma: private, include "gmock/gmock.h" -// IWYU pragma: friend gmock/.* - -#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_ -#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_ - -#include - -#include -#include // NOLINT - -#include "gmock/internal/gmock-port.h" -#include "gtest/gtest.h" - -GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ -/* class A needs to have dll-interface to be used by clients of class B */) - -namespace testing { - -// To implement a cardinality Foo, define: -// 1. a class FooCardinality that implements the -// CardinalityInterface interface, and -// 2. a factory function that creates a Cardinality object from a -// const FooCardinality*. -// -// The two-level delegation design follows that of Matcher, providing -// consistency for extension developers. It also eases ownership -// management as Cardinality objects can now be copied like plain values. - -// The implementation of a cardinality. -class CardinalityInterface { - public: - virtual ~CardinalityInterface() = default; - - // Conservative estimate on the lower/upper bound of the number of - // calls allowed. - virtual int ConservativeLowerBound() const { return 0; } - virtual int ConservativeUpperBound() const { return INT_MAX; } - - // Returns true if and only if call_count calls will satisfy this - // cardinality. - virtual bool IsSatisfiedByCallCount(int call_count) const = 0; - - // Returns true if and only if call_count calls will saturate this - // cardinality. - virtual bool IsSaturatedByCallCount(int call_count) const = 0; - - // Describes self to an ostream. - virtual void DescribeTo(::std::ostream* os) const = 0; -}; - -// A Cardinality is a copyable and IMMUTABLE (except by assignment) -// object that specifies how many times a mock function is expected to -// be called. The implementation of Cardinality is just a std::shared_ptr -// to const CardinalityInterface. Don't inherit from Cardinality! -class GTEST_API_ Cardinality { - public: - // Constructs a null cardinality. Needed for storing Cardinality - // objects in STL containers. - Cardinality() = default; - - // Constructs a Cardinality from its implementation. - explicit Cardinality(const CardinalityInterface* impl) : impl_(impl) {} - - // Conservative estimate on the lower/upper bound of the number of - // calls allowed. - int ConservativeLowerBound() const { return impl_->ConservativeLowerBound(); } - int ConservativeUpperBound() const { return impl_->ConservativeUpperBound(); } - - // Returns true if and only if call_count calls will satisfy this - // cardinality. - bool IsSatisfiedByCallCount(int call_count) const { - return impl_->IsSatisfiedByCallCount(call_count); - } - - // Returns true if and only if call_count calls will saturate this - // cardinality. - bool IsSaturatedByCallCount(int call_count) const { - return impl_->IsSaturatedByCallCount(call_count); - } - - // Returns true if and only if call_count calls will over-saturate this - // cardinality, i.e. exceed the maximum number of allowed calls. - bool IsOverSaturatedByCallCount(int call_count) const { - return impl_->IsSaturatedByCallCount(call_count) && - !impl_->IsSatisfiedByCallCount(call_count); - } - - // Describes self to an ostream - void DescribeTo(::std::ostream* os) const { impl_->DescribeTo(os); } - - // Describes the given actual call count to an ostream. - static void DescribeActualCallCountTo(int actual_call_count, - ::std::ostream* os); - - private: - std::shared_ptr impl_; -}; - -// Creates a cardinality that allows at least n calls. -GTEST_API_ Cardinality AtLeast(int n); - -// Creates a cardinality that allows at most n calls. -GTEST_API_ Cardinality AtMost(int n); - -// Creates a cardinality that allows any number of calls. -GTEST_API_ Cardinality AnyNumber(); - -// Creates a cardinality that allows between min and max calls. -GTEST_API_ Cardinality Between(int min, int max); - -// Creates a cardinality that allows exactly n calls. -GTEST_API_ Cardinality Exactly(int n); - -// Creates a cardinality from its implementation. -inline Cardinality MakeCardinality(const CardinalityInterface* c) { - return Cardinality(c); -} - -} // namespace testing - -GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 - -#endif // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_ diff --git a/test/unit/googletest/googlemock/include/gmock/gmock-function-mocker.h b/test/unit/googletest/googlemock/include/gmock/gmock-function-mocker.h deleted file mode 100644 index d2cb13c..0000000 --- a/test/unit/googletest/googlemock/include/gmock/gmock-function-mocker.h +++ /dev/null @@ -1,519 +0,0 @@ -// Copyright 2007, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Google Mock - a framework for writing C++ mock classes. -// -// This file implements MOCK_METHOD. - -// IWYU pragma: private, include "gmock/gmock.h" -// IWYU pragma: friend gmock/.* - -#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_FUNCTION_MOCKER_H_ -#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_FUNCTION_MOCKER_H_ - -#include -#include // IWYU pragma: keep -#include // IWYU pragma: keep - -#include "gmock/gmock-spec-builders.h" -#include "gmock/internal/gmock-internal-utils.h" -#include "gmock/internal/gmock-pp.h" - -namespace testing { -namespace internal { -template -using identity_t = T; - -template -struct ThisRefAdjuster { - template - using AdjustT = typename std::conditional< - std::is_const::type>::value, - typename std::conditional::value, - const T&, const T&&>::type, - typename std::conditional::value, T&, - T&&>::type>::type; - - template - static AdjustT Adjust(const MockType& mock) { - return static_cast>(const_cast(mock)); - } -}; - -constexpr bool PrefixOf(const char* a, const char* b) { - return *a == 0 || (*a == *b && internal::PrefixOf(a + 1, b + 1)); -} - -template -constexpr bool StartsWith(const char (&prefix)[N], const char (&str)[M]) { - return N <= M && internal::PrefixOf(prefix, str); -} - -template -constexpr bool EndsWith(const char (&suffix)[N], const char (&str)[M]) { - return N <= M && internal::PrefixOf(suffix, str + M - N); -} - -template -constexpr bool Equals(const char (&a)[N], const char (&b)[M]) { - return N == M && internal::PrefixOf(a, b); -} - -template -constexpr bool ValidateSpec(const char (&spec)[N]) { - return internal::Equals("const", spec) || - internal::Equals("override", spec) || - internal::Equals("final", spec) || - internal::Equals("noexcept", spec) || - (internal::StartsWith("noexcept(", spec) && - internal::EndsWith(")", spec)) || - internal::Equals("ref(&)", spec) || - internal::Equals("ref(&&)", spec) || - (internal::StartsWith("Calltype(", spec) && - internal::EndsWith(")", spec)); -} - -} // namespace internal - -// The style guide prohibits "using" statements in a namespace scope -// inside a header file. However, the FunctionMocker class template -// is meant to be defined in the ::testing namespace. The following -// line is just a trick for working around a bug in MSVC 8.0, which -// cannot handle it if we define FunctionMocker in ::testing. -using internal::FunctionMocker; -} // namespace testing - -#define MOCK_METHOD(...) \ - GMOCK_INTERNAL_WARNING_PUSH() \ - GMOCK_INTERNAL_WARNING_CLANG(ignored, "-Wunused-member-function") \ - GMOCK_PP_VARIADIC_CALL(GMOCK_INTERNAL_MOCK_METHOD_ARG_, __VA_ARGS__) \ - GMOCK_INTERNAL_WARNING_POP() - -#define GMOCK_INTERNAL_MOCK_METHOD_ARG_1(...) \ - GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__) - -#define GMOCK_INTERNAL_MOCK_METHOD_ARG_2(...) \ - GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__) - -#define GMOCK_INTERNAL_MOCK_METHOD_ARG_3(_Ret, _MethodName, _Args) \ - GMOCK_INTERNAL_MOCK_METHOD_ARG_4(_Ret, _MethodName, _Args, ()) - -#define GMOCK_INTERNAL_MOCK_METHOD_ARG_4(_Ret, _MethodName, _Args, _Spec) \ - GMOCK_INTERNAL_ASSERT_PARENTHESIS(_Args); \ - GMOCK_INTERNAL_ASSERT_PARENTHESIS(_Spec); \ - GMOCK_INTERNAL_ASSERT_VALID_SIGNATURE( \ - GMOCK_PP_NARG0 _Args, GMOCK_INTERNAL_SIGNATURE(_Ret, _Args)); \ - GMOCK_INTERNAL_ASSERT_VALID_SPEC(_Spec) \ - GMOCK_INTERNAL_MOCK_METHOD_IMPL( \ - GMOCK_PP_NARG0 _Args, _MethodName, GMOCK_INTERNAL_HAS_CONST(_Spec), \ - GMOCK_INTERNAL_HAS_OVERRIDE(_Spec), GMOCK_INTERNAL_HAS_FINAL(_Spec), \ - GMOCK_INTERNAL_GET_NOEXCEPT_SPEC(_Spec), \ - GMOCK_INTERNAL_GET_CALLTYPE_SPEC(_Spec), \ - GMOCK_INTERNAL_GET_REF_SPEC(_Spec), \ - (GMOCK_INTERNAL_SIGNATURE(_Ret, _Args))) - -#define GMOCK_INTERNAL_MOCK_METHOD_ARG_5(...) \ - GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__) - -#define GMOCK_INTERNAL_MOCK_METHOD_ARG_6(...) \ - GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__) - -#define GMOCK_INTERNAL_MOCK_METHOD_ARG_7(...) \ - GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__) - -#define GMOCK_INTERNAL_WRONG_ARITY(...) \ - static_assert( \ - false, \ - "MOCK_METHOD must be called with 3 or 4 arguments. _Ret, " \ - "_MethodName, _Args and optionally _Spec. _Args and _Spec must be " \ - "enclosed in parentheses. If _Ret is a type with unprotected commas, " \ - "it must also be enclosed in parentheses.") - -#define GMOCK_INTERNAL_ASSERT_PARENTHESIS(_Tuple) \ - static_assert( \ - GMOCK_PP_IS_ENCLOSED_PARENS(_Tuple), \ - GMOCK_PP_STRINGIZE(_Tuple) " should be enclosed in parentheses.") - -#define GMOCK_INTERNAL_ASSERT_VALID_SIGNATURE(_N, ...) \ - static_assert( \ - std::is_function<__VA_ARGS__>::value, \ - "Signature must be a function type, maybe return type contains " \ - "unprotected comma."); \ - static_assert( \ - ::testing::tuple_size::ArgumentTuple>::value == _N, \ - "This method does not take " GMOCK_PP_STRINGIZE( \ - _N) " arguments. Parenthesize all types with unprotected commas.") - -#define GMOCK_INTERNAL_ASSERT_VALID_SPEC(_Spec) \ - GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_ASSERT_VALID_SPEC_ELEMENT, ~, _Spec) - -#define GMOCK_INTERNAL_MOCK_METHOD_IMPL(_N, _MethodName, _Constness, \ - _Override, _Final, _NoexceptSpec, \ - _CallType, _RefSpec, _Signature) \ - typename ::testing::internal::Function::Result \ - GMOCK_INTERNAL_EXPAND(_CallType) \ - _MethodName(GMOCK_PP_REPEAT(GMOCK_INTERNAL_PARAMETER, _Signature, _N)) \ - GMOCK_PP_IF(_Constness, const, ) \ - _RefSpec _NoexceptSpec GMOCK_PP_IF(_Override, override, ) \ - GMOCK_PP_IF(_Final, final, ) { \ - GMOCK_MOCKER_(_N, _Constness, _MethodName) \ - .SetOwnerAndName(this, #_MethodName); \ - return GMOCK_MOCKER_(_N, _Constness, _MethodName) \ - .Invoke(GMOCK_PP_REPEAT(GMOCK_INTERNAL_FORWARD_ARG, _Signature, _N)); \ - } \ - ::testing::MockSpec gmock_##_MethodName( \ - GMOCK_PP_REPEAT(GMOCK_INTERNAL_MATCHER_PARAMETER, _Signature, _N)) \ - GMOCK_PP_IF(_Constness, const, ) _RefSpec { \ - GMOCK_MOCKER_(_N, _Constness, _MethodName).RegisterOwner(this); \ - return GMOCK_MOCKER_(_N, _Constness, _MethodName) \ - .With(GMOCK_PP_REPEAT(GMOCK_INTERNAL_MATCHER_ARGUMENT, , _N)); \ - } \ - ::testing::MockSpec gmock_##_MethodName( \ - const ::testing::internal::WithoutMatchers&, \ - GMOCK_PP_IF(_Constness, const, )::testing::internal::Function< \ - GMOCK_PP_REMOVE_PARENS(_Signature)>*) const _RefSpec _NoexceptSpec { \ - return ::testing::internal::ThisRefAdjuster::Adjust(*this) \ - .gmock_##_MethodName(GMOCK_PP_REPEAT( \ - GMOCK_INTERNAL_A_MATCHER_ARGUMENT, _Signature, _N)); \ - } \ - mutable ::testing::FunctionMocker \ - GMOCK_MOCKER_(_N, _Constness, _MethodName) - -#define GMOCK_INTERNAL_EXPAND(...) __VA_ARGS__ - -// Valid modifiers. -#define GMOCK_INTERNAL_HAS_CONST(_Tuple) \ - GMOCK_PP_HAS_COMMA(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_DETECT_CONST, ~, _Tuple)) - -#define GMOCK_INTERNAL_HAS_OVERRIDE(_Tuple) \ - GMOCK_PP_HAS_COMMA( \ - GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_DETECT_OVERRIDE, ~, _Tuple)) - -#define GMOCK_INTERNAL_HAS_FINAL(_Tuple) \ - GMOCK_PP_HAS_COMMA(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_DETECT_FINAL, ~, _Tuple)) - -#define GMOCK_INTERNAL_GET_NOEXCEPT_SPEC(_Tuple) \ - GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_NOEXCEPT_SPEC_IF_NOEXCEPT, ~, _Tuple) - -#define GMOCK_INTERNAL_NOEXCEPT_SPEC_IF_NOEXCEPT(_i, _, _elem) \ - GMOCK_PP_IF( \ - GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_NOEXCEPT(_i, _, _elem)), \ - _elem, ) - -#define GMOCK_INTERNAL_GET_CALLTYPE_SPEC(_Tuple) \ - GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_CALLTYPE_SPEC_IF_CALLTYPE, ~, _Tuple) - -#define GMOCK_INTERNAL_CALLTYPE_SPEC_IF_CALLTYPE(_i, _, _elem) \ - GMOCK_PP_IF( \ - GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_CALLTYPE(_i, _, _elem)), \ - GMOCK_PP_CAT(GMOCK_INTERNAL_UNPACK_, _elem), ) - -#define GMOCK_INTERNAL_GET_REF_SPEC(_Tuple) \ - GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_REF_SPEC_IF_REF, ~, _Tuple) - -#define GMOCK_INTERNAL_REF_SPEC_IF_REF(_i, _, _elem) \ - GMOCK_PP_IF(GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_REF(_i, _, _elem)), \ - GMOCK_PP_CAT(GMOCK_INTERNAL_UNPACK_, _elem), ) - -#ifdef GMOCK_INTERNAL_STRICT_SPEC_ASSERT -#define GMOCK_INTERNAL_ASSERT_VALID_SPEC_ELEMENT(_i, _, _elem) \ - static_assert( \ - ::testing::internal::ValidateSpec(GMOCK_PP_STRINGIZE(_elem)), \ - "Token \'" GMOCK_PP_STRINGIZE( \ - _elem) "\' cannot be recognized as a valid specification " \ - "modifier. Is a ',' missing?"); -#else -#define GMOCK_INTERNAL_ASSERT_VALID_SPEC_ELEMENT(_i, _, _elem) \ - static_assert( \ - (GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_CONST(_i, _, _elem)) + \ - GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_OVERRIDE(_i, _, _elem)) + \ - GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_FINAL(_i, _, _elem)) + \ - GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_NOEXCEPT(_i, _, _elem)) + \ - GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_REF(_i, _, _elem)) + \ - GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_CALLTYPE(_i, _, _elem))) == 1, \ - GMOCK_PP_STRINGIZE( \ - _elem) " cannot be recognized as a valid specification modifier."); -#endif // GMOCK_INTERNAL_STRICT_SPEC_ASSERT - -// Modifiers implementation. -#define GMOCK_INTERNAL_DETECT_CONST(_i, _, _elem) \ - GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_CONST_I_, _elem) - -#define GMOCK_INTERNAL_DETECT_CONST_I_const , - -#define GMOCK_INTERNAL_DETECT_OVERRIDE(_i, _, _elem) \ - GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_OVERRIDE_I_, _elem) - -#define GMOCK_INTERNAL_DETECT_OVERRIDE_I_override , - -#define GMOCK_INTERNAL_DETECT_FINAL(_i, _, _elem) \ - GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_FINAL_I_, _elem) - -#define GMOCK_INTERNAL_DETECT_FINAL_I_final , - -#define GMOCK_INTERNAL_DETECT_NOEXCEPT(_i, _, _elem) \ - GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_NOEXCEPT_I_, _elem) - -#define GMOCK_INTERNAL_DETECT_NOEXCEPT_I_noexcept , - -#define GMOCK_INTERNAL_DETECT_REF(_i, _, _elem) \ - GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_REF_I_, _elem) - -#define GMOCK_INTERNAL_DETECT_REF_I_ref , - -#define GMOCK_INTERNAL_UNPACK_ref(x) x - -#define GMOCK_INTERNAL_DETECT_CALLTYPE(_i, _, _elem) \ - GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_CALLTYPE_I_, _elem) - -#define GMOCK_INTERNAL_DETECT_CALLTYPE_I_Calltype , - -#define GMOCK_INTERNAL_UNPACK_Calltype(...) __VA_ARGS__ - -// Note: The use of `identity_t` here allows _Ret to represent return types that -// would normally need to be specified in a different way. For example, a method -// returning a function pointer must be written as -// -// fn_ptr_return_t (*method(method_args_t...))(fn_ptr_args_t...) -// -// But we only support placing the return type at the beginning. To handle this, -// we wrap all calls in identity_t, so that a declaration will be expanded to -// -// identity_t method(method_args_t...) -// -// This allows us to work around the syntactic oddities of function/method -// types. -#define GMOCK_INTERNAL_SIGNATURE(_Ret, _Args) \ - ::testing::internal::identity_t( \ - GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_GET_TYPE, _, _Args)) - -#define GMOCK_INTERNAL_GET_TYPE(_i, _, _elem) \ - GMOCK_PP_COMMA_IF(_i) \ - GMOCK_PP_IF(GMOCK_PP_IS_BEGIN_PARENS(_elem), GMOCK_PP_REMOVE_PARENS, \ - GMOCK_PP_IDENTITY) \ - (_elem) - -#define GMOCK_INTERNAL_PARAMETER(_i, _Signature, _) \ - GMOCK_PP_COMMA_IF(_i) \ - GMOCK_INTERNAL_ARG_O(_i, GMOCK_PP_REMOVE_PARENS(_Signature)) \ - gmock_a##_i - -#define GMOCK_INTERNAL_FORWARD_ARG(_i, _Signature, _) \ - GMOCK_PP_COMMA_IF(_i) \ - ::std::forward(gmock_a##_i) - -#define GMOCK_INTERNAL_MATCHER_PARAMETER(_i, _Signature, _) \ - GMOCK_PP_COMMA_IF(_i) \ - GMOCK_INTERNAL_MATCHER_O(_i, GMOCK_PP_REMOVE_PARENS(_Signature)) \ - gmock_a##_i - -#define GMOCK_INTERNAL_MATCHER_ARGUMENT(_i, _1, _2) \ - GMOCK_PP_COMMA_IF(_i) \ - gmock_a##_i - -#define GMOCK_INTERNAL_A_MATCHER_ARGUMENT(_i, _Signature, _) \ - GMOCK_PP_COMMA_IF(_i) \ - ::testing::A() - -#define GMOCK_INTERNAL_ARG_O(_i, ...) \ - typename ::testing::internal::Function<__VA_ARGS__>::template Arg<_i>::type - -#define GMOCK_INTERNAL_MATCHER_O(_i, ...) \ - const ::testing::Matcher::template Arg<_i>::type>& - -#define MOCK_METHOD0(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 0, __VA_ARGS__) -#define MOCK_METHOD1(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 1, __VA_ARGS__) -#define MOCK_METHOD2(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 2, __VA_ARGS__) -#define MOCK_METHOD3(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 3, __VA_ARGS__) -#define MOCK_METHOD4(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 4, __VA_ARGS__) -#define MOCK_METHOD5(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 5, __VA_ARGS__) -#define MOCK_METHOD6(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 6, __VA_ARGS__) -#define MOCK_METHOD7(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 7, __VA_ARGS__) -#define MOCK_METHOD8(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 8, __VA_ARGS__) -#define MOCK_METHOD9(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 9, __VA_ARGS__) -#define MOCK_METHOD10(m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(, , m, 10, __VA_ARGS__) - -#define MOCK_CONST_METHOD0(m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(const, , m, 0, __VA_ARGS__) -#define MOCK_CONST_METHOD1(m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(const, , m, 1, __VA_ARGS__) -#define MOCK_CONST_METHOD2(m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(const, , m, 2, __VA_ARGS__) -#define MOCK_CONST_METHOD3(m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(const, , m, 3, __VA_ARGS__) -#define MOCK_CONST_METHOD4(m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(const, , m, 4, __VA_ARGS__) -#define MOCK_CONST_METHOD5(m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(const, , m, 5, __VA_ARGS__) -#define MOCK_CONST_METHOD6(m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(const, , m, 6, __VA_ARGS__) -#define MOCK_CONST_METHOD7(m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(const, , m, 7, __VA_ARGS__) -#define MOCK_CONST_METHOD8(m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(const, , m, 8, __VA_ARGS__) -#define MOCK_CONST_METHOD9(m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(const, , m, 9, __VA_ARGS__) -#define MOCK_CONST_METHOD10(m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(const, , m, 10, __VA_ARGS__) - -#define MOCK_METHOD0_T(m, ...) MOCK_METHOD0(m, __VA_ARGS__) -#define MOCK_METHOD1_T(m, ...) MOCK_METHOD1(m, __VA_ARGS__) -#define MOCK_METHOD2_T(m, ...) MOCK_METHOD2(m, __VA_ARGS__) -#define MOCK_METHOD3_T(m, ...) MOCK_METHOD3(m, __VA_ARGS__) -#define MOCK_METHOD4_T(m, ...) MOCK_METHOD4(m, __VA_ARGS__) -#define MOCK_METHOD5_T(m, ...) MOCK_METHOD5(m, __VA_ARGS__) -#define MOCK_METHOD6_T(m, ...) MOCK_METHOD6(m, __VA_ARGS__) -#define MOCK_METHOD7_T(m, ...) MOCK_METHOD7(m, __VA_ARGS__) -#define MOCK_METHOD8_T(m, ...) MOCK_METHOD8(m, __VA_ARGS__) -#define MOCK_METHOD9_T(m, ...) MOCK_METHOD9(m, __VA_ARGS__) -#define MOCK_METHOD10_T(m, ...) MOCK_METHOD10(m, __VA_ARGS__) - -#define MOCK_CONST_METHOD0_T(m, ...) MOCK_CONST_METHOD0(m, __VA_ARGS__) -#define MOCK_CONST_METHOD1_T(m, ...) MOCK_CONST_METHOD1(m, __VA_ARGS__) -#define MOCK_CONST_METHOD2_T(m, ...) MOCK_CONST_METHOD2(m, __VA_ARGS__) -#define MOCK_CONST_METHOD3_T(m, ...) MOCK_CONST_METHOD3(m, __VA_ARGS__) -#define MOCK_CONST_METHOD4_T(m, ...) MOCK_CONST_METHOD4(m, __VA_ARGS__) -#define MOCK_CONST_METHOD5_T(m, ...) MOCK_CONST_METHOD5(m, __VA_ARGS__) -#define MOCK_CONST_METHOD6_T(m, ...) MOCK_CONST_METHOD6(m, __VA_ARGS__) -#define MOCK_CONST_METHOD7_T(m, ...) MOCK_CONST_METHOD7(m, __VA_ARGS__) -#define MOCK_CONST_METHOD8_T(m, ...) MOCK_CONST_METHOD8(m, __VA_ARGS__) -#define MOCK_CONST_METHOD9_T(m, ...) MOCK_CONST_METHOD9(m, __VA_ARGS__) -#define MOCK_CONST_METHOD10_T(m, ...) MOCK_CONST_METHOD10(m, __VA_ARGS__) - -#define MOCK_METHOD0_WITH_CALLTYPE(ct, m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 0, __VA_ARGS__) -#define MOCK_METHOD1_WITH_CALLTYPE(ct, m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 1, __VA_ARGS__) -#define MOCK_METHOD2_WITH_CALLTYPE(ct, m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 2, __VA_ARGS__) -#define MOCK_METHOD3_WITH_CALLTYPE(ct, m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 3, __VA_ARGS__) -#define MOCK_METHOD4_WITH_CALLTYPE(ct, m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 4, __VA_ARGS__) -#define MOCK_METHOD5_WITH_CALLTYPE(ct, m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 5, __VA_ARGS__) -#define MOCK_METHOD6_WITH_CALLTYPE(ct, m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 6, __VA_ARGS__) -#define MOCK_METHOD7_WITH_CALLTYPE(ct, m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 7, __VA_ARGS__) -#define MOCK_METHOD8_WITH_CALLTYPE(ct, m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 8, __VA_ARGS__) -#define MOCK_METHOD9_WITH_CALLTYPE(ct, m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 9, __VA_ARGS__) -#define MOCK_METHOD10_WITH_CALLTYPE(ct, m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 10, __VA_ARGS__) - -#define MOCK_CONST_METHOD0_WITH_CALLTYPE(ct, m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 0, __VA_ARGS__) -#define MOCK_CONST_METHOD1_WITH_CALLTYPE(ct, m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 1, __VA_ARGS__) -#define MOCK_CONST_METHOD2_WITH_CALLTYPE(ct, m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 2, __VA_ARGS__) -#define MOCK_CONST_METHOD3_WITH_CALLTYPE(ct, m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 3, __VA_ARGS__) -#define MOCK_CONST_METHOD4_WITH_CALLTYPE(ct, m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 4, __VA_ARGS__) -#define MOCK_CONST_METHOD5_WITH_CALLTYPE(ct, m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 5, __VA_ARGS__) -#define MOCK_CONST_METHOD6_WITH_CALLTYPE(ct, m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 6, __VA_ARGS__) -#define MOCK_CONST_METHOD7_WITH_CALLTYPE(ct, m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 7, __VA_ARGS__) -#define MOCK_CONST_METHOD8_WITH_CALLTYPE(ct, m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 8, __VA_ARGS__) -#define MOCK_CONST_METHOD9_WITH_CALLTYPE(ct, m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 9, __VA_ARGS__) -#define MOCK_CONST_METHOD10_WITH_CALLTYPE(ct, m, ...) \ - GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 10, __VA_ARGS__) - -#define MOCK_METHOD0_T_WITH_CALLTYPE(ct, m, ...) \ - MOCK_METHOD0_WITH_CALLTYPE(ct, m, __VA_ARGS__) -#define MOCK_METHOD1_T_WITH_CALLTYPE(ct, m, ...) \ - MOCK_METHOD1_WITH_CALLTYPE(ct, m, __VA_ARGS__) -#define MOCK_METHOD2_T_WITH_CALLTYPE(ct, m, ...) \ - MOCK_METHOD2_WITH_CALLTYPE(ct, m, __VA_ARGS__) -#define MOCK_METHOD3_T_WITH_CALLTYPE(ct, m, ...) \ - MOCK_METHOD3_WITH_CALLTYPE(ct, m, __VA_ARGS__) -#define MOCK_METHOD4_T_WITH_CALLTYPE(ct, m, ...) \ - MOCK_METHOD4_WITH_CALLTYPE(ct, m, __VA_ARGS__) -#define MOCK_METHOD5_T_WITH_CALLTYPE(ct, m, ...) \ - MOCK_METHOD5_WITH_CALLTYPE(ct, m, __VA_ARGS__) -#define MOCK_METHOD6_T_WITH_CALLTYPE(ct, m, ...) \ - MOCK_METHOD6_WITH_CALLTYPE(ct, m, __VA_ARGS__) -#define MOCK_METHOD7_T_WITH_CALLTYPE(ct, m, ...) \ - MOCK_METHOD7_WITH_CALLTYPE(ct, m, __VA_ARGS__) -#define MOCK_METHOD8_T_WITH_CALLTYPE(ct, m, ...) \ - MOCK_METHOD8_WITH_CALLTYPE(ct, m, __VA_ARGS__) -#define MOCK_METHOD9_T_WITH_CALLTYPE(ct, m, ...) \ - MOCK_METHOD9_WITH_CALLTYPE(ct, m, __VA_ARGS__) -#define MOCK_METHOD10_T_WITH_CALLTYPE(ct, m, ...) \ - MOCK_METHOD10_WITH_CALLTYPE(ct, m, __VA_ARGS__) - -#define MOCK_CONST_METHOD0_T_WITH_CALLTYPE(ct, m, ...) \ - MOCK_CONST_METHOD0_WITH_CALLTYPE(ct, m, __VA_ARGS__) -#define MOCK_CONST_METHOD1_T_WITH_CALLTYPE(ct, m, ...) \ - MOCK_CONST_METHOD1_WITH_CALLTYPE(ct, m, __VA_ARGS__) -#define MOCK_CONST_METHOD2_T_WITH_CALLTYPE(ct, m, ...) \ - MOCK_CONST_METHOD2_WITH_CALLTYPE(ct, m, __VA_ARGS__) -#define MOCK_CONST_METHOD3_T_WITH_CALLTYPE(ct, m, ...) \ - MOCK_CONST_METHOD3_WITH_CALLTYPE(ct, m, __VA_ARGS__) -#define MOCK_CONST_METHOD4_T_WITH_CALLTYPE(ct, m, ...) \ - MOCK_CONST_METHOD4_WITH_CALLTYPE(ct, m, __VA_ARGS__) -#define MOCK_CONST_METHOD5_T_WITH_CALLTYPE(ct, m, ...) \ - MOCK_CONST_METHOD5_WITH_CALLTYPE(ct, m, __VA_ARGS__) -#define MOCK_CONST_METHOD6_T_WITH_CALLTYPE(ct, m, ...) \ - MOCK_CONST_METHOD6_WITH_CALLTYPE(ct, m, __VA_ARGS__) -#define MOCK_CONST_METHOD7_T_WITH_CALLTYPE(ct, m, ...) \ - MOCK_CONST_METHOD7_WITH_CALLTYPE(ct, m, __VA_ARGS__) -#define MOCK_CONST_METHOD8_T_WITH_CALLTYPE(ct, m, ...) \ - MOCK_CONST_METHOD8_WITH_CALLTYPE(ct, m, __VA_ARGS__) -#define MOCK_CONST_METHOD9_T_WITH_CALLTYPE(ct, m, ...) \ - MOCK_CONST_METHOD9_WITH_CALLTYPE(ct, m, __VA_ARGS__) -#define MOCK_CONST_METHOD10_T_WITH_CALLTYPE(ct, m, ...) \ - MOCK_CONST_METHOD10_WITH_CALLTYPE(ct, m, __VA_ARGS__) - -#define GMOCK_INTERNAL_MOCK_METHODN(constness, ct, Method, args_num, ...) \ - GMOCK_INTERNAL_ASSERT_VALID_SIGNATURE( \ - args_num, ::testing::internal::identity_t<__VA_ARGS__>); \ - GMOCK_INTERNAL_MOCK_METHOD_IMPL( \ - args_num, Method, GMOCK_PP_NARG0(constness), 0, 0, , ct, , \ - (::testing::internal::identity_t<__VA_ARGS__>)) - -#define GMOCK_MOCKER_(arity, constness, Method) \ - GTEST_CONCAT_TOKEN_(gmock##constness##arity##_##Method##_, __LINE__) - -#endif // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_FUNCTION_MOCKER_H_ diff --git a/test/unit/googletest/googlemock/include/gmock/gmock-matchers.h b/test/unit/googletest/googlemock/include/gmock/gmock-matchers.h deleted file mode 100644 index 16cf9c3..0000000 --- a/test/unit/googletest/googlemock/include/gmock/gmock-matchers.h +++ /dev/null @@ -1,5677 +0,0 @@ -// Copyright 2007, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Google Mock - a framework for writing C++ mock classes. -// -// The MATCHER* family of macros can be used in a namespace scope to -// define custom matchers easily. -// -// Basic Usage -// =========== -// -// The syntax -// -// MATCHER(name, description_string) { statements; } -// -// defines a matcher with the given name that executes the statements, -// which must return a bool to indicate if the match succeeds. Inside -// the statements, you can refer to the value being matched by 'arg', -// and refer to its type by 'arg_type'. -// -// The description string documents what the matcher does, and is used -// to generate the failure message when the match fails. Since a -// MATCHER() is usually defined in a header file shared by multiple -// C++ source files, we require the description to be a C-string -// literal to avoid possible side effects. It can be empty, in which -// case we'll use the sequence of words in the matcher name as the -// description. -// -// For example: -// -// MATCHER(IsEven, "") { return (arg % 2) == 0; } -// -// allows you to write -// -// // Expects mock_foo.Bar(n) to be called where n is even. -// EXPECT_CALL(mock_foo, Bar(IsEven())); -// -// or, -// -// // Verifies that the value of some_expression is even. -// EXPECT_THAT(some_expression, IsEven()); -// -// If the above assertion fails, it will print something like: -// -// Value of: some_expression -// Expected: is even -// Actual: 7 -// -// where the description "is even" is automatically calculated from the -// matcher name IsEven. -// -// Argument Type -// ============= -// -// Note that the type of the value being matched (arg_type) is -// determined by the context in which you use the matcher and is -// supplied to you by the compiler, so you don't need to worry about -// declaring it (nor can you). This allows the matcher to be -// polymorphic. For example, IsEven() can be used to match any type -// where the value of "(arg % 2) == 0" can be implicitly converted to -// a bool. In the "Bar(IsEven())" example above, if method Bar() -// takes an int, 'arg_type' will be int; if it takes an unsigned long, -// 'arg_type' will be unsigned long; and so on. -// -// Parameterizing Matchers -// ======================= -// -// Sometimes you'll want to parameterize the matcher. For that you -// can use another macro: -// -// MATCHER_P(name, param_name, description_string) { statements; } -// -// For example: -// -// MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; } -// -// will allow you to write: -// -// EXPECT_THAT(Blah("a"), HasAbsoluteValue(n)); -// -// which may lead to this message (assuming n is 10): -// -// Value of: Blah("a") -// Expected: has absolute value 10 -// Actual: -9 -// -// Note that both the matcher description and its parameter are -// printed, making the message human-friendly. -// -// In the matcher definition body, you can write 'foo_type' to -// reference the type of a parameter named 'foo'. For example, in the -// body of MATCHER_P(HasAbsoluteValue, value) above, you can write -// 'value_type' to refer to the type of 'value'. -// -// We also provide MATCHER_P2, MATCHER_P3, ..., up to MATCHER_P$n to -// support multi-parameter matchers. -// -// Describing Parameterized Matchers -// ================================= -// -// The last argument to MATCHER*() is a string-typed expression. The -// expression can reference all of the matcher's parameters and a -// special bool-typed variable named 'negation'. When 'negation' is -// false, the expression should evaluate to the matcher's description; -// otherwise it should evaluate to the description of the negation of -// the matcher. For example, -// -// using testing::PrintToString; -// -// MATCHER_P2(InClosedRange, low, hi, -// std::string(negation ? "is not" : "is") + " in range [" + -// PrintToString(low) + ", " + PrintToString(hi) + "]") { -// return low <= arg && arg <= hi; -// } -// ... -// EXPECT_THAT(3, InClosedRange(4, 6)); -// EXPECT_THAT(3, Not(InClosedRange(2, 4))); -// -// would generate two failures that contain the text: -// -// Expected: is in range [4, 6] -// ... -// Expected: is not in range [2, 4] -// -// If you specify "" as the description, the failure message will -// contain the sequence of words in the matcher name followed by the -// parameter values printed as a tuple. For example, -// -// MATCHER_P2(InClosedRange, low, hi, "") { ... } -// ... -// EXPECT_THAT(3, InClosedRange(4, 6)); -// EXPECT_THAT(3, Not(InClosedRange(2, 4))); -// -// would generate two failures that contain the text: -// -// Expected: in closed range (4, 6) -// ... -// Expected: not (in closed range (2, 4)) -// -// Types of Matcher Parameters -// =========================== -// -// For the purpose of typing, you can view -// -// MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... } -// -// as shorthand for -// -// template -// FooMatcherPk -// Foo(p1_type p1, ..., pk_type pk) { ... } -// -// When you write Foo(v1, ..., vk), the compiler infers the types of -// the parameters v1, ..., and vk for you. If you are not happy with -// the result of the type inference, you can specify the types by -// explicitly instantiating the template, as in Foo(5, -// false). As said earlier, you don't get to (or need to) specify -// 'arg_type' as that's determined by the context in which the matcher -// is used. You can assign the result of expression Foo(p1, ..., pk) -// to a variable of type FooMatcherPk. This -// can be useful when composing matchers. -// -// While you can instantiate a matcher template with reference types, -// passing the parameters by pointer usually makes your code more -// readable. If, however, you still want to pass a parameter by -// reference, be aware that in the failure message generated by the -// matcher you will see the value of the referenced object but not its -// address. -// -// Explaining Match Results -// ======================== -// -// Sometimes the matcher description alone isn't enough to explain why -// the match has failed or succeeded. For example, when expecting a -// long string, it can be very helpful to also print the diff between -// the expected string and the actual one. To achieve that, you can -// optionally stream additional information to a special variable -// named result_listener, whose type is a pointer to class -// MatchResultListener: -// -// MATCHER_P(EqualsLongString, str, "") { -// if (arg == str) return true; -// -// *result_listener << "the difference: " -/// << DiffStrings(str, arg); -// return false; -// } -// -// Overloading Matchers -// ==================== -// -// You can overload matchers with different numbers of parameters: -// -// MATCHER_P(Blah, a, description_string1) { ... } -// MATCHER_P2(Blah, a, b, description_string2) { ... } -// -// Caveats -// ======= -// -// When defining a new matcher, you should also consider implementing -// MatcherInterface or using MakePolymorphicMatcher(). These -// approaches require more work than the MATCHER* macros, but also -// give you more control on the types of the value being matched and -// the matcher parameters, which may leads to better compiler error -// messages when the matcher is used wrong. They also allow -// overloading matchers based on parameter types (as opposed to just -// based on the number of parameters). -// -// MATCHER*() can only be used in a namespace scope as templates cannot be -// declared inside of a local class. -// -// More Information -// ================ -// -// To learn more about using these macros, please search for 'MATCHER' -// on -// https://github.com/google/googletest/blob/main/docs/gmock_cook_book.md -// -// This file also implements some commonly used argument matchers. More -// matchers can be defined by the user implementing the -// MatcherInterface interface if necessary. -// -// See googletest/include/gtest/gtest-matchers.h for the definition of class -// Matcher, class MatcherInterface, and others. - -// IWYU pragma: private, include "gmock/gmock.h" -// IWYU pragma: friend gmock/.* - -#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_ -#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // NOLINT -#include -#include -#include -#include -#include - -#include "gmock/internal/gmock-internal-utils.h" -#include "gmock/internal/gmock-port.h" -#include "gmock/internal/gmock-pp.h" -#include "gtest/gtest.h" - -// MSVC warning C5046 is new as of VS2017 version 15.8. -#if defined(_MSC_VER) && _MSC_VER >= 1915 -#define GMOCK_MAYBE_5046_ 5046 -#else -#define GMOCK_MAYBE_5046_ -#endif - -GTEST_DISABLE_MSC_WARNINGS_PUSH_( - 4251 GMOCK_MAYBE_5046_ /* class A needs to have dll-interface to be used by - clients of class B */ - /* Symbol involving type with internal linkage not defined */) - -namespace testing { - -// To implement a matcher Foo for type T, define: -// 1. a class FooMatcherImpl that implements the -// MatcherInterface interface, and -// 2. a factory function that creates a Matcher object from a -// FooMatcherImpl*. -// -// The two-level delegation design makes it possible to allow a user -// to write "v" instead of "Eq(v)" where a Matcher is expected, which -// is impossible if we pass matchers by pointers. It also eases -// ownership management as Matcher objects can now be copied like -// plain values. - -// A match result listener that stores the explanation in a string. -class StringMatchResultListener : public MatchResultListener { - public: - StringMatchResultListener() : MatchResultListener(&ss_) {} - - // Returns the explanation accumulated so far. - std::string str() const { return ss_.str(); } - - // Clears the explanation accumulated so far. - void Clear() { ss_.str(""); } - - private: - ::std::stringstream ss_; - - StringMatchResultListener(const StringMatchResultListener&) = delete; - StringMatchResultListener& operator=(const StringMatchResultListener&) = - delete; -}; - -// Anything inside the 'internal' namespace IS INTERNAL IMPLEMENTATION -// and MUST NOT BE USED IN USER CODE!!! -namespace internal { - -// The MatcherCastImpl class template is a helper for implementing -// MatcherCast(). We need this helper in order to partially -// specialize the implementation of MatcherCast() (C++ allows -// class/struct templates to be partially specialized, but not -// function templates.). - -// This general version is used when MatcherCast()'s argument is a -// polymorphic matcher (i.e. something that can be converted to a -// Matcher but is not one yet; for example, Eq(value)) or a value (for -// example, "hello"). -template -class MatcherCastImpl { - public: - static Matcher Cast(const M& polymorphic_matcher_or_value) { - // M can be a polymorphic matcher, in which case we want to use - // its conversion operator to create Matcher. Or it can be a value - // that should be passed to the Matcher's constructor. - // - // We can't call Matcher(polymorphic_matcher_or_value) when M is a - // polymorphic matcher because it'll be ambiguous if T has an implicit - // constructor from M (this usually happens when T has an implicit - // constructor from any type). - // - // It won't work to unconditionally implicit_cast - // polymorphic_matcher_or_value to Matcher because it won't trigger - // a user-defined conversion from M to T if one exists (assuming M is - // a value). - return CastImpl(polymorphic_matcher_or_value, - std::is_convertible>{}, - std::is_convertible{}); - } - - private: - template - static Matcher CastImpl(const M& polymorphic_matcher_or_value, - std::true_type /* convertible_to_matcher */, - std::integral_constant) { - // M is implicitly convertible to Matcher, which means that either - // M is a polymorphic matcher or Matcher has an implicit constructor - // from M. In both cases using the implicit conversion will produce a - // matcher. - // - // Even if T has an implicit constructor from M, it won't be called because - // creating Matcher would require a chain of two user-defined conversions - // (first to create T from M and then to create Matcher from T). - return polymorphic_matcher_or_value; - } - - // M can't be implicitly converted to Matcher, so M isn't a polymorphic - // matcher. It's a value of a type implicitly convertible to T. Use direct - // initialization to create a matcher. - static Matcher CastImpl(const M& value, - std::false_type /* convertible_to_matcher */, - std::true_type /* convertible_to_T */) { - return Matcher(ImplicitCast_(value)); - } - - // M can't be implicitly converted to either Matcher or T. Attempt to use - // polymorphic matcher Eq(value) in this case. - // - // Note that we first attempt to perform an implicit cast on the value and - // only fall back to the polymorphic Eq() matcher afterwards because the - // latter calls bool operator==(const Lhs& lhs, const Rhs& rhs) in the end - // which might be undefined even when Rhs is implicitly convertible to Lhs - // (e.g. std::pair vs. std::pair). - // - // We don't define this method inline as we need the declaration of Eq(). - static Matcher CastImpl(const M& value, - std::false_type /* convertible_to_matcher */, - std::false_type /* convertible_to_T */); -}; - -// This more specialized version is used when MatcherCast()'s argument -// is already a Matcher. This only compiles when type T can be -// statically converted to type U. -template -class MatcherCastImpl> { - public: - static Matcher Cast(const Matcher& source_matcher) { - return Matcher(new Impl(source_matcher)); - } - - private: - class Impl : public MatcherInterface { - public: - explicit Impl(const Matcher& source_matcher) - : source_matcher_(source_matcher) {} - - // We delegate the matching logic to the source matcher. - bool MatchAndExplain(T x, MatchResultListener* listener) const override { - using FromType = typename std::remove_cv::type>::type>::type; - using ToType = typename std::remove_cv::type>::type>::type; - // Do not allow implicitly converting base*/& to derived*/&. - static_assert( - // Do not trigger if only one of them is a pointer. That implies a - // regular conversion and not a down_cast. - (std::is_pointer::type>::value != - std::is_pointer::type>::value) || - std::is_same::value || - !std::is_base_of::value, - "Can't implicitly convert from to "); - - // Do the cast to `U` explicitly if necessary. - // Otherwise, let implicit conversions do the trick. - using CastType = - typename std::conditional::value, - T&, U>::type; - - return source_matcher_.MatchAndExplain(static_cast(x), - listener); - } - - void DescribeTo(::std::ostream* os) const override { - source_matcher_.DescribeTo(os); - } - - void DescribeNegationTo(::std::ostream* os) const override { - source_matcher_.DescribeNegationTo(os); - } - - private: - const Matcher source_matcher_; - }; -}; - -// This even more specialized version is used for efficiently casting -// a matcher to its own type. -template -class MatcherCastImpl> { - public: - static Matcher Cast(const Matcher& matcher) { return matcher; } -}; - -// Template specialization for parameterless Matcher. -template -class MatcherBaseImpl { - public: - MatcherBaseImpl() = default; - - template - operator ::testing::Matcher() const { // NOLINT(runtime/explicit) - return ::testing::Matcher(new - typename Derived::template gmock_Impl()); - } -}; - -// Template specialization for Matcher with parameters. -template