diff --git a/c_glib/arrow-glib/decimal.cpp b/c_glib/arrow-glib/decimal.cpp index 5947868edd3bc..edc2af7a7e051 100644 --- a/c_glib/arrow-glib/decimal.cpp +++ b/c_glib/arrow-glib/decimal.cpp @@ -24,6 +24,24 @@ template struct DecimalConverter { }; +template <> struct DecimalConverter +{ + using ArrowType = arrow::Decimal64; + using GArrowType = GArrowDecimal64; + + GArrowType * + new_raw(std::shared_ptr *arrow_decimal64) + { + return garrow_decimal64_new_raw(arrow_decimal64); + } + + std::shared_ptr + get_raw(GArrowType *decimal64) + { + return garrow_decimal64_get_raw(decimal64); + } +}; + template <> struct DecimalConverter { using ArrowType = arrow::Decimal128; @@ -301,9 +319,11 @@ G_BEGIN_DECLS /** * SECTION: decimal * @section_id: decimal - * @title: 128-bit and 256-bit decimal classes + * @title: 64-bit, 128-bit and 256-bit decimal classes * @include: arrow-glib/arrow-glib.h * + * #GArrowDecimal64 is a 64-bit decimal class. + * * #GArrowDecimal128 is a 128-bit decimal class. * * #GArrowDecimal256 is a 256-bit decimal class. @@ -311,6 +331,404 @@ G_BEGIN_DECLS * Since: 0.10.0 */ +typedef struct GArrowDecimal64Private_ +{ + std::shared_ptr decimal64; +} GArrowDecimal64Private; + +enum { + PROP_DECIMAL64 = 1 +}; + +G_DEFINE_TYPE_WITH_PRIVATE(GArrowDecimal64, garrow_decimal64, G_TYPE_OBJECT) + +#define GARROW_DECIMAL64_GET_PRIVATE(obj) \ + static_cast( \ + garrow_decimal64_get_instance_private(GARROW_DECIMAL64(obj))) + +static void +garrow_decimal64_finalize(GObject *object) +{ + auto priv = GARROW_DECIMAL64_GET_PRIVATE(object); + + priv->decimal64.~shared_ptr(); + + G_OBJECT_CLASS(garrow_decimal64_parent_class)->finalize(object); +} + +static void +garrow_decimal64_set_property(GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + auto priv = GARROW_DECIMAL64_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_DECIMAL64: + priv->decimal64 = + *static_cast *>(g_value_get_pointer(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +garrow_decimal64_init(GArrowDecimal64 *object) +{ + auto priv = GARROW_DECIMAL64_GET_PRIVATE(object); + new (&priv->decimal64) std::shared_ptr; +} + +static void +garrow_decimal64_class_init(GArrowDecimal64Class *klass) +{ + auto gobject_class = G_OBJECT_CLASS(klass); + + gobject_class->finalize = garrow_decimal64_finalize; + gobject_class->set_property = garrow_decimal64_set_property; + + GParamSpec *spec; + spec = g_param_spec_pointer( + "decimal64", + "Decimal64", + "The raw std::shared *", + static_cast(G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property(gobject_class, PROP_DECIMAL64, spec); +} + +/** + * garrow_decimal64_new_string: + * @data: The data of the decimal. + * @error: (nullable): Return location for a #GError or %NULL. + * + * Returns: (nullable): + * A newly created #GArrowDecimal64 on success, %NULL on error. + * + * Since: 19.0.0 + */ +GArrowDecimal64 * +garrow_decimal64_new_string(const gchar *data, GError **error) +{ + return garrow_decimal_new_string(data, + error, + "[decimal64][new][string]"); +} + +/** + * garrow_decimal64_new_integer: + * @data: The data of the decimal. + * + * Returns: A newly created #GArrowDecimal64. + * + * Since: 19.0.0 + */ +GArrowDecimal64 * +garrow_decimal64_new_integer(const gint64 data) +{ + return garrow_decimal_new_integer(data); +} + +/** + * garrow_decimal64_copy: + * @decimal: The decimal to be copied. + * + * Returns: (transfer full): A copied #GArrowDecimal64. + * + * Since: 19.0.0 + */ +GArrowDecimal64 * +garrow_decimal64_copy(GArrowDecimal64 *decimal) +{ + return garrow_decimal_copy(decimal); +} + +/** + * garrow_decimal64_equal: + * @decimal: A #GArrowDecimal64. + * @other_decimal: A #GArrowDecimal64 to be compared. + * + * Returns: %TRUE if the decimal is equal to the other decimal, %FALSE + * otherwise. + * + * Since: 19.0.0 + */ +gboolean +garrow_decimal64_equal(GArrowDecimal64 *decimal, GArrowDecimal64 *other_decimal) +{ + return garrow_decimal_equal(decimal, other_decimal); +} + +/** + * garrow_decimal64_not_equal: + * @decimal: A #GArrowDecimal64. + * @other_decimal: A #GArrowDecimal64 to be compared. + * + * Returns: %TRUE if the decimal isn't equal to the other decimal, + * %FALSE otherwise. + * + * Since: 19.0.0 + */ +gboolean +garrow_decimal64_not_equal(GArrowDecimal64 *decimal, GArrowDecimal64 *other_decimal) +{ + return garrow_decimal_not_equal(decimal, other_decimal); +} + +/** + * garrow_decimal64_less_than: + * @decimal: A #GArrowDecimal64. + * @other_decimal: A #GArrowDecimal64 to be compared. + * + * Returns: %TRUE if the decimal is less than the other decimal, + * %FALSE otherwise. + * + * Since: 19.0.0 + */ +gboolean +garrow_decimal64_less_than(GArrowDecimal64 *decimal, GArrowDecimal64 *other_decimal) +{ + return garrow_decimal_less_than(decimal, other_decimal); +} + +/** + * garrow_decimal64_less_than_or_equal: + * @decimal: A #GArrowDecimal64. + * @other_decimal: A #GArrowDecimal64 to be compared. + * + * Returns: %TRUE if the decimal is less than the other decimal + * or equal to the other decimal, %FALSE otherwise. + * + * Since: 19.0.0 + */ +gboolean +garrow_decimal64_less_than_or_equal(GArrowDecimal64 *decimal, + GArrowDecimal64 *other_decimal) +{ + return garrow_decimal_less_than_or_equal(decimal, other_decimal); +} + +/** + * garrow_decimal64_greater_than: + * @decimal: A #GArrowDecimal64. + * @other_decimal: A #GArrowDecimal64 to be compared. + * + * Returns: %TRUE if the decimal is greater than the other decimal, + * %FALSE otherwise. + * + * Since: 19.0.0 + */ +gboolean +garrow_decimal64_greater_than(GArrowDecimal64 *decimal, GArrowDecimal64 *other_decimal) +{ + return garrow_decimal_greater_than(decimal, other_decimal); +} + +/** + * garrow_decimal64_greater_than_or_equal: + * @decimal: A #GArrowDecimal64. + * @other_decimal: A #GArrowDecimal64 to be compared. + * + * Returns: %TRUE if the decimal is greater than the other decimal + * or equal to the other decimal, %FALSE otherwise. + * + * Since: 19.0.0 + */ +gboolean +garrow_decimal64_greater_than_or_equal(GArrowDecimal64 *decimal, + GArrowDecimal64 *other_decimal) +{ + return garrow_decimal_greater_than_or_equal(decimal, other_decimal); +} + +/** + * garrow_decimal64_to_string_scale: + * @decimal: A #GArrowDecimal64. + * @scale: The scale of the decimal. + * + * Returns: The string representation of the decimal. + * + * It should be freed with g_free() when no longer needed. + * + * Since: 19.0.0 + */ +gchar * +garrow_decimal64_to_string_scale(GArrowDecimal64 *decimal, gint32 scale) +{ + return garrow_decimal_to_string_scale(decimal, scale); +} + +/** + * garrow_decimal64_to_string: + * @decimal: A #GArrowDecimal64. + * + * Returns: The string representation of the decimal. + * + * It should be freed with g_free() when no longer needed. + * + * Since: 19.0.0 + */ +gchar * +garrow_decimal64_to_string(GArrowDecimal64 *decimal) +{ + return garrow_decimal_to_string(decimal); +} + +/** + * garrow_decimal64_to_bytes: + * @decimal: A #GArrowDecimal64. + * + * Returns: (transfer full): The binary representation of the decimal. + * + * Since: 19.0.0 + */ +GBytes * +garrow_decimal64_to_bytes(GArrowDecimal64 *decimal) +{ + return garrow_decimal_to_bytes(decimal); +} + +/** + * garrow_decimal64_abs: + * @decimal: A #GArrowDecimal64. + * + * Computes the absolute value of the @decimal destructively. + * + * Since: 19.0.0 + */ +void +garrow_decimal64_abs(GArrowDecimal64 *decimal) +{ + garrow_decimal_abs(decimal); +} + +/** + * garrow_decimal64_negate: + * @decimal: A #GArrowDecimal64. + * + * Negate the current value of the @decimal destructively. + * + * Since: 19.0.0 + */ +void +garrow_decimal64_negate(GArrowDecimal64 *decimal) +{ + garrow_decimal_negate(decimal); +} + +/** + * garrow_decimal64_to_integer: + * @decimal: A #GArrowDecimal64. + * + * Returns: The 64-bit integer representation of the decimal. + * + * Since: 19.0.0 + */ +gint64 +garrow_decimal64_to_integer(GArrowDecimal64 *decimal) +{ + auto arrow_decimal = garrow_decimal64_get_raw(decimal); + return static_cast(*arrow_decimal); +} + +/** + * garrow_decimal64_plus: + * @left: A #GArrowDecimal64. + * @right: A #GArrowDecimal64. + * + * Returns: (transfer full): The added value of these decimals. + * + * Since: 19.0.0 + */ +GArrowDecimal64 * +garrow_decimal64_plus(GArrowDecimal64 *left, GArrowDecimal64 *right) +{ + return garrow_decimal_plus(left, right); +} + +/** + * garrow_decimal64_minus: + * @left: A #GArrowDecimal64. + * @right: A #GArrowDecimal64. + * + * Returns: (transfer full): The subtracted value of these decimals. + * + * Since: 19.0.0 + */ +GArrowDecimal64 * +garrow_decimal64_minus(GArrowDecimal64 *left, GArrowDecimal64 *right) +{ + return garrow_decimal_minus(left, right); +} + +/** + * garrow_decimal64_multiply: + * @left: A #GArrowDecimal64. + * @right: A #GArrowDecimal64. + * + * Returns: (transfer full): The multiplied value of these decimals. + * + * Since: 19.0.0 + */ +GArrowDecimal64 * +garrow_decimal64_multiply(GArrowDecimal64 *left, GArrowDecimal64 *right) +{ + return garrow_decimal_multiply(left, right); +} + +/** + * garrow_decimal64_divide: + * @left: A #GArrowDecimal64. + * @right: A #GArrowDecimal64. + * @remainder: (out) (nullable): A return location for the remainder + * value of these decimals. The returned #GArrowDecimal64 be + * unreferred with g_object_unref() when no longer needed. + * @error: (nullable): Return location for a #GError or %NULL. + * + * Returns: (nullable) (transfer full): The divided value of + * these decimals or %NULL on error. + * + * Since: 19.0.0 + */ +GArrowDecimal64 * +garrow_decimal64_divide(GArrowDecimal64 *left, + GArrowDecimal64 *right, + GArrowDecimal64 **remainder, + GError **error) +{ + return garrow_decimal_divide(left, + right, + remainder, + error, + "[decimal64][divide]"); +} + +/** + * garrow_decimal64_rescale: + * @decimal: A #GArrowDecimal64. + * @original_scale: A scale to be converted from. + * @new_scale: A scale to be converted to. + * @error: (nullable): Return location for a #GError or %NULL. + * + * Returns: (nullable) (transfer full): The rescaled decimal or %NULL on error. + * + * Since: 19.0.0 + */ +GArrowDecimal64 * +garrow_decimal64_rescale(GArrowDecimal64 *decimal, + gint32 original_scale, + gint32 new_scale, + GError **error) +{ + return garrow_decimal_rescale(decimal, + original_scale, + new_scale, + error, + "[decimal64][rescale]"); +} + typedef struct GArrowDecimal128Private_ { std::shared_ptr decimal128; @@ -1081,6 +1499,21 @@ garrow_decimal256_rescale(GArrowDecimal256 *decimal, G_END_DECLS +GArrowDecimal64 * +garrow_decimal64_new_raw(std::shared_ptr *arrow_decimal64) +{ + auto decimal64 = + g_object_new(garrow_decimal64_get_type(), "decimal64", arrow_decimal64, NULL); + return GARROW_DECIMAL64(decimal64); +} + +std::shared_ptr +garrow_decimal64_get_raw(GArrowDecimal64 *decimal64) +{ + auto priv = GARROW_DECIMAL64_GET_PRIVATE(decimal64); + return priv->decimal64; +} + GArrowDecimal128 * garrow_decimal128_new_raw(std::shared_ptr *arrow_decimal128) { diff --git a/c_glib/arrow-glib/decimal.h b/c_glib/arrow-glib/decimal.h index f64afa800a19b..bb266424b4cc0 100644 --- a/c_glib/arrow-glib/decimal.h +++ b/c_glib/arrow-glib/decimal.h @@ -25,6 +25,85 @@ G_BEGIN_DECLS +/* Disabled because it conflicts with GARROW_TYPE_DECIMAL64 in GArrowType. */ +/* #define GARROW_TYPE_DECIMAL64 (garrow_decimal64_get_type()) */ +GARROW_AVAILABLE_IN_ALL +G_DECLARE_DERIVABLE_TYPE(GArrowDecimal64, garrow_decimal64, GARROW, DECIMAL64, GObject) + +struct _GArrowDecimal64Class +{ + GObjectClass parent_class; +}; + +GARROW_AVAILABLE_IN_19_0 +GArrowDecimal64 * +garrow_decimal64_new_string(const gchar *data, GError **error); +GARROW_AVAILABLE_IN_19_0 +GArrowDecimal64 * +garrow_decimal64_new_integer(const gint64 data); +GARROW_AVAILABLE_IN_19_0 +GArrowDecimal64 * +garrow_decimal64_copy(GArrowDecimal64 *decimal); +GARROW_AVAILABLE_IN_19_0 +gboolean +garrow_decimal64_equal(GArrowDecimal64 *decimal, GArrowDecimal64 *other_decimal); +GARROW_AVAILABLE_IN_19_0 +gboolean +garrow_decimal64_not_equal(GArrowDecimal64 *decimal, GArrowDecimal64 *other_decimal); +GARROW_AVAILABLE_IN_19_0 +gboolean +garrow_decimal64_less_than(GArrowDecimal64 *decimal, GArrowDecimal64 *other_decimal); +GARROW_AVAILABLE_IN_19_0 +gboolean +garrow_decimal64_less_than_or_equal(GArrowDecimal64 *decimal, + GArrowDecimal64 *other_decimal); +GARROW_AVAILABLE_IN_19_0 +gboolean +garrow_decimal64_greater_than(GArrowDecimal64 *decimal, GArrowDecimal64 *other_decimal); +GARROW_AVAILABLE_IN_19_0 +gboolean +garrow_decimal64_greater_than_or_equal(GArrowDecimal64 *decimal, + GArrowDecimal64 *other_decimal); +GARROW_AVAILABLE_IN_19_0 +gchar * +garrow_decimal64_to_string_scale(GArrowDecimal64 *decimal, gint32 scale); +GARROW_AVAILABLE_IN_19_0 +gchar * +garrow_decimal64_to_string(GArrowDecimal64 *decimal); +GARROW_AVAILABLE_IN_19_0 +GBytes * +garrow_decimal64_to_bytes(GArrowDecimal64 *decimal); +GARROW_AVAILABLE_IN_19_0 +void +garrow_decimal64_abs(GArrowDecimal64 *decimal); +GARROW_AVAILABLE_IN_19_0 +void +garrow_decimal64_negate(GArrowDecimal64 *decimal); +GARROW_AVAILABLE_IN_19_0 +gint64 +garrow_decimal64_to_integer(GArrowDecimal64 *decimal); +GARROW_AVAILABLE_IN_19_0 +GArrowDecimal64 * +garrow_decimal64_plus(GArrowDecimal64 *left, GArrowDecimal64 *right); +GARROW_AVAILABLE_IN_19_0 +GArrowDecimal64 * +garrow_decimal64_minus(GArrowDecimal64 *left, GArrowDecimal64 *right); +GARROW_AVAILABLE_IN_19_0 +GArrowDecimal64 * +garrow_decimal64_multiply(GArrowDecimal64 *left, GArrowDecimal64 *right); +GARROW_AVAILABLE_IN_19_0 +GArrowDecimal64 * +garrow_decimal64_divide(GArrowDecimal64 *left, + GArrowDecimal64 *right, + GArrowDecimal64 **remainder, + GError **error); +GARROW_AVAILABLE_IN_19_0 +GArrowDecimal64 * +garrow_decimal64_rescale(GArrowDecimal64 *decimal, + gint32 original_scale, + gint32 new_scale, + GError **error); + /* Disabled because it conflicts with GARROW_TYPE_DECIMAL128 in GArrowType. */ /* #define GARROW_TYPE_DECIMAL128 (garrow_decimal128_get_type()) */ GARROW_AVAILABLE_IN_ALL diff --git a/c_glib/arrow-glib/decimal.hpp b/c_glib/arrow-glib/decimal.hpp index 054a91b836054..dbfb7f30c60e0 100644 --- a/c_glib/arrow-glib/decimal.hpp +++ b/c_glib/arrow-glib/decimal.hpp @@ -25,6 +25,11 @@ #include +GArrowDecimal64 * +garrow_decimal64_new_raw(std::shared_ptr *arrow_decimal64); +std::shared_ptr +garrow_decimal64_get_raw(GArrowDecimal64 *decimal); + GArrowDecimal128 * garrow_decimal128_new_raw(std::shared_ptr *arrow_decimal128); std::shared_ptr diff --git a/c_glib/test/test-decimal64.rb b/c_glib/test/test-decimal64.rb new file mode 100644 index 0000000000000..add4f3e0b4909 --- /dev/null +++ b/c_glib/test/test-decimal64.rb @@ -0,0 +1,222 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +class TestDecimal64 < Test::Unit::TestCase + def test_new_string_invalid + message = + "[decimal64][new][string]: Invalid: " + + "The string '1,1' is not a valid decimal64 number" + error = assert_raise(Arrow::Error::Invalid) do + Arrow::Decimal64.new("1,1") + end + assert_equal(message, + error.message.lines.first.chomp) + end + + def test_copy + decimal = Arrow::Decimal64.new("234.23445") + assert_equal(decimal, decimal.copy) + end + + def test_to_string_scale + integer_data = 23423445 + string_data = "234.23445" + decimal = Arrow::Decimal64.new(integer_data) + assert_equal(string_data, decimal.to_string_scale(5)) + end + + def test_to_string + string_data = "999999999999999999" + decimal = Arrow::Decimal64.new(string_data) + assert_equal(string_data, decimal.to_s) + end + + def test_to_bytes + decimal = Arrow::Decimal64.new("12.3") + assert_equal([123].pack("q*"), + decimal.to_bytes.to_s) + end + + def test_abs + absolute_value = "230492239423435324" + negative_value = "-230492239423435324" + decimal = Arrow::Decimal64.new(negative_value) + decimal.abs + assert_equal(absolute_value, decimal.to_s) + end + + def test_negate + positive_value = "230492239423435324" + negative_value = "-230492239423435324" + decimal = Arrow::Decimal64.new(positive_value) + decimal.negate + assert_equal(negative_value, decimal.to_s) + decimal.negate + assert_equal(positive_value, decimal.to_s) + end + + def test_plus + integer_data1 = 23423445 + integer_data2 = 5443 + decimal1 = Arrow::Decimal64.new(integer_data1) + decimal2 = Arrow::Decimal64.new(integer_data2) + decimal3 = decimal1.plus(decimal2) + assert_equal((integer_data1 + integer_data2).to_s, + decimal3.to_s) + end + + def test_multiply + integer_data1 = 23423445 + integer_data2 = 5443 + decimal1 = Arrow::Decimal64.new(integer_data1) + decimal2 = Arrow::Decimal64.new(integer_data2) + decimal3 = decimal1.multiply(decimal2) + assert_equal((integer_data1 * integer_data2).to_s, + decimal3.to_s) + end + + def test_divide + integer_data1 = 23423445 + integer_data2 = -5443 + decimal1 = Arrow::Decimal64.new(integer_data1) + decimal2 = Arrow::Decimal64.new(integer_data2) + result, remainder = decimal1.divide(decimal2) + assert_equal([ + integer_data1.quo(integer_data2).truncate.to_s, + integer_data1.remainder(integer_data2).to_s, + ], + [result.to_s, remainder.to_s]) + end + + def test_divide_zero + decimal1 = Arrow::Decimal64.new(23423445) + decimal2 = Arrow::Decimal64.new(0) + message = + "[decimal64][divide]: Invalid: Division by 0 in Decimal64" + assert_raise(Arrow::Error::Invalid.new(message)) do + decimal1.divide(decimal2) + end + end + + def test_equal + decimal = Arrow::Decimal64.new(10) + other_decimal1 = Arrow::Decimal64.new(10) + other_decimal2 = Arrow::Decimal64.new(11) + assert_equal([ + true, + false, + ], + [ + decimal == other_decimal1, + decimal == other_decimal2, + ]) + end + + def test_not_equal + decimal = Arrow::Decimal64.new(10) + other_decimal1 = Arrow::Decimal64.new(10) + other_decimal2 = Arrow::Decimal64.new(11) + assert_equal([ + false, + true, + ], + [ + decimal != other_decimal1, + decimal != other_decimal2, + ]) + end + + def test_less_than + decimal = Arrow::Decimal64.new(10) + other_decimal1 = Arrow::Decimal64.new(11) + other_decimal2 = Arrow::Decimal64.new(9) + assert_equal([ + true, + false, + false + ], + [ + decimal < other_decimal1, + decimal < other_decimal2, + decimal < decimal, + ]) + end + + def test_less_than_or_equal + decimal = Arrow::Decimal64.new(10) + other_decimal1 = Arrow::Decimal64.new(11) + other_decimal2 = Arrow::Decimal64.new(9) + assert_equal([ + true, + false, + true + ], + [ + decimal <= other_decimal1, + decimal <= other_decimal2, + decimal <= decimal + ]) + end + + def test_greater_than + decimal = Arrow::Decimal64.new(10) + other_decimal1 = Arrow::Decimal64.new(11) + other_decimal2 = Arrow::Decimal64.new(9) + assert_equal([ + false, + true, + false + ], + [ + decimal > other_decimal1, + decimal > other_decimal2, + decimal > decimal + ]) + end + + def test_greater_than_or_equal + decimal = Arrow::Decimal64.new(10) + other_decimal1 = Arrow::Decimal64.new(11) + other_decimal2 = Arrow::Decimal64.new(9) + assert_equal([ + false, + true, + true + ], + [ + decimal >= other_decimal1, + decimal >= other_decimal2, + decimal >= decimal + ]) + end + + def test_rescale + decimal = Arrow::Decimal64.new(10) + assert_equal(Arrow::Decimal64.new(1000), + decimal.rescale(1, 3)) + end + + def test_rescale_fail + decimal = Arrow::Decimal64.new(10) + message = + "[decimal64][rescale]: Invalid: " + + "Rescaling Decimal64 value would cause data loss" + assert_raise(Arrow::Error::Invalid.new(message)) do + decimal.rescale(1, -1) + end + end +end