@Override
  public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {
    if (arguments.length != 2) {
      throw new UDFArgumentLengthException(
          "ICEBERG_BUCKET requires 2 arguments (value, bucketCount), but got " + arguments.length);
    }

    numBuckets = getNumBuckets(arguments[1]);

    if (arguments[0].getCategory() != ObjectInspector.Category.PRIMITIVE) {
      throw new UDFArgumentException(
          "ICEBERG_BUCKET first argument takes primitive types, got " + argumentOI.getTypeName());
    }
    argumentOI = (PrimitiveObjectInspector) arguments[0];

    PrimitiveObjectInspector.PrimitiveCategory inputType = argumentOI.getPrimitiveCategory();
    ObjectInspector outputOI;
    switch (inputType) {
      case CHAR:
      case VARCHAR:
      case STRING:
        converter = new PrimitiveObjectInspectorConverter.StringConverter(argumentOI);
        Function<Object, Integer> stringTransform = Transforms.bucket(numBuckets).bind(Types.StringType.get());
        evaluator = arg -> {
          String val = (String) converter.convert(arg.get());
          result.set(stringTransform.apply(val));
        };
        break;

      case BINARY:
        converter = new PrimitiveObjectInspectorConverter.BinaryConverter(argumentOI,
            PrimitiveObjectInspectorFactory.writableBinaryObjectInspector);
        Function<Object, Integer> byteBufferTransform = Transforms.bucket(numBuckets).bind(Types.BinaryType.get());
        evaluator = arg -> {
          BytesWritable val = (BytesWritable) converter.convert(arg.get());
          ByteBuffer byteBuffer = ByteBuffer.wrap(val.getBytes(), 0, val.getLength());
          result.set(byteBufferTransform.apply(byteBuffer));
        };
        break;

      case INT:
        converter = new PrimitiveObjectInspectorConverter.IntConverter(argumentOI,
            PrimitiveObjectInspectorFactory.writableIntObjectInspector);
        Function<Object, Integer> intTransform = Transforms.bucket(numBuckets).bind(Types.IntegerType.get());
        evaluator = arg -> {
          IntWritable val = (IntWritable) converter.convert(arg.get());
          result.set(intTransform.apply(val.get()));
        };
        break;

      case LONG:
        converter = new PrimitiveObjectInspectorConverter.LongConverter(argumentOI,
            PrimitiveObjectInspectorFactory.writableLongObjectInspector);
        Function<Object, Integer> longTransform = Transforms.bucket(numBuckets).bind(Types.LongType.get());
        evaluator = arg -> {
          LongWritable val = (LongWritable) converter.convert(arg.get());
          result.set(longTransform.apply(val.get()));
        };
        break;

      case DECIMAL:
        DecimalTypeInfo decimalTypeInfo = (DecimalTypeInfo) TypeInfoUtils.getTypeInfoFromObjectInspector(argumentOI);
        Type.PrimitiveType decimalIcebergType = Types.DecimalType.of(decimalTypeInfo.getPrecision(),
            decimalTypeInfo.getScale());

        converter = new PrimitiveObjectInspectorConverter.HiveDecimalConverter(argumentOI,
            new WritableHiveDecimalObjectInspector(decimalTypeInfo));
        Function<Object, Integer> bigDecimalTransform = Transforms.bucket(numBuckets).bind(decimalIcebergType);
        evaluator = arg -> {
          HiveDecimalWritable val = (HiveDecimalWritable) converter.convert(arg.get());
          result.set(bigDecimalTransform.apply(val.getHiveDecimal().bigDecimalValue()));
        };
        break;

      case FLOAT:
        converter = new PrimitiveObjectInspectorConverter.FloatConverter(argumentOI,
            PrimitiveObjectInspectorFactory.writableFloatObjectInspector);
        Function<Object, Integer> floatTransform = Transforms.bucket(numBuckets).bind(Types.FloatType.get());
        evaluator = arg -> {
          FloatWritable val = (FloatWritable) converter.convert(arg.get());
          result.set(floatTransform.apply(val.get()));
        };
        break;

      case DOUBLE:
        converter = new PrimitiveObjectInspectorConverter.DoubleConverter(argumentOI,
            PrimitiveObjectInspectorFactory.writableDoubleObjectInspector);
        Function<Object, Integer> doubleTransform = Transforms.bucket(numBuckets).bind(Types.DoubleType.get());
        evaluator = arg -> {
          DoubleWritable val = (DoubleWritable) converter.convert(arg.get());
          result.set(doubleTransform.apply(val.get()));
        };
        break;

      case DATE:
        converter = new PrimitiveObjectInspectorConverter.DateConverter(argumentOI,
          PrimitiveObjectInspectorFactory.writableDateObjectInspector);
        Function<Object, Integer> dateTransform = Transforms.bucket(numBuckets).bind(Types.DateType.get());
        evaluator = arg -> {
          DateWritableV2 val = (DateWritableV2) converter.convert(arg.get());
          result.set(dateTransform.apply(val.getDays()));
        };
        break;

      default:
        throw new UDFArgumentException(
            " ICEBERG_BUCKET() only takes STRING/CHAR/VARCHAR/BINARY/INT/LONG/DECIMAL/FLOAT/DOUBLE/DATE" +
                " types as first argument, got " + inputType);
    }

    outputOI = PrimitiveObjectInspectorFactory.writableIntObjectInspector;
    return outputOI;
  }
GenericUDFIcebergBucket - UDF that wraps around Iceberg's bucket transform function
iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/udf/GenericUDFIcebergBucket.java