/* -*- c-basic-offset: 2 -*- */
/*
  Copyright(C) 2015-2016 Brazil

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License version 2.1 as published by the Free Software Foundation.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "ts_expr_node.h"

#include <math.h>
#include <string.h>

#include "../grn_ctx.h"
#include "../grn_dat.h"
#include "../grn_db.h"
#include "../grn_geo.h"
#include "../grn_hash.h"
#include "../grn_pat.h"
#include "../grn_store.h"

#include "ts_log.h"
#include "ts_str.h"
#include "ts_util.h"

/*-------------------------------------------------------------
 * Built-in data kinds.
 */

/* grn_ts_bool_is_valid() returns whether a value is valid or not. */
grn_inline static grn_ts_bool
grn_ts_bool_is_valid(grn_ts_bool value)
{
  return GRN_TRUE;
}

/* grn_ts_int_is_valid() returns whether a value is valid or not. */
grn_inline static grn_ts_bool
grn_ts_int_is_valid(grn_ts_int value)
{
  return GRN_TRUE;
}

/* grn_ts_float_is_valid() returns whether a value is valid or not. */
grn_inline static grn_ts_bool
grn_ts_float_is_valid(grn_ts_float value)
{
  return isfinite(value);
}

/* grn_ts_time_is_valid() returns whether a value is valid or not. */
grn_inline static grn_ts_bool
grn_ts_time_is_valid(grn_ts_time value)
{
  return GRN_TRUE;
}

/* grn_ts_text_is_valid() returns whether a value is valid or not. */
grn_inline static grn_ts_bool
grn_ts_text_is_valid(grn_ts_text value)
{
  return value.ptr || !value.size;
}

/* grn_ts_geo_is_valid() returns whether a value is valid or not. */
grn_inline static grn_ts_bool
grn_ts_geo_is_valid(grn_ts_geo value)
{
  return ((value.latitude >= GRN_GEO_MIN_LATITUDE) &&
          (value.latitude <= GRN_GEO_MAX_LATITUDE)) &&
         ((value.longitude >= GRN_GEO_MIN_LONGITUDE) &&
          (value.longitude <= GRN_GEO_MAX_LONGITUDE));
}

#define GRN_TS_VECTOR_IS_VALID(type)\
  if (value.size) {\
    size_t i;\
    if (!value.ptr) {\
      return GRN_FALSE;\
    }\
    for (i = 0; i < value.size; i++) {\
      if (!grn_ts_ ## type ## _is_valid(value.ptr[i])) {\
        return GRN_FALSE;\
      }\
    }\
  }\
  return GRN_TRUE;
/* grn_ts_bool_vector_is_valid() returns whether a value is valid or not. */
grn_inline static grn_ts_bool
grn_ts_bool_vector_is_valid(grn_ts_bool_vector value)
{
  GRN_TS_VECTOR_IS_VALID(bool)
}

/* grn_ts_int_vector_is_valid() returns whether a value is valid or not. */
grn_inline static grn_ts_bool
grn_ts_int_vector_is_valid(grn_ts_int_vector value)
{
  GRN_TS_VECTOR_IS_VALID(int)
}

/* grn_ts_float_vector_is_valid() returns whether a value is valid or not. */
grn_inline static grn_ts_bool
grn_ts_float_vector_is_valid(grn_ts_float_vector value)
{
  GRN_TS_VECTOR_IS_VALID(float)
}

/* grn_ts_time_vector_is_valid() returns whether a value is valid or not. */
grn_inline static grn_ts_bool
grn_ts_time_vector_is_valid(grn_ts_time_vector value)
{
  GRN_TS_VECTOR_IS_VALID(time)
}

/* grn_ts_text_vector_is_valid() returns whether a value is valid or not. */
grn_inline static grn_ts_bool
grn_ts_text_vector_is_valid(grn_ts_text_vector value)
{
  GRN_TS_VECTOR_IS_VALID(text)
}

/* grn_ts_geo_vector_is_valid() returns whether a value is valid or not. */
grn_inline static grn_ts_bool
grn_ts_geo_vector_is_valid(grn_ts_geo_vector value)
{
  GRN_TS_VECTOR_IS_VALID(geo)
}
#undef GRN_TS_VECTOR_IS_VALID

/* grn_ts_bool_zero() returns a zero. */
grn_inline static grn_ts_bool
grn_ts_bool_zero(void)
{
  return GRN_FALSE;
}

/* grn_ts_int_zero() returns a zero. */
grn_inline static grn_ts_int
grn_ts_int_zero(void)
{
  return 0;
}

/* grn_ts_float_zero() returns a zero. */
grn_inline static grn_ts_float
grn_ts_float_zero(void)
{
  return 0.0;
}

/* grn_ts_time_zero() returns a zero. */
grn_inline static grn_ts_time
grn_ts_time_zero(void)
{
  return 0;
}

/* grn_ts_text_zero() returns a zero. */
grn_inline static grn_ts_text
grn_ts_text_zero(void)
{
  return (grn_ts_text){ NULL, 0 };
}

/* grn_ts_geo_zero() returns a zero. */
grn_inline static grn_ts_geo
grn_ts_geo_zero(void)
{
  return (grn_ts_geo){ 0, 0 };
}

/* grn_ts_ref_zero() returns a zero. */
grn_inline static grn_ts_ref
grn_ts_ref_zero(void)
{
  return (grn_ts_ref){ 0, 0.0 };
}

/* grn_ts_bool_vector_zero() returns a zero. */
grn_inline static grn_ts_bool_vector
grn_ts_bool_vector_zero(void)
{
  return (grn_ts_bool_vector){ NULL, 0 };
}

/* grn_ts_int_vector_zero() returns a zero. */
grn_inline static grn_ts_int_vector
grn_ts_int_vector_zero(void)
{
  return (grn_ts_int_vector){ NULL, 0 };
}

/* grn_ts_float_vector_zero() returns a zero. */
grn_inline static grn_ts_float_vector
grn_ts_float_vector_zero(void)
{
  return (grn_ts_float_vector){ NULL, 0 };
}

/* grn_ts_time_vector_zero() returns a zero. */
grn_inline static grn_ts_time_vector
grn_ts_time_vector_zero(void)
{
  return (grn_ts_time_vector){ NULL, 0 };
}

/* grn_ts_text_vector_zero() returns a zero. */
grn_inline static grn_ts_text_vector
grn_ts_text_vector_zero(void)
{
  return (grn_ts_text_vector){ NULL, 0 };
}

/* grn_ts_geo_vector_zero() returns a zero. */
grn_inline static grn_ts_geo_vector
grn_ts_geo_vector_zero(void)
{
  return (grn_ts_geo_vector){ NULL, 0 };
}

/* grn_ts_ref_vector_zero() returns a zero. */
grn_inline static grn_ts_ref_vector
grn_ts_ref_vector_zero(void)
{
  return (grn_ts_ref_vector){ NULL, 0 };
}

/* grn_ts_data_type_to_kind() returns a kind associated with a type. */
static grn_ts_data_kind
grn_ts_data_type_to_kind(grn_ts_data_type type)
{
  switch (type) {
    case GRN_DB_VOID: {
      return GRN_TS_VOID;
    }
    case GRN_DB_BOOL: {
      return GRN_TS_BOOL;
    }
    case GRN_DB_INT8:
    case GRN_DB_INT16:
    case GRN_DB_INT32:
    case GRN_DB_INT64:
    case GRN_DB_UINT8:
    case GRN_DB_UINT16:
    case GRN_DB_UINT32:
    case GRN_DB_UINT64: {
      return GRN_TS_INT;
    }
    case GRN_DB_FLOAT: {
      return GRN_TS_FLOAT;
    }
    case GRN_DB_TIME: {
      return GRN_TS_TIME;
    }
    case GRN_DB_SHORT_TEXT:
    case GRN_DB_TEXT:
    case GRN_DB_LONG_TEXT: {
      return GRN_TS_TEXT;
    }
    case GRN_DB_TOKYO_GEO_POINT:
    case GRN_DB_WGS84_GEO_POINT: {
      return GRN_TS_GEO;
    }
    default: {
      return GRN_TS_REF;
    }
  }
}

/* grn_ts_data_kind_to_type() returns a type associated with a kind. */
static grn_ts_data_type
grn_ts_data_kind_to_type(grn_ts_data_kind kind)
{
  switch (kind & ~GRN_TS_VECTOR_FLAG) {
    case GRN_TS_BOOL: {
      return GRN_DB_BOOL;
    }
    case GRN_TS_INT: {
      return GRN_DB_INT64;
    }
    case GRN_TS_FLOAT: {
      return GRN_DB_FLOAT;
    }
    case GRN_TS_TIME: {
      return GRN_DB_TIME;
    }
    case GRN_TS_TEXT: {
      return GRN_DB_TEXT;
    }
    case GRN_TS_GEO: {
      /* GRN_DB_TOKYO_GEO_POINT or GRN_DB_WGS84_GEO_POINT. */
      return GRN_DB_VOID;
    }
    case GRN_TS_REF: {
      /*
       * grn_ts_data_kind does not have enough information to get a correct
       * table ID.
       */
      return GRN_DB_VOID;
    }
    default: {
      return GRN_DB_VOID;
    }
  }
}

/*-------------------------------------------------------------
 * Operators.
 */

/* grn_ts_op_logical_not_bool() returns !arg. */
grn_inline static grn_ts_bool
grn_ts_op_logical_not_bool(grn_ts_bool arg)
{
  return !arg;
}

/* grn_ts_op_bitwise_not_bool() returns ~arg. */
grn_inline static grn_ts_bool
grn_ts_op_bitwise_not_bool(grn_ts_bool arg)
{
  return !arg;
}

/* grn_ts_op_bitwise_not_int() returns ~arg. */
grn_inline static grn_ts_int
grn_ts_op_bitwise_not_int(grn_ts_int arg)
{
  return ~arg;
}

/* grn_ts_op_positive_int() returns +arg. */
grn_inline static grn_ts_int
grn_ts_op_positive_int(grn_ts_int arg)
{
  return arg;
}

/* grn_ts_op_positive_float() returns +arg. */
grn_inline static grn_ts_float
grn_ts_op_positive_float(grn_ts_float arg)
{
  return arg;
}

/* grn_ts_op_negative_int() returns -arg. */
grn_inline static grn_ts_int
grn_ts_op_negative_int(grn_ts_int arg)
{
  return -arg;
}

/* grn_ts_op_negative_float() returns -arg. */
grn_inline static grn_ts_float
grn_ts_op_negative_float(grn_ts_float arg)
{
  return -arg;
}

/* grn_ts_op_float() returns (Float)arg. */
static grn_rc
grn_ts_op_float(grn_ctx *ctx, grn_ts_int arg, grn_ts_float *out)
{
  *out = (grn_ts_float)arg;
  return GRN_SUCCESS;
}

/* grn_ts_op_time() returns (Time)arg. */
static grn_rc
grn_ts_op_time(grn_ctx *ctx, grn_ts_text arg, grn_ts_time *out)
{
  grn_timeval value;
  grn_rc rc = grn_str2timeval(arg.ptr, arg.size, &value);
  if (rc != GRN_SUCCESS) {
    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "grn_str2timeval failed");
  }
  *out = (grn_ts_time)((value.tv_sec * 1000000) + (value.tv_nsec / 1000));
  return GRN_SUCCESS;
}

/* grn_ts_op_bitwise_and_bool() returns lhs & rhs. */
grn_inline static grn_ts_bool
grn_ts_op_bitwise_and_bool(grn_ts_bool lhs, grn_ts_bool rhs)
{
  return lhs & rhs;
}

/* grn_ts_op_bitwise_and_int() returns lhs & rhs. */
grn_inline static grn_ts_int
grn_ts_op_bitwise_and_int(grn_ts_int lhs, grn_ts_int rhs)
{
  return lhs & rhs;
}

/* grn_ts_op_bitwise_or_bool() returns lhs | rhs. */
grn_inline static grn_ts_bool
grn_ts_op_bitwise_or_bool(grn_ts_bool lhs, grn_ts_bool rhs)
{
  return lhs | rhs;
}

/* grn_ts_op_bitwise_or_int() returns lhs | rhs. */
grn_inline static grn_ts_int
grn_ts_op_bitwise_or_int(grn_ts_int lhs, grn_ts_int rhs)
{
  return lhs | rhs;
}

/* grn_ts_op_bitwise_xor_bool() returns lhs ^ rhs. */
grn_inline static grn_ts_bool
grn_ts_op_bitwise_xor_bool(grn_ts_bool lhs, grn_ts_bool rhs)
{
  return lhs ^ rhs;
}

/* grn_ts_op_bitwise_xor_int() returns lhs ^ rhs. */
grn_inline static grn_ts_int
grn_ts_op_bitwise_xor_int(grn_ts_int lhs, grn_ts_int rhs)
{
  return lhs ^ rhs;
}

/* grn_ts_op_equal_bool() returns lhs == rhs. */
grn_inline static grn_ts_bool
grn_ts_op_equal_bool(grn_ts_bool lhs, grn_ts_bool rhs)
{
  return lhs == rhs;
}

/* grn_ts_op_equal_int() returns lhs == rhs. */
grn_inline static grn_ts_bool
grn_ts_op_equal_int(grn_ts_int lhs, grn_ts_int rhs)
{
  return lhs == rhs;
}

/* grn_ts_op_equal_float() returns lhs == rhs. */
grn_inline static grn_ts_bool
grn_ts_op_equal_float(grn_ts_float lhs, grn_ts_float rhs)
{
  /* To suppress warnings, "lhs == rhs" is not used. */
  return (lhs <= rhs) && (lhs >= rhs);
}

/* grn_ts_op_equal_time() returns lhs == rhs. */
grn_inline static grn_ts_bool
grn_ts_op_equal_time(grn_ts_time lhs, grn_ts_time rhs)
{
  return lhs == rhs;
}

/* grn_ts_op_equal_text() returns lhs == rhs. */
grn_inline static grn_ts_bool
grn_ts_op_equal_text(grn_ts_text lhs, grn_ts_text rhs)
{
  return (lhs.size == rhs.size) && !memcmp(lhs.ptr, rhs.ptr, lhs.size);
}

/* grn_ts_op_equal_geo() returns lhs == rhs. */
grn_inline static grn_ts_bool
grn_ts_op_equal_geo(grn_ts_geo lhs, grn_ts_geo rhs)
{
  return (lhs.latitude == rhs.latitude) && (lhs.longitude == rhs.longitude);
}

/* grn_ts_op_equal_ref() returns lhs == rhs. */
grn_inline static grn_ts_bool
grn_ts_op_equal_ref(grn_ts_ref lhs, grn_ts_ref rhs)
{
  /* Ignore scores. */
  return lhs.id == rhs.id;
}

#define GRN_TS_OP_EQUAL_VECTOR(kind)\
  size_t i;\
  if (lhs.size != rhs.size) {\
    return GRN_FALSE;\
  }\
  for (i = 0; i < lhs.size; i++) {\
    if (!grn_ts_op_equal_ ## kind(lhs.ptr[i], rhs.ptr[i])) {\
      return GRN_FALSE;\
    }\
  }\
  return GRN_TRUE;
/* grn_ts_op_equal_bool_vector() returns lhs == rhs. */
grn_inline static grn_ts_bool
grn_ts_op_equal_bool_vector(grn_ts_bool_vector lhs, grn_ts_bool_vector rhs)
{
  GRN_TS_OP_EQUAL_VECTOR(bool)
}

/* grn_ts_op_equal_int_vector() returns lhs == rhs. */
grn_inline static grn_ts_bool
grn_ts_op_equal_int_vector(grn_ts_int_vector lhs, grn_ts_int_vector rhs)
{
  GRN_TS_OP_EQUAL_VECTOR(int)
}

/* grn_ts_op_equal_float_vector() returns lhs == rhs. */
grn_inline static grn_ts_bool
grn_ts_op_equal_float_vector(grn_ts_float_vector lhs, grn_ts_float_vector rhs)
{
  GRN_TS_OP_EQUAL_VECTOR(float)
}

/* grn_ts_op_equal_time_vector() returns lhs == rhs. */
grn_inline static grn_ts_bool
grn_ts_op_equal_time_vector(grn_ts_time_vector lhs, grn_ts_time_vector rhs)
{
  GRN_TS_OP_EQUAL_VECTOR(time)
}

/* grn_ts_op_equal_text_vector() returns lhs == rhs. */
grn_inline static grn_ts_bool
grn_ts_op_equal_text_vector(grn_ts_text_vector lhs, grn_ts_text_vector rhs)
{
  GRN_TS_OP_EQUAL_VECTOR(text)
}

/* grn_ts_op_equal_geo_vector() returns lhs == rhs. */
grn_inline static grn_ts_bool
grn_ts_op_equal_geo_vector(grn_ts_geo_vector lhs, grn_ts_geo_vector rhs)
{
  GRN_TS_OP_EQUAL_VECTOR(geo)
}

/* grn_ts_op_equal_ref_vector() returns lhs == rhs. */
grn_inline static grn_ts_bool
grn_ts_op_equal_ref_vector(grn_ts_ref_vector lhs, grn_ts_ref_vector rhs)
{
  GRN_TS_OP_EQUAL_VECTOR(ref)
}
#undef GRN_TS_OP_EQUAL_VECTOR

/* grn_ts_op_not_equal_bool() returns lhs != rhs. */
grn_inline static grn_ts_bool
grn_ts_op_not_equal_bool(grn_ts_bool lhs, grn_ts_bool rhs)
{
  return lhs != rhs;
}

/* grn_ts_op_not_equal_int() returns lhs != rhs. */
grn_inline static grn_ts_bool
grn_ts_op_not_equal_int(grn_ts_int lhs, grn_ts_int rhs)
{
  return lhs != rhs;
}

/* grn_ts_op_not_equal_float() returns lhs != rhs. */
grn_inline static grn_ts_bool
grn_ts_op_not_equal_float(grn_ts_float lhs, grn_ts_float rhs)
{
  /* To suppress warnings, "lhs != rhs" is not used. */
  return (lhs < rhs) || (lhs > rhs);
}

/* grn_ts_op_not_equal_time() returns lhs != rhs. */
grn_inline static grn_ts_bool
grn_ts_op_not_equal_time(grn_ts_time lhs, grn_ts_time rhs)
{
  return lhs != rhs;
}

/* grn_ts_op_not_equal_text() returns lhs != rhs. */
grn_inline static grn_ts_bool
grn_ts_op_not_equal_text(grn_ts_text lhs, grn_ts_text rhs)
{
  return (lhs.size != rhs.size) || memcmp(lhs.ptr, rhs.ptr, lhs.size);
}

/* grn_ts_op_not_equal_geo() returns lhs != rhs. */
grn_inline static grn_ts_bool
grn_ts_op_not_equal_geo(grn_ts_geo lhs, grn_ts_geo rhs)
{
  return (lhs.latitude != rhs.latitude) || (lhs.longitude != rhs.longitude);
}

/* grn_ts_op_not_equal_ref() returns lhs != rhs. */
grn_inline static grn_ts_bool
grn_ts_op_not_equal_ref(grn_ts_ref lhs, grn_ts_ref rhs)
{
  /* Ignore scores. */
  return lhs.id != rhs.id;
}

#define GRN_TS_OP_NOT_EQUAL_VECTOR(kind)\
  size_t i;\
  if (lhs.size != rhs.size) {\
    return GRN_TRUE;\
  }\
  for (i = 0; i < lhs.size; i++) {\
    if (grn_ts_op_not_equal_ ## kind(lhs.ptr[i], rhs.ptr[i])) {\
      return GRN_TRUE;\
    }\
  }\
  return GRN_FALSE;
/* grn_ts_op_not_equal_bool_vector() returns lhs != rhs. */
grn_inline static grn_ts_bool
grn_ts_op_not_equal_bool_vector(grn_ts_bool_vector lhs, grn_ts_bool_vector rhs)
{
  GRN_TS_OP_NOT_EQUAL_VECTOR(bool)
}

/* grn_ts_op_not_equal_int_vector() returns lhs != rhs. */
grn_inline static grn_ts_bool
grn_ts_op_not_equal_int_vector(grn_ts_int_vector lhs, grn_ts_int_vector rhs)
{
  GRN_TS_OP_NOT_EQUAL_VECTOR(int)
}

/* grn_ts_op_not_equal_float_vector() returns lhs != rhs. */
grn_inline static grn_ts_bool
grn_ts_op_not_equal_float_vector(grn_ts_float_vector lhs,
                                 grn_ts_float_vector rhs)
{
  GRN_TS_OP_NOT_EQUAL_VECTOR(float)
}

/* grn_ts_op_not_equal_time_vector() returns lhs != rhs. */
grn_inline static grn_ts_bool
grn_ts_op_not_equal_time_vector(grn_ts_time_vector lhs, grn_ts_time_vector rhs)
{
  GRN_TS_OP_NOT_EQUAL_VECTOR(time)
}

/* grn_ts_op_not_equal_text_vector() returns lhs != rhs. */
grn_inline static grn_ts_bool
grn_ts_op_not_equal_text_vector(grn_ts_text_vector lhs, grn_ts_text_vector rhs)
{
  GRN_TS_OP_NOT_EQUAL_VECTOR(text)
}

/* grn_ts_op_not_equal_geo_vector() returns lhs != rhs. */
grn_inline static grn_ts_bool
grn_ts_op_not_equal_geo_vector(grn_ts_geo_vector lhs, grn_ts_geo_vector rhs)
{
  GRN_TS_OP_NOT_EQUAL_VECTOR(geo)
}

/* grn_ts_op_not_equal_ref_vector() returns lhs != rhs. */
grn_inline static grn_ts_bool
grn_ts_op_not_equal_ref_vector(grn_ts_ref_vector lhs, grn_ts_ref_vector rhs)
{
  GRN_TS_OP_NOT_EQUAL_VECTOR(ref)
}
#undef GRN_TS_OP_NOT_EQUAL_VECTOR

/* grn_ts_op_less_int() returns lhs < rhs. */
grn_inline static grn_ts_bool
grn_ts_op_less_int(grn_ts_int lhs, grn_ts_int rhs)
{
  return lhs < rhs;
}

/* grn_ts_op_less_float() returns lhs < rhs. */
grn_inline static grn_ts_bool
grn_ts_op_less_float(grn_ts_float lhs, grn_ts_float rhs)
{
  return lhs < rhs;
}

/* grn_ts_op_less_time() returns lhs < rhs. */
grn_inline static grn_ts_bool
grn_ts_op_less_time(grn_ts_time lhs, grn_ts_time rhs)
{
  return lhs < rhs;
}

/* grn_ts_op_less_text() returns lhs < rhs. */
grn_inline static grn_ts_bool
grn_ts_op_less_text(grn_ts_text lhs, grn_ts_text rhs)
{
  size_t min_size = (lhs.size < rhs.size) ? lhs.size : rhs.size;
  int cmp = memcmp(lhs.ptr, rhs.ptr, min_size);
  return cmp ? (cmp < 0) : (lhs.size < rhs.size);
}

#define GRN_TS_OP_LESS_VECTOR(kind)\
  size_t i, min_size = (lhs.size < rhs.size) ? lhs.size : rhs.size;\
  for (i = 0; i < min_size; i++) {\
    if (grn_ts_op_not_equal_ ## kind(lhs.ptr[i], rhs.ptr[i])) {\
      if (grn_ts_op_less_ ## kind(lhs.ptr[i], rhs.ptr[i])) {\
        return GRN_TRUE;\
      }\
    }\
  }\
  return lhs.size < rhs.size;
/* grn_ts_op_less_int_vector() returns lhs < rhs. */
grn_inline static grn_ts_bool
grn_ts_op_less_int_vector(grn_ts_int_vector lhs, grn_ts_int_vector rhs)
{
  GRN_TS_OP_LESS_VECTOR(int)
}

/* grn_ts_op_less_float_vector() returns lhs < rhs. */
grn_inline static grn_ts_bool
grn_ts_op_less_float_vector(grn_ts_float_vector lhs, grn_ts_float_vector rhs)
{
  GRN_TS_OP_LESS_VECTOR(float)
}

/* grn_ts_op_less_time_vector() returns lhs < rhs. */
grn_inline static grn_ts_bool
grn_ts_op_less_time_vector(grn_ts_time_vector lhs, grn_ts_time_vector rhs)
{
  GRN_TS_OP_LESS_VECTOR(time)
}

/* grn_ts_op_less_text_vector() returns lhs < rhs. */
grn_inline static grn_ts_bool
grn_ts_op_less_text_vector(grn_ts_text_vector lhs, grn_ts_text_vector rhs)
{
  GRN_TS_OP_LESS_VECTOR(text)
}
#undef GRN_TS_OP_LESS_VECTOR

/* grn_ts_op_less_equal_int() returns lhs <= rhs. */
grn_inline static grn_ts_bool
grn_ts_op_less_equal_int(grn_ts_int lhs, grn_ts_int rhs)
{
  return lhs <= rhs;
}

/* grn_ts_op_less_equal_float() returns lhs <= rhs. */
grn_inline static grn_ts_bool
grn_ts_op_less_equal_float(grn_ts_float lhs, grn_ts_float rhs)
{
  return lhs <= rhs;
}

/* grn_ts_op_less_equal_time() returns lhs <= rhs. */
grn_inline static grn_ts_bool
grn_ts_op_less_equal_time(grn_ts_time lhs, grn_ts_time rhs)
{
  return lhs <= rhs;
}

/* grn_ts_op_less_equal_text() returns lhs <= rhs. */
grn_inline static grn_ts_bool
grn_ts_op_less_equal_text(grn_ts_text lhs, grn_ts_text rhs)
{
  size_t min_size = (lhs.size < rhs.size) ? lhs.size : rhs.size;
  int cmp = memcmp(lhs.ptr, rhs.ptr, min_size);
  return cmp ? (cmp < 0) : (lhs.size <= rhs.size);
}

#define GRN_TS_OP_LESS_EQUAL_VECTOR(kind)\
  size_t i, min_size = (lhs.size < rhs.size) ? lhs.size : rhs.size;\
  for (i = 0; i < min_size; i++) {\
    if (grn_ts_op_not_equal_ ## kind(lhs.ptr[i], rhs.ptr[i])) {\
      if (grn_ts_op_less_ ## kind(lhs.ptr[i], rhs.ptr[i])) {\
        return GRN_TRUE;\
      }\
    }\
  }\
  return lhs.size <= rhs.size;
/* grn_ts_op_less_equal_int_vector() returns lhs <= rhs. */
grn_inline static grn_ts_bool
grn_ts_op_less_equal_int_vector(grn_ts_int_vector lhs, grn_ts_int_vector rhs)
{
  GRN_TS_OP_LESS_EQUAL_VECTOR(int)
}

/* grn_ts_op_less_equal_float_vector() returns lhs <= rhs. */
grn_inline static grn_ts_bool
grn_ts_op_less_equal_float_vector(grn_ts_float_vector lhs,
                                  grn_ts_float_vector rhs)
{
  GRN_TS_OP_LESS_EQUAL_VECTOR(float)
}

/* grn_ts_op_less_equal_time_vector() returns lhs <= rhs. */
grn_inline static grn_ts_bool
grn_ts_op_less_equal_time_vector(grn_ts_time_vector lhs,
                                 grn_ts_time_vector rhs)
{
  GRN_TS_OP_LESS_EQUAL_VECTOR(time)
}

/* grn_ts_op_less_equal_text_vector() returns lhs <= rhs. */
grn_inline static grn_ts_bool
grn_ts_op_less_equal_text_vector(grn_ts_text_vector lhs,
                                 grn_ts_text_vector rhs)
{
  GRN_TS_OP_LESS_EQUAL_VECTOR(text)
}
#undef GRN_TS_OP_LESS_EQUAL_VECTOR

/* grn_ts_op_greater_int() returns lhs > rhs. */
grn_inline static grn_ts_bool
grn_ts_op_greater_int(grn_ts_int lhs, grn_ts_int rhs)
{
  return lhs > rhs;
}

/* grn_ts_op_greater_float() returns lhs > rhs. */
grn_inline static grn_ts_bool
grn_ts_op_greater_float(grn_ts_float lhs, grn_ts_float rhs)
{
  return lhs > rhs;
}

/* grn_ts_op_greater_time() returns lhs > rhs. */
grn_inline static grn_ts_bool
grn_ts_op_greater_time(grn_ts_time lhs, grn_ts_time rhs)
{
  return lhs > rhs;
}

/* grn_ts_op_greater_text() returns lhs > rhs. */
grn_inline static grn_ts_bool
grn_ts_op_greater_text(grn_ts_text lhs, grn_ts_text rhs)
{
  size_t min_size = (lhs.size < rhs.size) ? lhs.size : rhs.size;
  int cmp = memcmp(lhs.ptr, rhs.ptr, min_size);
  return cmp ? (cmp > 0) : (lhs.size > rhs.size);
}

#define GRN_TS_OP_GREATER_VECTOR(kind)\
  size_t i, min_size = (lhs.size < rhs.size) ? lhs.size : rhs.size;\
  for (i = 0; i < min_size; i++) {\
    if (grn_ts_op_not_equal_ ## kind(lhs.ptr[i], rhs.ptr[i])) {\
      if (grn_ts_op_greater_ ## kind(lhs.ptr[i], rhs.ptr[i])) {\
        return GRN_TRUE;\
      }\
    }\
  }\
  return lhs.size > rhs.size;
/* grn_ts_op_greater_int_vector() returns lhs > rhs. */
grn_inline static grn_ts_bool
grn_ts_op_greater_int_vector(grn_ts_int_vector lhs, grn_ts_int_vector rhs)
{
  GRN_TS_OP_GREATER_VECTOR(int)
}

/* grn_ts_op_greater_float_vector() returns lhs > rhs. */
grn_inline static grn_ts_bool
grn_ts_op_greater_float_vector(grn_ts_float_vector lhs,
                               grn_ts_float_vector rhs)
{
  GRN_TS_OP_GREATER_VECTOR(float)
}

/* grn_ts_op_greater_time_vector() returns lhs > rhs. */
grn_inline static grn_ts_bool
grn_ts_op_greater_time_vector(grn_ts_time_vector lhs, grn_ts_time_vector rhs)
{
  GRN_TS_OP_GREATER_VECTOR(time)
}

/* grn_ts_op_greater_text_vector() returns lhs > rhs. */
grn_inline static grn_ts_bool
grn_ts_op_greater_text_vector(grn_ts_text_vector lhs, grn_ts_text_vector rhs)
{
  GRN_TS_OP_GREATER_VECTOR(text)
}
#undef GRN_TS_OP_GREATER_VECTOR

/* grn_ts_op_greater_equal_int() returns lhs >= rhs. */
grn_inline static grn_ts_bool
grn_ts_op_greater_equal_int(grn_ts_int lhs, grn_ts_int rhs)
{
  return lhs >= rhs;
}

/* grn_ts_op_greater_equal_float() returns lhs >= rhs. */
grn_inline static grn_ts_bool
grn_ts_op_greater_equal_float(grn_ts_float lhs, grn_ts_float rhs)
{
  return lhs >= rhs;
}

/* grn_ts_op_greater_equal_time() returns lhs >= rhs. */
grn_inline static grn_ts_bool
grn_ts_op_greater_equal_time(grn_ts_time lhs, grn_ts_time rhs)
{
  return lhs >= rhs;
}

/* grn_ts_op_greater_equal_text() returns lhs >= rhs. */
grn_inline static grn_ts_bool
grn_ts_op_greater_equal_text(grn_ts_text lhs, grn_ts_text rhs)
{
  size_t min_size = (lhs.size < rhs.size) ? lhs.size : rhs.size;
  int cmp = memcmp(lhs.ptr, rhs.ptr, min_size);
  return cmp ? (cmp > 0) : (lhs.size >= rhs.size);
}

#define GRN_TS_OP_GREATER_EQUAL_VECTOR(kind)\
  size_t i, min_size = (lhs.size < rhs.size) ? lhs.size : rhs.size;\
  for (i = 0; i < min_size; i++) {\
    if (grn_ts_op_not_equal_ ## kind(lhs.ptr[i], rhs.ptr[i])) {\
      if (grn_ts_op_greater_ ## kind(lhs.ptr[i], rhs.ptr[i])) {\
        return GRN_TRUE;\
      }\
    }\
  }\
  return lhs.size >= rhs.size;
/* grn_ts_op_greater_equal_int_vector() returns lhs >= rhs. */
grn_inline static grn_ts_bool
grn_ts_op_greater_equal_int_vector(grn_ts_int_vector lhs,
                                   grn_ts_int_vector rhs)
{
  GRN_TS_OP_GREATER_EQUAL_VECTOR(int)
}

/* grn_ts_op_greater_equal_float_vector() returns lhs >= rhs. */
grn_inline static grn_ts_bool
grn_ts_op_greater_equal_float_vector(grn_ts_float_vector lhs,
                                     grn_ts_float_vector rhs)
{
  GRN_TS_OP_GREATER_EQUAL_VECTOR(float)
}

/* grn_ts_op_greater_equal_time_vector() returns lhs >= rhs. */
grn_inline static grn_ts_bool
grn_ts_op_greater_equal_time_vector(grn_ts_time_vector lhs,
                                    grn_ts_time_vector rhs)
{
  GRN_TS_OP_GREATER_EQUAL_VECTOR(time)
}

/* grn_ts_op_greater_equal_text_vector() returns lhs >= rhs. */
grn_inline static grn_ts_bool
grn_ts_op_greater_equal_text_vector(grn_ts_text_vector lhs,
                                    grn_ts_text_vector rhs)
{
  GRN_TS_OP_GREATER_EQUAL_VECTOR(text)
}
#undef GRN_TS_OP_GREATER_EQUAL_VECTOR

/* grn_ts_op_shift_arithmetic_left() returns lhs << rhs. */
grn_inline static grn_ts_int
grn_ts_op_shift_arithmetic_left(grn_ts_int lhs, grn_ts_int rhs)
{
  return lhs << rhs;
}

/* grn_ts_op_shift_arithmetic_right() returns lhs << rhs. */
grn_inline static grn_ts_int
grn_ts_op_shift_arithmetic_right(grn_ts_int lhs, grn_ts_int rhs)
{
  return lhs >> rhs;
}

/* grn_ts_op_shift_logical_left() returns lhs << rhs. */
grn_inline static grn_ts_int
grn_ts_op_shift_logical_left(grn_ts_int lhs, grn_ts_int rhs)
{
  return lhs << rhs;
}

/* grn_ts_op_shift_logical_right() returns lhs << rhs. */
grn_inline static grn_ts_int
grn_ts_op_shift_logical_right(grn_ts_int lhs, grn_ts_int rhs)
{
  return (uint64_t)lhs >> rhs;
}

grn_inline static grn_rc
grn_ts_op_plus_int_int(grn_ctx *ctx, grn_ts_int lhs, grn_ts_int rhs,
                       grn_ts_int *out)
{
  *out = lhs + rhs;
  return GRN_SUCCESS;
}

grn_inline static grn_rc
grn_ts_op_plus_float_float(grn_ctx *ctx, grn_ts_float lhs, grn_ts_float rhs,
                           grn_ts_float *out)
{
  *out = lhs + rhs;
  if (!grn_ts_float_is_valid(*out)) {
    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "%g + %g = %g", lhs, rhs, *out);
  }
  return GRN_SUCCESS;
}

grn_inline static grn_rc
grn_ts_op_plus_time_int(grn_ctx *ctx, grn_ts_time lhs, grn_ts_int rhs,
                        grn_ts_time *out)
{
  *out = lhs + (rhs * 1000000);
  return GRN_SUCCESS;
}

grn_inline static grn_rc
grn_ts_op_plus_time_float(grn_ctx *ctx, grn_ts_time lhs, grn_ts_float rhs,
                          grn_ts_time *out)
{
  *out = (grn_ts_time)(lhs + (rhs * 1000000.0));
  return GRN_SUCCESS;
}

grn_inline static grn_rc
grn_ts_op_minus_int_int(grn_ctx *ctx, grn_ts_int lhs, grn_ts_int rhs,
                        grn_ts_int *out)
{
  *out = lhs - rhs;
  return GRN_SUCCESS;
}

grn_inline static grn_rc
grn_ts_op_minus_float_float(grn_ctx *ctx, grn_ts_float lhs, grn_ts_float rhs,
                            grn_ts_float *out)
{
  *out = lhs - rhs;
  if (!grn_ts_float_is_valid(*out)) {
    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "%g - %g = %g", lhs, rhs, *out);
  }
  return GRN_SUCCESS;
}

grn_inline static grn_rc
grn_ts_op_minus_time_time(grn_ctx *ctx, grn_ts_time lhs, grn_ts_time rhs,
                          grn_ts_float *out)
{
  *out = (lhs - rhs) * 0.000001;
  return GRN_SUCCESS;
}

grn_inline static grn_rc
grn_ts_op_minus_time_int(grn_ctx *ctx, grn_ts_time lhs, grn_ts_int rhs,
                         grn_ts_time *out)
{
  *out = lhs - (rhs * 1000000);
  return GRN_SUCCESS;
}

grn_inline static grn_rc
grn_ts_op_minus_time_float(grn_ctx *ctx, grn_ts_time lhs, grn_ts_float rhs,
                           grn_ts_time *out)
{
  *out = lhs - (grn_ts_int)(rhs * 1000000.0);
  return GRN_SUCCESS;
}

grn_inline static grn_rc
grn_ts_op_multiplication_int_int(grn_ctx *ctx, grn_ts_int lhs, grn_ts_int rhs,
                                 grn_ts_int *out)
{
  *out = lhs * rhs;
  return GRN_SUCCESS;
}

grn_inline static grn_rc
grn_ts_op_multiplication_float_float(grn_ctx *ctx, grn_ts_float lhs,
                                     grn_ts_float rhs, grn_ts_float *out)
{
  *out = lhs * rhs;
  if (!grn_ts_float_is_valid(*out)) {
    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "%g * %g = %g", lhs, rhs, *out);
  }
  return GRN_SUCCESS;
}

grn_inline static grn_rc
grn_ts_op_division_int_int(grn_ctx *ctx, grn_ts_int lhs, grn_ts_int rhs,
                           grn_ts_int *out)
{
  if (!rhs) {
    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT,
                      "%" GRN_FMT_INT64D " / %" GRN_FMT_INT64D
                      " causes division by zero",
                      lhs, rhs);
  }
  *out = (rhs != -1) ? (lhs / rhs) : -lhs;
  return GRN_SUCCESS;
}

grn_inline static grn_rc
grn_ts_op_division_float_float(grn_ctx *ctx, grn_ts_float lhs,
                               grn_ts_float rhs, grn_ts_float *out)
{
  *out = lhs / rhs;
  if (!grn_ts_float_is_valid(*out)) {
    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "%g / %g = %g", lhs, rhs, *out);
  }
  return GRN_SUCCESS;
}

grn_inline static grn_rc
grn_ts_op_modulus_int_int(grn_ctx *ctx, grn_ts_int lhs, grn_ts_int rhs,
                          grn_ts_int *out)
{
  if (!rhs) {
    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT,
                      "%" GRN_FMT_INT64D " %% %" GRN_FMT_INT64D
                      " causes division by zero",
                      lhs, rhs);
  }
  *out = (rhs != -1) ? (lhs % rhs) : -lhs;
  return GRN_SUCCESS;
}

grn_inline static grn_rc
grn_ts_op_modulus_float_float(grn_ctx *ctx, grn_ts_float lhs, grn_ts_float rhs,
                              grn_ts_float *out)
{
  *out = fmod(lhs, rhs);
  if (!grn_ts_float_is_valid(*out)) {
    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "%g %% %g = %g", lhs, rhs, *out);
  }
  return GRN_SUCCESS;
}

static grn_ts_bool
grn_ts_op_match(grn_ts_text lhs, grn_ts_text rhs)
{
  const char *lhs_ptr, *lhs_ptr_last;
  if (lhs.size < rhs.size) {
    return GRN_FALSE;
  }
  lhs_ptr_last = lhs.ptr + lhs.size - rhs.size;
  for (lhs_ptr = lhs.ptr; lhs_ptr <= lhs_ptr_last; lhs_ptr++) {
    size_t i;
    for (i = 0; i < rhs.size; i++) {
      if (lhs_ptr[i] != rhs.ptr[i]) {
        break;
      }
    }
    if (i == rhs.size) {
      return GRN_TRUE;
    }
  }
  return GRN_FALSE;
}

static grn_ts_bool
grn_ts_op_prefix_match(grn_ts_text lhs, grn_ts_text rhs)
{
  size_t i;
  if (lhs.size < rhs.size) {
    return GRN_FALSE;
  }
  for (i = 0; i < rhs.size; i++) {
    if (lhs.ptr[i] != rhs.ptr[i]) {
      return GRN_FALSE;
    }
  }
  return GRN_TRUE;
}

static grn_ts_bool
grn_ts_op_suffix_match(grn_ts_text lhs, grn_ts_text rhs)
{
  size_t i;
  const char *lhs_ptr;
  if (lhs.size < rhs.size) {
    return GRN_FALSE;
  }
  lhs_ptr = lhs.ptr + lhs.size - rhs.size;
  for (i = 0; i < rhs.size; i++) {
    if (lhs_ptr[i] != rhs.ptr[i]) {
      return GRN_FALSE;
    }
  }
  return GRN_TRUE;
}

/*-------------------------------------------------------------
 * Groonga objects.
 */

#define GRN_TS_TABLE_GET_KEY(type)\
  uint32_t key_size;\
  const void *key_ptr = _grn_ ## type ## _key(ctx, type, id, &key_size);\
  if (!key_ptr) {\
    GRN_TS_ERR_RETURN(GRN_UNKNOWN_ERROR, "_grn_" #type "_key failed: %u", id);\
  }\
/* grn_ts_hash_get_bool_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_hash_get_bool_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id,
                         grn_ts_bool *key)
{
  GRN_TS_TABLE_GET_KEY(hash)
  *key = *(const grn_ts_bool *)key_ptr;
  return GRN_SUCCESS;
}

/* grn_ts_hash_get_int8_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_hash_get_int8_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id,
                         grn_ts_int *key)
{
  GRN_TS_TABLE_GET_KEY(hash)
  *key = *(const int8_t *)key_ptr;
  return GRN_SUCCESS;
}

/* grn_ts_hash_get_int16_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_hash_get_int16_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id,
                          grn_ts_int *key)
{
  GRN_TS_TABLE_GET_KEY(hash)
  *key = *(const int16_t *)key_ptr;
  return GRN_SUCCESS;
}

/* grn_ts_hash_get_int32_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_hash_get_int32_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id,
                          grn_ts_int *key)
{
  GRN_TS_TABLE_GET_KEY(hash)
  *key = *(const int32_t *)key_ptr;
  return GRN_SUCCESS;
}

/* grn_ts_hash_get_int64_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_hash_get_int64_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id,
                          grn_ts_int *key)
{
  GRN_TS_TABLE_GET_KEY(hash)
  *key = *(const int64_t *)key_ptr;
  return GRN_SUCCESS;
}

/* grn_ts_hash_get_uint8_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_hash_get_uint8_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id,
                          grn_ts_int *key)
{
  GRN_TS_TABLE_GET_KEY(hash)
  *key = *(const uint8_t *)key_ptr;
  return GRN_SUCCESS;
}

/* grn_ts_hash_get_uint16_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_hash_get_uint16_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id,
                           grn_ts_int *key)
{
  GRN_TS_TABLE_GET_KEY(hash)
  *key = *(const uint16_t *)key_ptr;
  return GRN_SUCCESS;
}

/* grn_ts_hash_get_uint32_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_hash_get_uint32_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id,
                           grn_ts_int *key)
{
  GRN_TS_TABLE_GET_KEY(hash)
  *key = *(const uint32_t *)key_ptr;
  return GRN_SUCCESS;
}

/* grn_ts_hash_get_uint64_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_hash_get_uint64_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id,
                           grn_ts_int *key)
{
  GRN_TS_TABLE_GET_KEY(hash)
  *key = (grn_ts_int)*(const uint64_t *)key_ptr;
  return GRN_SUCCESS;
}

/* grn_ts_hash_get_float_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_hash_get_float_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id,
                          grn_ts_float *key)
{
  GRN_TS_TABLE_GET_KEY(hash)
  *key = *(const grn_ts_float *)key_ptr;
  return GRN_SUCCESS;
}

/* grn_ts_hash_get_time_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_hash_get_time_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id,
                         grn_ts_time *key)
{
  GRN_TS_TABLE_GET_KEY(hash)
  *key = *(const grn_ts_time *)key_ptr;
  return GRN_SUCCESS;
}

/* grn_ts_hash_get_geo_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_hash_get_geo_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id,
                        grn_ts_geo *key)
{
  GRN_TS_TABLE_GET_KEY(hash)
  *key = *(const grn_ts_geo *)key_ptr;
  return GRN_SUCCESS;
}

/* grn_ts_hash_get_text_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_hash_get_text_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id,
                         grn_ts_text *key)
{
  GRN_TS_TABLE_GET_KEY(hash)
  key->ptr = key_ptr;
  key->size = key_size;
  return GRN_SUCCESS;
}

/* grn_ts_hash_get_ref_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_hash_get_ref_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id,
                        grn_ts_ref *key)
{
  GRN_TS_TABLE_GET_KEY(hash)
  key->id = *(const grn_ts_id *)key_ptr;
  return GRN_SUCCESS;
}

/* grn_ts_pat_get_bool_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_pat_get_bool_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id,
                        grn_ts_bool *key)
{
  GRN_TS_TABLE_GET_KEY(pat)
  *key = *(const grn_ts_bool *)key_ptr;
  return GRN_SUCCESS;
}

/* grn_ts_pat_get_int8_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_pat_get_int8_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id,
                        grn_ts_int *key)
{
  int8_t tmp;
  GRN_TS_TABLE_GET_KEY(pat)
  grn_ntohi(&tmp, key_ptr, sizeof(tmp));
  *key = tmp;
  return GRN_SUCCESS;
}

/* grn_ts_pat_get_int16_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_pat_get_int16_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id,
                         grn_ts_int *key)
{
  int16_t tmp;
  GRN_TS_TABLE_GET_KEY(pat)
  grn_ntohi(&tmp, key_ptr, sizeof(tmp));
  *key = tmp;
  return GRN_SUCCESS;
}

/* grn_ts_pat_get_int32_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_pat_get_int32_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id,
                         grn_ts_int *key)
{
  int32_t tmp;
  GRN_TS_TABLE_GET_KEY(pat)
  grn_ntohi(&tmp, key_ptr, sizeof(tmp));
  *key = tmp;
  return GRN_SUCCESS;
}

/* grn_ts_pat_get_int64_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_pat_get_int64_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id,
                         grn_ts_int *key)
{
  GRN_TS_TABLE_GET_KEY(pat)
  grn_ntohi(key, key_ptr, sizeof(grn_ts_int));
  return GRN_SUCCESS;
}

/* grn_ts_pat_get_uint8_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_pat_get_uint8_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id,
                         grn_ts_int *key)
{
  GRN_TS_TABLE_GET_KEY(pat)
  *key = *(const uint8_t *)key_ptr;
  return GRN_SUCCESS;
}

/* grn_ts_pat_get_uint16_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_pat_get_uint16_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id,
                          grn_ts_int *key)
{
  uint16_t tmp;
  GRN_TS_TABLE_GET_KEY(pat)
  grn_ntoh(&tmp, key_ptr, sizeof(tmp));
  *key = tmp;
  return GRN_SUCCESS;
}

/* grn_ts_pat_get_uint32_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_pat_get_uint32_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id,
                          grn_ts_int *key)
{
  uint32_t tmp;
  GRN_TS_TABLE_GET_KEY(pat)
  grn_ntoh(&tmp, key_ptr, sizeof(tmp));
  *key = tmp;
  return GRN_SUCCESS;
}

/* grn_ts_pat_get_uint64_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_pat_get_uint64_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id,
                          grn_ts_int *key)
{
  GRN_TS_TABLE_GET_KEY(pat)
  grn_ntoh(key, key_ptr, sizeof(grn_ts_int));
  return GRN_SUCCESS;
}

/* grn_ts_pat_get_float_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_pat_get_float_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id,
                         grn_ts_float *key)
{
  int64_t tmp;
  GRN_TS_TABLE_GET_KEY(pat)
  grn_ntoh(&tmp, key_ptr, sizeof(tmp));
  tmp ^= (((tmp ^ ((int64_t)1 << 63)) >> 63) | ((int64_t)1 << 63));
  *(int64_t *)key = tmp;
  return GRN_SUCCESS;
}

/* grn_ts_pat_get_time_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_pat_get_time_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id,
                        grn_ts_time *key)
{
  GRN_TS_TABLE_GET_KEY(pat)
  grn_ntohi(key, key_ptr, sizeof(grn_ts_time));
  return GRN_SUCCESS;
}

/* grn_ts_pat_get_geo_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_pat_get_geo_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id,
                       grn_ts_geo *key)
{
  GRN_TS_TABLE_GET_KEY(pat)
  grn_ntog(key, key_ptr, sizeof(grn_ts_geo));
  return GRN_SUCCESS;
}

/* grn_ts_pat_get_text_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_pat_get_text_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id,
                        grn_ts_text *key)
{
  GRN_TS_TABLE_GET_KEY(pat)
  key->ptr = key_ptr;
  key->size = key_size;
  return GRN_SUCCESS;
}

/* grn_ts_pat_get_ref_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_pat_get_ref_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id,
                       grn_ts_ref *key)
{
  GRN_TS_TABLE_GET_KEY(pat)
  grn_ntoh(&key->id, key_ptr, sizeof(key->id));
  return GRN_SUCCESS;
}

/* grn_ts_dat_get_text_key() gets a reference to a key (_key). */
static grn_rc
grn_ts_dat_get_text_key(grn_ctx *ctx, grn_dat *dat, grn_ts_id id,
                        grn_ts_text *key)
{
  GRN_TS_TABLE_GET_KEY(dat)
  key->ptr = key_ptr;
  key->size = key_size;
  return GRN_SUCCESS;
}
#undef GRN_TS_TABLE_GET_KEY

/*-------------------------------------------------------------
 * grn_ts_expr_id_node.
 */

typedef struct {
  GRN_TS_EXPR_NODE_COMMON_MEMBERS
} grn_ts_expr_id_node;

/* grn_ts_expr_id_node_init() initializes a node. */
static void
grn_ts_expr_id_node_init(grn_ctx *ctx, grn_ts_expr_id_node *node)
{
  memset(node, 0, sizeof(*node));
  node->type = GRN_TS_EXPR_ID_NODE;
  node->data_kind = GRN_TS_INT;
  node->data_type = GRN_DB_UINT32;
}

/* grn_ts_expr_id_node_fin() finalizes a node. */
static void
grn_ts_expr_id_node_fin(grn_ctx *ctx, grn_ts_expr_id_node *node)
{
  /* Nothing to do. */
}

grn_rc
grn_ts_expr_id_node_open(grn_ctx *ctx, grn_ts_expr_node **node)
{
  grn_ts_expr_id_node *new_node = GRN_MALLOCN(grn_ts_expr_id_node, 1);
  if (!new_node) {
    GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
                      "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1",
                      sizeof(grn_ts_expr_id_node));
  }
  grn_ts_expr_id_node_init(ctx, new_node);
  *node = (grn_ts_expr_node *)new_node;
  return GRN_SUCCESS;
}

/* grn_ts_expr_id_node_close() destroys a node. */
static void
grn_ts_expr_id_node_close(grn_ctx *ctx, grn_ts_expr_id_node *node)
{
  grn_ts_expr_id_node_fin(ctx, node);
  GRN_FREE(node);
}

/* grn_ts_expr_id_node_evaluate() outputs IDs. */
static grn_rc
grn_ts_expr_id_node_evaluate(grn_ctx *ctx, grn_ts_expr_id_node *node,
                             const grn_ts_record *in, size_t n_in, void *out)
{
  size_t i;
  grn_ts_int *out_ptr = (grn_ts_int *)out;
  for (i = 0; i < n_in; i++) {
    out_ptr[i] = (grn_ts_int)in[i].id;
  }
  return GRN_SUCCESS;
}

/*-------------------------------------------------------------
 * grn_ts_expr_score_node.
 */

typedef struct {
  GRN_TS_EXPR_NODE_COMMON_MEMBERS
} grn_ts_expr_score_node;

/* grn_ts_expr_score_node_init() initializes a node. */
static void
grn_ts_expr_score_node_init(grn_ctx *ctx, grn_ts_expr_score_node *node)
{
  memset(node, 0, sizeof(*node));
  node->type = GRN_TS_EXPR_SCORE_NODE;
  node->data_kind = GRN_TS_FLOAT;
  node->data_type = GRN_DB_FLOAT;
}

/* grn_ts_expr_score_node_fin() finalizes a node. */
static void
grn_ts_expr_score_node_fin(grn_ctx *ctx, grn_ts_expr_score_node *node)
{
  /* Nothing to do. */
}

grn_rc
grn_ts_expr_score_node_open(grn_ctx *ctx, grn_ts_expr_node **node)
{
  grn_ts_expr_score_node *new_node = GRN_MALLOCN(grn_ts_expr_score_node, 1);
  if (!new_node) {
    GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
                      "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1",
                      sizeof(grn_ts_expr_score_node));
  }
  grn_ts_expr_score_node_init(ctx, new_node);
  *node = (grn_ts_expr_node *)new_node;
  return GRN_SUCCESS;
}

/* grn_ts_expr_score_node_close() destroys a node. */
static void
grn_ts_expr_score_node_close(grn_ctx *ctx, grn_ts_expr_score_node *node)
{
  grn_ts_expr_score_node_fin(ctx, node);
  GRN_FREE(node);
}

/* grn_ts_expr_score_node_evaluate() outputs scores. */
static grn_rc
grn_ts_expr_score_node_evaluate(grn_ctx *ctx, grn_ts_expr_score_node *node,
                                const grn_ts_record *in, size_t n_in,
                                void *out)
{
  size_t i;
  grn_ts_float *out_ptr = (grn_ts_float *)out;
  for (i = 0; i < n_in; i++) {
    out_ptr[i] = (grn_ts_float)in[i].score;
  }
  return GRN_SUCCESS;
}

/* grn_ts_expr_score_node_adjust() does nothing. */
static grn_rc
grn_ts_expr_score_node_adjust(grn_ctx *ctx, grn_ts_expr_score_node *node,
                              grn_ts_record *io, size_t n_io)
{
  /* Nothing to do. */
  return GRN_SUCCESS;
}

/*-------------------------------------------------------------
 * grn_ts_expr_key_node.
 */

typedef struct {
  GRN_TS_EXPR_NODE_COMMON_MEMBERS
  grn_obj *table;
  grn_ts_buf buf;
} grn_ts_expr_key_node;

/* grn_ts_expr_key_node_init() initializes a node. */
static void
grn_ts_expr_key_node_init(grn_ctx *ctx, grn_ts_expr_key_node *node)
{
  memset(node, 0, sizeof(*node));
  node->type = GRN_TS_EXPR_KEY_NODE;
  node->table = NULL;
  grn_ts_buf_init(ctx, &node->buf);
}

/* grn_ts_expr_key_node_fin() finalizes a node. */
static void
grn_ts_expr_key_node_fin(grn_ctx *ctx, grn_ts_expr_key_node *node)
{
  grn_ts_buf_fin(ctx, &node->buf);
  if (node->table) {
    grn_obj_unlink(ctx, node->table);
  }
}

grn_rc
grn_ts_expr_key_node_open(grn_ctx *ctx, grn_obj *table,
                          grn_ts_expr_node **node)
{
  grn_rc rc;
  grn_ts_expr_key_node *new_node;
  if (!grn_ts_table_has_key(ctx, table)) {
    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "the table has no _key");
  }
  new_node = GRN_MALLOCN(grn_ts_expr_key_node, 1);
  if (!new_node) {
    GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
                      "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1",
                      sizeof(grn_ts_expr_key_node));
  }
  grn_ts_expr_key_node_init(ctx, new_node);
  rc = grn_ts_obj_increment_ref_count(ctx, table);
  if (rc != GRN_SUCCESS) {
    grn_ts_expr_key_node_fin(ctx, new_node);
    GRN_FREE(new_node);
    return rc;
  }
  new_node->data_kind = grn_ts_data_type_to_kind(table->header.domain);
  new_node->data_type = table->header.domain;
  new_node->table = table;
  *node = (grn_ts_expr_node *)new_node;
  return GRN_SUCCESS;
}

/* grn_ts_expr_key_node_close() destroys a node. */
static void
grn_ts_expr_key_node_close(grn_ctx *ctx, grn_ts_expr_key_node *node)
{
  grn_ts_expr_key_node_fin(ctx, node);
  GRN_FREE(node);
}

#define GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE(table, KIND, kind)\
  case GRN_TS_ ## KIND: {\
    grn_ts_ ## kind *out_ptr = (grn_ts_ ## kind *)out;\
    for (i = 0; i < n_in; i++) {\
      rc = grn_ts_ ## table ## _get_ ## kind ## _key(ctx, table, in[i].id,\
                                                     &out_ptr[i]);\
      if (rc != GRN_SUCCESS) {\
        out_ptr[i] = grn_ts_ ## kind ## _zero();\
      }\
    }\
    return GRN_SUCCESS;\
  }
#define GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(table, TYPE, type)\
  case GRN_DB_ ## TYPE: {\
    grn_ts_int *out_ptr = (grn_ts_int *)out;\
    for (i = 0; i < n_in; i++) {\
      rc = grn_ts_ ## table ## _get_ ## type ## _key(ctx, table, in[i].id,\
                                                     &out_ptr[i]);\
      if (rc != GRN_SUCCESS) {\
        out_ptr[i] = grn_ts_int_zero();\
      }\
    }\
    return GRN_SUCCESS;\
  }
#define GRN_TS_EXPR_KEY_NODE_EVALUATE_TEXT_CASE(table)\
  case GRN_TS_TEXT: {\
    char *buf_ptr;\
    grn_ts_text *out_ptr = (grn_ts_text *)out;\
    node->buf.pos = 0;\
    for (i = 0; i < n_in; i++) {\
      grn_ts_text key;\
      rc = grn_ts_ ## table ## _get_text_key(ctx, table, in[i].id, &key);\
      if (rc != GRN_SUCCESS) {\
        key = grn_ts_text_zero();\
      }\
      rc = grn_ts_buf_write(ctx, &node->buf, key.ptr, key.size);\
      if (rc != GRN_SUCCESS) {\
        return rc;\
      }\
      out_ptr[i].size = key.size;\
    }\
    buf_ptr = (char *)node->buf.ptr;\
    for (i = 0; i < n_in; i++) {\
      out_ptr[i].ptr = buf_ptr;\
      buf_ptr += out_ptr[i].size;\
    }\
    return GRN_SUCCESS;\
  }
#define GRN_TS_EXPR_KEY_NODE_EVALUATE_REF_CASE(table)\
  case GRN_TS_REF: {\
    grn_ts_ref *out_ptr = (grn_ts_ref *)out;\
    for (i = 0; i < n_in; i++) {\
      rc = grn_ts_ ## table ## _get_ref_key(ctx, table, in[i].id,\
                                            &out_ptr[i]);\
      if (rc != GRN_SUCCESS) {\
        out_ptr[i] = grn_ts_ref_zero();\
      }\
      out_ptr[i].score = in[i].score;\
    }\
    return GRN_SUCCESS;\
  }
/* grn_ts_expr_key_node_evaluate() outputs keys. */
static grn_rc
grn_ts_expr_key_node_evaluate(grn_ctx *ctx, grn_ts_expr_key_node *node,
                              const grn_ts_record *in, size_t n_in, void *out)
{
  size_t i;
  grn_rc rc;
  switch (node->table->header.type) {
    case GRN_TABLE_HASH_KEY: {
      grn_hash *hash = (grn_hash *)node->table;
      switch (node->data_kind) {
        GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE(hash, BOOL, bool)
        case GRN_TS_INT: {
          switch (node->data_type) {
            GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(hash, INT8, int8)
            GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(hash, INT16, int16)
            GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(hash, INT32, int32)
            GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(hash, INT64, int64)
            GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(hash, UINT8, uint8)
            GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(hash, UINT16, uint16)
            GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(hash, UINT32, uint32)
            GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(hash, UINT64, uint64)
          }
        }
        GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE(hash, FLOAT, float)
        GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE(hash, TIME, time)
        GRN_TS_EXPR_KEY_NODE_EVALUATE_TEXT_CASE(hash)
        GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE(hash, GEO, geo)
        GRN_TS_EXPR_KEY_NODE_EVALUATE_REF_CASE(hash)
        default: {
          GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",
                            node->data_kind);
        }
      }
    }
    case GRN_TABLE_PAT_KEY: {
      grn_pat *pat = (grn_pat *)node->table;
      switch (node->data_kind) {
        GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE(pat, BOOL, bool)
        case GRN_TS_INT: {
          switch (node->data_type) {
            GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(pat, INT8, int8)
            GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(pat, INT16, int16)
            GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(pat, INT32, int32)
            GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(pat, INT64, int64)
            GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(pat, UINT8, uint8)
            GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(pat, UINT16, uint16)
            GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(pat, UINT32, uint32)
            GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(pat, UINT64, uint64)
          }
        }
        GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE(pat, FLOAT, float)
        GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE(pat, TIME, time)
        GRN_TS_EXPR_KEY_NODE_EVALUATE_TEXT_CASE(pat)
        GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE(pat, GEO, geo)
        GRN_TS_EXPR_KEY_NODE_EVALUATE_REF_CASE(pat)
        default: {
          GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",
                            node->data_kind);
        }
      }
    }
    case GRN_TABLE_DAT_KEY: {
      grn_dat *dat = (grn_dat *)node->table;
      switch (node->data_kind) {
        GRN_TS_EXPR_KEY_NODE_EVALUATE_TEXT_CASE(dat)
        /* GRN_TABLE_DAT_KEY supports only Text. */
        default: {
          GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",
                            node->data_kind);
        }
      }
    }
    /* GRN_TABLE_NO_KEY doesn't support _key. */
    default: {
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid table type: %d",
                        node->table->header.type);
    }
  }
}
#undef GRN_TS_EXPR_KEY_NODE_EVALUATE_REF_CASE
#undef GRN_TS_EXPR_KEY_NODE_EVALUATE_TEXT_CASE
#undef GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE
#undef GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE

/* grn_ts_expr_key_node_filter() filters records. */
static grn_rc
grn_ts_expr_key_node_filter(grn_ctx *ctx, grn_ts_expr_key_node *node,
                            grn_ts_record *in, size_t n_in,
                            grn_ts_record *out, size_t *n_out)
{
  size_t i, count;
  grn_ts_bool key;
  switch (node->table->header.type) {
    case GRN_TABLE_HASH_KEY: {
      grn_hash *hash = (grn_hash *)node->table;
      for (i = 0, count = 0; i < n_in; i++) {
        grn_rc rc = grn_ts_hash_get_bool_key(ctx, hash, in[i].id, &key);
        if (rc != GRN_SUCCESS) {
          key = grn_ts_bool_zero();
        }
        if (key) {
          out[count++] = in[i];
        }
      }
      *n_out = count;
      return GRN_SUCCESS;
    }
    case GRN_TABLE_PAT_KEY: {
      grn_pat *pat = (grn_pat *)node->table;
      for (i = 0, count = 0; i < n_in; i++) {
        grn_rc rc = grn_ts_pat_get_bool_key(ctx, pat, in[i].id, &key);
        if (rc != GRN_SUCCESS) {
          key = grn_ts_bool_zero();
        }
        if (key) {
          out[count++] = in[i];
        }
      }
      *n_out = count;
      return GRN_SUCCESS;
    }
    /* GRN_TABLE_DAT_KEY and GRN_TABLE_NO_KEY don't support a Bool key. */
    default: {
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid table type: %d",
                        node->table->header.type);
    }
  }
}

/* grn_ts_expr_key_node_adjust() updates scores. */
static grn_rc
grn_ts_expr_key_node_adjust(grn_ctx *ctx, grn_ts_expr_key_node *node,
                            grn_ts_record *io, size_t n_io)
{
  size_t i;
  grn_ts_float key;
  switch (node->table->header.type) {
    case GRN_TABLE_HASH_KEY: {
      grn_hash *hash = (grn_hash *)node->table;
      for (i = 0; i < n_io; i++) {
        grn_rc rc = grn_ts_hash_get_float_key(ctx, hash, io[i].id, &key);
        if (rc != GRN_SUCCESS) {
          key = grn_ts_float_zero();
        }
        io[i].score = (grn_ts_score)key;
      }
      return GRN_SUCCESS;
    }
    case GRN_TABLE_PAT_KEY: {
      grn_pat *pat = (grn_pat *)node->table;
      for (i = 0; i < n_io; i++) {
        grn_rc rc = grn_ts_pat_get_float_key(ctx, pat, io[i].id, &key);
        if (rc != GRN_SUCCESS) {
          key = grn_ts_float_zero();
        }
        io[i].score = (grn_ts_score)key;
      }
      return GRN_SUCCESS;
    }
    /* GRN_TABLE_DAT_KEY and GRN_TABLE_NO_KEY don't support a Float key. */
    default: {
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid table type: %d",
                        node->table->header.type);
    }
  }
}

/*-------------------------------------------------------------
 * grn_ts_expr_value_node.
 */

typedef struct {
  GRN_TS_EXPR_NODE_COMMON_MEMBERS
  grn_obj *table;
} grn_ts_expr_value_node;

/* grn_ts_expr_value_node_init() initializes a node. */
static void
grn_ts_expr_value_node_init(grn_ctx *ctx, grn_ts_expr_value_node *node)
{
  memset(node, 0, sizeof(*node));
  node->type = GRN_TS_EXPR_VALUE_NODE;
  node->table = NULL;
}

/* grn_ts_expr_value_node_fin() finalizes a node. */
static void
grn_ts_expr_value_node_fin(grn_ctx *ctx, grn_ts_expr_value_node *node)
{
  if (node->table) {
    grn_obj_unlink(ctx, node->table);
  }
}

grn_rc
grn_ts_expr_value_node_open(grn_ctx *ctx, grn_obj *table,
                            grn_ts_expr_node **node)
{
  grn_rc rc;
  grn_ts_expr_value_node *new_node;
  if (!grn_ts_table_has_value(ctx, table)) {
    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "table has no _value");
  }
  new_node = GRN_MALLOCN(grn_ts_expr_value_node, 1);
  if (!new_node) {
    GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
                      "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1",
                      sizeof(grn_ts_expr_value_node));
  }
  grn_ts_expr_value_node_init(ctx, new_node);
  rc = grn_ts_obj_increment_ref_count(ctx, table);
  if (rc != GRN_SUCCESS) {
    GRN_FREE(new_node);
    return rc;
  }
  new_node->data_kind = grn_ts_data_type_to_kind(DB_OBJ(table)->range);
  new_node->data_type = DB_OBJ(table)->range;
  new_node->table = table;
  *node = (grn_ts_expr_node *)new_node;
  return GRN_SUCCESS;
}

/* grn_ts_expr_value_node_close() destroys a node. */
static void
grn_ts_expr_value_node_close(grn_ctx *ctx, grn_ts_expr_value_node *node)
{
  grn_ts_expr_value_node_fin(ctx, node);
  GRN_FREE(node);
}

#define GRN_TS_EXPR_VALUE_NODE_EVALUATE_CASE(KIND, kind)\
  case GRN_TS_ ## KIND: {\
    size_t i;\
    grn_ts_ ## kind *out_ptr = (grn_ts_ ## kind *)out;\
    for (i = 0; i < n_in; i++) {\
      const void *ptr = grn_ts_table_get_value(ctx, node->table, in[i].id);\
      if (ptr) {\
        out_ptr[i] = *(const grn_ts_ ## kind *)ptr;\
      } else {\
        out_ptr[i] = grn_ts_ ## kind ## _zero();\
      }\
    }\
    return GRN_SUCCESS;\
  }
#define GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE(TYPE, type)\
  case GRN_DB_ ## TYPE: {\
    size_t i;\
    grn_ts_int *out_ptr = (grn_ts_int *)out;\
    for (i = 0; i < n_in; i++) {\
      const void *ptr = grn_ts_table_get_value(ctx, node->table, in[i].id);\
      if (ptr) {\
        out_ptr[i] = (grn_ts_int)*(const type ## _t *)ptr;\
      } else {\
        out_ptr[i] = grn_ts_int_zero();\
      }\
    }\
    return GRN_SUCCESS;\
  }
/* grn_ts_expr_value_node_evaluate() outputs values. */
static grn_rc
grn_ts_expr_value_node_evaluate(grn_ctx *ctx, grn_ts_expr_value_node *node,
                                const grn_ts_record *in, size_t n_in,
                                void *out)
{
  switch (node->data_kind) {
    GRN_TS_EXPR_VALUE_NODE_EVALUATE_CASE(BOOL, bool)
    case GRN_TS_INT: {
      switch (node->data_type) {
        GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE(INT8, int8)
        GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE(INT16, int16)
        GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE(INT32, int32)
        GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE(INT64, int64)
        GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE(UINT8, uint8)
        GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE(UINT16, uint16)
        GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE(UINT32, uint32)
        GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE(UINT64, uint64)
        default: {
          GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data type: %d",
                            node->data_type);
        }
      }
    }
    GRN_TS_EXPR_VALUE_NODE_EVALUATE_CASE(FLOAT, float)
    GRN_TS_EXPR_VALUE_NODE_EVALUATE_CASE(TIME, time)
    GRN_TS_EXPR_VALUE_NODE_EVALUATE_CASE(GEO, geo)
    case GRN_TS_REF: {
      size_t i;
      grn_ts_ref *out_ptr = (grn_ts_ref *)out;
      for (i = 0; i < n_in; i++) {
        const void *ptr = grn_ts_table_get_value(ctx, node->table, in[i].id);
        if (ptr) {
          out_ptr[i].id = *(const grn_ts_id *)ptr;
          out_ptr[i].score = in[i].score;
        } else {
          out_ptr[i] = grn_ts_ref_zero();
        }
      }
      return GRN_SUCCESS;
    }
    default: {
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",
                        node->data_kind);
    }
  }
}
#undef GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE
#undef GRN_TS_EXPR_VALUE_NODE_EVALUATE_CASE

/* grn_ts_expr_value_node_filter() filters records. */
static grn_rc
grn_ts_expr_value_node_filter(grn_ctx *ctx, grn_ts_expr_value_node *node,
                              grn_ts_record *in, size_t n_in,
                              grn_ts_record *out, size_t *n_out)
{
  size_t i, count = 0;
  for (i = 0; i < n_in; i++) {
    const void *ptr = grn_ts_table_get_value(ctx, node->table, in[i].id);
    if (ptr && *(const grn_ts_bool *)ptr) {
      out[count++] = in[i];
    }
  }
  *n_out = count;
  return GRN_SUCCESS;
}

/* grn_ts_expr_value_node_adjust() updates scores. */
static grn_rc
grn_ts_expr_value_node_adjust(grn_ctx *ctx, grn_ts_expr_value_node *node,
                              grn_ts_record *io, size_t n_io)
{
  size_t i;
  for (i = 0; i < n_io; i++) {
    const void *ptr = grn_ts_table_get_value(ctx, node->table, io[i].id);
    if (ptr) {
      io[i].score = (grn_ts_score)*(const grn_ts_float *)ptr;
    }
  }
  return GRN_SUCCESS;
}

/*-------------------------------------------------------------
 * grn_ts_expr_const_node.
 */

typedef struct {
  GRN_TS_EXPR_NODE_COMMON_MEMBERS
  grn_ts_any content;
  grn_ts_buf text_buf;
  grn_ts_buf vector_buf;
} grn_ts_expr_const_node;

/* grn_ts_expr_const_node_init() initializes a node. */
static void
grn_ts_expr_const_node_init(grn_ctx *ctx, grn_ts_expr_const_node *node)
{
  memset(node, 0, sizeof(*node));
  node->type = GRN_TS_EXPR_CONST_NODE;
  grn_ts_buf_init(ctx, &node->text_buf);
  grn_ts_buf_init(ctx, &node->vector_buf);
}

/* grn_ts_expr_const_node_fin() finalizes a node. */
static void
grn_ts_expr_const_node_fin(grn_ctx *ctx, grn_ts_expr_const_node *node)
{
  grn_ts_buf_fin(ctx, &node->vector_buf);
  grn_ts_buf_fin(ctx, &node->text_buf);
}

#define GRN_TS_EXPR_CONST_NODE_SET_SCALAR_CASE(KIND, kind)\
  case GRN_TS_ ## KIND: {\
    node->content.as_ ## kind = value.as_ ## kind;\
    return GRN_SUCCESS;\
  }
/* grn_ts_expr_const_node_set_scalar() sets a scalar value. */
static grn_rc
grn_ts_expr_const_node_set_scalar(grn_ctx *ctx, grn_ts_expr_const_node *node,
                                  grn_ts_any value)
{
  switch (node->data_kind) {
    GRN_TS_EXPR_CONST_NODE_SET_SCALAR_CASE(BOOL, bool)
    GRN_TS_EXPR_CONST_NODE_SET_SCALAR_CASE(INT, int)
    GRN_TS_EXPR_CONST_NODE_SET_SCALAR_CASE(FLOAT, float)
    GRN_TS_EXPR_CONST_NODE_SET_SCALAR_CASE(TIME, time)
    case GRN_TS_TEXT: {
      grn_rc rc = grn_ts_buf_write(ctx, &node->text_buf,
                                   value.as_text.ptr, value.as_text.size);
      if (rc != GRN_SUCCESS) {
        return rc;
      }
      node->content.as_text.ptr = (const char *)node->text_buf.ptr;
      node->content.as_text.size = value.as_text.size;
      return GRN_SUCCESS;
    }
    GRN_TS_EXPR_CONST_NODE_SET_SCALAR_CASE(GEO, geo)
    default: {
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",
                        node->data_kind);
    }
  }
}
#undef GRN_TS_EXPR_CONST_NODE_SET_SCALAR_CASE

#define GRN_TS_EXPR_CONST_NODE_SET_VECTOR_CASE(KIND, kind)\
  case GRN_TS_ ## KIND ## _VECTOR: {\
    grn_rc rc;\
    size_t n_bytes;\
    const grn_ts_ ## kind *buf_ptr;\
    grn_ts_ ## kind ## _vector vector;\
    vector = value.as_ ## kind ## _vector;\
    n_bytes = sizeof(grn_ts_ ## kind) * vector.size;\
    rc = grn_ts_buf_write(ctx, &node->vector_buf, vector.ptr, n_bytes);\
    if (rc != GRN_SUCCESS) {\
      return rc;\
    }\
    buf_ptr = (const grn_ts_ ## kind *)node->vector_buf.ptr;\
    node->content.as_ ## kind ## _vector.ptr = buf_ptr;\
    node->content.as_ ## kind ## _vector.size = vector.size;\
    return GRN_SUCCESS;\
  }
/* grn_ts_expr_const_node_set_vector() sets a vector value. */
static grn_rc
grn_ts_expr_const_node_set_vector(grn_ctx *ctx, grn_ts_expr_const_node *node,
                                  grn_ts_any value)
{
  switch (node->data_kind) {
    GRN_TS_EXPR_CONST_NODE_SET_VECTOR_CASE(BOOL, bool)
    GRN_TS_EXPR_CONST_NODE_SET_VECTOR_CASE(INT, int)
    GRN_TS_EXPR_CONST_NODE_SET_VECTOR_CASE(FLOAT, float)
    GRN_TS_EXPR_CONST_NODE_SET_VECTOR_CASE(TIME, time)
    case GRN_TS_TEXT_VECTOR: {
      grn_rc rc;
      size_t i, n_bytes, offset, total_size;
      grn_ts_text_vector vector = value.as_text_vector;
      grn_ts_text *vector_buf;
      char *text_buf;
      n_bytes = sizeof(grn_ts_text) * vector.size;
      rc = grn_ts_buf_resize(ctx, &node->vector_buf, n_bytes);
      if (rc != GRN_SUCCESS) {
        return rc;
      }
      vector_buf = (grn_ts_text *)node->vector_buf.ptr;
      total_size = 0;
      for (i = 0; i < vector.size; i++) {
        total_size += vector.ptr[i].size;
      }
      rc = grn_ts_buf_resize(ctx, &node->text_buf, total_size);
      if (rc != GRN_SUCCESS) {
        return rc;
      }
      text_buf = (char *)node->text_buf.ptr;
      offset = 0;
      for (i = 0; i < vector.size; i++) {
        grn_memcpy(text_buf + offset, vector.ptr[i].ptr, vector.ptr[i].size);
        vector_buf[i].ptr = text_buf + offset;
        vector_buf[i].size = vector.ptr[i].size;
        offset += vector.ptr[i].size;
      }
      node->content.as_text_vector.ptr = vector_buf;
      node->content.as_text_vector.size = vector.size;
      return GRN_SUCCESS;
    }
    GRN_TS_EXPR_CONST_NODE_SET_VECTOR_CASE(GEO, geo)
    default: {
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",
                        node->data_kind);
    }
  }
}
#undef GRN_TS_EXPR_CONST_NODE_SET_VECTOR_CASE

#define GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(KIND, kind)\
  case GRN_TS_ ## KIND: {\
    if (!grn_ts_ ## kind ## _is_valid(value.as_ ## kind)) {\
      GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");\
    }\
    return GRN_SUCCESS;\
  }
static grn_rc
grn_ts_expr_const_node_check_value(grn_ctx *ctx, grn_ts_data_kind kind,
                                   grn_ts_any value)
{
  switch (kind) {
    GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(BOOL, bool)
    GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(INT, int)
    GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(FLOAT, float)
    GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(TIME, time)
    GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(TEXT, text)
    GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(GEO, geo)
    GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(BOOL_VECTOR, bool_vector)
    GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(INT_VECTOR, int_vector)
    GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(FLOAT_VECTOR, float_vector)
    GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(TIME_VECTOR, time_vector)
    GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(TEXT_VECTOR, text_vector)
    GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(GEO_VECTOR, geo_vector)
    default: {
      GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
    }
  }
}
#undef GRN_TS_EXPR_CONST_NODE_CHECK_VALUE

grn_rc
grn_ts_expr_const_node_open(grn_ctx *ctx, grn_ts_data_kind data_kind,
                            grn_ts_data_type data_type,
                            grn_ts_any value, grn_ts_expr_node **node)
{
  grn_rc rc = grn_ts_expr_const_node_check_value(ctx, data_kind, value);
  grn_ts_expr_const_node *new_node;
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  new_node = GRN_MALLOCN(grn_ts_expr_const_node, 1);
  if (!new_node) {
    GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
                      "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1",
                      sizeof(grn_ts_expr_const_node));
  }
  grn_ts_expr_const_node_init(ctx, new_node);
  new_node->data_kind = data_kind;
  if (data_type != GRN_DB_VOID) {
    new_node->data_type = data_type;
  } else {
    new_node->data_type = grn_ts_data_kind_to_type(data_kind);
  }
  if (data_kind & GRN_TS_VECTOR_FLAG) {
    rc = grn_ts_expr_const_node_set_vector(ctx, new_node, value);
  } else {
    rc = grn_ts_expr_const_node_set_scalar(ctx, new_node, value);
  }
  if (rc != GRN_SUCCESS) {
    grn_ts_expr_const_node_fin(ctx, new_node);
    GRN_FREE(new_node);
    return rc;
  }
  *node = (grn_ts_expr_node *)new_node;
  return GRN_SUCCESS;
}

/* grn_ts_expr_const_node_close() destroys a node. */
static void
grn_ts_expr_const_node_close(grn_ctx *ctx, grn_ts_expr_const_node *node)
{
  grn_ts_expr_const_node_fin(ctx, node);
  GRN_FREE(node);
}

#define GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE(KIND, kind)\
  case GRN_TS_ ## KIND: {\
    size_t i;\
    grn_ts_ ## kind *out_ptr = (grn_ts_ ## kind *)out;\
    for (i = 0; i < n_in; i++) {\
      out_ptr[i] = node->content.as_ ## kind;\
    }\
    return GRN_SUCCESS;\
  }
#define GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE(KIND, kind)\
  GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE(KIND ## _VECTOR, kind ## _vector)
/* grn_ts_expr_const_node_evaluate() outputs the stored const. */
static grn_rc
grn_ts_expr_const_node_evaluate(grn_ctx *ctx, grn_ts_expr_const_node *node,
                                const grn_ts_record *in, size_t n_in,
                                void *out)
{
  switch (node->data_kind) {
    GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE(BOOL, bool)
    GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE(INT, int)
    GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE(FLOAT, float)
    GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE(TIME, time)
    GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE(TEXT, text)
    GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE(GEO, geo)
    GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE(BOOL, bool)
    GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE(INT, int)
    GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE(FLOAT, float)
    GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE(TIME, time)
    GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE(TEXT, text)
    GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE(GEO, geo)
    default: {
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",
                        node->data_kind);
    }
  }
}
#undef GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE
#undef GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE

/* grn_ts_expr_const_node_filter() filters records. */
static grn_rc
grn_ts_expr_const_node_filter(grn_ctx *ctx, grn_ts_expr_const_node *node,
                              grn_ts_record *in, size_t n_in,
                              grn_ts_record *out, size_t *n_out)
{
  if (node->content.as_bool) {
    /* All the records pass through the filter. */
    if (in != out) {
      size_t i;
      for (i = 0; i < n_in; i++) {
        out[i] = in[i];
      }
    }
    *n_out = n_in;
  } else {
    /* All the records are discarded. */
    *n_out = 0;
  }
  return GRN_SUCCESS;
}

/* grn_ts_expr_const_node_adjust() updates scores. */
static grn_rc
grn_ts_expr_const_node_adjust(grn_ctx *ctx, grn_ts_expr_const_node *node,
                              grn_ts_record *io, size_t n_io)
{
  size_t i;
  grn_ts_score score = (grn_ts_score)node->content.as_float;
  for (i = 0; i < n_io; i++) {
    io[i].score = score;
  }
  return GRN_SUCCESS;
}

/*-------------------------------------------------------------
 * grn_ts_expr_column_node.
 */

typedef struct {
  GRN_TS_EXPR_NODE_COMMON_MEMBERS
  grn_obj *column;
  grn_ts_buf buf;
  grn_ts_buf body_buf;
  grn_ja_reader *reader;
} grn_ts_expr_column_node;

/* grn_ts_expr_column_node_init() initializes a node. */
static void
grn_ts_expr_column_node_init(grn_ctx *ctx, grn_ts_expr_column_node *node)
{
  memset(node, 0, sizeof(*node));
  node->type = GRN_TS_EXPR_COLUMN_NODE;
  node->column = NULL;
  grn_ts_buf_init(ctx, &node->buf);
  grn_ts_buf_init(ctx, &node->body_buf);
  node->reader = NULL;
}

/* grn_ts_expr_column_node_fin() finalizes a node. */
static void
grn_ts_expr_column_node_fin(grn_ctx *ctx, grn_ts_expr_column_node *node)
{
  if (node->reader) {
    grn_ja_reader_close(ctx, node->reader);
  }
  grn_ts_buf_fin(ctx, &node->body_buf);
  grn_ts_buf_fin(ctx, &node->buf);
  if (node->column) {
    grn_obj_unlink(ctx, node->column);
  }
}

#define GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE(TYPE)\
  case GRN_DB_ ## TYPE: {\
    GRN_ ## TYPE ## _INIT(&new_node->buf, GRN_OBJ_VECTOR);\
    break;\
  }
grn_rc
grn_ts_expr_column_node_open(grn_ctx *ctx, grn_obj *column,
                             grn_ts_expr_node **node)
{
  grn_rc rc;
  grn_ts_expr_column_node *new_node = GRN_MALLOCN(grn_ts_expr_column_node, 1);
  if (!new_node) {
    GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
                      "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1",
                      sizeof(grn_ts_expr_column_node));
  }
  grn_ts_expr_column_node_init(ctx, new_node);
  new_node->data_kind = grn_ts_data_type_to_kind(DB_OBJ(column)->range);
  if (column->header.type == GRN_COLUMN_VAR_SIZE) {
    grn_obj_flags type = column->header.flags & GRN_OBJ_COLUMN_TYPE_MASK;
    if (type == GRN_OBJ_COLUMN_VECTOR) {
      new_node->data_kind |= GRN_TS_VECTOR_FLAG;
    }
  }
  new_node->data_type = DB_OBJ(column)->range;
  rc = grn_ts_obj_increment_ref_count(ctx, column);
  if (rc != GRN_SUCCESS) {
    grn_ts_expr_column_node_fin(ctx, new_node);
    GRN_FREE(new_node);
    return rc;
  }
  new_node->column = column;
  *node = (grn_ts_expr_node *)new_node;
  return GRN_SUCCESS;
}
#undef GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE

/* grn_ts_expr_column_node_close() destroys a node. */
static void
grn_ts_expr_column_node_close(grn_ctx *ctx, grn_ts_expr_column_node *node)
{
  grn_ts_expr_column_node_fin(ctx, node);
  GRN_FREE(node);
}

#define GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_CASE(KIND, kind)\
  case GRN_TS_ ## KIND: {\
    size_t i;\
    grn_ts_ ## kind *out_ptr = (grn_ts_ ## kind *)out;\
    grn_ra *ra = (grn_ra *)node->column;\
    grn_ra_cache cache;\
    GRN_RA_CACHE_INIT(ra, &cache);\
    for (i = 0; i < n_in; i++) {\
      grn_ts_ ## kind *ptr = NULL;\
      if (in[i].id) {\
        ptr = (grn_ts_ ## kind *)grn_ra_ref_cache(ctx, ra, in[i].id, &cache);\
      }\
      out_ptr[i] = ptr ? *ptr : grn_ts_ ## kind ## _zero();\
    }\
    GRN_RA_CACHE_FIN(ra, &cache);\
    return GRN_SUCCESS;\
  }
#define GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE(TYPE, type)\
  case GRN_DB_ ## TYPE: {\
    size_t i;\
    grn_ts_int *out_ptr = (grn_ts_int *)out;\
    grn_ra *ra = (grn_ra *)node->column;\
    grn_ra_cache cache;\
    GRN_RA_CACHE_INIT(ra, &cache);\
    for (i = 0; i < n_in; i++) {\
      type ## _t *ptr = NULL;\
      if (in[i].id) {\
        ptr = (type ## _t *)grn_ra_ref_cache(ctx, ra, in[i].id, &cache);\
      }\
      out_ptr[i] = ptr ? (grn_ts_int)*ptr : grn_ts_int_zero();\
    }\
    GRN_RA_CACHE_FIN(ra, &cache);\
    return GRN_SUCCESS;\
  }
/* grn_ts_expr_column_node_evaluate_scalar() outputs scalar column values. */
static grn_rc
grn_ts_expr_column_node_evaluate_scalar(grn_ctx *ctx,
                                        grn_ts_expr_column_node *node,
                                        const grn_ts_record *in, size_t n_in,
                                        void *out)
{
  switch (node->data_kind) {
    GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_CASE(BOOL, bool)
    case GRN_TS_INT: {
      switch (node->data_type) {
        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE(INT8, int8)
        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE(INT16, int16)
        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE(INT32, int32)
        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE(INT64, int64)
        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE(UINT8, uint8)
        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE(UINT16, uint16)
        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE(UINT32, uint32)
        /* The behavior is undefined if a value is greater than 2^63 - 1. */
        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE(UINT64, uint64)
        default: {
          GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data type: %d",
                            node->data_type);
        }
      }
    }
    GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_CASE(FLOAT, float)
    GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_CASE(TIME, time)
    case GRN_TS_TEXT: {
      size_t i;
      char *buf_ptr;
      grn_rc rc;
      grn_ts_text *out_ptr = (grn_ts_text *)out;
      if (!node->reader) {
        rc = grn_ja_reader_open(ctx, (grn_ja *)node->column, &node->reader);
        if (rc != GRN_SUCCESS) {
          GRN_TS_ERR_RETURN(rc, "grn_ja_reader_open failed");
        }
      } else {
        grn_ja_reader_unref(ctx, node->reader);
      }
      node->buf.pos = 0;
      for (i = 0; i < n_in; i++) {
        rc = grn_ja_reader_seek(ctx, node->reader, in[i].id);
        if (rc == GRN_SUCCESS) {
          if (node->reader->ref_avail) {
            void *addr;
            rc = grn_ja_reader_ref(ctx, node->reader, &addr);
            if (rc == GRN_SUCCESS) {
              out_ptr[i].ptr = (char *)addr;
            }
          } else {
            rc = grn_ts_buf_reserve(ctx, &node->buf,
                                    node->buf.pos + node->reader->value_size);
            if (rc == GRN_SUCCESS) {
              rc = grn_ja_reader_read(ctx, node->reader,
                                      (char *)node->buf.ptr + node->buf.pos);
              if (rc == GRN_SUCCESS) {
                out_ptr[i].ptr = NULL;
                node->buf.pos += node->reader->value_size;
              }
            }
          }
        }
        if (rc == GRN_SUCCESS) {
          out_ptr[i].size = node->reader->value_size;
        } else {
          out_ptr[i].ptr = NULL;
          out_ptr[i].size = 0;
        }
      }
      buf_ptr = (char *)node->buf.ptr;
      for (i = 0; i < n_in; i++) {
        if (!out_ptr[i].ptr) {
          out_ptr[i].ptr = buf_ptr;
          buf_ptr += out_ptr[i].size;
        }
      }
      return GRN_SUCCESS;
    }
    GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_CASE(GEO, geo)
    case GRN_TS_REF: {
      size_t i;
      grn_ts_ref *out_ptr = (grn_ts_ref *)out;
      grn_ra *ra = (grn_ra *)node->column;
      grn_ra_cache cache;
      GRN_RA_CACHE_INIT(ra, &cache);
      for (i = 0; i < n_in; i++) {
        grn_ts_id *ptr = NULL;
        if (in[i].id) {
          ptr = (grn_ts_id *)grn_ra_ref_cache(ctx, ra, in[i].id, &cache);
        }
        out_ptr[i].id = ptr ? *ptr : GRN_ID_NIL;
        out_ptr[i].score = in[i].score;
      }
      GRN_RA_CACHE_FIN(ra, &cache);
      return GRN_SUCCESS;
    }
    default: {
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",
                        node->data_kind);
    }
  }
}
#undef GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE
#undef GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_CASE

/*
 * grn_ts_expr_column_node_evaluate_text_vector() outputs text vector column
 * values.
 */
static grn_rc
grn_ts_expr_column_node_evaluate_text_vector(grn_ctx *ctx,
                                             grn_ts_expr_column_node *node,
                                             const grn_ts_record *in,
                                             size_t n_in, void *out)
{
  grn_rc rc;
  char *buf_ptr;
  size_t i, j, n_bytes, n_values, total_n_bytes = 0, total_n_values = 0;
  grn_ts_text *text_ptr;
  grn_ts_text_vector *out_ptr = (grn_ts_text_vector *)out;
  /* Read encoded values into node->body_buf and get the size of each value. */
  node->body_buf.pos = 0;
  for (i = 0; i < n_in; i++) {
    char *ptr;
    rc = grn_ts_ja_get_value(ctx, node->column, in[i].id,
                             &node->body_buf, &n_bytes);
    if (rc == GRN_SUCCESS) {
      ptr = (char *)node->body_buf.ptr + total_n_bytes;
      GRN_B_DEC(n_values, ptr);
    } else {
      n_bytes = 0;
      n_values = 0;
    }
    /* The cast is just for suppressing wrong Visual C++ warning. */
    grn_memcpy((void *)(&out_ptr[i].ptr), &n_bytes, sizeof(n_bytes));
    out_ptr[i].size = n_values;
    total_n_bytes += n_bytes;
    total_n_values += n_values;
  }
  /* Resize node->buf. */
  n_bytes = sizeof(grn_ts_text) * total_n_values;
  rc = grn_ts_buf_reserve(ctx, &node->buf, n_bytes);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  /* Decode values and compose the result. */
  buf_ptr = (char *)node->body_buf.ptr;
  text_ptr = (grn_ts_text *)node->buf.ptr;
  for (i = 0; i < n_in; i++) {
    char *ptr = buf_ptr;
    grn_memcpy(&n_bytes, &out_ptr[i].ptr, sizeof(n_bytes));
    buf_ptr += n_bytes;
    GRN_B_DEC(n_values, ptr);
    out_ptr[i].ptr = text_ptr;
    for (j = 0; j < out_ptr[i].size; j++) {
      GRN_B_DEC(text_ptr[j].size, ptr);
    }
    for (j = 0; j < out_ptr[i].size; j++) {
      text_ptr[j].ptr = ptr;
      ptr += text_ptr[j].size;
    }
    text_ptr += out_ptr[i].size;
  }
  return GRN_SUCCESS;
}

/*
 * grn_ts_expr_column_node_evaluate_ref_vector() outputs ref vector column
 * values.
 */
static grn_rc
grn_ts_expr_column_node_evaluate_ref_vector(grn_ctx *ctx,
                                             grn_ts_expr_column_node *node,
                                             const grn_ts_record *in,
                                             size_t n_in, void *out)
{
  grn_rc rc;
  size_t i, j, n_bytes, offset = 0;
  grn_ts_id *buf_ptr;
  grn_ts_ref *ref_ptr;
  grn_ts_ref_vector *out_ptr = (grn_ts_ref_vector *)out;
  /* Read column values into node->body_buf and get the size of each value. */
  node->body_buf.pos = 0;
  for (i = 0; i < n_in; i++) {
    size_t size;
    rc = grn_ts_ja_get_value(ctx, node->column, in[i].id,
                             &node->body_buf, &size);
    if (rc == GRN_SUCCESS) {
      out_ptr[i].size = size / sizeof(grn_ts_id);
      offset += out_ptr[i].size;
    } else {
      out_ptr[i].size = 0;
    }
  }
  /* Resize node->buf. */
  n_bytes = sizeof(grn_ts_ref) * offset;
  rc = grn_ts_buf_reserve(ctx, &node->buf, n_bytes);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  /* Compose the result. */
  buf_ptr = (grn_ts_id *)node->body_buf.ptr;
  ref_ptr = (grn_ts_ref *)node->buf.ptr;
  for (i = 0; i < n_in; i++) {
    out_ptr[i].ptr = ref_ptr;
    for (j = 0; j < out_ptr[i].size; j++, buf_ptr++, ref_ptr++) {
      ref_ptr->id = *buf_ptr;
      ref_ptr->score = in[i].score;
    }
  }
  return GRN_SUCCESS;
}

#define GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_CASE(KIND, kind)\
  case GRN_TS_ ## KIND ## _VECTOR: {\
    size_t i;\
    grn_ts_ ## kind *buf_ptr;\
    grn_ts_ ## kind ## _vector *out_ptr = (grn_ts_ ## kind ## _vector *)out;\
    /* Read column values into node->buf and save the size of each value. */\
    node->buf.pos = 0;\
    for (i = 0; i < n_in; i++) {\
      size_t n_bytes;\
      grn_rc rc = grn_ts_ja_get_value(ctx, node->column, in[i].id,\
                                      &node->buf, &n_bytes);\
      if (rc == GRN_SUCCESS) {\
        out_ptr[i].size = n_bytes / sizeof(grn_ts_ ## kind);\
      } else {\
        out_ptr[i].size = 0;\
      }\
    }\
    buf_ptr = (grn_ts_ ## kind *)node->buf.ptr;\
    for (i = 0; i < n_in; i++) {\
      out_ptr[i].ptr = buf_ptr;\
      buf_ptr += out_ptr[i].size;\
    }\
    return GRN_SUCCESS;\
  }
#define GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE(TYPE, type)\
  case GRN_DB_ ## TYPE: {\
    size_t i, j;\
    grn_ts_int *buf_ptr;\
    grn_ts_int_vector *out_ptr = (grn_ts_int_vector *)out;\
    /*
     * Read column values into body_buf and typecast the values to grn_ts_int.
     * Then, store the grn_ts_int values into node->buf and save the size of
     * each value.
     */\
    node->buf.pos = 0;\
    for (i = 0; i < n_in; i++) {\
      grn_rc rc;\
      size_t n_bytes, new_n_bytes;\
      node->body_buf.pos = 0;\
      rc = grn_ts_ja_get_value(ctx, node->column, in[i].id,\
                               &node->body_buf, &n_bytes);\
      if (rc == GRN_SUCCESS) {\
        out_ptr[i].size = n_bytes / sizeof(type ## _t);\
      } else {\
        out_ptr[i].size = 0;\
      }\
      new_n_bytes = node->buf.pos + (sizeof(grn_ts_int) * out_ptr[i].size);\
      rc = grn_ts_buf_reserve(ctx, &node->buf, new_n_bytes);\
      if (rc == GRN_SUCCESS) {\
        type ## _t *src_ptr = (type ## _t *)node->body_buf.ptr;\
        grn_ts_int *dest_ptr;\
        dest_ptr = (grn_ts_int *)((char *)node->buf.ptr + node->buf.pos);\
        for (j = 0; j < out_ptr[i].size; j++) {\
          dest_ptr[j] = (grn_ts_int)src_ptr[j];\
        }\
        node->buf.pos = new_n_bytes;\
      } else {\
        out_ptr[i].size = 0;\
      }\
    }\
    buf_ptr = (grn_ts_int *)node->buf.ptr;\
    for (i = 0; i < n_in; i++) {\
      out_ptr[i].ptr = buf_ptr;\
      buf_ptr += out_ptr[i].size;\
    }\
    return GRN_SUCCESS;\
  }
/* grn_ts_expr_column_node_evaluate_vector() outputs vector column values. */
static grn_rc
grn_ts_expr_column_node_evaluate_vector(grn_ctx *ctx,
                                        grn_ts_expr_column_node *node,
                                        const grn_ts_record *in, size_t n_in,
                                        void *out)
{
  switch (node->data_kind) {
    GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_CASE(BOOL, bool)
    case GRN_TS_INT_VECTOR: {
      switch (node->data_type) {
        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE(INT8, int8)
        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE(INT16, int16)
        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE(INT32, int32)
        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE(INT64, int64)
        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE(UINT8, uint8)
        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE(UINT16, uint16)
        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE(UINT32, uint32)
        /* The behavior is undefined if a value is greater than 2^63 - 1. */
        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE(UINT64, uint64)
        default: {
          GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data type: %d",
                            node->data_type);
        }
      }
    }
    GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_CASE(FLOAT, float)
    GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_CASE(TIME, time)
    case GRN_TS_TEXT_VECTOR: {
      return grn_ts_expr_column_node_evaluate_text_vector(ctx, node, in, n_in,
                                                          out);
    }
    GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_CASE(GEO, geo)
    case GRN_TS_REF_VECTOR: {
      return grn_ts_expr_column_node_evaluate_ref_vector(ctx, node, in, n_in,
                                                         out);
    }
    default: {
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",
                        node->data_kind);
    }
  }
}
#undef GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE
#undef GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_CASE

/* grn_ts_expr_column_node_evaluate() outputs column values. */
static grn_rc
grn_ts_expr_column_node_evaluate(grn_ctx *ctx, grn_ts_expr_column_node *node,
                                 const grn_ts_record *in, size_t n_in,
                                 void *out)
{
  if (node->data_kind & GRN_TS_VECTOR_FLAG) {
    return grn_ts_expr_column_node_evaluate_vector(ctx, node, in, n_in, out);
  } else {
    return grn_ts_expr_column_node_evaluate_scalar(ctx, node, in, n_in, out);
  }
}

/* grn_ts_expr_column_node_filter() filters records. */
static grn_rc
grn_ts_expr_column_node_filter(grn_ctx *ctx, grn_ts_expr_column_node *node,
                               grn_ts_record *in, size_t n_in,
                               grn_ts_record *out, size_t *n_out)
{
  size_t i, count = 0;
  grn_ra *ra = (grn_ra *)node->column;
  grn_ra_cache cache;
  GRN_RA_CACHE_INIT(ra, &cache);
  for (i = 0; i < n_in; i++) {
    grn_ts_bool *ptr = NULL;
    if (in[i].id) {
      ptr = grn_ra_ref_cache(ctx, ra, in[i].id, &cache);
    }
    if (ptr && *ptr) {
      out[count++] = in[i];
    }
  }
  GRN_RA_CACHE_FIN(ra, &cache);
  *n_out = count;
  return GRN_SUCCESS;
}

/* grn_ts_expr_column_node_adjust() updates scores. */
static grn_rc
grn_ts_expr_column_node_adjust(grn_ctx *ctx, grn_ts_expr_column_node *node,
                               grn_ts_record *io, size_t n_io)
{
  size_t i;
  grn_ra *ra = (grn_ra *)node->column;
  grn_ra_cache cache;
  GRN_RA_CACHE_INIT(ra, &cache);
  for (i = 0; i < n_io; i++) {
    grn_ts_float *ptr = NULL;
    if (io[i].id) {
      ptr = grn_ra_ref_cache(ctx, ra, io[i].id, &cache);
    }
    if (ptr) {
      io[i].score = (grn_ts_score)*ptr;
    }
  }
  GRN_RA_CACHE_FIN(ra, &cache);
  return GRN_SUCCESS;
}

/*-------------------------------------------------------------
 * grn_ts_expr_op_node.
 */

enum {
  GRN_TS_EXPR_OP_NODE_MAX_N_ARGS = 3,
  GRN_TS_EXPR_OP_NODE_N_BUFS = 3
};

typedef struct {
  GRN_TS_EXPR_NODE_COMMON_MEMBERS
  grn_ts_op_type op_type;
  grn_ts_expr_node *args[GRN_TS_EXPR_OP_NODE_MAX_N_ARGS];
  size_t n_args;
  grn_ts_buf bufs[GRN_TS_EXPR_OP_NODE_N_BUFS];
} grn_ts_expr_op_node;

/* grn_ts_expr_op_node_init() initializes a node. */
static void
grn_ts_expr_op_node_init(grn_ctx *ctx, grn_ts_expr_op_node *node)
{
  size_t i;
  memset(node, 0, sizeof(*node));
  node->type = GRN_TS_EXPR_OP_NODE;
  for (i = 0; i < GRN_TS_EXPR_OP_NODE_MAX_N_ARGS; i++) {
    node->args[i] = NULL;
  }
  for (i = 0; i < GRN_TS_EXPR_OP_NODE_N_BUFS; i++) {
    grn_ts_buf_init(ctx, &node->bufs[i]);
  }
}

/* grn_ts_expr_op_node_fin() finalizes a node. */
static void
grn_ts_expr_op_node_fin(grn_ctx *ctx, grn_ts_expr_op_node *node)
{
  size_t i;
  for (i = 0; i < GRN_TS_EXPR_OP_NODE_N_BUFS; i++) {
    grn_ts_buf_fin(ctx, &node->bufs[i]);
  }
  for (i = 0; i < GRN_TS_EXPR_OP_NODE_MAX_N_ARGS; i++) {
    if (node->args[i]) {
      grn_ts_expr_node_close(ctx, node->args[i]);
    }
  }
}

/*
 * grn_ts_expr_op_node_deref_args_for_equal() resolves references if required.
 */
static grn_rc
grn_ts_expr_op_node_deref_args_for_equal(grn_ctx *ctx,
                                         grn_ts_expr_op_node *node)
{
  grn_rc rc;
  if (node->n_args != 2) {
    GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid #args: %" GRN_FMT_SIZE,
                      node->n_args);
  }
  if ((node->args[0]->data_kind & ~GRN_TS_VECTOR_FLAG) != GRN_TS_REF) {
    return grn_ts_expr_node_deref(ctx, &node->args[1]);
  }
  if ((node->args[1]->data_kind & ~GRN_TS_VECTOR_FLAG) != GRN_TS_REF) {
    return grn_ts_expr_node_deref(ctx, &node->args[0]);
  }

  /* FIXME: Arguments should be compared as references if possible. */
  rc = grn_ts_expr_node_deref(ctx, &node->args[0]);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  rc = grn_ts_expr_node_deref(ctx, &node->args[1]);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  return GRN_SUCCESS;
}

/* grn_ts_expr_op_node_deref_args() resolves references if required. */
static grn_rc
grn_ts_expr_op_node_deref_args(grn_ctx *ctx, grn_ts_expr_op_node *node)
{
  switch (node->op_type) {
    case GRN_TS_OP_EQUAL:
    case GRN_TS_OP_NOT_EQUAL: {
      return grn_ts_expr_op_node_deref_args_for_equal(ctx, node);
    }
    /* TODO: Add a ternary operator. */
    default: {
      size_t i;
      for (i = 0; i < node->n_args; i++) {
        grn_rc rc = grn_ts_expr_node_deref(ctx, &node->args[i]);
        if (rc != GRN_SUCCESS) {
          return rc;
        }
      }
      return GRN_SUCCESS;
    }
  }
}

/*
 * grn_ts_op_plus_check_args() checks arguments. Note that arguments are
 * rearranged in some cases.
 */
static grn_rc
grn_ts_op_plus_check_args(grn_ctx *ctx, grn_ts_expr_op_node *node)
{
  grn_rc rc;
  if ((node->args[0]->data_kind == GRN_TS_INT) &&
      (node->args[1]->data_kind == GRN_TS_FLOAT)) {
    rc = grn_ts_expr_op_node_open(ctx, GRN_TS_OP_FLOAT, &node->args[0],
                                  1, &node->args[0]);
    if (rc != GRN_SUCCESS) {
      node->args[0] = NULL;
      return rc;
    }
  } else if ((node->args[0]->data_kind == GRN_TS_FLOAT) &&
             (node->args[1]->data_kind == GRN_TS_INT)) {
    rc = grn_ts_expr_op_node_open(ctx, GRN_TS_OP_FLOAT, &node->args[1],
                                  1, &node->args[1]);
    if (rc != GRN_SUCCESS) {
      node->args[1] = NULL;
      return rc;
    }
  }

  switch (node->args[0]->data_kind) {
    case GRN_TS_INT: {
      switch (node->args[1]->data_kind) {
        case GRN_TS_INT: {
          /* Int + Int = Int. */
          node->data_kind = GRN_TS_INT;
          node->data_type = GRN_DB_INT64;
          return GRN_SUCCESS;
        }
        case GRN_TS_TIME: {
          /* Int + Time = Time + Int = Time. */
          grn_ts_expr_node *tmp = node->args[0];
          node->args[0] = node->args[1];
          node->args[1] = tmp;
          node->data_kind = GRN_TS_TIME;
          node->data_type = GRN_DB_TIME;
          return GRN_SUCCESS;
        }
        default: {
          GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d",
                            node->args[1]->data_kind);
        }
      }
    }
    case GRN_TS_FLOAT: {
      switch (node->args[1]->data_kind) {
        case GRN_TS_FLOAT: {
          /* Float + Float = Float. */
          node->data_kind = GRN_TS_FLOAT;
          node->data_type = GRN_DB_FLOAT;
          return GRN_SUCCESS;
        }
        case GRN_TS_TIME: {
          /* Float + Time = Time + Float = Time. */
          grn_ts_expr_node *tmp = node->args[0];
          node->args[0] = node->args[1];
          node->args[1] = tmp;
          node->data_kind = GRN_TS_TIME;
          node->data_type = GRN_DB_TIME;
          return GRN_SUCCESS;
        }
        default: {
          GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d",
                            node->args[1]->data_kind);
        }
      }
    }
    case GRN_TS_TIME: {
      switch (node->args[1]->data_kind) {
        case GRN_TS_INT:
        case GRN_TS_FLOAT: {
          /* Time + Int or Float = Time. */
          node->data_kind = GRN_TS_TIME;
          node->data_type = GRN_DB_TIME;
          return GRN_SUCCESS;
        }
        default: {
          GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d",
                            node->args[1]->data_kind);
        }
      }
    }
    default: {
      GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d",
                        node->args[0]->data_kind);
    }
  }
}

/* grn_ts_op_minus_check_args() checks arguments. */
static grn_rc
grn_ts_op_minus_check_args(grn_ctx *ctx, grn_ts_expr_op_node *node)
{
  grn_rc rc;
  if ((node->args[0]->data_kind == GRN_TS_INT) &&
      (node->args[1]->data_kind == GRN_TS_FLOAT)) {
    rc = grn_ts_expr_op_node_open(ctx, GRN_TS_OP_FLOAT, &node->args[0],
                                  1, &node->args[0]);
    if (rc != GRN_SUCCESS) {
      node->args[0] = NULL;
      return rc;
    }
  } else if ((node->args[0]->data_kind == GRN_TS_FLOAT) &&
             (node->args[1]->data_kind == GRN_TS_INT)) {
    rc = grn_ts_expr_op_node_open(ctx, GRN_TS_OP_FLOAT, &node->args[1],
                                  1, &node->args[1]);
    if (rc != GRN_SUCCESS) {
      node->args[1] = NULL;
      return rc;
    }
  }

  switch (node->args[0]->data_kind) {
    case GRN_TS_INT: {
      if (node->args[1]->data_kind != GRN_TS_INT) {
        GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d",
                          node->args[1]->data_kind);
      }
      /* Int - Int = Int. */
      node->data_kind = GRN_TS_INT;
      node->data_type = GRN_DB_INT64;
      return GRN_SUCCESS;
    }
    case GRN_TS_FLOAT: {
      if (node->args[1]->data_kind != GRN_TS_FLOAT) {
        GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d",
                          node->args[1]->data_kind);
      }
      /* Float - Float = Float. */
      node->data_kind = GRN_TS_FLOAT;
      node->data_type = GRN_DB_FLOAT;
      return GRN_SUCCESS;
    }
    case GRN_TS_TIME: {
      switch (node->args[1]->data_kind) {
        case GRN_TS_INT:
        case GRN_TS_FLOAT: {
          /* Time - Int or Float = Time. */
          node->data_kind = GRN_TS_TIME;
          node->data_type = GRN_DB_TIME;
          return GRN_SUCCESS;
        }
        case GRN_TS_TIME: {
          /* Time - Time = Float. */
          node->data_kind = GRN_TS_FLOAT;
          node->data_type = GRN_DB_FLOAT;
          return GRN_SUCCESS;
        }
        default: {
          GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d",
                            node->args[1]->data_kind);
        }
      }
    }
    default: {
      GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d",
                        node->args[0]->data_kind);
    }
  }
}

/*
 * grn_ts_expr_op_node_typecast_args_for_cmp() inserts a typecast operator for
 * comparison.
 */
static grn_rc
grn_ts_expr_op_node_typecast_args_for_cmp(grn_ctx *ctx,
                                          grn_ts_expr_op_node *node)
{
  grn_rc rc;
  if ((node->args[0]->data_kind == GRN_TS_INT) &&
      (node->args[1]->data_kind == GRN_TS_FLOAT)) {
    rc = grn_ts_expr_op_node_open(ctx, GRN_TS_OP_FLOAT, &node->args[0],
                                  1, &node->args[0]);
    if (rc != GRN_SUCCESS) {
      node->args[0] = NULL;
      return rc;
    }
  } else if ((node->args[0]->data_kind == GRN_TS_FLOAT) &&
             (node->args[1]->data_kind == GRN_TS_INT)) {
    rc = grn_ts_expr_op_node_open(ctx, GRN_TS_OP_FLOAT, &node->args[1],
                                  1, &node->args[1]);
    if (rc != GRN_SUCCESS) {
      node->args[1] = NULL;
      return rc;
    }
  } else if ((node->args[0]->data_kind == GRN_TS_TIME) &&
             (node->args[1]->data_kind == GRN_TS_TEXT)) {
    rc = grn_ts_expr_op_node_open(ctx, GRN_TS_OP_TIME, &node->args[1],
                                  1, &node->args[1]);
    if (rc != GRN_SUCCESS) {
      node->args[1] = NULL;
      return rc;
    }
  } else if ((node->args[0]->data_kind == GRN_TS_TEXT) &&
             (node->args[1]->data_kind == GRN_TS_TIME)) {
    rc = grn_ts_expr_op_node_open(ctx, GRN_TS_OP_TIME, &node->args[0],
                                  1, &node->args[0]);
    if (rc != GRN_SUCCESS) {
      node->args[0] = NULL;
      return rc;
    }
  } else {
    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT,
                      "data kind conflict: %d != %d",
                      node->args[0]->data_kind,
                      node->args[1]->data_kind);
  }
  return GRN_SUCCESS;
}

/*
 * grn_ts_expr_op_node_check_args() checks the combination of an operator and
 * its arguments.
 */
static grn_rc
grn_ts_expr_op_node_check_args(grn_ctx *ctx, grn_ts_expr_op_node *node)
{
  switch (node->op_type) {
    case GRN_TS_OP_LOGICAL_NOT: {
      if (node->args[0]->data_kind != GRN_TS_BOOL) {
        GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d",
                          node->args[0]->data_kind);
      }
      node->data_kind = GRN_TS_BOOL;
      node->data_type = GRN_DB_BOOL;
      return GRN_SUCCESS;
    }
    case GRN_TS_OP_BITWISE_NOT: {
      switch (node->args[0]->data_kind) {
        case GRN_TS_BOOL:
        case GRN_TS_INT: {
          node->data_kind = node->args[0]->data_kind;
          node->data_type = grn_ts_data_kind_to_type(node->data_kind);
          return GRN_SUCCESS;
        }
        default: {
          GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d",
                            node->args[0]->data_kind);
        }
      }
    }
    case GRN_TS_OP_POSITIVE:
    case GRN_TS_OP_NEGATIVE: {
      if ((node->args[0]->data_kind != GRN_TS_INT) &&
          (node->args[0]->data_kind != GRN_TS_FLOAT)) {
        GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d",
                          node->args[0]->data_kind);
      }
      node->data_kind = node->args[0]->data_kind;
      node->data_type = grn_ts_data_kind_to_type(node->data_kind);
      return GRN_SUCCESS;
    }
    case GRN_TS_OP_FLOAT: {
      if (node->args[0]->data_kind != GRN_TS_INT) {
        GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d",
                          node->args[0]->data_kind);
      }
      node->data_kind = GRN_TS_FLOAT;
      node->data_type = GRN_DB_FLOAT;
      return GRN_SUCCESS;
    }
    case GRN_TS_OP_TIME: {
      if (node->args[0]->data_kind != GRN_TS_TEXT) {
        GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d",
                          node->args[0]->data_kind);
      }
      node->data_kind = GRN_TS_TIME;
      node->data_type = GRN_DB_TIME;
      return GRN_SUCCESS;
    }
    case GRN_TS_OP_LOGICAL_AND:
    case GRN_TS_OP_LOGICAL_OR:
    case GRN_TS_OP_LOGICAL_SUB: {
      if ((node->args[0]->data_kind != GRN_TS_BOOL) ||
          (node->args[1]->data_kind != GRN_TS_BOOL)) {
        GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d, %d",
                          node->args[0]->data_kind, node->args[1]->data_kind);
      }
      node->data_kind = GRN_TS_BOOL;
      node->data_type = GRN_DB_BOOL;
      return GRN_SUCCESS;
    }
    case GRN_TS_OP_BITWISE_AND:
    case GRN_TS_OP_BITWISE_OR:
    case GRN_TS_OP_BITWISE_XOR: {
      if (node->args[0]->data_kind != node->args[1]->data_kind) {
        GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "data kind conflict: %d != %d",
                          node->args[0]->data_kind, node->args[1]->data_kind);
      }
      switch (node->args[0]->data_kind) {
        case GRN_TS_BOOL:
        case GRN_TS_INT: {
          node->data_kind = node->args[0]->data_kind;
          node->data_type = grn_ts_data_kind_to_type(node->data_kind);
          return GRN_SUCCESS;
        }
        default: {
          GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d",
                            node->args[0]->data_kind);
        }
      }
      node->data_kind = GRN_TS_BOOL;
      node->data_type = GRN_DB_BOOL;
      return GRN_SUCCESS;
    }
    case GRN_TS_OP_EQUAL:
    case GRN_TS_OP_NOT_EQUAL: {
      grn_ts_data_kind scalar_data_kind;
      if (node->args[0]->data_kind != node->args[1]->data_kind) {
        grn_rc rc = grn_ts_expr_op_node_typecast_args_for_cmp(ctx, node);
        if (rc != GRN_SUCCESS) {
          return rc;
        }
      }
      scalar_data_kind = node->args[0]->data_kind & ~GRN_TS_VECTOR_FLAG;
      if (((scalar_data_kind == GRN_TS_REF) ||
           (scalar_data_kind == GRN_TS_GEO)) &&
          (node->args[0]->data_type != node->args[1]->data_type)) {
        GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "data type conflict: %d != %d",
                          node->args[0]->data_type, node->args[1]->data_type);
      }
      node->data_kind = GRN_TS_BOOL;
      node->data_type = GRN_DB_BOOL;
      return GRN_SUCCESS;
    }
    case GRN_TS_OP_LESS:
    case GRN_TS_OP_LESS_EQUAL:
    case GRN_TS_OP_GREATER:
    case GRN_TS_OP_GREATER_EQUAL: {
      if (node->args[0]->data_kind != node->args[1]->data_kind) {
        grn_rc rc = grn_ts_expr_op_node_typecast_args_for_cmp(ctx, node);
        if (rc != GRN_SUCCESS) {
          return rc;
        }
      }
      switch (node->args[0]->data_kind) {
        case GRN_TS_INT:
        case GRN_TS_FLOAT:
        case GRN_TS_TIME:
        case GRN_TS_TEXT:
        case GRN_TS_INT_VECTOR:
        case GRN_TS_FLOAT_VECTOR:
        case GRN_TS_TIME_VECTOR:
        case GRN_TS_TEXT_VECTOR: {
          node->data_kind = GRN_TS_BOOL;
          node->data_type = GRN_DB_BOOL;
          return GRN_SUCCESS;
        }
        default: {
          GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d",
                            node->args[0]->data_kind);
        }
      }
      case GRN_TS_OP_SHIFT_ARITHMETIC_LEFT:
      case GRN_TS_OP_SHIFT_ARITHMETIC_RIGHT:
      case GRN_TS_OP_SHIFT_LOGICAL_LEFT:
      case GRN_TS_OP_SHIFT_LOGICAL_RIGHT: {
        if ((node->args[0]->data_kind != GRN_TS_INT) ||
            (node->args[1]->data_kind != GRN_TS_INT)) {
          GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d, %d",
                            node->args[0]->data_kind,
                            node->args[1]->data_kind);
        }
        node->data_kind = GRN_TS_INT;
        node->data_type = GRN_DB_INT64;
        return GRN_SUCCESS;
      }
      case GRN_TS_OP_PLUS: {
        return grn_ts_op_plus_check_args(ctx, node);
      }
      case GRN_TS_OP_MINUS: {
        return grn_ts_op_minus_check_args(ctx, node);
      }
      case GRN_TS_OP_MULTIPLICATION:
      case GRN_TS_OP_DIVISION:
      case GRN_TS_OP_MODULUS: {
        if (node->args[0]->data_kind != node->args[1]->data_kind) {
          grn_rc rc;
          if ((node->args[0]->data_kind == GRN_TS_INT) &&
              (node->args[1]->data_kind == GRN_TS_FLOAT)) {
            rc = grn_ts_expr_op_node_open(ctx, GRN_TS_OP_FLOAT, &node->args[0],
                                          1, &node->args[0]);
            if (rc != GRN_SUCCESS) {
              node->args[0] = NULL;
              return rc;
            }
          } else if ((node->args[0]->data_kind == GRN_TS_FLOAT) &&
                     (node->args[1]->data_kind == GRN_TS_INT)) {
            rc = grn_ts_expr_op_node_open(ctx, GRN_TS_OP_FLOAT, &node->args[1],
                                          1, &node->args[1]);
            if (rc != GRN_SUCCESS) {
              node->args[1] = NULL;
              return rc;
            }
          } else {
            GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT,
                              "data kind conflict: %d != %d",
                              node->args[0]->data_kind,
                              node->args[1]->data_kind);
          }
        }
        switch (node->args[0]->data_kind) {
          case GRN_TS_INT:
          case GRN_TS_FLOAT: {
            node->data_kind = node->args[0]->data_kind;
            node->data_type = grn_ts_data_kind_to_type(node->data_kind);
            return GRN_SUCCESS;
          }
          default: {
            GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d",
                              node->args[0]->data_kind);
          }
        }
      }
    }
    case GRN_TS_OP_MATCH:
    case GRN_TS_OP_PREFIX_MATCH:
    case GRN_TS_OP_SUFFIX_MATCH: {
      if ((node->args[0]->data_kind != GRN_TS_TEXT) ||
          (node->args[1]->data_kind != GRN_TS_TEXT)) {
        GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d, %d",
                          node->args[0]->data_kind,
                          node->args[1]->data_kind);
      }
      node->data_kind = GRN_TS_BOOL;
      node->data_type = GRN_DB_BOOL;
      return GRN_SUCCESS;
    }
    default: {
      GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid operator: %d",
                        node->op_type);
    }
  }
}

/* grn_ts_expr_op_node_setup() sets up an operator node. */
static grn_rc
grn_ts_expr_op_node_setup(grn_ctx *ctx, grn_ts_expr_op_node *node)
{
  grn_rc rc = grn_ts_expr_op_node_deref_args(ctx, node);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  rc = grn_ts_expr_op_node_check_args(ctx, node);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  if (node->data_kind == GRN_TS_VOID) {
    GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",
                      GRN_TS_VOID);
  } else if (node->data_type == GRN_DB_VOID) {
    GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data type: %d",
                      GRN_DB_VOID);
  }
  return GRN_SUCCESS;
}

grn_rc
grn_ts_expr_op_node_open(grn_ctx *ctx, grn_ts_op_type op_type,
                         grn_ts_expr_node **args, size_t n_args,
                         grn_ts_expr_node **node)
{
  size_t i;
  grn_rc rc;
  grn_ts_expr_op_node *new_node = GRN_MALLOCN(grn_ts_expr_op_node, 1);
  if (!new_node) {
    for (i = 0; i < n_args; i++) {
      grn_ts_expr_node_close(ctx, args[i]);
    }
    GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
                      "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1",
                      sizeof(grn_ts_expr_op_node));
  }
  grn_ts_expr_op_node_init(ctx, new_node);
  new_node->op_type = op_type;
  for (i = 0; i < n_args; i++) {
    new_node->args[i] = args[i];
  }
  new_node->n_args = n_args;
  rc = grn_ts_expr_op_node_setup(ctx, new_node);
  if (rc != GRN_SUCCESS) {
    grn_ts_expr_op_node_fin(ctx, new_node);
    GRN_FREE(new_node);
    return rc;
  }
  *node = (grn_ts_expr_node *)new_node;
  return GRN_SUCCESS;
}

/* grn_ts_expr_op_node_close() destroys a node. */
static void
grn_ts_expr_op_node_close(grn_ctx *ctx, grn_ts_expr_op_node *node)
{
  grn_ts_expr_op_node_fin(ctx, node);
  GRN_FREE(node);
}

/* grn_ts_op_logical_not_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_logical_not_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                               const grn_ts_record *in, size_t n_in, void *out)
{
  size_t i;
  grn_ts_bool *out_ptr = (grn_ts_bool *)out;
  grn_rc rc = grn_ts_expr_node_evaluate(ctx, node->args[0], in, n_in, out);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  for (i = 0; i < n_in; i++) {
    out_ptr[i] = grn_ts_op_logical_not_bool(out_ptr[i]);
  }
  return GRN_SUCCESS;
}

/* grn_ts_op_bitwise_not_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_bitwise_not_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                               const grn_ts_record *in, size_t n_in, void *out)
{
  size_t i;
  grn_rc rc = grn_ts_expr_node_evaluate(ctx, node->args[0], in, n_in, out);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  switch (node->data_kind) {
    case GRN_TS_BOOL: {
      grn_ts_bool *out_ptr = (grn_ts_bool *)out;
      for (i = 0; i < n_in; i++) {
        out_ptr[i] = grn_ts_op_bitwise_not_bool(out_ptr[i]);
      }
      return GRN_SUCCESS;
    }
    case GRN_TS_INT: {
      grn_ts_int *out_ptr = (grn_ts_int *)out;
      for (i = 0; i < n_in; i++) {
        out_ptr[i] = grn_ts_op_bitwise_not_int(out_ptr[i]);
      }
      return GRN_SUCCESS;
    }
    default: {
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",
                        node->data_kind);
    }
  }
}

#define GRN_TS_OP_SIGN_EVALUATE_CASE(type, KIND, kind) \
  case GRN_TS_ ## KIND: {\
    grn_ts_ ## kind *out_ptr = (grn_ts_ ## kind *)out;\
    for (i = 0; i < n_in; i++) {\
      out_ptr[i] = grn_ts_op_ ## type ## _ ## kind(out_ptr[i]);\
    }\
    return GRN_SUCCESS;\
  }
#define GRN_TS_OP_SIGN_EVALUATE(type) \
  size_t i;\
  grn_rc rc = grn_ts_expr_node_evaluate(ctx, node->args[0], in, n_in, out);\
  if (rc != GRN_SUCCESS) {\
    return rc;\
  }\
  switch (node->data_kind) {\
    GRN_TS_OP_SIGN_EVALUATE_CASE(type, INT, int)\
    GRN_TS_OP_SIGN_EVALUATE_CASE(type, FLOAT, float)\
    default: {\
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",\
                        node->data_kind);\
    }\
  }
/* grn_ts_op_positive_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_positive_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                            const grn_ts_record *in, size_t n_in, void *out)
{
  GRN_TS_OP_SIGN_EVALUATE(positive)
}

/* grn_ts_op_negative_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_negative_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                            const grn_ts_record *in, size_t n_in, void *out)
{
  GRN_TS_OP_SIGN_EVALUATE(negative)
}
#undef GRN_TS_OP_SIGN_EVALUATE
#undef GRN_TS_OP_SIGN_EVALUATE_CASE

/* grn_ts_op_float_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_float_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                         const grn_ts_record *in, size_t n_in, void *out)
{
  size_t i;
  grn_ts_int *buf_ptr;
  grn_ts_float *out_ptr = (grn_ts_float *)out;
  grn_rc rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[0], in, n_in,
                                               &node->bufs[0]);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  buf_ptr = (grn_ts_int *)node->bufs[0].ptr;
  for (i = 0; i < n_in; i++) {
    rc = grn_ts_op_float(ctx, buf_ptr[i], &out_ptr[i]);
    if (rc != GRN_SUCCESS) {
      return rc;
    }
  }
  return GRN_SUCCESS;
}

/* grn_ts_op_time_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_time_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                        const grn_ts_record *in, size_t n_in, void *out)
{
  size_t i;
  grn_ts_text *buf_ptr;
  grn_ts_time *out_ptr = (grn_ts_time *)out;
  grn_rc rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[0], in, n_in,
                                               &node->bufs[0]);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  buf_ptr = (grn_ts_text *)node->bufs[0].ptr;
  for (i = 0; i < n_in; i++) {
    rc = grn_ts_op_time(ctx, buf_ptr[i], &out_ptr[i]);
    if (rc != GRN_SUCCESS) {
      return rc;
    }
  }
  return GRN_SUCCESS;
}

/* grn_ts_op_logical_and_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_logical_and_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                               const grn_ts_record *in, size_t n_in, void *out)
{
  size_t i, j, count;
  grn_rc rc;
  grn_ts_bool *buf_ptrs[2], *out_ptr = (grn_ts_bool *)out;
  grn_ts_buf *tmp_in_buf = &node->bufs[2];
  grn_ts_record *tmp_in;

  /* Evaluate the 1st argument. */
  rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[0], in, n_in,
                                        &node->bufs[0]);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  buf_ptrs[0] = (grn_ts_bool *)node->bufs[0].ptr;

  /* Create a list of true records. */
  rc = grn_ts_buf_reserve(ctx, tmp_in_buf, sizeof(grn_ts_record) * n_in);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  tmp_in = (grn_ts_record *)tmp_in_buf->ptr;
  count = 0;
  for (i = 0; i < n_in; i++) {
    if (buf_ptrs[0][i]) {
      tmp_in[count++] = in[i];
    }
  }

  /* Evaluate the 2nd argument. */
  rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1], tmp_in, count,
                                        &node->bufs[1]);
  buf_ptrs[1] = (grn_ts_bool *)node->bufs[1].ptr;

  /* Merge the results. */
  count = 0;
  for (i = 0, j = 0; i < n_in; i++) {
    out_ptr[count++] = buf_ptrs[0][i] && buf_ptrs[1][j++];
  }
  return GRN_SUCCESS;
}

/* grn_ts_op_logical_or_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_logical_or_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                               const grn_ts_record *in, size_t n_in, void *out)
{
  size_t i, j, count;
  grn_rc rc;
  grn_ts_bool *buf_ptrs[2], *out_ptr = (grn_ts_bool *)out;
  grn_ts_buf *tmp_in_buf = &node->bufs[2];
  grn_ts_record *tmp_in;

  /* Evaluate the 1st argument. */
  rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[0], in, n_in,
                                        &node->bufs[0]);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  buf_ptrs[0] = (grn_ts_bool *)node->bufs[0].ptr;

  /* Create a list of false records. */
  rc = grn_ts_buf_reserve(ctx, tmp_in_buf, sizeof(grn_ts_record) * n_in);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  tmp_in = (grn_ts_record *)tmp_in_buf->ptr;
  count = 0;
  for (i = 0; i < n_in; i++) {
    if (!buf_ptrs[0][i]) {
      tmp_in[count++] = in[i];
    }
  }

  /* Evaluate the 2nd argument. */
  rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1], tmp_in, count,
                                        &node->bufs[1]);
  buf_ptrs[1] = (grn_ts_bool *)node->bufs[1].ptr;

  /* Merge the results. */
  count = 0;
  for (i = 0, j = 0; i < n_in; i++) {
    out_ptr[count++] = buf_ptrs[0][i] || buf_ptrs[1][j++];
  }
  return GRN_SUCCESS;
}

/* grn_ts_op_logical_sub_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_logical_sub_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                               const grn_ts_record *in, size_t n_in, void *out)
{
  size_t i, j, count;
  grn_rc rc;
  grn_ts_bool *buf_ptrs[2], *out_ptr = (grn_ts_bool *)out;
  grn_ts_buf *tmp_in_buf = &node->bufs[2];
  grn_ts_record *tmp_in;

  /* Evaluate the 1st argument. */
  rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[0], in, n_in,
                                        &node->bufs[0]);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  buf_ptrs[0] = (grn_ts_bool *)node->bufs[0].ptr;

  /* Create a list of true records. */
  rc = grn_ts_buf_reserve(ctx, tmp_in_buf, sizeof(grn_ts_record) * n_in);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  tmp_in = (grn_ts_record *)tmp_in_buf->ptr;
  count = 0;
  for (i = 0; i < n_in; i++) {
    if (buf_ptrs[0][i]) {
      tmp_in[count++] = in[i];
    }
  }

  /* Evaluate the 2nd argument. */
  rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1], tmp_in, count,
                                        &node->bufs[1]);
  buf_ptrs[1] = (grn_ts_bool *)node->bufs[1].ptr;

  /* Merge the results. */
  count = 0;
  for (i = 0, j = 0; i < n_in; i++) {
    out_ptr[count++] = buf_ptrs[0][i] &&
                       grn_ts_op_logical_not_bool(buf_ptrs[1][j++]);
  }
  return GRN_SUCCESS;
}

#define GRN_TS_OP_BITWISE_EVALUATE_CASE(type, KIND, kind)\
  case GRN_TS_ ## KIND: {\
    /*
     * Use the output buffer to put evaluation results of the 1st argument,
     * because the data kind is same.
     */\
    size_t i;\
    grn_rc rc;\
    grn_ts_ ## kind *out_ptr = (grn_ts_ ## kind *)out;\
    rc = grn_ts_expr_node_evaluate(ctx, node->args[0], in, n_in, out);\
    if (rc == GRN_SUCCESS) {\
      rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1],\
                                            in, n_in, &node->bufs[0]);\
      if (rc == GRN_SUCCESS) {\
        grn_ts_ ## kind *buf_ptr = (grn_ts_ ## kind *)node->bufs[0].ptr;\
        for (i = 0; i < n_in; i++) {\
          out_ptr[i] = grn_ts_op_bitwise_ ## type ## _ ## kind(out_ptr[i],\
                                                               buf_ptr[i]);\
        }\
      }\
    }\
    return rc;\
  }
/* grn_ts_op_bitwise_and_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_bitwise_and_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                               const grn_ts_record *in, size_t n_in, void *out)
{
  switch (node->args[0]->data_kind) {
    GRN_TS_OP_BITWISE_EVALUATE_CASE(and, BOOL, bool)
    GRN_TS_OP_BITWISE_EVALUATE_CASE(and, INT, int)
    default: {
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",
                        node->args[0]->data_kind);
    }
  }
}

/* grn_ts_op_bitwise_or_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_bitwise_or_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                              const grn_ts_record *in, size_t n_in, void *out)
{
  switch (node->args[0]->data_kind) {
    GRN_TS_OP_BITWISE_EVALUATE_CASE(or, BOOL, bool)
    GRN_TS_OP_BITWISE_EVALUATE_CASE(or, INT, int)
    default: {
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",
                        node->args[0]->data_kind);
    }
  }
}

/* grn_ts_op_bitwise_xor_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_bitwise_xor_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                               const grn_ts_record *in, size_t n_in, void *out)
{
  switch (node->args[0]->data_kind) {
    GRN_TS_OP_BITWISE_EVALUATE_CASE(xor, BOOL, bool)
    GRN_TS_OP_BITWISE_EVALUATE_CASE(xor, INT, int)
    default: {
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",
                        node->args[0]->data_kind);
    }
  }
}
#undef GRN_TS_OP_BITWISE_EVALUATE_CASE

#define GRN_TS_OP_CHK_EVALUATE_CASE(type, KIND, kind)\
  case GRN_TS_ ## KIND: {\
    grn_ts_ ## kind *buf_ptrs[] = {\
      (grn_ts_ ## kind *)node->bufs[0].ptr,\
      (grn_ts_ ## kind *)node->bufs[1].ptr\
    };\
    for (i = 0; i < n_in; i++) {\
      out_ptr[i] = grn_ts_op_ ## type ## _ ## kind(buf_ptrs[0][i],\
                                                   buf_ptrs[1][i]);\
    }\
    return GRN_SUCCESS;\
  }
#define GRN_TS_OP_CHK_EVALUATE_VECTOR_CASE(type, KIND, kind)\
  GRN_TS_OP_CHK_EVALUATE_CASE(type, KIND ## _VECTOR, kind ## _vector)
#define GRN_TS_OP_CHK_EVALUATE(type)\
  size_t i;\
  grn_rc rc;\
  grn_ts_bool *out_ptr = (grn_ts_bool *)out;\
  if (node->args[0]->data_kind == GRN_TS_BOOL) {\
    /*
     * Use the output buffer to put evaluation results of the 1st argument,
     * because the data kind is same.
     */\
    rc = grn_ts_expr_node_evaluate(ctx, node->args[0], in, n_in, out);\
    if (rc == GRN_SUCCESS) {\
      grn_ts_buf *buf = &node->bufs[0];\
      rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1],\
                                            in, n_in, buf);\
      if (rc == GRN_SUCCESS) {\
        grn_ts_bool *buf_ptr = (grn_ts_bool *)buf->ptr;\
        for (i = 0; i < n_in; i++) {\
          out_ptr[i] = grn_ts_op_ ## type ## _bool(out_ptr[i], buf_ptr[i]);\
        }\
      }\
    }\
    return rc;\
  }\
  for (i = 0; i < 2; i++) {\
    rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[i], in, n_in,\
                                          &node->bufs[i]);\
    if (rc != GRN_SUCCESS) {\
      return rc;\
    }\
  }\
  switch (node->args[0]->data_kind) {\
    GRN_TS_OP_CHK_EVALUATE_CASE(type, INT, int)\
    GRN_TS_OP_CHK_EVALUATE_CASE(type, FLOAT, float)\
    GRN_TS_OP_CHK_EVALUATE_CASE(type, TIME, time)\
    GRN_TS_OP_CHK_EVALUATE_CASE(type, TEXT, text)\
    GRN_TS_OP_CHK_EVALUATE_CASE(type, GEO, geo)\
    GRN_TS_OP_CHK_EVALUATE_CASE(type, REF, ref)\
    GRN_TS_OP_CHK_EVALUATE_VECTOR_CASE(type, BOOL, bool)\
    GRN_TS_OP_CHK_EVALUATE_VECTOR_CASE(type, INT, int)\
    GRN_TS_OP_CHK_EVALUATE_VECTOR_CASE(type, FLOAT, float)\
    GRN_TS_OP_CHK_EVALUATE_VECTOR_CASE(type, TIME, time)\
    GRN_TS_OP_CHK_EVALUATE_VECTOR_CASE(type, TEXT, text)\
    GRN_TS_OP_CHK_EVALUATE_VECTOR_CASE(type, GEO, geo)\
    GRN_TS_OP_CHK_EVALUATE_VECTOR_CASE(type, REF, ref)\
    default: {\
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",\
                        node->args[0]->data_kind);\
    }\
  }
/* grn_ts_op_equal_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_equal_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                         const grn_ts_record *in, size_t n_in, void *out)
{
  GRN_TS_OP_CHK_EVALUATE(equal)
}

/* grn_ts_op_not_equal_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_not_equal_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                             const grn_ts_record *in, size_t n_in, void *out)
{
  GRN_TS_OP_CHK_EVALUATE(not_equal)
}
#undef GRN_TS_OP_CHK_EVALUATE
#undef GRN_TS_OP_CHK_EVALUATE_VECTOR_CASE
#undef GRN_TS_OP_CHK_EVALUATE_CASE

#define GRN_TS_OP_CMP_EVALUATE_CASE(type, KIND, kind)\
  case GRN_TS_ ## KIND: {\
    grn_ts_ ## kind *buf_ptrs[] = {\
      (grn_ts_ ## kind *)node->bufs[0].ptr,\
      (grn_ts_ ## kind *)node->bufs[1].ptr\
    };\
    for (i = 0; i < n_in; i++) {\
      out_ptr[i] = grn_ts_op_ ## type ## _ ## kind(buf_ptrs[0][i],\
                                                   buf_ptrs[1][i]);\
    }\
    return GRN_SUCCESS;\
  }
#define GRN_TS_OP_CMP_EVALUATE_VECTOR_CASE(type, KIND, kind)\
  GRN_TS_OP_CMP_EVALUATE_CASE(type, KIND ## _VECTOR, kind ## _vector)
#define GRN_TS_OP_CMP_EVALUATE(type)\
  size_t i;\
  grn_rc rc;\
  grn_ts_bool *out_ptr = (grn_ts_bool *)out;\
  for (i = 0; i < 2; i++) {\
    rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[i], in, n_in,\
                                          &node->bufs[i]);\
    if (rc != GRN_SUCCESS) {\
      return rc;\
    }\
  }\
  switch (node->args[0]->data_kind) {\
    GRN_TS_OP_CMP_EVALUATE_CASE(type, INT, int)\
    GRN_TS_OP_CMP_EVALUATE_CASE(type, FLOAT, float)\
    GRN_TS_OP_CMP_EVALUATE_CASE(type, TIME, time)\
    GRN_TS_OP_CMP_EVALUATE_CASE(type, TEXT, text)\
    GRN_TS_OP_CMP_EVALUATE_VECTOR_CASE(type, INT, int)\
    GRN_TS_OP_CMP_EVALUATE_VECTOR_CASE(type, FLOAT, float)\
    GRN_TS_OP_CMP_EVALUATE_VECTOR_CASE(type, TIME, time)\
    GRN_TS_OP_CMP_EVALUATE_VECTOR_CASE(type, TEXT, text)\
    default: {\
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",\
                        node->args[0]->data_kind);\
    }\
  }
/* grn_ts_op_less_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_less_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                        const grn_ts_record *in, size_t n_in, void *out)
{
  GRN_TS_OP_CMP_EVALUATE(less)
}

/* grn_ts_op_less_equal_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_less_equal_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                              const grn_ts_record *in, size_t n_in, void *out)
{
  GRN_TS_OP_CMP_EVALUATE(less_equal)
}

/* grn_ts_op_greater_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_greater_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                           const grn_ts_record *in, size_t n_in, void *out)
{
  GRN_TS_OP_CMP_EVALUATE(greater)
}

/* grn_ts_op_greater_equal_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_greater_equal_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                                 const grn_ts_record *in, size_t n_in,
                                 void *out)
{
  GRN_TS_OP_CMP_EVALUATE(greater_equal)
}
#undef GRN_TS_OP_CMP_EVALUATE
#undef GRN_TS_OP_CMP_EVALUATE_VECTOR_CASE
#undef GRN_TS_OP_CMP_EVALUATE_CASE

#define GRN_TS_OP_SHIFT_EVALUATE(type)\
  size_t i;\
  grn_rc rc;\
  grn_ts_int *out_ptr = (grn_ts_int *)out;\
  rc = grn_ts_expr_node_evaluate(ctx, node->args[0], in, n_in, out);\
  if (rc == GRN_SUCCESS) {\
    rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1],\
                                          in, n_in, &node->bufs[0]);\
    if (rc == GRN_SUCCESS) {\
      grn_ts_int *buf_ptr = (grn_ts_int *)node->bufs[0].ptr;\
      for (i = 0; i < n_in; i++) {\
        out_ptr[i] = grn_ts_op_shift_ ## type(out_ptr[i], buf_ptr[i]);\
      }\
    }\
  }\
  return rc;
/* grn_ts_op_shift_arithmetic_left_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_shift_arithmetic_left_evaluate(grn_ctx *ctx,
                                         grn_ts_expr_op_node *node,
                                         const grn_ts_record *in, size_t n_in,
                                         void *out)
{
  GRN_TS_OP_SHIFT_EVALUATE(arithmetic_left)
}

/* grn_ts_op_shift_arithmetic_right_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_shift_arithmetic_right_evaluate(grn_ctx *ctx,
                                          grn_ts_expr_op_node *node,
                                          const grn_ts_record *in, size_t n_in,
                                          void *out)
{
  GRN_TS_OP_SHIFT_EVALUATE(arithmetic_right)
}

/* grn_ts_op_shift_logical_left_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_shift_logical_left_evaluate(grn_ctx *ctx,
                                      grn_ts_expr_op_node *node,
                                      const grn_ts_record *in, size_t n_in,
                                      void *out)
{
  GRN_TS_OP_SHIFT_EVALUATE(logical_left)
}

/* grn_ts_op_shift_logical_right_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_shift_logical_right_evaluate(grn_ctx *ctx,
                                       grn_ts_expr_op_node *node,
                                       const grn_ts_record *in, size_t n_in,
                                       void *out)
{
  GRN_TS_OP_SHIFT_EVALUATE(logical_right)
}
#undef GRN_TS_OP_SHIFT_EVALUATE

#define GRN_TS_OP_ARITH_EVALUATE(type, lhs_kind, rhs_kind)\
  /*
   * Use the output buffer to put evaluation results of the 1st argument,
   * because the data kind is same.
   */\
  size_t i;\
  grn_rc rc;\
  grn_ts_ ## lhs_kind *out_ptr = (grn_ts_ ## lhs_kind *)out;\
  rc = grn_ts_expr_node_evaluate(ctx, node->args[0], in, n_in, out);\
  if (rc == GRN_SUCCESS) {\
    rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1],\
                                          in, n_in, &node->bufs[0]);\
    if (rc == GRN_SUCCESS) {\
      grn_ts_ ## rhs_kind *buf_ptr = (grn_ts_ ## rhs_kind *)node->bufs[0].ptr;\
      for (i = 0; i < n_in; i++) {\
        rc = grn_ts_op_ ## type ## _ ## lhs_kind ## _ ## rhs_kind(\
          ctx, out_ptr[i], buf_ptr[i], &out_ptr[i]);\
        if (rc != GRN_SUCCESS) {\
          return rc;\
        }\
      }\
    }\
  }\
  return rc;

#define GRN_TS_OP_ARITH_EVALUATE_CASE(type, KIND, kind)\
  case GRN_TS_ ## KIND: {\
    /*
     * Use the output buffer to put evaluation results of the 1st argument,
     * because the data kind is same.
     */\
    size_t i;\
    grn_rc rc;\
    grn_ts_ ## kind *out_ptr = (grn_ts_ ## kind *)out;\
    rc = grn_ts_expr_node_evaluate(ctx, node->args[0], in, n_in, out);\
    if (rc == GRN_SUCCESS) {\
      rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1],\
                                            in, n_in, &node->bufs[0]);\
      if (rc == GRN_SUCCESS) {\
        grn_ts_ ## kind *buf_ptr = (grn_ts_ ## kind *)node->bufs[0].ptr;\
        for (i = 0; i < n_in; i++) {\
          out_ptr[i] = grn_ts_op_ ## type ## _ ## kind(out_ptr[i],\
                                                       buf_ptr[i]);\
        }\
      }\
    }\
    return rc;\
  }
#define GRN_TS_OP_ARITH_EVALUATE_TIME_CASE(type, KIND, lhs, rhs)\
  case GRN_TS_ ## KIND: {\
    /*
     * Use the output buffer to put evaluation results of the 1st argument,
     * because the data kind is same.
     */\
    size_t i;\
    grn_rc rc;\
    grn_ts_ ## lhs *out_ptr = (grn_ts_ ## lhs *)out;\
    rc = grn_ts_expr_node_evaluate(ctx, node->args[0], in, n_in, out);\
    if (rc == GRN_SUCCESS) {\
      rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1],\
                                            in, n_in, &node->bufs[0]);\
      if (rc == GRN_SUCCESS) {\
        grn_ts_ ## rhs *buf_ptr = (grn_ts_ ## rhs *)node->bufs[0].ptr;\
        for (i = 0; i < n_in; i++) {\
          out_ptr[i] = grn_ts_op_ ## type ## _ ## lhs ## _ ## rhs(out_ptr[i],\
                                                                  buf_ptr[i]);\
        }\
      }\
    }\
    return rc;\
  }
/* grn_ts_op_plus_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_plus_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                        const grn_ts_record *in, size_t n_in, void *out)
{
  switch (node->args[0]->data_kind) {
    case GRN_TS_INT: {
      GRN_TS_OP_ARITH_EVALUATE(plus, int, int)
    }
    case GRN_TS_FLOAT: {
      GRN_TS_OP_ARITH_EVALUATE(plus, float, float)
    }
    case GRN_TS_TIME: {
      switch (node->args[1]->data_kind) {
        case GRN_TS_INT: {
          GRN_TS_OP_ARITH_EVALUATE(plus, time, int)
        }
        case GRN_TS_FLOAT: {
          GRN_TS_OP_ARITH_EVALUATE(plus, time, float)
        }
        default: {
          GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "data kind conflict: %d, %d",
                            node->args[0]->data_kind,
                            node->args[1]->data_kind);
        }
      }
    }
    default: {
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",
                        node->args[0]->data_kind);
    }
  }
}

/* grn_ts_op_minus_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_minus_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                         const grn_ts_record *in, size_t n_in, void *out)
{
  switch (node->args[0]->data_kind) {
    case GRN_TS_INT: {
      GRN_TS_OP_ARITH_EVALUATE(minus, int, int)
    }
    case GRN_TS_FLOAT: {
      GRN_TS_OP_ARITH_EVALUATE(minus, float, float)
    }
    case GRN_TS_TIME: {
      switch (node->args[1]->data_kind) {
        case GRN_TS_INT: {
          GRN_TS_OP_ARITH_EVALUATE(minus, time, int)
        }
        case GRN_TS_FLOAT: {
          GRN_TS_OP_ARITH_EVALUATE(minus, time, float)
        }
        case GRN_TS_TIME: {
          size_t i;
          grn_rc rc;
          grn_ts_float *out_ptr = (grn_ts_float *)out;
          grn_ts_time *buf_ptrs[2];
          rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[0], in, n_in,
                                                &node->bufs[0]);
          if (rc != GRN_SUCCESS) {
            return rc;
          }
          rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1], in, n_in,
                                                &node->bufs[1]);
          if (rc != GRN_SUCCESS) {
            return rc;
          }
          buf_ptrs[0] = (grn_ts_time *)node->bufs[0].ptr;
          buf_ptrs[1] = (grn_ts_time *)node->bufs[1].ptr;
          for (i = 0; i < n_in; i++) {
            rc = grn_ts_op_minus_time_time(ctx, buf_ptrs[0][i], buf_ptrs[1][i],
                                           &out_ptr[i]);
            if (rc != GRN_SUCCESS) {
              return rc;
            }
          }
          return GRN_SUCCESS;
        }
        default: {
          GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "data kind conflict: %d, %d",
                            node->args[0]->data_kind,
                            node->args[1]->data_kind);
        }
      }
    }
    default: {
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",
                        node->args[0]->data_kind);
    }
  }
}
#undef GRN_TS_OP_ARITH_EVALUATE_TIME_CASE

/* grn_ts_op_multiplication_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_multiplication_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                                  const grn_ts_record *in, size_t n_in,
                                  void *out)
{
  switch (node->data_kind) {
    case GRN_TS_INT: {
      GRN_TS_OP_ARITH_EVALUATE(multiplication, int, int)
    }
    case GRN_TS_FLOAT: {
      GRN_TS_OP_ARITH_EVALUATE(multiplication, float, float)
    }
    default: {
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",
                        node->data_kind);
    }
  }
}

/* grn_ts_op_division_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_division_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                            const grn_ts_record *in, size_t n_in, void *out)
{
  switch (node->data_kind) {
    case GRN_TS_INT: {
      GRN_TS_OP_ARITH_EVALUATE(division, int, int)
    }
    case GRN_TS_FLOAT: {
      GRN_TS_OP_ARITH_EVALUATE(division, float, float)
    }
    default: {
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",
                        node->data_kind);
    }
  }
}

/* grn_ts_op_modulus_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_modulus_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                           const grn_ts_record *in, size_t n_in, void *out)
{
  switch (node->data_kind) {
    case GRN_TS_INT: {
      GRN_TS_OP_ARITH_EVALUATE(modulus, int, int)
    }
    case GRN_TS_FLOAT: {
      GRN_TS_OP_ARITH_EVALUATE(modulus, float, float)
    }
    default: {
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",
                        node->data_kind);
    }
  }
}
#undef GRN_TS_OP_ARITH_EVALUATE_CASE

#define GRN_TS_OP_MATCH_EVALUATE(type)\
  size_t i;\
  grn_rc rc;\
  grn_ts_bool *out_ptr = (grn_ts_bool *)out;\
  grn_ts_text *buf_ptrs[2];\
  for (i = 0; i < 2; i++) {\
    rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[i], in, n_in,\
                                          &node->bufs[i]);\
    if (rc != GRN_SUCCESS) {\
      return rc;\
    }\
  }\
  buf_ptrs[0] = (grn_ts_text *)node->bufs[0].ptr;\
  buf_ptrs[1] = (grn_ts_text *)node->bufs[1].ptr;\
  for (i = 0; i < n_in; i++) {\
    out_ptr[i] = grn_ts_op_ ## type(buf_ptrs[0][i], buf_ptrs[1][i]);\
  }\
  return GRN_SUCCESS;\
/* grn_ts_op_match_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_match_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                         const grn_ts_record *in, size_t n_in, void *out)
{
  GRN_TS_OP_MATCH_EVALUATE(match)
}

/* grn_ts_op_prefix_match_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_prefix_match_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                                const grn_ts_record *in, size_t n_in,
                                void *out)
{
  GRN_TS_OP_MATCH_EVALUATE(prefix_match)
}

/* grn_ts_op_suffix_match_evaluate() evaluates an operator. */
static grn_rc
grn_ts_op_suffix_match_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                                const grn_ts_record *in, size_t n_in,
                                void *out)
{
  GRN_TS_OP_MATCH_EVALUATE(suffix_match)
}
#undef GRN_TS_OP_MATCH_EVALUATE

/* grn_ts_expr_op_node_evaluate() evaluates an operator. */
static grn_rc
grn_ts_expr_op_node_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
                             const grn_ts_record *in, size_t n_in, void *out)
{
  switch (node->op_type) {
    case GRN_TS_OP_LOGICAL_NOT: {
      return grn_ts_op_logical_not_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_BITWISE_NOT: {
      return grn_ts_op_bitwise_not_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_POSITIVE: {
      return grn_ts_op_positive_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_NEGATIVE: {
      return grn_ts_op_negative_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_FLOAT: {
      return grn_ts_op_float_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_TIME: {
      return grn_ts_op_time_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_LOGICAL_AND: {
      return grn_ts_op_logical_and_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_LOGICAL_OR: {
      return grn_ts_op_logical_or_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_LOGICAL_SUB: {
      return grn_ts_op_logical_sub_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_BITWISE_AND: {
      return grn_ts_op_bitwise_and_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_BITWISE_OR: {
      return grn_ts_op_bitwise_or_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_BITWISE_XOR: {
      return grn_ts_op_bitwise_xor_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_EQUAL: {
      return grn_ts_op_equal_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_NOT_EQUAL: {
      return grn_ts_op_not_equal_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_LESS: {
      return grn_ts_op_less_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_LESS_EQUAL: {
      return grn_ts_op_less_equal_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_GREATER: {
      return grn_ts_op_greater_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_GREATER_EQUAL: {
      return grn_ts_op_greater_equal_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_SHIFT_ARITHMETIC_LEFT: {
      return grn_ts_op_shift_arithmetic_left_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_SHIFT_ARITHMETIC_RIGHT: {
      return grn_ts_op_shift_arithmetic_right_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_SHIFT_LOGICAL_LEFT: {
      return grn_ts_op_shift_logical_left_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_SHIFT_LOGICAL_RIGHT: {
      return grn_ts_op_shift_logical_right_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_PLUS: {
      return grn_ts_op_plus_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_MINUS: {
      return grn_ts_op_minus_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_MULTIPLICATION: {
      return grn_ts_op_multiplication_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_DIVISION: {
      return grn_ts_op_division_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_MODULUS: {
      return grn_ts_op_modulus_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_MATCH: {
      return grn_ts_op_match_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_PREFIX_MATCH: {
      return grn_ts_op_prefix_match_evaluate(ctx, node, in, n_in, out);
    }
    case GRN_TS_OP_SUFFIX_MATCH: {
      return grn_ts_op_suffix_match_evaluate(ctx, node, in, n_in, out);
    }
    // TODO: Add operators.
    default: {
      GRN_TS_ERR_RETURN(GRN_OPERATION_NOT_SUPPORTED,
                        "operator not supported: %d", node->op_type);
    }
  }
}

/* grn_ts_op_logical_not_filter() filters records. */
static grn_rc
grn_ts_op_logical_not_filter(grn_ctx *ctx, grn_ts_expr_op_node *node,
                             grn_ts_record *in, size_t n_in,
                             grn_ts_record *out, size_t *n_out)
{
  size_t i, count;
  grn_rc rc;
  grn_ts_bool *buf_ptr;
  rc = grn_ts_buf_reserve(ctx, &node->bufs[0], sizeof(grn_ts_bool) * n_in);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  buf_ptr = (grn_ts_bool *)node->bufs[0].ptr;
  rc = grn_ts_expr_node_evaluate(ctx, node->args[0], in, n_in, buf_ptr);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  for (i = 0, count = 0; i < n_in; i++) {
    if (grn_ts_op_logical_not_bool(buf_ptr[i])) {
      out[count++] = in[i];
    }
  }
  *n_out = count;
  return GRN_SUCCESS;
}

/* grn_ts_op_bitwise_not_filter() filters records. */
static grn_rc
grn_ts_op_bitwise_not_filter(grn_ctx *ctx, grn_ts_expr_op_node *node,
                             grn_ts_record *in, size_t n_in,
                             grn_ts_record *out, size_t *n_out)
{
  size_t i, count;
  grn_rc rc;
  grn_ts_bool *buf_ptr;
  rc = grn_ts_buf_reserve(ctx, &node->bufs[0], sizeof(grn_ts_bool) * n_in);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  buf_ptr = (grn_ts_bool *)node->bufs[0].ptr;
  rc = grn_ts_expr_node_evaluate(ctx, node->args[0], in, n_in, buf_ptr);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  for (i = 0, count = 0; i < n_in; i++) {
    if (grn_ts_op_bitwise_not_bool(buf_ptr[i])) {
      out[count++] = in[i];
    }
  }
  *n_out = count;
  return GRN_SUCCESS;
}

/* grn_ts_op_logical_and_filter() filters records. */
static grn_rc
grn_ts_op_logical_and_filter(grn_ctx *ctx, grn_ts_expr_op_node *node,
                             grn_ts_record *in, size_t n_in,
                             grn_ts_record *out, size_t *n_out)
{
  grn_rc rc = grn_ts_expr_node_filter(ctx, node->args[0], in, n_in,
                                      out, n_out);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  return grn_ts_expr_node_filter(ctx, node->args[1], out, *n_out, out, n_out);
}

/* grn_ts_op_logical_or_filter() filters records. */
static grn_rc
grn_ts_op_logical_or_filter(grn_ctx *ctx, grn_ts_expr_op_node *node,
                            grn_ts_record *in, size_t n_in,
                            grn_ts_record *out, size_t *n_out)
{
  size_t i, j, count;
  grn_rc rc;
  grn_ts_bool *buf_ptrs[2];
  grn_ts_buf *tmp_in_buf = &node->bufs[2];
  grn_ts_record *tmp_in;

  /* Evaluate the 1st argument. */
  rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[0], in, n_in,
                                        &node->bufs[0]);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  buf_ptrs[0] = (grn_ts_bool *)node->bufs[0].ptr;

  /* Create a list of false records. */
  rc = grn_ts_buf_reserve(ctx, tmp_in_buf, sizeof(grn_ts_record) * n_in);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  tmp_in = (grn_ts_record *)tmp_in_buf->ptr;
  count = 0;
  for (i = 0; i < n_in; i++) {
    if (!buf_ptrs[0][i]) {
      tmp_in[count++] = in[i];
    }
  }

  /* Evaluate the 2nd argument. */
  rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1], tmp_in, count,
                                        &node->bufs[1]);
  buf_ptrs[1] = (grn_ts_bool *)node->bufs[1].ptr;

  /* Merge the results. */
  count = 0;
  for (i = 0, j = 0; i < n_in; i++) {
    if (buf_ptrs[0][i] || buf_ptrs[1][j++]) {
      out[count++] = in[i];
    }
  }
  *n_out = count;
  return GRN_SUCCESS;
}

/* grn_ts_op_logical_sub_filter() filters records. */
static grn_rc
grn_ts_op_logical_sub_filter(grn_ctx *ctx, grn_ts_expr_op_node *node,
                             grn_ts_record *in, size_t n_in,
                             grn_ts_record *out, size_t *n_out)
{
  size_t i, n, count;
  grn_ts_bool *buf_ptr;
  grn_rc rc = grn_ts_expr_node_filter(ctx, node->args[0], in, n_in, out, &n);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1], out, n,
                                        &node->bufs[0]);
  buf_ptr = (grn_ts_bool *)node->bufs[0].ptr;
  for (i = 0, count = 0; i < n; i++) {
    if (grn_ts_op_logical_not_bool(buf_ptr[i])) {
      out[count++] = out[i];
    }
  }
  *n_out = count;
  return GRN_SUCCESS;
}

#define GRN_TS_OP_BITWISE_FILTER(type)\
  size_t i, count = 0;\
  grn_ts_bool *buf_ptrs[2];\
  for (i = 0; i < 2; i++) {\
    grn_rc rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[i], in, n_in,\
                                                 &node->bufs[i]);\
    if (rc != GRN_SUCCESS) {\
      return rc;\
    }\
    buf_ptrs[i] = (grn_ts_bool *)node->bufs[i].ptr;\
  }\
  for (i = 0; i < n_in; i++) {\
    if (grn_ts_op_bitwise_ ## type ## _bool(buf_ptrs[0][i], buf_ptrs[1][i])) {\
      out[count++] = in[i];\
    }\
  }\
  *n_out = count;\
  return GRN_SUCCESS;\
/* grn_ts_op_bitwise_and_filter() filters records. */
static grn_rc
grn_ts_op_bitwise_and_filter(grn_ctx *ctx, grn_ts_expr_op_node *node,
                             grn_ts_record *in, size_t n_in,
                             grn_ts_record *out, size_t *n_out)
{
  GRN_TS_OP_BITWISE_FILTER(and);
}

/* grn_ts_op_bitwise_or_filter() filters records. */
static grn_rc
grn_ts_op_bitwise_or_filter(grn_ctx *ctx, grn_ts_expr_op_node *node,
                            grn_ts_record *in, size_t n_in,
                            grn_ts_record *out, size_t *n_out)
{
  GRN_TS_OP_BITWISE_FILTER(or);
}

/* grn_ts_op_bitwise_xor_filter() filters records. */
static grn_rc
grn_ts_op_bitwise_xor_filter(grn_ctx *ctx, grn_ts_expr_op_node *node,
                             grn_ts_record *in, size_t n_in,
                             grn_ts_record *out, size_t *n_out)
{
  GRN_TS_OP_BITWISE_FILTER(xor);
}
#undef GRN_TS_OP_BITWISE_FILTER_CASE

#define GRN_TS_OP_CHK_FILTER_CASE(type, KIND, kind)\
  case GRN_TS_ ## KIND: {\
    grn_ts_ ## kind *buf_ptrs[] = {\
      (grn_ts_ ## kind *)node->bufs[0].ptr,\
      (grn_ts_ ## kind *)node->bufs[1].ptr\
    };\
    for (i = 0; i < n_in; i++) {\
      if (grn_ts_op_ ## type ## _ ## kind(buf_ptrs[0][i], buf_ptrs[1][i])) {\
        out[count++] = in[i];\
      }\
    }\
    *n_out = count;\
    return GRN_SUCCESS;\
  }
#define GRN_TS_OP_CHK_FILTER_VECTOR_CASE(type, KIND, kind)\
  GRN_TS_OP_CHK_FILTER_CASE(type, KIND ## _VECTOR, kind ## _vector)
#define GRN_TS_OP_CHK_FILTER(type)\
  size_t i, count = 0;\
  for (i = 0; i < 2; i++) {\
    grn_rc rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[i], in, n_in,\
                                                 &node->bufs[i]);\
    if (rc != GRN_SUCCESS) {\
      return rc;\
    }\
  }\
  switch (node->args[0]->data_kind) {\
    GRN_TS_OP_CHK_FILTER_CASE(type, BOOL, bool)\
    GRN_TS_OP_CHK_FILTER_CASE(type, INT, int)\
    GRN_TS_OP_CHK_FILTER_CASE(type, FLOAT, float)\
    GRN_TS_OP_CHK_FILTER_CASE(type, TIME, time)\
    GRN_TS_OP_CHK_FILTER_CASE(type, TEXT, text)\
    GRN_TS_OP_CHK_FILTER_CASE(type, GEO, geo)\
    GRN_TS_OP_CHK_FILTER_CASE(type, REF, ref)\
    GRN_TS_OP_CHK_FILTER_VECTOR_CASE(type, BOOL, bool)\
    GRN_TS_OP_CHK_FILTER_VECTOR_CASE(type, INT, int)\
    GRN_TS_OP_CHK_FILTER_VECTOR_CASE(type, FLOAT, float)\
    GRN_TS_OP_CHK_FILTER_VECTOR_CASE(type, TIME, time)\
    GRN_TS_OP_CHK_FILTER_VECTOR_CASE(type, TEXT, text)\
    GRN_TS_OP_CHK_FILTER_VECTOR_CASE(type, GEO, geo)\
    GRN_TS_OP_CHK_FILTER_VECTOR_CASE(type, REF, ref)\
    default: {\
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",\
                        node->args[0]->data_kind);\
    }\
  }
/* grn_ts_op_equal_filter() filters records. */
static grn_rc
grn_ts_op_equal_filter(grn_ctx *ctx, grn_ts_expr_op_node *node,
                       const grn_ts_record *in, size_t n_in,
                       grn_ts_record *out, size_t *n_out)
{
  GRN_TS_OP_CHK_FILTER(equal)
}

/* grn_ts_op_not_equal_filter() filters records. */
static grn_rc
grn_ts_op_not_equal_filter(grn_ctx *ctx, grn_ts_expr_op_node *node,
                           const grn_ts_record *in, size_t n_in,
                           grn_ts_record *out, size_t *n_out)
{
  GRN_TS_OP_CHK_FILTER(not_equal)
}
#undef GRN_TS_OP_CHK_FILTER
#undef GRN_TS_OP_CHK_FILTER_VECTOR_CASE
#undef GRN_TS_OP_CHK_FILTER_CASE

#define GRN_TS_OP_CMP_FILTER_CASE(type, KIND, kind)\
  case GRN_TS_ ## KIND: {\
    grn_ts_ ## kind *buf_ptrs[] = {\
      (grn_ts_ ## kind *)node->bufs[0].ptr,\
      (grn_ts_ ## kind *)node->bufs[1].ptr\
    };\
    for (i = 0; i < n_in; i++) {\
      if (grn_ts_op_ ## type ## _ ## kind(buf_ptrs[0][i], buf_ptrs[1][i])) {\
        out[count++] = in[i];\
      }\
    }\
    *n_out = count;\
    return GRN_SUCCESS;\
  }
#define GRN_TS_OP_CMP_FILTER_VECTOR_CASE(type, KIND, kind)\
  GRN_TS_OP_CMP_FILTER_CASE(type, KIND ## _VECTOR, kind ## _vector)
#define GRN_TS_OP_CMP_FILTER(type)\
  size_t i, count = 0;\
  for (i = 0; i < 2; i++) {\
    grn_rc rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[i], in, n_in,\
                                                 &node->bufs[i]);\
    if (rc != GRN_SUCCESS) {\
      return rc;\
    }\
  }\
  switch (node->args[0]->data_kind) {\
    GRN_TS_OP_CMP_FILTER_CASE(type, INT, int)\
    GRN_TS_OP_CMP_FILTER_CASE(type, FLOAT, float)\
    GRN_TS_OP_CMP_FILTER_CASE(type, TIME, time)\
    GRN_TS_OP_CMP_FILTER_CASE(type, TEXT, text)\
    GRN_TS_OP_CMP_FILTER_VECTOR_CASE(type, INT, int)\
    GRN_TS_OP_CMP_FILTER_VECTOR_CASE(type, FLOAT, float)\
    GRN_TS_OP_CMP_FILTER_VECTOR_CASE(type, TIME, time)\
    GRN_TS_OP_CMP_FILTER_VECTOR_CASE(type, TEXT, text)\
    default: {\
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",\
                        node->args[0]->data_kind);\
    }\
  }
/* grn_ts_op_less_filter() filters records. */
static grn_rc
grn_ts_op_less_filter(grn_ctx *ctx, grn_ts_expr_op_node *node,
                      const grn_ts_record *in, size_t n_in,
                      grn_ts_record *out, size_t *n_out)
{
  GRN_TS_OP_CMP_FILTER(less)
}

/* grn_ts_op_less_equal_filter() filters records. */
static grn_rc
grn_ts_op_less_equal_filter(grn_ctx *ctx, grn_ts_expr_op_node *node,
                            const grn_ts_record *in, size_t n_in,
                            grn_ts_record *out, size_t *n_out)
{
  GRN_TS_OP_CMP_FILTER(less_equal)
}

/* grn_ts_op_greater_filter() filters records. */
static grn_rc
grn_ts_op_greater_filter(grn_ctx *ctx, grn_ts_expr_op_node *node,
                         const grn_ts_record *in, size_t n_in,
                         grn_ts_record *out, size_t *n_out)
{
  GRN_TS_OP_CMP_FILTER(greater)
}

/* grn_ts_op_greater_equal_filter() filters records. */
static grn_rc
grn_ts_op_greater_equal_filter(grn_ctx *ctx, grn_ts_expr_op_node *node,
                               const grn_ts_record *in, size_t n_in,
                               grn_ts_record *out, size_t *n_out)
{
  GRN_TS_OP_CMP_FILTER(greater_equal)
}
#undef GRN_TS_OP_CMP_FILTER
#undef GRN_TS_OP_CMP_FILTER_VECTOR_CASE
#undef GRN_TS_OP_CMP_FILTER_CASE

#define GRN_TS_OP_MATCH_FILTER_CASE(type, KIND, kind)\
  case GRN_TS_ ## KIND: {\
    grn_ts_ ## kind *buf_ptrs[] = {\
      (grn_ts_ ## kind *)node->bufs[0].ptr,\
      (grn_ts_ ## kind *)node->bufs[1].ptr\
    };\
    for (i = 0; i < n_in; i++) {\
      if (grn_ts_op_ ## type ## _ ## kind(buf_ptrs[0][i], buf_ptrs[1][i])) {\
        out[count++] = in[i];\
      }\
    }\
    *n_out = count;\
    return GRN_SUCCESS;\
  }

#define GRN_TS_OP_MATCH_FILTER(type)\
  size_t i, count = 0;\
  grn_ts_text *buf_ptrs[2];\
  for (i = 0; i < 2; i++) {\
    grn_rc rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[i], in, n_in,\
                                                 &node->bufs[i]);\
    if (rc != GRN_SUCCESS) {\
      return rc;\
    }\
  }\
  buf_ptrs[0] = (grn_ts_text *)node->bufs[0].ptr;\
  buf_ptrs[1] = (grn_ts_text *)node->bufs[1].ptr;\
  for (i = 0; i < n_in; i++) {\
    if (grn_ts_op_ ## type(buf_ptrs[0][i], buf_ptrs[1][i])) {\
      out[count++] = in[i];\
    }\
  }\
  *n_out = count;\
  return GRN_SUCCESS;\
/* grn_ts_op_match_filter() filters records. */
static grn_rc
grn_ts_op_match_filter(grn_ctx *ctx, grn_ts_expr_op_node *node,
                       const grn_ts_record *in, size_t n_in,
                       grn_ts_record *out, size_t *n_out)
{
  GRN_TS_OP_MATCH_FILTER(match)
}

/* grn_ts_op_prefix_match_filter() filters records. */
static grn_rc
grn_ts_op_prefix_match_filter(grn_ctx *ctx, grn_ts_expr_op_node *node,
                              const grn_ts_record *in, size_t n_in,
                              grn_ts_record *out, size_t *n_out)
{
  GRN_TS_OP_MATCH_FILTER(prefix_match)
}

/* grn_ts_op_suffix_match_filter() filters records. */
static grn_rc
grn_ts_op_suffix_match_filter(grn_ctx *ctx, grn_ts_expr_op_node *node,
                              const grn_ts_record *in, size_t n_in,
                              grn_ts_record *out, size_t *n_out)
{
  GRN_TS_OP_MATCH_FILTER(suffix_match)
}
#undef GRN_TS_OP_MATCH_FILTER

/* grn_ts_expr_op_node_filter() filters records. */
static grn_rc
grn_ts_expr_op_node_filter(grn_ctx *ctx, grn_ts_expr_op_node *node,
                           grn_ts_record *in, size_t n_in,
                           grn_ts_record *out, size_t *n_out)
{
  switch (node->op_type) {
    case GRN_TS_OP_LOGICAL_NOT: {
      return grn_ts_op_logical_not_filter(ctx, node, in, n_in, out, n_out);
    }
    case GRN_TS_OP_BITWISE_NOT: {
      return grn_ts_op_bitwise_not_filter(ctx, node, in, n_in, out, n_out);
    }
    case GRN_TS_OP_LOGICAL_AND: {
      return grn_ts_op_logical_and_filter(ctx, node, in, n_in, out, n_out);
    }
    case GRN_TS_OP_LOGICAL_OR: {
      return grn_ts_op_logical_or_filter(ctx, node, in, n_in, out, n_out);
    }
    case GRN_TS_OP_LOGICAL_SUB: {
      return grn_ts_op_logical_sub_filter(ctx, node, in, n_in, out, n_out);
    }
    case GRN_TS_OP_BITWISE_AND: {
      return grn_ts_op_bitwise_and_filter(ctx, node, in, n_in, out, n_out);
    }
    case GRN_TS_OP_BITWISE_OR: {
      return grn_ts_op_bitwise_or_filter(ctx, node, in, n_in, out, n_out);
    }
    case GRN_TS_OP_BITWISE_XOR: {
      return grn_ts_op_bitwise_xor_filter(ctx, node, in, n_in, out, n_out);
    }
    case GRN_TS_OP_EQUAL: {
      return grn_ts_op_equal_filter(ctx, node, in, n_in, out, n_out);
    }
    case GRN_TS_OP_NOT_EQUAL: {
      return grn_ts_op_not_equal_filter(ctx, node, in, n_in, out, n_out);
    }
    case GRN_TS_OP_LESS: {
      return grn_ts_op_less_filter(ctx, node, in, n_in, out, n_out);
    }
    case GRN_TS_OP_LESS_EQUAL: {
      return grn_ts_op_less_equal_filter(ctx, node, in, n_in, out, n_out);
    }
    case GRN_TS_OP_GREATER: {
      return grn_ts_op_greater_filter(ctx, node, in, n_in, out, n_out);
    }
    case GRN_TS_OP_GREATER_EQUAL: {
      return grn_ts_op_greater_equal_filter(ctx, node, in, n_in, out, n_out);
    }
    case GRN_TS_OP_MATCH: {
      return grn_ts_op_match_filter(ctx, node, in, n_in, out, n_out);
    }
    case GRN_TS_OP_PREFIX_MATCH: {
      return grn_ts_op_prefix_match_filter(ctx, node, in, n_in, out, n_out);
    }
    case GRN_TS_OP_SUFFIX_MATCH: {
      return grn_ts_op_suffix_match_filter(ctx, node, in, n_in, out, n_out);
    }
    // TODO: Add operators.
    default: {
      GRN_TS_ERR_RETURN(GRN_OPERATION_NOT_SUPPORTED,
                        "operator not supported: %d", node->op_type);
    }
  }
}

#define GRN_TS_OP_SIGN_ADJUST(type)\
  size_t i;\
  grn_ts_float *buf_ptr;\
  grn_rc rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[0], io, n_io,\
                                               &node->bufs[0]);\
  if (rc != GRN_SUCCESS) {\
    return rc;\
  }\
  buf_ptr = (grn_ts_float *)node->bufs[0].ptr;\
  for (i = 0; i < n_io; i++) {\
    grn_ts_float result = grn_ts_op_ ## type ## _float(buf_ptr[i]);\
    io[i].score = (grn_ts_score)result;\
    if (!isfinite(io[i].score)) {\
      GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid score: %g", result);\
    }\
  }\
  return GRN_SUCCESS;
/* grn_ts_op_positive_adjust() updates scores. */
static grn_rc
grn_ts_op_positive_adjust(grn_ctx *ctx, grn_ts_expr_op_node *node,
                          grn_ts_record *io, size_t n_io)
{
  GRN_TS_OP_SIGN_ADJUST(positive)
}

/* grn_ts_op_negative_adjust() updates scores. */
static grn_rc
grn_ts_op_negative_adjust(grn_ctx *ctx, grn_ts_expr_op_node *node,
                          grn_ts_record *io, size_t n_io)
{
  GRN_TS_OP_SIGN_ADJUST(negative)
}
#undef GRN_TS_OP_SIGN_ADJUST

/* grn_ts_op_float_adjust() updates scores. */
static grn_rc
grn_ts_op_float_adjust(grn_ctx *ctx, grn_ts_expr_op_node *node,
                       grn_ts_record *io, size_t n_io)
{
  size_t i;
  grn_ts_int *buf_ptr;
  grn_rc rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[0], io, n_io,
                                               &node->bufs[0]);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  buf_ptr = (grn_ts_int *)node->bufs[0].ptr;
  for (i = 0; i < n_io; i++) {
    grn_ts_float result;
    rc = grn_ts_op_float(ctx, buf_ptr[i], &result);
    io[i].score = (grn_ts_score)result;
    if (!isfinite(io[i].score)) {
      GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid score: %g", result);
    }
  }
  return GRN_SUCCESS;
}

#define GRN_TS_OP_ARITH_ADJUST(type)\
  grn_rc rc;\
  size_t i;\
  grn_ts_float *buf_ptrs[2];\
  for (i = 0; i < 2; i++) {\
    rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[i], io, n_io,\
                                          &node->bufs[i]);\
    if (rc != GRN_SUCCESS) {\
      return rc;\
    }\
  }\
  buf_ptrs[0] = (grn_ts_float *)node->bufs[0].ptr;\
  buf_ptrs[1] = (grn_ts_float *)node->bufs[1].ptr;\
  for (i = 0; i < n_io; i++) {\
    grn_ts_float result;\
    rc = grn_ts_op_ ## type ## _float_float(ctx, buf_ptrs[0][i],\
                                            buf_ptrs[1][i], &result);\
    io[i].score = (grn_ts_score)result;\
    if (!isfinite(io[i].score)) {\
      GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid score: %g", result);\
    }\
  }\
  return GRN_SUCCESS;
/* grn_ts_op_plus_adjust() updates scores. */
static grn_rc
grn_ts_op_plus_adjust(grn_ctx *ctx, grn_ts_expr_op_node *node,
                      grn_ts_record *io, size_t n_io)
{
  GRN_TS_OP_ARITH_ADJUST(plus)
}

/* grn_ts_op_minus_adjust() updates scores. */
static grn_rc
grn_ts_op_minus_adjust(grn_ctx *ctx, grn_ts_expr_op_node *node,
                       grn_ts_record *io, size_t n_io)
{
  GRN_TS_OP_ARITH_ADJUST(minus)
}

/* grn_ts_op_multiplication_adjust() updates scores. */
static grn_rc
grn_ts_op_multiplication_adjust(grn_ctx *ctx, grn_ts_expr_op_node *node,
                                grn_ts_record *io, size_t n_io)
{
  GRN_TS_OP_ARITH_ADJUST(multiplication)
}

/* grn_ts_op_division_adjust() updates scores. */
static grn_rc
grn_ts_op_division_adjust(grn_ctx *ctx, grn_ts_expr_op_node *node,
                          grn_ts_record *io, size_t n_io)
{
  GRN_TS_OP_ARITH_ADJUST(division)
}

/* grn_ts_op_modulus_adjust() updates scores. */
static grn_rc
grn_ts_op_modulus_adjust(grn_ctx *ctx, grn_ts_expr_op_node *node,
                         grn_ts_record *io, size_t n_io)
{
  GRN_TS_OP_ARITH_ADJUST(modulus)
}
#undef GRN_TS_OP_ARITH_ADJUST

/* grn_ts_expr_op_node_adjust() updates scores. */
static grn_rc
grn_ts_expr_op_node_adjust(grn_ctx *ctx, grn_ts_expr_op_node *node,
                           grn_ts_record *io, size_t n_io)
{
  switch (node->op_type) {
    case GRN_TS_OP_POSITIVE: {
      return grn_ts_op_positive_adjust(ctx, node, io, n_io);
    }
    case GRN_TS_OP_NEGATIVE: {
      return grn_ts_op_negative_adjust(ctx, node, io, n_io);
    }
    case GRN_TS_OP_FLOAT: {
      return grn_ts_op_float_adjust(ctx, node, io, n_io);
    }
    case GRN_TS_OP_PLUS: {
      return grn_ts_op_plus_adjust(ctx, node, io, n_io);
    }
    case GRN_TS_OP_MINUS: {
      return grn_ts_op_minus_adjust(ctx, node, io, n_io);
    }
    case GRN_TS_OP_MULTIPLICATION: {
      return grn_ts_op_multiplication_adjust(ctx, node, io, n_io);
    }
    case GRN_TS_OP_DIVISION: {
      return grn_ts_op_division_adjust(ctx, node, io, n_io);
    }
    case GRN_TS_OP_MODULUS: {
      return grn_ts_op_modulus_adjust(ctx, node, io, n_io);
    }
    // TODO: Add operators.
    default: {
      GRN_TS_ERR_RETURN(GRN_OPERATION_NOT_SUPPORTED,
                        "operator not supported: %d", node->op_type);
    }
  }
}

/*-------------------------------------------------------------
 * grn_ts_expr_bridge_node.
 */

enum { GRN_TS_EXPR_BRIDGE_NODE_N_BUFS = 2 };

typedef struct {
  GRN_TS_EXPR_NODE_COMMON_MEMBERS
  grn_ts_expr_node *src;
  grn_ts_expr_node *dest;
  grn_ts_buf bufs[GRN_TS_EXPR_BRIDGE_NODE_N_BUFS];
} grn_ts_expr_bridge_node;

/* grn_ts_expr_bridge_node_init() initializes a node. */
static void
grn_ts_expr_bridge_node_init(grn_ctx *ctx, grn_ts_expr_bridge_node *node)
{
  size_t i;
  memset(node, 0, sizeof(*node));
  node->type = GRN_TS_EXPR_BRIDGE_NODE;
  node->src = NULL;
  node->dest = NULL;
  for (i = 0; i < GRN_TS_EXPR_BRIDGE_NODE_N_BUFS; i++) {
    grn_ts_buf_init(ctx, &node->bufs[i]);
  }
}

/* grn_ts_expr_bridge_node_fin() finalizes a node. */
static void
grn_ts_expr_bridge_node_fin(grn_ctx *ctx, grn_ts_expr_bridge_node *node)
{
  size_t i;
  for (i = 0; i < GRN_TS_EXPR_BRIDGE_NODE_N_BUFS; i++) {
    grn_ts_buf_fin(ctx, &node->bufs[i]);
  }
  if (node->dest) {
    grn_ts_expr_node_close(ctx, node->dest);
  }
  if (node->src) {
    grn_ts_expr_node_close(ctx, node->src);
  }
}

grn_rc
grn_ts_expr_bridge_node_open(grn_ctx *ctx, grn_ts_expr_node *src,
                             grn_ts_expr_node *dest, grn_ts_expr_node **node)
{
  grn_ts_expr_bridge_node *new_node = GRN_MALLOCN(grn_ts_expr_bridge_node, 1);
  if (!new_node) {
    GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
                      "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1",
                      sizeof(grn_ts_expr_bridge_node));
  }
  grn_ts_expr_bridge_node_init(ctx, new_node);
  new_node->data_kind = dest->data_kind;
  new_node->data_type = dest->data_type;
  new_node->src = src;
  new_node->dest = dest;
  *node = (grn_ts_expr_node *)new_node;
  return GRN_SUCCESS;
}

/* grn_ts_expr_bridge_node_close() destroys a node. */
static void
grn_ts_expr_bridge_node_close(grn_ctx *ctx, grn_ts_expr_bridge_node *node)
{
  grn_ts_expr_bridge_node_fin(ctx, node);
  GRN_FREE(node);
}

/* grn_ts_expr_bridge_node_evaluate() evaluates a bridge. */
static grn_rc
grn_ts_expr_bridge_node_evaluate(grn_ctx *ctx, grn_ts_expr_bridge_node *node,
                                 const grn_ts_record *in, size_t n_in,
                                 void *out)
{
  grn_ts_record *tmp;
  grn_rc rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->src, in, n_in,
                                               &node->bufs[0]);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  tmp = (grn_ts_record *)node->bufs[0].ptr;
  return grn_ts_expr_node_evaluate(ctx, node->dest, tmp, n_in, out);
}

/* grn_ts_expr_bridge_node_filter() filters records. */
static grn_rc
grn_ts_expr_bridge_node_filter(grn_ctx *ctx, grn_ts_expr_bridge_node *node,
                               grn_ts_record *in, size_t n_in,
                               grn_ts_record *out, size_t *n_out)
{
  size_t i, count;
  grn_ts_bool *values;
  grn_ts_record *tmp;
  grn_rc rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->src, in, n_in,
                                               &node->bufs[0]);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  tmp = (grn_ts_record *)node->bufs[0].ptr;
  rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->dest, in, n_in,
                                        &node->bufs[1]);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  values = (grn_ts_bool *)&node->bufs[1].ptr;
  for (i = 0, count = 0; i < n_in; i++) {
    if (values[i]) {
      out[count++] = in[i];
    }
  }
  *n_out = count;
  return GRN_SUCCESS;
}

/* grn_ts_expr_bridge_node_adjust() updates scores. */
static grn_rc
grn_ts_expr_bridge_node_adjust(grn_ctx *ctx, grn_ts_expr_bridge_node *node,
                               grn_ts_record *io, size_t n_io)
{
  size_t i;
  grn_ts_record *tmp;
  grn_rc rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->src, io, n_io,
                                               &node->bufs[0]);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  tmp = (grn_ts_record *)node->bufs[0].ptr;
  rc = grn_ts_expr_node_adjust(ctx, node->dest, tmp, n_io);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  for (i = 0; i < n_io; i++) {
    io[i].score = tmp[i].score;
  }
  return GRN_SUCCESS;
}

/*-------------------------------------------------------------
 * grn_ts_expr_node.
 */

#define GRN_TS_EXPR_NODE_CLOSE_CASE(TYPE, type)\
  case GRN_TS_EXPR_ ## TYPE ## _NODE: {\
    grn_ts_expr_ ## type ## _node *type ## _node;\
    type ## _node = (grn_ts_expr_ ## type ## _node *)node;\
    grn_ts_expr_ ## type ## _node_close(ctx, type ## _node);\
    return;\
  }
void
grn_ts_expr_node_close(grn_ctx *ctx, grn_ts_expr_node *node)
{
  switch (node->type) {
    GRN_TS_EXPR_NODE_CLOSE_CASE(ID, id)
    GRN_TS_EXPR_NODE_CLOSE_CASE(SCORE, score)
    GRN_TS_EXPR_NODE_CLOSE_CASE(KEY, key)
    GRN_TS_EXPR_NODE_CLOSE_CASE(VALUE, value)
    GRN_TS_EXPR_NODE_CLOSE_CASE(CONST, const)
    GRN_TS_EXPR_NODE_CLOSE_CASE(COLUMN, column)
    GRN_TS_EXPR_NODE_CLOSE_CASE(OP, op)
    GRN_TS_EXPR_NODE_CLOSE_CASE(BRIDGE, bridge)
  }
}
#undef GRN_TS_EXPR_NODE_CLOSE_CASE

/* grn_ts_expr_node_deref_once() resolves a reference. */
static grn_rc
grn_ts_expr_node_deref_once(grn_ctx *ctx, grn_ts_expr_node *in,
                            grn_ts_expr_node **out)
{
  grn_rc rc;
  grn_id table_id = in->data_type;
  grn_ts_expr_node *key_node, *bridge_node;
  grn_obj *table = grn_ctx_at(ctx, table_id);
  if (!table) {
    GRN_TS_ERR_RETURN(GRN_UNKNOWN_ERROR, "grn_ctx_at failed: %d", table_id);
  }
  if (!grn_ts_obj_is_table(ctx, table)) {
    grn_obj_unlink(ctx, table);
    GRN_TS_ERR_RETURN(GRN_UNKNOWN_ERROR, "not table: %d", table_id);
  }
  rc = grn_ts_expr_key_node_open(ctx, table, &key_node);
  grn_obj_unlink(ctx, table);
  if (rc != GRN_SUCCESS) {
    return rc;
  }
  rc = grn_ts_expr_bridge_node_open(ctx, in, key_node, &bridge_node);
  if (rc != GRN_SUCCESS) {
    grn_ts_expr_node_close(ctx, key_node);
    return rc;
  }
  *out = bridge_node;
  return GRN_SUCCESS;
}

grn_rc
grn_ts_expr_node_deref(grn_ctx *ctx, grn_ts_expr_node **node_ptr)
{
  grn_ts_expr_node *node = *node_ptr, **in_ptr = NULL;
  while ((node->data_kind & ~GRN_TS_VECTOR_FLAG) == GRN_TS_REF) {
    grn_ts_expr_node *new_node;
    grn_rc rc = grn_ts_expr_node_deref_once(ctx, node, &new_node);
    if (rc != GRN_SUCCESS) {
      if (in_ptr) {
        *in_ptr = NULL;
        grn_ts_expr_node_close(ctx, node);
      }
      return rc;
    }
    if (node == *node_ptr) {
      grn_ts_expr_bridge_node *bridge_node;
      bridge_node = (grn_ts_expr_bridge_node *)new_node;
      if (bridge_node->src != node) {
        GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "broken bridge node");
      }
      in_ptr = &bridge_node->src;
    }
    node = new_node;
  }
  *node_ptr = node;
  return GRN_SUCCESS;
}

#define GRN_TS_EXPR_NODE_EVALUATE_CASE(TYPE, type)\
  case GRN_TS_EXPR_ ## TYPE ## _NODE: {\
    grn_ts_expr_ ## type ## _node *type ## _node;\
    type ## _node = (grn_ts_expr_ ## type ## _node *)node;\
    return grn_ts_expr_ ## type ## _node_evaluate(ctx, type ## _node,\
                                                  in, n_in, out);\
  }
grn_rc
grn_ts_expr_node_evaluate(grn_ctx *ctx, grn_ts_expr_node *node,
                          const grn_ts_record *in, size_t n_in, void *out)
{
  switch (node->type) {
    GRN_TS_EXPR_NODE_EVALUATE_CASE(ID, id)
    GRN_TS_EXPR_NODE_EVALUATE_CASE(SCORE, score)
    GRN_TS_EXPR_NODE_EVALUATE_CASE(KEY, key)
    GRN_TS_EXPR_NODE_EVALUATE_CASE(VALUE, value)
    GRN_TS_EXPR_NODE_EVALUATE_CASE(CONST, const)
    GRN_TS_EXPR_NODE_EVALUATE_CASE(COLUMN, column)
    GRN_TS_EXPR_NODE_EVALUATE_CASE(OP, op)
    GRN_TS_EXPR_NODE_EVALUATE_CASE(BRIDGE, bridge)
    default: {
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT,
                        "invalid node type: %d", node->type);
    }
  }
}
#undef GRN_TS_EXPR_NODE_EVALUATE_CASE

#define GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_CASE(KIND, kind)\
  case GRN_TS_ ## KIND: {\
    grn_rc rc = grn_ts_buf_reserve(ctx, out, sizeof(grn_ts_ ## kind) * n_in);\
    if (rc != GRN_SUCCESS) {\
      return rc;\
    }\
    return grn_ts_expr_node_evaluate(ctx, node, in, n_in, out->ptr);\
  }
#define GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_VECTOR_CASE(KIND, kind)\
  GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_CASE(KIND ## _VECTOR, kind ## _vector)
grn_rc
grn_ts_expr_node_evaluate_to_buf(grn_ctx *ctx, grn_ts_expr_node *node,
                                 const grn_ts_record *in, size_t n_in,
                                 grn_ts_buf *out)
{
  switch (node->data_kind) {
    GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_CASE(BOOL, bool)
    GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_CASE(INT, int)
    GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_CASE(FLOAT, float)
    GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_CASE(TIME, time)
    GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_CASE(TEXT, text)
    GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_CASE(GEO, geo)
    GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_CASE(REF, ref)
    GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_VECTOR_CASE(BOOL, bool)
    GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_VECTOR_CASE(INT, int)
    GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_VECTOR_CASE(FLOAT, float)
    GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_VECTOR_CASE(TIME, time)
    GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_VECTOR_CASE(TEXT, text)
    GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_VECTOR_CASE(GEO, geo)
    GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_VECTOR_CASE(REF, ref)
    default: {
      GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT,
                        "invalid data kind: %d", node->data_kind);
    }
  }
}
#undef GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_VECTOR_CASE
#undef GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_CASE

#define GRN_TS_EXPR_NODE_FILTER_CASE(TYPE, type)\
  case GRN_TS_EXPR_ ## TYPE ## _NODE: {\
    grn_ts_expr_ ## type ## _node *type ## _node;\
    type ## _node = (grn_ts_expr_ ## type ## _node *)node;\
    return grn_ts_expr_ ## type ## _node_filter(ctx, type ## _node,\
                                                in, n_in, out, n_out);\
  }
grn_rc
grn_ts_expr_node_filter(grn_ctx *ctx, grn_ts_expr_node *node,
                        grn_ts_record *in, size_t n_in,
                        grn_ts_record *out, size_t *n_out)
{
  if (node->data_kind != GRN_TS_BOOL) {
    GRN_TS_ERR_RETURN(GRN_OPERATION_NOT_SUPPORTED,
                      "invalid data kind: %d", node->data_kind);
  }
  switch (node->type) {
    GRN_TS_EXPR_NODE_FILTER_CASE(KEY, key)
    GRN_TS_EXPR_NODE_FILTER_CASE(VALUE, value)
    GRN_TS_EXPR_NODE_FILTER_CASE(CONST, const)
    GRN_TS_EXPR_NODE_FILTER_CASE(COLUMN, column)
    GRN_TS_EXPR_NODE_FILTER_CASE(OP, op)
    GRN_TS_EXPR_NODE_FILTER_CASE(BRIDGE, bridge)
    default: {
      GRN_TS_ERR_RETURN(GRN_OPERATION_NOT_SUPPORTED,
                        "invalid node type: %d", node->type);
    }
  }
}
#undef GRN_TS_EXPR_NODE_FILTER_CASE

#define GRN_TS_EXPR_NODE_ADJUST_CASE(TYPE, type)\
  case GRN_TS_EXPR_ ## TYPE ## _NODE: {\
    grn_ts_expr_ ## type ## _node *type ## _node;\
    type ## _node = (grn_ts_expr_ ## type ## _node *)node;\
    return grn_ts_expr_ ## type ## _node_adjust(ctx, type ## _node, io, n_io);\
  }
grn_rc
grn_ts_expr_node_adjust(grn_ctx *ctx, grn_ts_expr_node *node,
                        grn_ts_record *io, size_t n_io)
{
  if (node->data_kind != GRN_TS_FLOAT) {
    GRN_TS_ERR_RETURN(GRN_OPERATION_NOT_SUPPORTED,
                      "invalid data kind: %d", node->data_kind);
  }
  switch (node->type) {
    GRN_TS_EXPR_NODE_ADJUST_CASE(SCORE, score)
    GRN_TS_EXPR_NODE_ADJUST_CASE(KEY, key)
    GRN_TS_EXPR_NODE_ADJUST_CASE(VALUE, value)
    GRN_TS_EXPR_NODE_ADJUST_CASE(CONST, const)
    GRN_TS_EXPR_NODE_ADJUST_CASE(COLUMN, column)
    GRN_TS_EXPR_NODE_ADJUST_CASE(OP, op)
    GRN_TS_EXPR_NODE_ADJUST_CASE(BRIDGE, bridge)
    default: {
      GRN_TS_ERR_RETURN(GRN_OPERATION_NOT_SUPPORTED,
                        "invalid node type: %d", node->type);
    }
  }
}
#undef GRN_TS_EXPR_NODE_ADJUST_CASE
