





from __future__ import annotations

from collections.abc import Callable
from typing import Any, Generic, TypeVar

_UpperCAmelCase							:			Dict        =							TypeVar("""T""")

class   lowerCAmelCase				(   Generic[T]     ):
 def __init__( self    : Dict     ,	UpperCAmelCase    : list[T]     ,	UpperCAmelCase    : Callable[[T, T], T]      )     ->      None:
     lowerCamelCase__       : Any | T   					=						None

     lowerCamelCase__       : int   					=						len(UpperCAmelCase      )
     lowerCamelCase__       : list[T]   					=						[any_type for _ in range(self.N      )] + arr
     lowerCamelCase__       : List[str]   					=						fnc
     self.build()
 def 	A_      ( self    : List[Any]      )     ->      None:
     for p in range(self.N - 1     ,	0     ,	-1      ):
         lowerCamelCase__       : List[Any]   					=						self.fn(self.st[p * 2]     ,	self.st[p * 2 + 1]      )
 def 	A_      ( self    : List[Any]     ,	UpperCAmelCase    : int     ,	UpperCAmelCase    : T      )     ->      None:
     p += self.N
     lowerCamelCase__       : Tuple   					=						v
     while p > 1:
         lowerCamelCase__       : Optional[int]   					=						p // 2
         lowerCamelCase__       : List[str]   					=						self.fn(self.st[p * 2]     ,	self.st[p * 2 + 1]      )







 def 	A_      ( self    : int     ,	UpperCAmelCase    : int     ,	UpperCAmelCase    : int      )     ->      T | None:  # noqa: E741
     lowerCamelCase__					,				lowerCamelCase__       : Optional[Any]   					=						l + self.N, r + self.N

     lowerCamelCase__       : T | None   					=						None
     while l <= r:
         if l % 2 == 1:
             lowerCamelCase__       : Tuple   					=						self.st[l] if res is None else self.fn(UpperCAmelCase     ,	self.st[l]      )
         if r % 2 == 0:
             lowerCamelCase__       : Optional[Any]   					=						self.st[r] if res is None else self.fn(UpperCAmelCase     ,	self.st[r]      )
         lowerCamelCase__					,				lowerCamelCase__       : Union[str, Any]   					=						(l + 1) // 2, (r - 1) // 2
     return res


if __name__ == "__main__":
    from functools import reduce

    _UpperCAmelCase							:			Tuple        =							[1, 10, -2, 9, -3, 8, 4, -7, 5, 6, 11, -12]

    _UpperCAmelCase							:			Any        =							{
        0: 7,
        1: 2,
        2: 6,
        3: -14,
        4: 5,
        5: 4,
        6: 7,
        7: -10,
        8: 9,
        9: 10,
        10: 12,
        11: 1,
    }

    _UpperCAmelCase							:			Tuple        =							SegmentTree(test_array, min)
    _UpperCAmelCase							:			Optional[Any]        =							SegmentTree(test_array, max)
    _UpperCAmelCase							:			Tuple        =							SegmentTree(test_array, lambda a, b: a + b)


    def 			SCREAMING_SNAKE_CASE (			)     ->	None:
        for i in range(len(_UpperCAmelCase					)					):
            for j in range(_UpperCAmelCase		,      len(_UpperCAmelCase					)					):
                lowerCamelCase__       : Optional[int]   					=						reduce(_UpperCAmelCase		,      test_array[i : j + 1]					)
                lowerCamelCase__       : Tuple   					=						reduce(_UpperCAmelCase		,      test_array[i : j + 1]					)
                lowerCamelCase__       : int   					=						reduce(lambda _UpperCAmelCase		,      _UpperCAmelCase					: a + b		,      test_array[i : j + 1]					)
                assert min_range == min_segment_tree.query(_UpperCAmelCase		,      _UpperCAmelCase					)
                assert max_range == max_segment_tree.query(_UpperCAmelCase		,      _UpperCAmelCase					)
                assert sum_range == sum_segment_tree.query(_UpperCAmelCase		,      _UpperCAmelCase					)

    test_all_segments()

    for index, value in test_updates.items():
        _UpperCAmelCase							:			Tuple        =							value
        min_segment_tree.update(index, value)
        max_segment_tree.update(index, value)
        sum_segment_tree.update(index, value)
        test_all_segments()












import gc
import threading
import time

import psutil
import torch

class   lowerCAmelCase				:
 def __init__( self    : str      )     ->      Union[str, Any]:
     lowerCamelCase__       : Optional[Any]   					=						psutil.Process()
     lowerCamelCase__       : Union[str, Any]   					=						False
 def 	A_      ( self    : Optional[int]      )     ->      int:
     lowerCamelCase__       : Optional[Any]   					=						-1

     while True:
         lowerCamelCase__       : Dict   					=						max(self.process.memory_info().rss     ,	self.cpu_memory_peak      )

         # can't sleep or will not catch the peak right (this comment is here on purpose)
         if not self.peak_monitoring:
             break
 def 	A_      ( self    : Tuple      )     ->      Dict:
     lowerCamelCase__       : List[Any]   					=						True
     lowerCamelCase__       : List[str]   					=						threading.Thread(target=self.peak_monitor      )
     lowerCamelCase__       : Union[str, Any]   					=						True
     self.thread.start()







 def 	A_      ( self    : str      )     ->      Dict:
     lowerCamelCase__       : int   					=						False
     self.thread.join()
     return self.cpu_memory_peak


_UpperCAmelCase							:			Dict        =							PeakCPUMemory()


def 			SCREAMING_SNAKE_CASE (			)     ->	Union[str, Any]:
    # Time
    lowerCamelCase__       : List[Any]   					=						{'time': time.time()}

    gc.collect()
    torch.cuda.empty_cache()

    # CPU mem
    lowerCamelCase__       : List[str]   					=						psutil.Process().memory_info().rss
    cpu_peak_tracker.start()

    # GPU mem
    for i in range(torch.cuda.device_count()					):
        lowerCamelCase__       : Union[str, Any]   					=						torch.cuda.memory_allocated(_UpperCAmelCase					)
    torch.cuda.reset_peak_memory_stats()

    return measures


def 			SCREAMING_SNAKE_CASE (			_UpperCAmelCase					)     ->	Tuple:
    # Time
    lowerCamelCase__       : Optional[int]   					=						{'time': time.time() - start_measures['time']}

    gc.collect()
    torch.cuda.empty_cache()

    # CPU mem
    lowerCamelCase__       : Dict   					=						(psutil.Process().memory_info().rss - start_measures['cpu']) / 2**20
    lowerCamelCase__       : int   					=						(cpu_peak_tracker.stop() - start_measures['cpu']) / 2**20

    # GPU mem
    for i in range(torch.cuda.device_count()					):
        lowerCamelCase__       : List[str]   					=						(torch.cuda.memory_allocated(_UpperCAmelCase					) - start_measures[str(_UpperCAmelCase					)]) / 2**20
        lowerCamelCase__       : Optional[Any]   					=						(torch.cuda.max_memory_allocated(_UpperCAmelCase					) - start_measures[str(_UpperCAmelCase					)]) / 2**20

    return measures


def 			SCREAMING_SNAKE_CASE (			_UpperCAmelCase		,      _UpperCAmelCase					)     ->	Dict:
    print(F"""{description}:"""					)
    print(F"""- Time: {measures["time"]:.2f}s"""					)
    for i in range(torch.cuda.device_count()					):
        print(F"""- GPU {i} allocated: {measures[str(_UpperCAmelCase					)]:.2f}MiB"""					)
        lowerCamelCase__       : List[str]   					=						measures[F"""{i}-peak"""]
        print(F"""- GPU {i} peak: {peak:.2f}MiB"""					)
    print(F"""- CPU RAM allocated: {measures["cpu"]:.2f}MiB"""					)
    print(F"""- CPU RAM peak: {measures["cpu-peak"]:.2f}MiB"""					)





