text: from functools import wraps
  from inspect import Parameter, signature
  from types import FunctionType
  
  from .query import Cond, QuerySet
  from .utils import NO_VALUE, arg_to_sql, comma_join, is_iterable
  
  
  def binary_operator(func):
      """
      Decorates a function to mark it as a binary operator.
      """
  
      @wraps(func)
      def wrapper(*args, **kwargs):
          ret = func(*args, **kwargs)
          ret.is_binary_operator = True
          return ret
  
      return wrapper
  
  
  def type_conversion(func):
      """
      Decorates a function to mark it as a type conversion function.
      The metaclass automatically generates "OrZero" and "OrNull" combinators
      for the decorated function.
      """
  
      @wraps(func)
      def wrapper(*args, **kwargs):
          return func(*args, **kwargs)
  
      wrapper.f_type = "type_conversion"
      return wrapper
  
  
  def aggregate(func):
      """
      Decorates a function to mark it as an aggregate function.
      The metaclass automatically generates combinators such as "OrDefault",
      "OrNull", "If" etc. for the decorated function.
      """
  
      @wraps(func)
      def wrapper(*args, **kwargs):
          return func(*args, **kwargs)
  
      wrapper.f_type = "aggregate"
      return wrapper
  
  
  def with_utf8_support(func):
      """
      Decorates a function to mark it as a string function that has a UTF8 variant.
      The metaclass automatically generates a "UTF8" combinator for the decorated function.
      """
  
      @wraps(func)
      def wrapper(*args, **kwargs):
          return func(*args, **kwargs)
  
      wrapper.f_type = "with_utf8_support"
      return wrapper
  
  
  def parametric(func):
      """
      Decorates a function to convert it to a parametric function, such
      as `quantile(level)(expr)`.
      """
  
      @wraps(func)
      def wrapper(*parameters):
          @wraps(func)
          def inner(*args, **kwargs):
              f = func(*args, **kwargs)
              # Append the parameter to the function name
              parameters_str = comma_join(map(str, parameters))
              f.name = "%s(%s)" % (f.name, parameters_str)
              return f
  
          return inner
  
      wrapper.f_parametric = True
      return wrapper
  
  
  class FunctionOperatorsMixin(object):
      """
      A mixin for implementing Python operators using F objects.
      """
  
      # Comparison operators
  
      def __lt__(self, other):
          return F.less(self, other)
  
      def __le__(self, other):
          return F.lessOrEquals(self, other)
  
      def __eq__(self, other):
          return F.equals(self, other)
  
      def __ne__(self, other):
          return F.notEquals(self, other)
  
      def __gt__(self, other):
          return F.greater(self, other)
  
      def __ge__(self, other):
          return F.greaterOrEquals(self, other)
  
      # Arithmetic operators
  
      def __add__(self, other):
          return F.plus(self, other)
  
      def __radd__(self, other):
          return F.plus(other, self)
  
      def __sub__(self, other):
          return F.minus(self, other)
  
      def __rsub__(self, other):
          return F.minus(other, self)
  
      def __mul__(self, other):
          return F.multiply(self, other)
  
      def __rmul__(self, other):
          return F.multiply(other, self)
  
      def __truediv__(self, other):
          return F.divide(self, other)
  
      def __rtruediv__(self, other):
          return F.divide(other, self)
  
      def __floordiv__(self, other):
          return F.intDiv(self, other)
  
      def __rfloordiv__(self, other):
          return F.intDiv(other, self)
  
      def __mod__(self, other):
          return F.modulo(self, other)
  
      def __rmod__(self, other):
          return F.modulo(other, self)
  
      def __neg__(self):
          return F.negate(self)
  
      def __pos__(self):
          return self
  
      # Logical operators
  
      def __and__(self, other):
          return F._and(self, other)
  
      def __rand__(self, other):
          return F._and(other, self)
  
      def __or__(self, other):
          return F._or(self, other)
  
      def __ror__(self, other):
          return F._or(other, self)
  
      def __xor__(self, other):
          return F._xor(self, other)
  
      def __rxor__(self, other):
          return F._xor(other, self)
  
      def __invert__(self):
          return F._not(self)
  
      def isIn(self, others):
          return F._in(self, others)
  
      def isNotIn(self, others):
          return F._notIn(self, others)
  
  
  class FMeta(type):
  
      FUNCTION_COMBINATORS = {
          "type_conversion": [
              {"suffix": "OrZero"},
              {"suffix": "OrNull"},
          ],
          "aggregate": [
              {"suffix": "OrDefault"},
              {"suffix": "OrNull"},
              {"suffix": "If", "args": ["cond"]},
              {"suffix": "OrDefaultIf", "args": ["cond"]},
              {"suffix": "OrNullIf", "args": ["cond"]},
          ],
          "with_utf8_support": [
              {"suffix": "UTF8"},
          ],
      }
  
      def __init__(cls, name, bases, dct):
          for name, obj in dct.items():
              if hasattr(obj, "__func__"):
                  f_type = getattr(obj.__func__, "f_type", "")
                  for combinator in FMeta.FUNCTION_COMBINATORS.get(f_type, []):
                      new_name = name + combinator["suffix"]
                      FMeta._add_func(cls, obj.__func__, new_name, combinator.get("args"))
  
      @staticmethod
      def _add_func(cls, base_func, new_name, extra_args):
          """
          Adds a new func to the cls, based on the signature of the given base_func but with a new name.
          """
          # Get the function's signature
          sig = signature(base_func)
          new_sig = str(sig)[1:-1]  # omit the parentheses
          args = comma_join(sig.parameters)
          # Add extra args
          if extra_args:
              if args:
                  args = comma_join([args] + extra_args)
                  new_sig = comma_join([new_sig] + extra_args)
              else:
                  args = comma_join(extra_args)
                  new_sig = comma_join(extra_args)
          # Get default values for args
          argdefs = tuple(p.default for p in sig.parameters.values() if p.default != Parameter.empty)
          # Build the new function
          new_code = compile(
              'def {new_name}({new_sig}): return F("{new_name}", {args})'.format(**locals()), __file__, "exec"
          )
          new_func = FunctionType(code=new_code.co_consts[0], globals=globals(), name=new_name, argdefs=argdefs)
          # If base_func was parametric, new_func should be too
          if getattr(base_func, "f_parametric", False):
              new_func = parametric(new_func)
          # Attach to class
          setattr(cls, new_name, new_func)
  
  
  class F(Cond, FunctionOperatorsMixin, metaclass=FMeta):
      """
      Represents a database function call and its arguments.
      It doubles as a query condition when the function returns a boolean result.
      """
  
      def __init__(self, name, *args):
          """
          Initializer.
  
          """
          self.name = name
          self.args = args
          self.is_binary_operator = False
  
      def __repr__(self):
          return self.to_sql()
  
      def to_sql(self, *args):
          """
          Generates an SQL string for this function and its arguments.
          For example if the function name is a symbol of a binary operator:
              (2.54 * `height`)
          For other functions:
              gcd(12, 300)
          """
          if self.is_binary_operator:
              prefix = ""
              sep = " " + self.name + " "
          else:
              prefix = self.name
              sep = ", "
          arg_strs = (arg_to_sql(arg) for arg in self.args if arg != NO_VALUE)
          return prefix + "(" + sep.join(arg_strs) + ")"
  
      # Arithmetic functions
  
      @staticmethod
      @binary_operator
      def plus(a, b):
          return F("+", a, b)
  
      @staticmethod
      @binary_operator
      def minus(a, b):
          return F("-", a, b)
  
      @staticmethod
      @binary_operator
      def multiply(a, b):
          return F("*", a, b)
  
      @staticmethod
      @binary_operator
      def divide(a, b):
          return F("/", a, b)
  
      @staticmethod
      def intDiv(a, b):
          return F("intDiv", a, b)
  
      @staticmethod
      def intDivOrZero(a, b):
          return F("intDivOrZero", a, b)
  
      @staticmethod
      @binary_operator
      def modulo(a, b):
          return F("%", a, b)
  
      @staticmethod
      def negate(a):
          return F("negate", a)
  
      @staticmethod
      def abs(a):
          return F("abs", a)
  
      @staticmethod
      def gcd(a, b):
          return F("gcd", a, b)
  
      @staticmethod
      def lcm(a, b):
          return F("lcm", a, b)
  
      # Comparison functions
  
      @staticmethod
      @binary_operator
      def equals(a, b):
          return F("=", a, b)
  
      @staticmethod
      @binary_operator
      def notEquals(a, b):
          return F("!=", a, b)
  
      @staticmethod
      @binary_operator
      def less(a, b):
          return F("<", a, b)
  
      @staticmethod
      @binary_operator
      def greater(a, b):
          return F(">", a, b)
  
      @staticmethod
      @binary_operator
      def lessOrEquals(a, b):
          return F("<=", a, b)
  
      @staticmethod
      @binary_operator
      def greaterOrEquals(a, b):
          return F(">=", a, b)
  
      # Logical functions (should be used as python operators: & | ^ ~)
  
      @staticmethod
      @binary_operator
      def _and(a, b):
          return F("AND", a, b)
  
      @staticmethod
      @binary_operator
      def _or(a, b):
          return F("OR", a, b)
  
      @staticmethod
      def _xor(a, b):
          return F("xor", a, b)
  
      @staticmethod
      def _not(a):
          return F("not", a)
  
      # in / not in
  
      @staticmethod
      @binary_operator
      def _in(a, b):
          if is_iterable(b) and not isinstance(b, (tuple, QuerySet)):
              b = tuple(b)
          return F("IN", a, b)
  
      @staticmethod
      @binary_operator
      def _notIn(a, b):
          if is_iterable(b) and not isinstance(b, (tuple, QuerySet)):
              b = tuple(b)
          return F("NOT IN", a, b)
  
      # Functions for working with dates and times
  
      @staticmethod
      def toYear(d, timezone=NO_VALUE):
          return F("toYear", d, timezone)
  
      @staticmethod
      def toISOYear(d, timezone=NO_VALUE):
          return F("toISOYear", d, timezone)
  
      @staticmethod
      def toQuarter(d, timezone=NO_VALUE):
          return F("toQuarter", d, timezone)
  
      @staticmethod
      def toMonth(d, timezone=NO_VALUE):
          return F("toMonth", d, timezone)
  
      @staticmethod
      def toWeek(d, mode=0, timezone=NO_VALUE):
          return F("toWeek", d, mode, timezone)
  
      @staticmethod
      def toISOWeek(d, timezone=NO_VALUE):
          return F("toISOWeek", d, timezone)
  
      @staticmethod
      def toDayOfYear(d, timezone=NO_VALUE):
          return F("toDayOfYear", d, timezone)
  
      @staticmethod
      def toDayOfMonth(d, timezone=NO_VALUE):
          return F("toDayOfMonth", d, timezone)
  
      @staticmethod
      def toDayOfWeek(d, timezone=NO_VALUE):
          return F("toDayOfWeek", d, timezone)
  
      @staticmethod
      def toHour(d, timezone=NO_VALUE):
          return F("toHour", d, timezone)
  
      @staticmethod
      def toMinute(d, timezone=NO_VALUE):
          return F("toMinute", d, timezone)
  
      @staticmethod
      def toSecond(d, timezone=NO_VALUE):
          return F("toSecond", d, timezone)
  
      @staticmethod
      def toMonday(d, timezone=NO_VALUE):
          return F("toMonday", d, timezone)
  
      @staticmethod
      def toStartOfMonth(d, timezone=NO_VALUE):
          return F("toStartOfMonth", d, timezone)
  
      @staticmethod
      def toStartOfQuarter(d, timezone=NO_VALUE):
          return F("toStartOfQuarter", d, timezone)
  
      @staticmethod
      def toStartOfYear(d, timezone=NO_VALUE):
          return F("toStartOfYear", d, timezone)
  
      @staticmethod
      def toStartOfISOYear(d, timezone=NO_VALUE):
          return F("toStartOfISOYear", d, timezone)
  
      @staticmethod
      def toStartOfTenMinutes(d, timezone=NO_VALUE):
          return F("toStartOfTenMinutes", d, timezone)
  
      @staticmethod
      def toStartOfWeek(d, timezone=NO_VALUE):
          return F("toStartOfWeek", d, timezone)
  
      @staticmethod
      def toStartOfMinute(d, timezone=NO_VALUE):
          return F("toStartOfMinute", d, timezone)
  
      @staticmethod
      def toStartOfFiveMinute(d, timezone=NO_VALUE):
          return F("toStartOfFiveMinute", d, timezone)
  
      @staticmethod
      def toStartOfFifteenMinutes(d, timezone=NO_VALUE):
          return F("toStartOfFifteenMinutes", d, timezone)
  
      @staticmethod
      def toStartOfHour(d, timezone=NO_VALUE):
          return F("toStartOfHour", d, timezone)
  
      @staticmethod
      def toStartOfDay(d, timezone=NO_VALUE):
          return F("toStartOfDay", d, timezone)
  
      @staticmethod
      def toTime(d, timezone=NO_VALUE):
          return F("toTime", d, timezone)
  
      @staticmethod
      def toTimeZone(dt, timezone):
          return F("toTimeZone", dt, timezone)
  
      @staticmethod
      def toUnixTimestamp(dt, timezone=NO_VALUE):
          return F("toUnixTimestamp", dt, timezone)
  
      @staticmethod
      def toYYYYMM(dt, timezone=NO_VALUE):
          return F("toYYYYMM", dt, timezone)
  
      @staticmethod
      def toYYYYMMDD(dt, timezone=NO_VALUE):
          return F("toYYYYMMDD", dt, timezone)
  
      @staticmethod
      def toYYYYMMDDhhmmss(dt, timezone=NO_VALUE):
          return F("toYYYYMMDDhhmmss", dt, timezone)
  
      @staticmethod
      def toRelativeYearNum(d, timezone=NO_VALUE):
          return F("toRelativeYearNum", d, timezone)
  
      @staticmethod
      def toRelativeMonthNum(d, timezone=NO_VALUE):
          return F("toRelativeMonthNum", d, timezone)
  
      @staticmethod
      def toRelativeWeekNum(d, timezone=NO_VALUE):
          return F("toRelativeWeekNum", d, timezone)
  
      @staticmethod
      def toRelativeDayNum(d, timezone=NO_VALUE):
          return F("toRelativeDayNum", d, timezone)
  
      @staticmethod
      def toRelativeHourNum(d, timezone=NO_VALUE):
          return F("toRelativeHourNum", d, timezone)
  
      @staticmethod
      def toRelativeMinuteNum(d, timezone=NO_VALUE):
          return F("toRelativeMinuteNum", d, timezone)
  
      @staticmethod
      def toRelativeSecondNum(d, timezone=NO_VALUE):
          return F("toRelativeSecondNum", d, timezone)
  
      @staticmethod
      def now():
          return F("now")
  
      @staticmethod
      def today():
          return F("today")
  
      @staticmethod
      def yesterday():
          return F("yesterday")
  
      @staticmethod
      def timeSlot(d):
          return F("timeSlot", d)
  
      @staticmethod
      def timeSlots(start_time, duration):
          return F("timeSlots", start_time, F.toUInt32(duration))
  
      @staticmethod
      def formatDateTime(d, format, timezone=NO_VALUE):
          return F("formatDateTime", d, format, timezone)
  
      @staticmethod
      def addDays(d, n, timezone=NO_VALUE):
          return F("addDays", d, n, timezone)
  
      @staticmethod
      def addHours(d, n, timezone=NO_VALUE):
          return F("addHours", d, n, timezone)
  
      @staticmethod
      def addMinutes(d, n, timezone=NO_VALUE):
          return F("addMinutes", d, n, timezone)
  
      @staticmethod
      def addMonths(d, n, timezone=NO_VALUE):
          return F("addMonths", d, n, timezone)
  
      @staticmethod
      def addQuarters(d, n, timezone=NO_VALUE):
          return F("addQuarters", d, n, timezone)
  
      @staticmethod
      def addSeconds(d, n, timezone=NO_VALUE):
          return F("addSeconds", d, n, timezone)
  
      @staticmethod
      def addWeeks(d, n, timezone=NO_VALUE):
          return F("addWeeks", d, n, timezone)
  
      @staticmethod
      def addYears(d, n, timezone=NO_VALUE):
          return F("addYears", d, n, timezone)
  
      @staticmethod
      def subtractDays(d, n, timezone=NO_VALUE):
          return F("subtractDays", d, n, timezone)
  
      @staticmethod
      def subtractHours(d, n, timezone=NO_VALUE):
          return F("subtractHours", d, n, timezone)
  
      @staticmethod
      def subtractMinutes(d, n, timezone=NO_VALUE):
          return F("subtractMinutes", d, n, timezone)
  
      @staticmethod
      def subtractMonths(d, n, timezone=NO_VALUE):
          return F("subtractMonths", d, n, timezone)
  
      @staticmethod
      def subtractQuarters(d, n, timezone=NO_VALUE):
          return F("subtractQuarters", d, n, timezone)
  
      @staticmethod
      def subtractSeconds(d, n, timezone=NO_VALUE):
          return F("subtractSeconds", d, n, timezone)
  
      @staticmethod
      def subtractWeeks(d, n, timezone=NO_VALUE):
          return F("subtractWeeks", d, n, timezone)
  
      @staticmethod
      def subtractYears(d, n, timezone=NO_VALUE):
          return F("subtractYears", d, n, timezone)
  
      @staticmethod
      def toIntervalSecond(number):
          return F("toIntervalSecond", number)
  
      @staticmethod
      def toIntervalMinute(number):
          return F("toIntervalMinute", number)
  
      @staticmethod
      def toIntervalHour(number):
          return F("toIntervalHour", number)
  
      @staticmethod
      def toIntervalDay(number):
          return F("toIntervalDay", number)
  
      @staticmethod
      def toIntervalWeek(number):
          return F("toIntervalWeek", number)
  
      @staticmethod
      def toIntervalMonth(number):
          return F("toIntervalMonth", number)
  
      @staticmethod
      def toIntervalQuarter(number):
          return F("toIntervalQuarter", number)
  
      @staticmethod
      def toIntervalYear(number):
          return F("toIntervalYear", number)
  
      # Type conversion functions
  
      @staticmethod
      @type_conversion
      def toUInt8(x):
          return F("toUInt8", x)
  
      @staticmethod
      @type_conversion
      def toUInt16(x):
          return F("toUInt16", x)
  
      @staticmethod
      @type_conversion
      def toUInt32(x):
          return F("toUInt32", x)
  
      @staticmethod
      @type_conversion
      def toUInt64(x):
          return F("toUInt64", x)
  
      @staticmethod
      @type_conversion
      def toInt8(x):
          return F("toInt8", x)
  
      @staticmethod
      @type_conversion
      def toInt16(x):
          return F("toInt16", x)
  
      @staticmethod
      @type_conversion
      def toInt32(x):
          return F("toInt32", x)
  
      @staticmethod
      @type_conversion
      def toInt64(x):
          return F("toInt64", x)
  
      @staticmethod
      @type_conversion
      def toFloat32(x):
          return F("toFloat32", x)
  
      @staticmethod
      @type_conversion
      def toFloat64(x):
          return F("toFloat64", x)
  
      @staticmethod
      @type_conversion
      def toDecimal32(x, scale):
          return F("toDecimal32", x, scale)
  
      @staticmethod
      @type_conversion
      def toDecimal64(x, scale):
          return F("toDecimal64", x, scale)
  
      @staticmethod
      @type_conversion
      def toDecimal128(x, scale):
          return F("toDecimal128", x, scale)
  
      @staticmethod
      @type_conversion
      def toDate(x):
          return F("toDate", x)
  
      @staticmethod
      @type_conversion
      def toDateTime(x):
          return F("toDateTime", x)
  
      @staticmethod
      @type_conversion
      def toDateTime64(x, precision, timezone=NO_VALUE):
          return F("toDateTime64", x, precision, timezone)
  
      @staticmethod
      def toString(x):
          return F("toString", x)
  
      @staticmethod
      def toFixedString(s, length):
          return F("toFixedString", s, length)
  
      @staticmethod
      def toStringCutToZero(s):
          return F("toStringCutToZero", s)
  
      @staticmethod
      def CAST(x, type):
          return F("CAST", x, type)
  
      @staticmethod
      @type_conversion
      def parseDateTimeBestEffort(d, timezone=NO_VALUE):
          return F("parseDateTimeBestEffort", d, timezone)
  
      # Functions for working with strings
  
      @staticmethod
      def empty(s):
          return F("empty", s)
  
      @staticmethod
      def notEmpty(s):
          return F("notEmpty", s)
  
      @staticmethod
      @with_utf8_support
      def length(s):
          return F("length", s)
  
      @staticmethod
      @with_utf8_support
      def lower(s):
          return F("lower", s)
  
      @staticmethod
      @with_utf8_support
      def upper(s):
          return F("upper", s)
  
      @staticmethod
      @with_utf8_support
      def reverse(s):
          return F("reverse", s)
  
      @staticmethod
      def concat(*args):
          return F("concat", *args)
  
      @staticmethod
      @with_utf8_support
      def substring(s, offset, length):
          return F("substring", s, offset, length)
  
      @staticmethod
      def appendTrailingCharIfAbsent(s, c):
          return F("appendTrailingCharIfAbsent", s, c)
  
      @staticmethod
      def convertCharset(s, from_charset, to_charset):
          return F("convertCharset", s, from_charset, to_charset)
  
      @staticmethod
      def base64Encode(s):
          return F("base64Encode", s)
  
      @staticmethod
      def base64Decode(s):
          return F("base64Decode", s)
  
      @staticmethod
      def tryBase64Decode(s):
          return F("tryBase64Decode", s)
  
      @staticmethod
      def endsWith(s, suffix):
          return F("endsWith", s, suffix)
  
      @staticmethod
      def startsWith(s, prefix):
          return F("startsWith", s, prefix)
  
      @staticmethod
      def trimLeft(s):
          return F("trimLeft", s)
  
      @staticmethod
      def trimRight(s):
          return F("trimRight", s)
  
      @staticmethod
      def trimBoth(s):
          return F("trimBoth", s)
  
      @staticmethod
      def CRC32(s):
          return F("CRC32", s)
  
      # Functions for searching in strings
  
      @staticmethod
      @with_utf8_support
      def position(haystack, needle):
          return F("position", haystack, needle)
  
      @staticmethod
      @with_utf8_support
      def positionCaseInsensitive(haystack, needle):
          return F("positionCaseInsensitive", haystack, needle)
  
      @staticmethod
      def like(haystack, pattern):
          return F("like", haystack, pattern)
  
      @staticmethod
      def notLike(haystack, pattern):
          return F("notLike", haystack, pattern)
  
      @staticmethod
      def match(haystack, pattern):
          return F("match", haystack, pattern)
  
      @staticmethod
      def extract(haystack, pattern):
          return F("extract", haystack, pattern)
  
      @staticmethod
      def extractAll(haystack, pattern):
          return F("extractAll", haystack, pattern)
  
      @staticmethod
      @with_utf8_support
      def ngramDistance(haystack, needle):
          return F("ngramDistance", haystack, needle)
  
      @staticmethod
      @with_utf8_support
      def ngramDistanceCaseInsensitive(haystack, needle):
          return F("ngramDistanceCaseInsensitive", haystack, needle)
  
      @staticmethod
      @with_utf8_support
      def ngramSearch(haystack, needle):
          return F("ngramSearch", haystack, needle)
  
      @staticmethod
      @with_utf8_support
      def ngramSearchCaseInsensitive(haystack, needle):
          return F("ngramSearchCaseInsensitive", haystack, needle)
  
      # Functions for replacing in strings
  
      @staticmethod
      def replace(haystack, pattern, replacement):
          return F("replace", haystack, pattern, replacement)
  
      replaceAll = replace
  
      @staticmethod
      def replaceAll(haystack, pattern, replacement):
          return F("replaceAll", haystack, pattern, replacement)
  
      @staticmethod
      def replaceOne(haystack, pattern, replacement):
          return F("replaceOne", haystack, pattern, replacement)
  
      @staticmethod
      def replaceRegexpAll(haystack, pattern, replacement):
          return F("replaceRegexpAll", haystack, pattern, replacement)
  
      @staticmethod
      def replaceRegexpOne(haystack, pattern, replacement):
          return F("replaceRegexpOne", haystack, pattern, replacement)
  
      @staticmethod
      def regexpQuoteMeta(x):
          return F("regexpQuoteMeta", x)
  
      # Mathematical functions
  
      @staticmethod
      def e():
          return F("e")
  
      @staticmethod
      def pi():
          return F("pi")
  
      @staticmethod
      def exp(x):
          return F("exp", x)
  
      @staticmethod
      def log(x):
          return F("log", x)
  
      ln = log
  
      @staticmethod
      def exp2(x):
          return F("exp2", x)
  
      @staticmethod
      def log2(x):
          return F("log2", x)
  
      @staticmethod
      def exp10(x):
          return F("exp10", x)
  
      @staticmethod
      def log10(x):
          return F("log10", x)
  
      @staticmethod
      def sqrt(x):
          return F("sqrt", x)
  
      @staticmethod
      def cbrt(x):
          return F("cbrt", x)
  
      @staticmethod
      def erf(x):
          return F("erf", x)
  
      @staticmethod
      def erfc(x):
          return F("erfc", x)
  
      @staticmethod
      def lgamma(x):
          return F("lgamma", x)
  
      @staticmethod
      def tgamma(x):
          return F("tgamma", x)
  
      @staticmethod
      def sin(x):
          return F("sin", x)
  
      @staticmethod
      def cos(x):
          return F("cos", x)
  
      @staticmethod
      def tan(x):
          return F("tan", x)
  
      @staticmethod
      def asin(x):
          return F("asin", x)
  
      @staticmethod
      def acos(x):
          return F("acos", x)
  
      @staticmethod
      def atan(x):
          return F("atan", x)
  
      @staticmethod
      def power(x, y):
          return F("power", x, y)
  
      pow = power
  
      @staticmethod
      def intExp10(x):
          return F("intExp10", x)
  
      @staticmethod
      def intExp2(x):
          return F("intExp2", x)
  
      # Rounding functions
  
      @staticmethod
      def floor(x, n=None):
          return F("floor", x, n) if n else F("floor", x)
  
      @staticmethod
      def ceiling(x, n=None):
          return F("ceiling", x, n) if n else F("ceiling", x)
  
      ceil = ceiling
  
      @staticmethod
      def round(x, n=None):
          return F("round", x, n) if n else F("round", x)
  
      @staticmethod
      def roundAge(x):
          return F("roundAge", x)
  
      @staticmethod
      def roundDown(x, y):
          return F("roundDown", x, y)
  
      @staticmethod
      def roundDuration(x):
          return F("roundDuration", x)
  
      @staticmethod
      def roundToExp2(x):
          return F("roundToExp2", x)
  
      # Functions for working with arrays
  
      @staticmethod
      def emptyArrayDate():
          return F("emptyArrayDate")
  
      @staticmethod
      def emptyArrayDateTime():
          return F("emptyArrayDateTime")
  
      @staticmethod
      def emptyArrayFloat32():
          return F("emptyArrayFloat32")
  
      @staticmethod
      def emptyArrayFloat64():
          return F("emptyArrayFloat64")
  
      @staticmethod
      def emptyArrayInt16():
          return F("emptyArrayInt16")
  
      @staticmethod
      def emptyArrayInt32():
          return F("emptyArrayInt32")
  
      @staticmethod
      def emptyArrayInt64():
          return F("emptyArrayInt64")
  
      @staticmethod
      def emptyArrayInt8():
          return F("emptyArrayInt8")
  
      @staticmethod
      def emptyArrayString():
          return F("emptyArrayString")
  
      @staticmethod
      def emptyArrayUInt16():
          return F("emptyArrayUInt16")
  
      @staticmethod
      def emptyArrayUInt32():
          return F("emptyArrayUInt32")
  
      @staticmethod
      def emptyArrayUInt64():
          return F("emptyArrayUInt64")
  
      @staticmethod
      def emptyArrayUInt8():
          return F("emptyArrayUInt8")
  
      @staticmethod
      def emptyArrayToSingle(x):
          return F("emptyArrayToSingle", x)
  
      @staticmethod
      def range(n):
          return F("range", n)
  
      @staticmethod
      def array(*args):
          return F("array", *args)
  
      @staticmethod
      def arrayConcat(*args):
          return F("arrayConcat", *args)
  
      @staticmethod
      def arrayElement(arr, n):
          return F("arrayElement", arr, n)
  
      @staticmethod
      def has(arr, x):
          return F("has", arr, x)
  
      @staticmethod
      def hasAll(arr, x):
          return F("hasAll", arr, x)
  
      @staticmethod
      def hasAny(arr, x):
          return F("hasAny", arr, x)
  
      @staticmethod
      def indexOf(arr, x):
          return F("indexOf", arr, x)
  
      @staticmethod
      def countEqual(arr, x):
          return F("countEqual", arr, x)
  
      @staticmethod
      def arrayEnumerate(arr):
          return F("arrayEnumerate", arr)
  
      @staticmethod
      def arrayEnumerateDense(*args):
          return F("arrayEnumerateDense", *args)
  
      @staticmethod
      def arrayEnumerateDenseRanked(*args):
          return F("arrayEnumerateDenseRanked", *args)
  
      @staticmethod
      def arrayEnumerateUniq(*args):
          return F("arrayEnumerateUniq", *args)
  
      @staticmethod
      def arrayEnumerateUniqRanked(*args):
          return F("arrayEnumerateUniqRanked", *args)
  
      @staticmethod
      def arrayPopBack(arr):
          return F("arrayPopBack", arr)
  
      @staticmethod
      def arrayPopFront(arr):
          return F("arrayPopFront", arr)
  
      @staticmethod
      def arrayPushBack(arr, x):
          return F("arrayPushBack", arr, x)
  
      @staticmethod
      def arrayPushFront(arr, x):
          return F("arrayPushFront", arr, x)
  
      @staticmethod
      def arrayResize(array, size, extender=None):
          return F("arrayResize", array, size, extender) if extender is not None else F("arrayResize", array, size)
  
      @staticmethod
      def arraySlice(array, offset, length=None):
          return F("arraySlice", array, offset, length) if length is not None else F("arraySlice", array, offset)
  
      @staticmethod
      def arrayUniq(*args):
          return F("arrayUniq", *args)
  
      @staticmethod
      def arrayJoin(arr):
          return F("arrayJoin", arr)
  
      @staticmethod
      def arrayDifference(arr):
          return F("arrayDifference", arr)
  
      @staticmethod
      def arrayDistinct(x):
          return F("arrayDistinct", x)
  
      @staticmethod
      def arrayIntersect(*args):
          return F("arrayIntersect", *args)
  
      @staticmethod
      def arrayReduce(agg_func_name, *args):
          return F("arrayReduce", agg_func_name, *args)
  
      @staticmethod
      def arrayReverse(arr):
          return F("arrayReverse", arr)
  
      # Functions for splitting and merging strings and arrays
  
      @staticmethod
      def splitByChar(sep, s):
          return F("splitByChar", sep, s)
  
      @staticmethod
      def splitByString(sep, s):
          return F("splitByString", sep, s)
  
      @staticmethod
      def arrayStringConcat(arr, sep=None):
          return F("arrayStringConcat", arr, sep) if sep else F("arrayStringConcat", arr)
  
      @staticmethod
      def alphaTokens(s):
          return F("alphaTokens", s)
  
      # Bit functions
  
      @staticmethod
      def bitAnd(x, y):
          return F("bitAnd", x, y)
  
      @staticmethod
      def bitNot(x):
          return F("bitNot", x)
  
      @staticmethod
      def bitOr(x, y):
          return F("bitOr", x, y)
  
      @staticmethod
      def bitRotateLeft(x, y):
          return F("bitRotateLeft", x, y)
  
      @staticmethod
      def bitRotateRight(x, y):
          return F("bitRotateRight", x, y)
  
      @staticmethod
      def bitShiftLeft(x, y):
          return F("bitShiftLeft", x, y)
  
      @staticmethod
      def bitShiftRight(x, y):
          return F("bitShiftRight", x, y)
  
      @staticmethod
      def bitTest(x, y):
          return F("bitTest", x, y)
  
      @staticmethod
      def bitTestAll(x, *args):
          return F("bitTestAll", x, *args)
  
      @staticmethod
      def bitTestAny(x, *args):
          return F("bitTestAny", x, *args)
  
      @staticmethod
      def bitXor(x, y):
          return F("bitXor", x, y)
  
      # Bitmap functions
  
      @staticmethod
      def bitmapAnd(x, y):
          return F("bitmapAnd", x, y)
  
      @staticmethod
      def bitmapAndCardinality(x, y):
          return F("bitmapAndCardinality", x, y)
  
      @staticmethod
      def bitmapAndnot(x, y):
          return F("bitmapAndnot", x, y)
  
      @staticmethod
      def bitmapAndnotCardinality(x, y):
          return F("bitmapAndnotCardinality", x, y)
  
      @staticmethod
      def bitmapBuild(x):
          return F("bitmapBuild", x)
  
      @staticmethod
      def bitmapCardinality(x):
          return F("bitmapCardinality", x)
  
      @staticmethod
      def bitmapContains(haystack, needle):
          return F("bitmapContains", haystack, needle)
  
      @staticmethod
      def bitmapHasAll(x, y):
          return F("bitmapHasAll", x, y)
  
      @staticmethod
      def bitmapHasAny(x, y):
          return F("bitmapHasAny", x, y)
  
      @staticmethod
      def bitmapOr(x, y):
          return F("bitmapOr", x, y)
  
      @staticmethod
      def bitmapOrCardinality(x, y):
          return F("bitmapOrCardinality", x, y)
  
      @staticmethod
      def bitmapToArray(x):
          return F("bitmapToArray", x)
  
      @staticmethod
      def bitmapXor(x, y):
          return F("bitmapXor", x, y)
  
      @staticmethod
      def bitmapXorCardinality(x, y):
          return F("bitmapXorCardinality", x, y)
  
      # Hash functions
  
      @staticmethod
      def halfMD5(*args):
          return F("halfMD5", *args)
  
      @staticmethod
      def MD5(s):
          return F("MD5", s)
  
      @staticmethod
      def sipHash128(*args):
          return F("sipHash128", *args)
  
      @staticmethod
      def sipHash64(*args):
          return F("sipHash64", *args)
  
      @staticmethod
      def cityHash64(*args):
          return F("cityHash64", *args)
  
      @staticmethod
      def intHash32(x):
          return F("intHash32", x)
  
      @staticmethod
      def intHash64(x):
          return F("intHash64", x)
  
      @staticmethod
      def SHA1(s):
          return F("SHA1", s)
  
      @staticmethod
      def SHA224(s):
          return F("SHA224", s)
  
      @staticmethod
      def SHA256(s):
          return F("SHA256", s)
  
      @staticmethod
      def URLHash(url, n=None):
          return F("URLHash", url, n) if n is not None else F("URLHash", url)
  
      @staticmethod
      def farmHash64(*args):
          return F("farmHash64", *args)
  
      @staticmethod
      def javaHash(s):
          return F("javaHash", s)
  
      @staticmethod
      def hiveHash(s):
          return F("hiveHash", s)
  
      @staticmethod
      def metroHash64(*args):
          return F("metroHash64", *args)
  
      @staticmethod
      def jumpConsistentHash(x, buckets):
          return F("jumpConsistentHash", x, buckets)
  
      @staticmethod
      def murmurHash2_32(*args):
          return F("murmurHash2_32", *args)
  
      @staticmethod
      def murmurHash2_64(*args):
          return F("murmurHash2_64", *args)
  
      @staticmethod
      def murmurHash3_32(*args):
          return F("murmurHash3_32", *args)
  
      @staticmethod
      def murmurHash3_64(*args):
          return F("murmurHash3_64", *args)
  
      @staticmethod
      def murmurHash3_128(s):
          return F("murmurHash3_128", s)
  
      @staticmethod
      def xxHash32(*args):
          return F("xxHash32", *args)
  
      @staticmethod
      def xxHash64(*args):
          return F("xxHash64", *args)
  
      # Functions for generating pseudo-random numbers
  
      @staticmethod
      def rand(dummy=None):
          return F("rand") if dummy is None else F("rand", dummy)
  
      @staticmethod
      def rand64(dummy=None):
          return F("rand64") if dummy is None else F("rand64", dummy)
  
      @staticmethod
      def randConstant(dummy=None):
          return F("randConstant") if dummy is None else F("randConstant", dummy)
  
      # Encoding functions
  
      @staticmethod
      def hex(x):
          return F("hex", x)
  
      @staticmethod
      def unhex(x):
          return F("unhex", x)
  
      @staticmethod
      def bitmaskToArray(x):
          return F("bitmaskToArray", x)
  
      @staticmethod
      def bitmaskToList(x):
          return F("bitmaskToList", x)
  
      # Functions for working with UUID
  
      @staticmethod
      def generateUUIDv4():
          return F("generateUUIDv4")
  
      @staticmethod
      def toUUID(s):
          return F("toUUID", s)
  
      @staticmethod
      def UUIDNumToString(s):
          return F("UUIDNumToString", s)
  
      @staticmethod
      def UUIDStringToNum(s):
          return F("UUIDStringToNum", s)
  
      # Functions for working with IP addresses
  
      @staticmethod
      def IPv4CIDRToRange(ipv4, cidr):
          return F("IPv4CIDRToRange", ipv4, cidr)
  
      @staticmethod
      def IPv4NumToString(num):
          return F("IPv4NumToString", num)
  
      @staticmethod
      def IPv4NumToStringClassC(num):
          return F("IPv4NumToStringClassC", num)
  
      @staticmethod
      def IPv4StringToNum(s):
          return F("IPv4StringToNum", s)
  
      @staticmethod
      def IPv4ToIPv6(ipv4):
          return F("IPv4ToIPv6", ipv4)
  
      @staticmethod
      def IPv6CIDRToRange(ipv6, cidr):
          return F("IPv6CIDRToRange", ipv6, cidr)
  
      @staticmethod
      def IPv6NumToString(num):
          return F("IPv6NumToString", num)
  
      @staticmethod
      def IPv6StringToNum(s):
          return F("IPv6StringToNum", s)
  
      @staticmethod
      def toIPv4(ipv4):
          return F("toIPv4", ipv4)
  
      @staticmethod
      def toIPv6(ipv6):
          return F("toIPv6", ipv6)
  
      # Aggregate functions
  
      @staticmethod
      @aggregate
      def any(x):
          return F("any", x)
  
      @staticmethod
      @aggregate
      def anyHeavy(x):
          return F("anyHeavy", x)
  
      @staticmethod
      @aggregate
      def anyLast(x):
          return F("anyLast", x)
  
      @staticmethod
      @aggregate
      def argMax(x, y):
          return F("argMax", x, y)
  
      @staticmethod
      @aggregate
      def argMin(x, y):
          return F("argMin", x, y)
  
      @staticmethod
      @aggregate
      def avg(x):
          return F("avg", x)
  
      @staticmethod
      @aggregate
      def corr(x, y):
          return F("corr", x, y)
  
      @staticmethod
      @aggregate
      def count():
          return F("count")
  
      @staticmethod
      @aggregate
      def covarPop(x, y):
          return F("covarPop", x, y)
  
      @staticmethod
      @aggregate
      def covarSamp(x, y):
          return F("covarSamp", x, y)
  
      @staticmethod
      @aggregate
      def kurtPop(x):
          return F("kurtPop", x)
  
      @staticmethod
      @aggregate
      def kurtSamp(x):
          return F("kurtSamp", x)
  
      @staticmethod
      @aggregate
      def min(x):
          return F("min", x)
  
      @staticmethod
      @aggregate
      def max(x):
          return F("max", x)
  
      @staticmethod
      @aggregate
      def skewPop(x):
          return F("skewPop", x)
  
      @staticmethod
      @aggregate
      def skewSamp(x):
          return F("skewSamp", x)
  
      @staticmethod
      @aggregate
      def sum(x):
          return F("sum", x)
  
      @staticmethod
      @aggregate
      def uniq(*args):
          return F("uniq", *args)
  
      @staticmethod
      @aggregate
      def uniqExact(*args):
          return F("uniqExact", *args)
  
      @staticmethod
      @aggregate
      def uniqHLL12(*args):
          return F("uniqHLL12", *args)
  
      @staticmethod
      @aggregate
      def varPop(x):
          return F("varPop", x)
  
      @staticmethod
      @aggregate
      def varSamp(x):
          return F("varSamp", x)
  
      @staticmethod
      @aggregate
      @parametric
      def quantile(expr):
          return F("quantile", expr)
  
      @staticmethod
      @aggregate
      @parametric
      def quantileDeterministic(expr, determinator):
          return F("quantileDeterministic", expr, determinator)
  
      @staticmethod
      @aggregate
      @parametric
      def quantileExact(expr):
          return F("quantileExact", expr)
  
      @staticmethod
      @aggregate
      @parametric
      def quantileExactWeighted(expr, weight):
          return F("quantileExactWeighted", expr, weight)
  
      @staticmethod
      @aggregate
      @parametric
      def quantileTiming(expr):
          return F("quantileTiming", expr)
  
      @staticmethod
      @aggregate
      @parametric
      def quantileTimingWeighted(expr, weight):
          return F("quantileTimingWeighted", expr, weight)
  
      @staticmethod
      @aggregate
      @parametric
      def quantileTDigest(expr):
          return F("quantileTDigest", expr)
  
      @staticmethod
      @aggregate
      @parametric
      def quantileTDigestWeighted(expr, weight):
          return F("quantileTDigestWeighted", expr, weight)
  
      @staticmethod
      @aggregate
      @parametric
      def quantiles(expr):
          return F("quantiles", expr)
  
      @staticmethod
      @aggregate
      @parametric
      def quantilesDeterministic(expr, determinator):
          return F("quantilesDeterministic", expr, determinator)
  
      @staticmethod
      @aggregate
      @parametric
      def quantilesExact(expr):
          return F("quantilesExact", expr)
  
      @staticmethod
      @aggregate
      @parametric
      def quantilesExactWeighted(expr, weight):
          return F("quantilesExactWeighted", expr, weight)
  
      @staticmethod
      @aggregate
      @parametric
      def quantilesTiming(expr):
          return F("quantilesTiming", expr)
  
      @staticmethod
      @aggregate
      @parametric
      def quantilesTimingWeighted(expr, weight):
          return F("quantilesTimingWeighted", expr, weight)
  
      @staticmethod
      @aggregate
      @parametric
      def quantilesTDigest(expr):
          return F("quantilesTDigest", expr)
  
      @staticmethod
      @aggregate
      @parametric
      def quantilesTDigestWeighted(expr, weight):
          return F("quantilesTDigestWeighted", expr, weight)
  
      @staticmethod
      @aggregate
      @parametric
      def topK(expr):
          return F("topK", expr)
  
      @staticmethod
      @aggregate
      @parametric
      def topKWeighted(expr, weight):
          return F("topKWeighted", expr, weight)
  
      # Null handling functions
  
      @staticmethod
      def ifNull(x, y):
          return F("ifNull", x, y)
  
      @staticmethod
      def nullIf(x, y):
          return F("nullIf", x, y)
  
      @staticmethod
      def isNotNull(x):
          return F("isNotNull", x)
  
      @staticmethod
      def isNull(x):
          return F("isNull", x)
  
      @staticmethod
      def coalesce(*args):
          return F("coalesce", *args)
  
      # Misc functions
  
      @staticmethod
      def ifNotFinite(x, y):
          return F("ifNotFinite", x, y)
  
      @staticmethod
      def isFinite(x):
          return F("isFinite", x)
  
      @staticmethod
      def isInfinite(x):
          return F("isInfinite", x)
  
      @staticmethod
      def isNaN(x):
          return F("isNaN", x)
  
      @staticmethod
      def least(x, y):
          return F("least", x, y)
  
      @staticmethod
      def greatest(x, y):
          return F("greatest", x, y)
  
      # Dictionary functions
  
      @staticmethod
      def dictGet(dict_name, attr_name, id_expr):
          return F("dictGet", dict_name, attr_name, id_expr)
  
      @staticmethod
      def dictGetOrDefault(dict_name, attr_name, id_expr, default):
          return F("dictGetOrDefault", dict_name, attr_name, id_expr, default)
  
      @staticmethod
      def dictHas(dict_name, id_expr):
          return F("dictHas", dict_name, id_expr)
  
      @staticmethod
      def dictGetHierarchy(dict_name, id_expr):
          return F("dictGetHierarchy", dict_name, id_expr)
  
      @staticmethod
      def dictIsIn(dict_name, child_id_expr, ancestor_id_expr):
          return F("dictIsIn", dict_name, child_id_expr, ancestor_id_expr)
  
  
  # Expose only relevant classes in import *
  __all__ = ["F"]
  