text: # Copyright 2016 The Cebes Authors. All Rights Reserved.
  #
  # Licensed under the Apache License, version 2.0 (the "License").
  # You may not use this work except in compliance with the License,
  # which is available at www.apache.org/licenses/LICENSE-2.0
  #
  # This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  # either express or implied, as more fully set forth in the License.
  #
  # See the NOTICE file distributed with this work for information regarding copyright ownership.
  
  from __future__ import absolute_import
  from __future__ import print_function
  from __future__ import unicode_literals
  
  from collections import namedtuple
  
  import six
  
  from pycebes.core.schema import StorageType
  from pycebes.internal.serializer import to_json
  
  _ParamConfig = namedtuple('_ParamConfig', ['name', 'param_type', 'server_name'])
  
  
  def param(name='param', param_type=None, server_name=None):
      """
      Decorator used for parameters of Expressions
  
      :param name: Name of the parameter (i.e. slot) 
      :param param_type: optionally, a string represent the type of this param (on the server)
          mostly used to disambiguous primitive types on server
      :type param_type: str
      :param server_name: name of this parameter on server, if different from 'name'
      :return: 
      """
  
      def decorate(cls):
          assert issubclass(cls, Expression)
          if getattr(cls, 'PARAMS', None) is None:
              cls.PARAMS = {}
  
          class_name = cls.__name__
          if class_name not in cls.PARAMS:
              cls.PARAMS[class_name] = []
  
          pc = _ParamConfig(name, param_type, name if server_name is None else server_name)
          if next((p for p in cls.PARAMS[class_name] if p.name == pc.name), None) is not None:
              raise ValueError('Duplicated parameter named {} in class {}'.format(pc.name, cls.__name__))
          cls.PARAMS[class_name].append(pc)
          return cls
  
      return decorate
  
  
  def server_namespace(ns='io.cebes.df.expressions'):
      """
      Decorator used for Expressions, to specify the namespace of this class on the server
      """
  
      def decorate(cls):
          assert issubclass(cls, Expression)
          setattr(cls, '_server_namespace', ns)
          return cls
  
      return decorate
  
  
  ############################################################################
  
  
  @six.python_2_unicode_compatible
  class Expression(object):
      def __init__(self, **kwargs):
          param_names = {pc.name for pc in self._get_params()}
          for k, v in kwargs.items():
              if k not in param_names:
                  raise ValueError('Invalid param name {} in class {}'.format(k, self.__class__.__name__))
              setattr(self, k, v)
  
      def to_json(self):
          js = {'className': '{}.{}'.format(self._get_server_namespace(), self.__class__.__name__)}
          for pc in self._get_params():
              assert isinstance(pc, _ParamConfig)
              value = getattr(self, pc.name, None)
              js[pc.server_name] = self._param_to_json(pc, value)
          return js
  
      def __repr__(self):
          params_str = ','.join('{}={!r}'.format(pc.name, getattr(self, pc.name, None)) for pc in self._get_params())
          return '{}({})'.format(self.__class__.__name__, params_str)
  
      def __str__(self):
          return super(Expression, self).__str__()
  
      @staticmethod
      def _param_to_json(pc, value):
          """
          Serialize a parameter to JSON, can be overriden by subclasses
          :type pc: _ParamConfig
          :return: a JSON value
          """
          return value.to_json() if isinstance(value, Expression) else to_json(value, pc.param_type)
  
      def _get_params(self):
          """
          Return the list of params of this expression
          :rtype: list[_ParamConfig]
          """
          params = []
          for parent_class in self.__class__.__mro__:
              params.extend(self.PARAMS.get(parent_class.__name__, []))
          return params
  
      @classmethod
      def _get_server_namespace(cls):
          return getattr(cls, '_server_namespace', 'io.cebes.df.expressions')
  
  
  ############################################################################
  # Helpers subclasses of Expression
  
  @param('children')
  class _WithChildren(Expression):
      """
      Subclass of Expression with serialization logic for "children" parameter,
      which is a list of Expressions
      """
  
      def __init__(self, **kwargs):
          super(_WithChildren, self).__init__(**kwargs)
  
      @staticmethod
      def _param_to_json(pc, value):
          if pc.name == 'children':
              assert isinstance(value, (list, tuple)) and all(isinstance(ex, Expression) for ex in value)
              return [ex.to_json() for ex in value]
          raise ValueError('Missing serialization logic for {} of class {!r}'.format(pc.name, value))
  
  
  @param('child')
  class _UnaryExpression(Expression):
      """
      Subclass of Expression with serialization logic for "child" parameter
      """
  
      def __init__(self, child, **kwargs):
          super(_UnaryExpression, self).__init__(child=child, **kwargs)
  
  
  @param('left')
  @param('right')
  class _BinaryExpression(Expression):
      """
      Subclass of Expression with serialization logic for "left" and "right" parameters
      """
  
      def __init__(self, left, right):
          super(_BinaryExpression, self).__init__(left=left, right=right)
  
  
  """
  Aggregation
  """
  
  
  @param('relative_sd', param_type='double', server_name='relativeSD')
  class ApproxCountDistinct(_UnaryExpression):
      def __init__(self, child, relative_sd=0.05):
          super(ApproxCountDistinct, self).__init__(child=child, relative_sd=relative_sd)
  
  
  class Average(_UnaryExpression):
      def __init__(self, child):
          super(Average, self).__init__(child=child)
  
  
  class CollectList(_UnaryExpression):
      def __init__(self, child):
          super(CollectList, self).__init__(child=child)
  
  
  class CollectSet(_UnaryExpression):
      def __init__(self, child):
          super(CollectSet, self).__init__(child=child)
  
  
  class Corr(_BinaryExpression):
      def __init__(self, left, right):
          super(Corr, self).__init__(left=left, right=right)
  
  
  class Count(_UnaryExpression):
      def __init__(self, child):
          super(Count, self).__init__(child=child)
  
  
  @param('expr')
  @param('exprs')
  class CountDistinct(Expression):
      def __init__(self, *exprs):
          if len(exprs) == 0:
              raise ValueError('Empty list of expressions for {}'.format(self.__class__.__name__))
          expr = exprs[0]
          exprs = exprs[1:]
          super(CountDistinct, self).__init__(expr=expr, exprs=exprs)
  
      @staticmethod
      def _param_to_json(pc, value):
          if pc.name == 'expr':
              assert isinstance(value, Expression)
              return value.to_json()
          if pc.name == 'exprs':
              return [v.to_json() for v in value]
          return Expression._param_to_json(pc, value)
  
  
  class CovPopulation(_BinaryExpression):
      def __init__(self, left, right):
          super(CovPopulation, self).__init__(left=left, right=right)
  
  
  class CovSample(_BinaryExpression):
      def __init__(self, left, right):
          super(CovSample, self).__init__(left=left, right=right)
  
  
  @param('ignore_nulls', param_type=bool, server_name='ignoreNulls')
  class First(_UnaryExpression):
      def __init__(self, child, ignore_nulls=False):
          super(First, self).__init__(child=child, ignore_nulls=ignore_nulls)
  
  
  class Grouping(_UnaryExpression):
      def __init__(self, child):
          super(Grouping, self).__init__(child=child)
  
  
  class GroupingID(_WithChildren):
      def __init__(self, children=()):
          super(GroupingID, self).__init__(children=children)
  
  
  class Kurtosis(_UnaryExpression):
      def __init__(self, child):
          super(Kurtosis, self).__init__(child=child)
  
  
  @param('ignore_nulls', param_type=bool, server_name='ignoreNulls')
  class Last(_UnaryExpression):
      def __init__(self, child, ignore_nulls=False):
          super(Last, self).__init__(child=child, ignore_nulls=ignore_nulls)
  
  
  class Max(_UnaryExpression):
      def __init__(self, child):
          super(Max, self).__init__(child=child)
  
  
  class Min(_UnaryExpression):
      def __init__(self, child):
          super(Min, self).__init__(child=child)
  
  
  class Skewness(_UnaryExpression):
      def __init__(self, child):
          super(Skewness, self).__init__(child=child)
  
  
  class StddevSamp(_UnaryExpression):
      def __init__(self, child):
          super(StddevSamp, self).__init__(child=child)
  
  
  class StddevPop(_UnaryExpression):
      def __init__(self, child):
          super(StddevPop, self).__init__(child=child)
  
  
  @param('is_distinct', param_type=bool, server_name='isDistinct')
  class Sum(_UnaryExpression):
      def __init__(self, child, is_distinct=False):
          super(Sum, self).__init__(child=child, is_distinct=is_distinct)
  
  
  class VarianceSamp(_UnaryExpression):
      def __init__(self, child):
          super(VarianceSamp, self).__init__(child=child)
  
  
  class VariancePop(_UnaryExpression):
      def __init__(self, child):
          super(VariancePop, self).__init__(child=child)
  
  
  """
  alias
  """
  
  
  @param('alias')
  class Alias(_UnaryExpression):
      def __init__(self, child, alias='alias'):
          super(Alias, self).__init__(child=child, alias=alias)
  
  
  @param('aliases')
  class MultiAlias(_UnaryExpression):
      def __init__(self, child, aliases=()):
          super(MultiAlias, self).__init__(child=child, aliases=aliases)
  
  
  @param('to')
  class Cast(_UnaryExpression):
      def __init__(self, child, to):
          super(Cast, self).__init__(child=child, to=to)
  
      @staticmethod
      def _param_to_json(pc, value):
          if pc.name == 'child':
              assert isinstance(value, Expression)
              return value.to_json()
  
          assert pc.name == 'to' and isinstance(value, Literal) and isinstance(value.value, StorageType)
          return value.value.to_json()
  
  
  """
  arithmetic
  """
  
  
  class UnaryMinus(_UnaryExpression):
      def __init__(self, child):
          super(UnaryMinus, self).__init__(child=child)
  
  
  class Add(_BinaryExpression):
      def __init__(self, left, right):
          super(Add, self).__init__(left=left, right=right)
  
  
  class Subtract(_BinaryExpression):
      def __init__(self, left, right):
          super(Subtract, self).__init__(left=left, right=right)
  
  
  class Multiply(_BinaryExpression):
      def __init__(self, left, right):
          super(Multiply, self).__init__(left=left, right=right)
  
  
  class Divide(_BinaryExpression):
      def __init__(self, left, right):
          super(Divide, self).__init__(left=left, right=right)
  
  
  class Remainder(_BinaryExpression):
      def __init__(self, left, right):
          super(Remainder, self).__init__(left=left, right=right)
  
  
  class Abs(_UnaryExpression):
      def __init__(self, child):
          super(Abs, self).__init__(child=child)
  
  
  class Sqrt(_UnaryExpression):
      def __init__(self, child):
          super(Sqrt, self).__init__(child=child)
  
  
  class Acos(_UnaryExpression):
      def __init__(self, child):
          super(Acos, self).__init__(child=child)
  
  
  class Asin(_UnaryExpression):
      def __init__(self, child):
          super(Asin, self).__init__(child=child)
  
  
  class Atan(_UnaryExpression):
      def __init__(self, child):
          super(Atan, self).__init__(child=child)
  
  
  class Atan2(_BinaryExpression):
      def __init__(self, left, right):
          super(Atan2, self).__init__(left=left, right=right)
  
  
  class Bin(_UnaryExpression):
      def __init__(self, child):
          super(Bin, self).__init__(child=child)
  
  
  class Cbrt(_UnaryExpression):
      def __init__(self, child):
          super(Cbrt, self).__init__(child=child)
  
  
  class Ceil(_UnaryExpression):
      def __init__(self, child):
          super(Ceil, self).__init__(child=child)
  
  
  @param('from_base', param_type='int', server_name='fromBase')
  @param('to_base', param_type='int', server_name='toBase')
  class Conv(_UnaryExpression):
      def __init__(self, child, from_base=10, to_base=2):
          super(Conv, self).__init__(child=child, from_base=from_base, to_base=to_base)
  
  
  class Cos(_UnaryExpression):
      def __init__(self, child):
          super(Cos, self).__init__(child=child)
  
  
  class Cosh(_UnaryExpression):
      def __init__(self, child):
          super(Cosh, self).__init__(child=child)
  
  
  class Exp(_UnaryExpression):
      def __init__(self, child):
          super(Exp, self).__init__(child=child)
  
  
  class Expm1(_UnaryExpression):
      def __init__(self, child):
          super(Expm1, self).__init__(child=child)
  
  
  class Factorial(_UnaryExpression):
      def __init__(self, child):
          super(Factorial, self).__init__(child=child)
  
  
  class Floor(_UnaryExpression):
      def __init__(self, child):
          super(Floor, self).__init__(child=child)
  
  
  class Greatest(_WithChildren):
      def __init__(self, children=()):
          super(Greatest, self).__init__(children=children)
  
  
  class Hex(_UnaryExpression):
      def __init__(self, child):
          super(Hex, self).__init__(child=child)
  
  
  class Unhex(_UnaryExpression):
      def __init__(self, child):
          super(Unhex, self).__init__(child=child)
  
  
  class Hypot(_BinaryExpression):
      def __init__(self, left, right):
          super(Hypot, self).__init__(left=left, right=right)
  
  
  class Least(_WithChildren):
      def __init__(self, children=()):
          super(Least, self).__init__(children=children)
  
  
  class Log(_UnaryExpression):
      def __init__(self, child):
          super(Log, self).__init__(child=child)
  
  
  @param('base', param_type='double')
  class Logarithm(_UnaryExpression):
      def __init__(self, child, base=10.0):
          super(Logarithm, self).__init__(child=child, base=base)
  
  
  class Log10(_UnaryExpression):
      def __init__(self, child):
          super(Log10, self).__init__(child=child)
  
  
  class Log1p(_UnaryExpression):
      def __init__(self, child):
          super(Log1p, self).__init__(child=child)
  
  
  class Log2(_UnaryExpression):
      def __init__(self, child):
          super(Log2, self).__init__(child=child)
  
  
  class Pow(_BinaryExpression):
      def __init__(self, left, right):
          super(Pow, self).__init__(left=left, right=right)
  
  
  class Pmod(_BinaryExpression):
      def __init__(self, left, right):
          super(Pmod, self).__init__(left=left, right=right)
  
  
  class Rint(_UnaryExpression):
      def __init__(self, child):
          super(Rint, self).__init__(child=child)
  
  
  class Round(_BinaryExpression):
      def __init__(self, left, right):
          super(Round, self).__init__(left=left, right=right)
  
  
  @param('scale', param_type='int')
  class BRound(_UnaryExpression):
      def __init__(self, child, scale=5):
          super(BRound, self).__init__(child=child, scale=scale)
  
  
  @param('num_bits', param_type='int', server_name='numBits')
  class ShiftLeft(_UnaryExpression):
      def __init__(self, child, num_bits=5):
          super(ShiftLeft, self).__init__(child=child, num_bits=num_bits)
  
  
  @param('num_bits', param_type='int', server_name='numBits')
  class ShiftRight(_UnaryExpression):
      def __init__(self, child, num_bits=5):
          super(ShiftRight, self).__init__(child=child, num_bits=num_bits)
  
  
  @param('num_bits', param_type='int', server_name='numBits')
  class ShiftRightUnsigned(_UnaryExpression):
      def __init__(self, child, num_bits=5):
          super(ShiftRightUnsigned, self).__init__(child=child, num_bits=num_bits)
  
  
  class Signum(_UnaryExpression):
      def __init__(self, child):
          super(Signum, self).__init__(child=child)
  
  
  class Sin(_UnaryExpression):
      def __init__(self, child):
          super(Sin, self).__init__(child=child)
  
  
  class Sinh(_UnaryExpression):
      def __init__(self, child):
          super(Sinh, self).__init__(child=child)
  
  
  class Tan(_UnaryExpression):
      def __init__(self, child):
          super(Tan, self).__init__(child=child)
  
  
  class Tanh(_UnaryExpression):
      def __init__(self, child):
          super(Tanh, self).__init__(child=child)
  
  
  class ToDegrees(_UnaryExpression):
      def __init__(self, child):
          super(ToDegrees, self).__init__(child=child)
  
  
  class ToRadians(_UnaryExpression):
      def __init__(self, child):
          super(ToRadians, self).__init__(child=child)
  
  
  """
  collections
  """
  
  
  class ArrayContains(_BinaryExpression):
      def __init__(self, left, right):
          super(ArrayContains, self).__init__(left=left, right=right)
  
  
  class Explode(_UnaryExpression):
      def __init__(self, child):
          super(Explode, self).__init__(child=child)
  
  
  class PosExplode(_UnaryExpression):
      def __init__(self, child):
          super(PosExplode, self).__init__(child=child)
  
  
  @param('path')
  class GetJsonObject(_UnaryExpression):
      def __init__(self, child, path=''):
          super(GetJsonObject, self).__init__(child=child, path=path)
  
  
  @param('fields')
  class JsonTuple(_UnaryExpression):
      def __init__(self, child, fields=()):
          super(JsonTuple, self).__init__(child=child, fields=fields)
  
  
  class Size(_UnaryExpression):
      def __init__(self, child):
          super(Size, self).__init__(child=child)
  
  
  @param('asc')
  class SortArray(_UnaryExpression):
      def __init__(self, child, asc=True):
          super(SortArray, self).__init__(child=child, asc=asc)
  
  
  """
  complex types
  """
  
  
  class CreateArray(_WithChildren):
      def __init__(self, children=()):
          super(CreateArray, self).__init__(children=children)
  
  
  class CreateMap(_WithChildren):
      def __init__(self, children=()):
          super(CreateMap, self).__init__(children=children)
  
  
  class CreateStruct(_WithChildren):
      def __init__(self, children=()):
          super(CreateStruct, self).__init__(children=children)
  
  
  """
  conditional
  """
  
  
  @param('branches')
  @param('else_value', server_name='elseValue')
  class CaseWhen(Expression):
      def __init__(self, branches, else_value=None):
          """
          :param branches: a list [(expr, expr)] where each tuple is a condition and the corresponding value
          :param else_value: another expression for the "else" value, or None if unspecified
          """
          super(CaseWhen, self).__init__(branches=branches, else_value=else_value)
  
      @staticmethod
      def _param_to_json(pc, value):
          if pc.name == 'branches':
              result = []
              for pair in value:
                  result.append([ex.to_json() for ex in pair])
              return result
  
          assert pc.name == 'else_value'
          assert value is None or isinstance(value, Expression)
          return value.to_json() if isinstance(value, Expression) else None
  
  
  class GetItem(_BinaryExpression):
      def __init__(self, left, right):
          super(GetItem, self).__init__(left=left, right=right)
  
  
  @param('field_name', server_name='fieldName')
  class GetField(_UnaryExpression):
      def __init__(self, child, field_name=''):
          super(GetField, self).__init__(child=child, field_name=field_name)
  
  
  """
  datetime
  """
  
  
  @param('num_months', param_type='int', server_name='numMonths')
  class AddMonths(_UnaryExpression):
      def __init__(self, child, num_months=1):
          super(AddMonths, self).__init__(child=child, num_months=num_months)
  
  
  class CurrentDate(Expression):
      def __init__(self):
          super(CurrentDate, self).__init__()
  
  
  class CurrentTimestamp(Expression):
      def __init__(self):
          super(CurrentTimestamp, self).__init__()
  
  
  @param('fmt', server_name='format')
  class DateFormatClass(_UnaryExpression):
      def __init__(self, child, fmt=''):
          super(DateFormatClass, self).__init__(child=child, fmt=fmt)
  
  
  @param('days', param_type='int')
  class DateAdd(_UnaryExpression):
      def __init__(self, child, days=1):
          super(DateAdd, self).__init__(child=child, days=days)
  
  
  @param('days', param_type='int')
  class DateSub(_UnaryExpression):
      def __init__(self, child, days=1):
          super(DateSub, self).__init__(child=child, days=days)
  
  
  class DateDiff(_BinaryExpression):
      def __init__(self, left, right):
          super(DateDiff, self).__init__(left=left, right=right)
  
  
  class Year(_UnaryExpression):
      def __init__(self, child):
          super(Year, self).__init__(child=child)
  
  
  class Quarter(_UnaryExpression):
      def __init__(self, child):
          super(Quarter, self).__init__(child=child)
  
  
  class Month(_UnaryExpression):
      def __init__(self, child):
          super(Month, self).__init__(child=child)
  
  
  class DayOfMonth(_UnaryExpression):
      def __init__(self, child):
          super(DayOfMonth, self).__init__(child=child)
  
  
  class DayOfYear(_UnaryExpression):
      def __init__(self, child):
          super(DayOfYear, self).__init__(child=child)
  
  
  class Hour(_UnaryExpression):
      def __init__(self, child):
          super(Hour, self).__init__(child=child)
  
  
  class LastDay(_UnaryExpression):
      def __init__(self, child):
          super(LastDay, self).__init__(child=child)
  
  
  class Minute(_UnaryExpression):
      def __init__(self, child):
          super(Minute, self).__init__(child=child)
  
  
  class MonthsBetween(_BinaryExpression):
      def __init__(self, left, right):
          super(MonthsBetween, self).__init__(left=left, right=right)
  
  
  @param('day_of_week', server_name='dayOfWeek')
  class NextDay(_UnaryExpression):
      def __init__(self, child, day_of_week=''):
          super(NextDay, self).__init__(child=child, day_of_week=day_of_week)
  
  
  class Second(_UnaryExpression):
      def __init__(self, child):
          super(Second, self).__init__(child=child)
  
  
  class WeekOfYear(_UnaryExpression):
      def __init__(self, child):
          super(WeekOfYear, self).__init__(child=child)
  
  
  @param('fmt', server_name='format')
  class FromUnixTime(_UnaryExpression):
      def __init__(self, child, fmt=''):
          super(FromUnixTime, self).__init__(child=child, fmt=fmt)
  
  
  @param('fmt', server_name='format')
  class UnixTimestamp(_UnaryExpression):
      def __init__(self, child, fmt=''):
          super(UnixTimestamp, self).__init__(child=child, fmt=fmt)
  
  
  class ToDate(_UnaryExpression):
      def __init__(self, child):
          super(ToDate, self).__init__(child=child)
  
  
  @param('fmt', server_name='format')
  class TruncDate(_UnaryExpression):
      def __init__(self, child, fmt=''):
          super(TruncDate, self).__init__(child=child, fmt=fmt)
  
  
  @param('tz')
  class FromUTCTimestamp(_UnaryExpression):
      def __init__(self, child, tz=''):
          super(FromUTCTimestamp, self).__init__(child=child, tz=tz)
  
  
  @param('tz')
  class ToUTCTimestamp(_UnaryExpression):
      def __init__(self, child, tz=''):
          super(ToUTCTimestamp, self).__init__(child=child, tz=tz)
  
  
  @param('window_duration', server_name='windowDuration')
  @param('slide_duration', server_name='slideDuration')
  @param('start_time', server_name='startTime')
  class TimeWindow(_UnaryExpression):
      def __init__(self, child, window_duration='', slide_duration='', start_time=''):
          super(TimeWindow, self).__init__(child=child, window_duration=window_duration,
                                           slide_duration=slide_duration,
                                           start_time=start_time)
  
  
  """
  misc
  """
  
  
  class Md5(_UnaryExpression):
      def __init__(self, child):
          super(Md5, self).__init__(child=child)
  
  
  class Sha1(_UnaryExpression):
      def __init__(self, child):
          super(Sha1, self).__init__(child=child)
  
  
  @param('num_bits', param_type='int', server_name='numBits')
  class Sha2(_UnaryExpression):
      def __init__(self, child, num_bits=0):
          super(Sha2, self).__init__(child=child, num_bits=num_bits)
  
  
  class Crc32(_UnaryExpression):
      def __init__(self, child):
          super(Crc32, self).__init__(child=child)
  
  
  class Murmur3Hash(_WithChildren):
      def __init__(self, children=()):
          super(Murmur3Hash, self).__init__(children=children)
  
  
  """
  non-aggregation
  """
  
  
  class Coalesce(_WithChildren):
      def __init__(self, children=()):
          super(Coalesce, self).__init__(children=children)
  
  
  class InputFileName(Expression):
      def __init__(self):
          super(InputFileName, self).__init__()
  
  
  class MonotonicallyIncreasingID(Expression):
      def __init__(self):
          super(MonotonicallyIncreasingID, self).__init__()
  
  
  class NaNvl(_BinaryExpression):
      def __init__(self, left, right):
          super(NaNvl, self).__init__(left=left, right=right)
  
  
  @param('seed', param_type='long')
  class Rand(Expression):
      def __init__(self, seed=42):
          super(Rand, self).__init__(seed=seed)
  
  
  @param('seed', param_type='long')
  class Randn(Expression):
      def __init__(self, seed=42):
          super(Randn, self).__init__(seed=seed)
  
  
  class SparkPartitionID(Expression):
      def __init__(self):
          super(SparkPartitionID, self).__init__()
  
  
  @param('expr')
  class RawExpression(Expression):
      def __init__(self, expr=''):
          super(RawExpression, self).__init__(expr=expr)
  
  
  """
  predicates
  """
  
  
  class Not(_UnaryExpression):
      def __init__(self, child):
          super(Not, self).__init__(child=child)
  
  
  class EqualTo(_BinaryExpression):
      def __init__(self, left, right):
          super(EqualTo, self).__init__(left=left, right=right)
  
  
  class GreaterThan(_BinaryExpression):
      def __init__(self, left, right):
          super(GreaterThan, self).__init__(left=left, right=right)
  
  
  class LessThan(_BinaryExpression):
      def __init__(self, left, right):
          super(LessThan, self).__init__(left=left, right=right)
  
  
  class GreaterThanOrEqual(_BinaryExpression):
      def __init__(self, left, right):
          super(GreaterThanOrEqual, self).__init__(left=left, right=right)
  
  
  class LessThanOrEqual(_BinaryExpression):
      def __init__(self, left, right):
          super(LessThanOrEqual, self).__init__(left=left, right=right)
  
  
  class EqualNullSafe(_BinaryExpression):
      def __init__(self, left, right):
          super(EqualNullSafe, self).__init__(left=left, right=right)
  
  
  class IsNaN(_UnaryExpression):
      def __init__(self, child):
          super(IsNaN, self).__init__(child=child)
  
  
  class IsNull(_UnaryExpression):
      def __init__(self, child):
          super(IsNull, self).__init__(child=child)
  
  
  class IsNotNull(_UnaryExpression):
      def __init__(self, child):
          super(IsNotNull, self).__init__(child=child)
  
  
  class Or(_BinaryExpression):
      def __init__(self, left, right):
          super(Or, self).__init__(left=left, right=right)
  
  
  class And(_BinaryExpression):
      def __init__(self, left, right):
          super(And, self).__init__(left=left, right=right)
  
  
  @param('value')
  @param('ls', server_name='list')
  class In(Expression):
      def __init__(self, value, ls=()):
          super(In, self).__init__(value=value, ls=ls)
  
      @staticmethod
      def _param_to_json(pc, value):
          if pc.name == 'value':
              assert isinstance(value, Expression)
              return value.to_json()
          assert pc.name == 'ls'
          assert isinstance(value, (tuple, list)) and all(isinstance(ex, Expression) for ex in value)
          return [ex.to_json() for ex in value]
  
  
  class BitwiseOr(_BinaryExpression):
      def __init__(self, left, right):
          super(BitwiseOr, self).__init__(left=left, right=right)
  
  
  class BitwiseAnd(_BinaryExpression):
      def __init__(self, left, right):
          super(BitwiseAnd, self).__init__(left=left, right=right)
  
  
  class BitwiseXor(_BinaryExpression):
      def __init__(self, left, right):
          super(BitwiseXor, self).__init__(left=left, right=right)
  
  
  class BitwiseNot(_UnaryExpression):
      def __init__(self, child):
          super(BitwiseNot, self).__init__(child=child)
  
  
  """
  primitives
  """
  
  
  @param('value')
  class Literal(Expression):
      def __init__(self, value):
          super(Literal, self).__init__(value=value)
  
  
  @param('col_name', server_name='colName')
  class UnresolvedColumnName(Expression):
      def __init__(self, col_name):
          super(UnresolvedColumnName, self).__init__(col_name=col_name)
  
  
  """
  SortOrder
  """
  
  
  @param('direction')
  class SortOrder(_UnaryExpression):
      Ascending = 'Ascending'
      Descending = 'Descending'
  
      def __init__(self, child, direction=Ascending):
          super(SortOrder, self).__init__(child=child, direction=direction)
  
  
  """
  strings
  """
  
  
  class Ascii(_UnaryExpression):
      def __init__(self, child):
          super(Ascii, self).__init__(child=child)
  
  
  class Base64(_UnaryExpression):
      def __init__(self, child):
          super(Base64, self).__init__(child=child)
  
  
  class Concat(_WithChildren):
      def __init__(self, children=()):
          super(Concat, self).__init__(children=children)
  
  
  @param('sep')
  class ConcatWs(_WithChildren):
      def __init__(self, children=(), sep=''):
          super(ConcatWs, self).__init__(sep=sep, children=children)
  
      @staticmethod
      def _param_to_json(pc, value):
          if pc.name == 'sep':
              return to_json(value, pc.param_type)
          return _WithChildren._param_to_json(pc, value)
  
  
  class Contains(_BinaryExpression):
      def __init__(self, left, right):
          super(Contains, self).__init__(left=left, right=right)
  
  
  @param('charset')
  class Decode(_UnaryExpression):
      def __init__(self, child, charset='ascii'):
          super(Decode, self).__init__(child=child, charset=charset)
  
  
  @param('charset')
  class Encode(_UnaryExpression):
      def __init__(self, child, charset='ascii'):
          super(Encode, self).__init__(child=child, charset=charset)
  
  
  class EndsWith(_BinaryExpression):
      def __init__(self, left, right):
          super(EndsWith, self).__init__(left=left, right=right)
  
  
  @param('precision', param_type='int')
  class FormatNumber(_UnaryExpression):
      def __init__(self, child, precision=2):
          super(FormatNumber, self).__init__(child=child, precision=precision)
  
  
  @param('fmt', server_name='format')
  class FormatString(_WithChildren):
      def __init__(self, fmt='', children=()):
          super(FormatString, self).__init__(fmt=fmt, children=children)
  
      @staticmethod
      def _param_to_json(pc, value):
          if pc.name == 'fmt':
              return to_json(value, pc.param_type)
          return _WithChildren._param_to_json(pc, value)
  
  
  class InitCap(_UnaryExpression):
      def __init__(self, child):
          super(InitCap, self).__init__(child=child)
  
  
  @param('sub_str', server_name='subStr')
  class StringInstr(_UnaryExpression):
      def __init__(self, child, sub_str=''):
          super(StringInstr, self).__init__(child=child, sub_str=sub_str)
  
  
  class Length(_UnaryExpression):
      def __init__(self, child):
          super(Length, self).__init__(child=child)
  
  
  @param('literal')
  class Like(_UnaryExpression):
      def __init__(self, child, literal=''):
          super(Like, self).__init__(child=child, literal=literal)
  
  
  class Lower(_UnaryExpression):
      def __init__(self, child):
          super(Lower, self).__init__(child=child)
  
  
  class Levenshtein(_BinaryExpression):
      def __init__(self, left, right):
          super(Levenshtein, self).__init__(left=left, right=right)
  
  
  @param('substr')
  @param('pos', param_type='int')
  class StringLocate(_UnaryExpression):
      def __init__(self, child, substr='', pos=0):
          super(StringLocate, self).__init__(child=child, substr=substr, pos=pos)
  
  
  @param('length', param_type='int', server_name='len')
  @param('padding', server_name='pad')
  class StringLPad(_UnaryExpression):
      def __init__(self, child, length=0, padding=''):
          super(StringLPad, self).__init__(child=child, length=length, padding=padding)
  
  
  class StringTrimLeft(_UnaryExpression):
      def __init__(self, child):
          super(StringTrimLeft, self).__init__(child=child)
  
  
  @param('expr')
  @param('group_idx', param_type='int', server_name='groupIdx')
  class RegExpExtract(_UnaryExpression):
      def __init__(self, child, expr='', group_idx=0):
          super(RegExpExtract, self).__init__(child=child, expr=expr, group_idx=group_idx)
  
  
  @param('pattern')
  @param('replacement')
  class RegExpReplace(_UnaryExpression):
      def __init__(self, child, pattern='', replacement=''):
          super(RegExpReplace, self).__init__(child=child, pattern=pattern, replacement=replacement)
  
  
  @param('literal')
  class RLike(_UnaryExpression):
      def __init__(self, child, literal=''):
          super(RLike, self).__init__(child=child, literal=literal)
  
  
  class StartsWith(_BinaryExpression):
      def __init__(self, left, right):
          super(StartsWith, self).__init__(left=left, right=right)
  
  
  @param('length', param_type='int', server_name='len')
  @param('padding', server_name='pad')
  class StringRPad(_UnaryExpression):
      def __init__(self, child, length=0, padding=''):
          super(StringRPad, self).__init__(child=child, length=length, padding=padding)
  
  
  @param('n', param_type='int')
  class StringRepeat(_UnaryExpression):
      def __init__(self, child, n=1):
          super(StringRepeat, self).__init__(child=child, n=n)
  
  
  class StringReverse(_UnaryExpression):
      def __init__(self, child):
          super(StringReverse, self).__init__(child=child)
  
  
  class StringTrimRight(_UnaryExpression):
      def __init__(self, child):
          super(StringTrimRight, self).__init__(child=child)
  
  
  class SoundEx(_UnaryExpression):
      def __init__(self, child):
          super(SoundEx, self).__init__(child=child)
  
  
  @param('pattern')
  class StringSplit(_UnaryExpression):
      def __init__(self, child, pattern=''):
          super(StringSplit, self).__init__(child=child, pattern=pattern)
  
  
  @param('matching')
  @param('replace')
  class StringTranslate(_UnaryExpression):
      def __init__(self, child, matching='', replace=''):
          super(StringTranslate, self).__init__(child=child, matching=matching, replace=replace)
  
  
  class StringTrim(_UnaryExpression):
      def __init__(self, child):
          super(StringTrim, self).__init__(child=child)
  
  
  @param('s', server_name='str')
  @param('pos')
  @param('length', server_name='len')
  class Substring(Expression):
      def __init__(self, s, pos, length):
          super(Substring, self).__init__(s=s, pos=pos, length=length)
  
  
  @param('delim')
  @param('cnt', param_type='int', server_name='count')
  class SubstringIndex(_UnaryExpression):
      def __init__(self, child, delim, cnt=1):
          super(SubstringIndex, self).__init__(child=child, delim=delim, cnt=cnt)
  
  
  class UnBase64(_UnaryExpression):
      def __init__(self, child):
          super(UnBase64, self).__init__(child=child)
  
  
  class Upper(_UnaryExpression):
      def __init__(self, child):
          super(Upper, self).__init__(child=child)
  
  
  """
  Spark-specific
  """
  
  
  @param('df_id', server_name='dfId')
  @param('col_name', server_name='colName')
  @server_namespace('io.cebes.spark.df.expressions')
  class SparkPrimitiveExpression(Expression):
      def __init__(self, df_id='', col_name=''):
          super(SparkPrimitiveExpression, self).__init__(df_id=df_id, col_name=col_name)
  