diff --git a/c_glib/arrow-glib/decimal.cpp b/c_glib/arrow-glib/decimal.cpp index edc2af7a7e051..30b596f7a33c0 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::Decimal32; + using GArrowType = GArrowDecimal32; + + GArrowType * + new_raw(std::shared_ptr *arrow_decimal32) + { + return garrow_decimal32_new_raw(arrow_decimal32); + } + + std::shared_ptr + get_raw(GArrowType *decimal32) + { + return garrow_decimal32_get_raw(decimal32); + } +}; + template <> struct DecimalConverter { using ArrowType = arrow::Decimal64; @@ -319,9 +337,11 @@ G_BEGIN_DECLS /** * SECTION: decimal * @section_id: decimal - * @title: 64-bit, 128-bit and 256-bit decimal classes + * @title: 32-bit, 64-bit, 128-bit and 256-bit decimal classes * @include: arrow-glib/arrow-glib.h * + * #GArrowDecimal32 is a 32-bit decimal class. + * * #GArrowDecimal64 is a 64-bit decimal class. * * #GArrowDecimal128 is a 128-bit decimal class. @@ -331,6 +351,403 @@ G_BEGIN_DECLS * Since: 0.10.0 */ +typedef struct GArrowDecimal32Private_ +{ + std::shared_ptr decimal32; +} GArrowDecimal32Private; + +enum { + PROP_DECIMAL32 = 1 +}; + +G_DEFINE_TYPE_WITH_PRIVATE(GArrowDecimal32, garrow_decimal32, G_TYPE_OBJECT) + +#define GARROW_DECIMAL32_GET_PRIVATE(obj) \ + static_cast( \ + garrow_decimal32_get_instance_private(GARROW_DECIMAL32(obj))) + +static void +garrow_decimal32_finalize(GObject *object) +{ + auto priv = GARROW_DECIMAL32_GET_PRIVATE(object); + + priv->decimal32.~shared_ptr(); + + G_OBJECT_CLASS(garrow_decimal32_parent_class)->finalize(object); +} + +static void +garrow_decimal32_set_property(GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + auto priv = GARROW_DECIMAL32_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_DECIMAL32: + priv->decimal32 = + *static_cast *>(g_value_get_pointer(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +garrow_decimal32_init(GArrowDecimal32 *object) +{ + auto priv = GARROW_DECIMAL32_GET_PRIVATE(object); + new (&priv->decimal32) std::shared_ptr; +} + +static void +garrow_decimal32_class_init(GArrowDecimal32Class *klass) +{ + auto gobject_class = G_OBJECT_CLASS(klass); + + gobject_class->finalize = garrow_decimal32_finalize; + gobject_class->set_property = garrow_decimal32_set_property; + + GParamSpec *spec; + spec = g_param_spec_pointer( + "decimal32", + "Decimal32", + "The raw std::shared *", + static_cast(G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property(gobject_class, PROP_DECIMAL32, spec); +} + +/** + * garrow_decimal32_new_string: + * @data: The data of the decimal. + * @error: (nullable): Return location for a #GError or %NULL. + * + * Returns: (nullable): + * A newly created #GArrowDecimal32 on success, %NULL on error. + * + * Since: 19.0.0 + */ +GArrowDecimal32 * +garrow_decimal32_new_string(const gchar *data, GError **error) +{ + return garrow_decimal_new_string(data, + error, + "[decimal32][new][string]"); +} + +/** + * garrow_decimal32_new_integer: + * @data: The data of the decimal. + * + * Returns: A newly created #GArrowDecimal32. + * + * Since: 19.0.0 + */ +GArrowDecimal32 * +garrow_decimal32_new_integer(const gint64 data) +{ + return garrow_decimal_new_integer(data); +} + +/** + * garrow_decimal32_copy: + * @decimal: The decimal to be copied. + * + * Returns: (transfer full): A copied #GArrowDecimal32. + * + * Since: 19.0.0 + */ +GArrowDecimal32 * +garrow_decimal32_copy(GArrowDecimal32 *decimal) +{ + return garrow_decimal_copy(decimal); +} + +/** + * garrow_decimal32_equal: + * @decimal: A #GArrowDecimal32. + * @other_decimal: A #GArrowDecimal32 to be compared. + * + * Returns: %TRUE if the decimal is equal to the other decimal, %FALSE + * otherwise. + * + * Since: 19.0.0 + */ +gboolean +garrow_decimal32_equal(GArrowDecimal32 *decimal, GArrowDecimal32 *other_decimal) +{ + return garrow_decimal_equal(decimal, other_decimal); +} + +/** + * garrow_decimal32_not_equal: + * @decimal: A #GArrowDecimal32. + * @other_decimal: A #GArrowDecimal32 to be compared. + * + * Returns: %TRUE if the decimal isn't equal to the other decimal, + * %FALSE otherwise. + * + * Since: 19.0.0 + */ +gboolean +garrow_decimal32_not_equal(GArrowDecimal32 *decimal, GArrowDecimal32 *other_decimal) +{ + return garrow_decimal_not_equal(decimal, other_decimal); +} + +/** + * garrow_decimal32_less_than: + * @decimal: A #GArrowDecimal32. + * @other_decimal: A #GArrowDecimal32 to be compared. + * + * Returns: %TRUE if the decimal is less than the other decimal, + * %FALSE otherwise. + * + * Since: 19.0.0 + */ +gboolean +garrow_decimal32_less_than(GArrowDecimal32 *decimal, GArrowDecimal32 *other_decimal) +{ + return garrow_decimal_less_than(decimal, other_decimal); +} + +/** + * garrow_decimal32_less_than_or_equal: + * @decimal: A #GArrowDecimal32. + * @other_decimal: A #GArrowDecimal32 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_decimal32_less_than_or_equal(GArrowDecimal32 *decimal, + GArrowDecimal32 *other_decimal) +{ + return garrow_decimal_less_than_or_equal(decimal, other_decimal); +} + +/** + * garrow_decimal32_greater_than: + * @decimal: A #GArrowDecimal32. + * @other_decimal: A #GArrowDecimal32 to be compared. + * + * Returns: %TRUE if the decimal is greater than the other decimal, + * %FALSE otherwise. + * + * Since: 19.0.0 + */ +gboolean +garrow_decimal32_greater_than(GArrowDecimal32 *decimal, GArrowDecimal32 *other_decimal) +{ + return garrow_decimal_greater_than(decimal, other_decimal); +} + +/** + * garrow_decimal32_greater_than_or_equal: + * @decimal: A #GArrowDecimal32. + * @other_decimal: A #GArrowDecimal32 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_decimal32_greater_than_or_equal(GArrowDecimal32 *decimal, + GArrowDecimal32 *other_decimal) +{ + return garrow_decimal_greater_than_or_equal(decimal, other_decimal); +} + +/** + * garrow_decimal32_to_string_scale: + * @decimal: A #GArrowDecimal32. + * @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_decimal32_to_string_scale(GArrowDecimal32 *decimal, gint32 scale) +{ + return garrow_decimal_to_string_scale(decimal, scale); +} + +/** + * garrow_decimal32_to_string: + * @decimal: A #GArrowDecimal32. + * + * Returns: The string representation of the decimal. + * + * It should be freed with g_free() when no longer needed. + * + * Since: 19.0.0 + */ +gchar * +garrow_decimal32_to_string(GArrowDecimal32 *decimal) +{ + return garrow_decimal_to_string(decimal); +} + +/** + * garrow_decimal32_to_bytes: + * @decimal: A #GArrowDecimal32. + * + * Returns: (transfer full): The binary representation of the decimal. + * + * Since: 19.0.0 + */ +GBytes * +garrow_decimal32_to_bytes(GArrowDecimal32 *decimal) +{ + return garrow_decimal_to_bytes(decimal); +} + +/** + * garrow_decimal32_abs: + * @decimal: A #GArrowDecimal32. + * + * Computes the absolute value of the @decimal destructively. + * + * Since: 19.0.0 + */ +void +garrow_decimal32_abs(GArrowDecimal32 *decimal) +{ + garrow_decimal_abs(decimal); +} + +/** + * garrow_decimal32_negate: + * @decimal: A #GArrowDecimal32. + * + * Negate the current value of the @decimal destructively. + * + * Since: 19.0.0 + */ +void +garrow_decimal32_negate(GArrowDecimal32 *decimal) +{ + garrow_decimal_negate(decimal); +} + +/** + * garrow_decimal32_to_integer: + * @decimal: A #GArrowDecimal32. + * + * Returns: The 64-bit integer representation of the decimal. + * + * Since: 19.0.0 + */ +gint64 +garrow_decimal32_to_integer(GArrowDecimal32 *decimal) +{ + auto arrow_decimal = garrow_decimal32_get_raw(decimal); + return static_cast(*arrow_decimal); +} + +/** + * garrow_decimal32_plus: + * @left: A #GArrowDecimal32. + * @right: A #GArrowDecimal32. + * + * Returns: (transfer full): The added value of these decimals. + * + * Since: 19.0.0 + */ +GArrowDecimal32 * +garrow_decimal32_plus(GArrowDecimal32 *left, GArrowDecimal32 *right) +{ + return garrow_decimal_plus(left, right); +} + +/** + * garrow_decimal32_minus: + * @left: A #GArrowDecimal32. + * @right: A #GArrowDecimal32. + * + * Returns: (transfer full): The subtracted value of these decimals. + * + * Since: 19.0.0 + */ +GArrowDecimal32 * +garrow_decimal32_minus(GArrowDecimal32 *left, GArrowDecimal32 *right) +{ + return garrow_decimal_minus(left, right); +} + +/** + * garrow_decimal32_multiply: + * @left: A #GArrowDecimal32. + * @right: A #GArrowDecimal32. + * + * Returns: (transfer full): The multiplied value of these decimals. + * + * Since: 19.0.0 + */ +GArrowDecimal32 * +garrow_decimal32_multiply(GArrowDecimal32 *left, GArrowDecimal32 *right) +{ + return garrow_decimal_multiply(left, right); +} + +/** + * garrow_decimal32_divide: + * @left: A #GArrowDecimal32. + * @right: A #GArrowDecimal32. + * @remainder: (out) (nullable): A return location for the remainder + * value of these decimals. The returned #GArrowDecimal32 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 + */ +GArrowDecimal32 * +garrow_decimal32_divide(GArrowDecimal32 *left, + GArrowDecimal32 *right, + GArrowDecimal32 **remainder, + GError **error) +{ + return garrow_decimal_divide(left, + right, + remainder, + error, + "[decimal32][divide]"); +} + +/** + * garrow_decimal32_rescale: + * @decimal: A #GArrowDecimal32. + * @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 + */ +GArrowDecimal32 * +garrow_decimal32_rescale(GArrowDecimal32 *decimal, + gint32 original_scale, + gint32 new_scale, + GError **error) +{ + return garrow_decimal_rescale(decimal, + original_scale, + new_scale, + error, + "[decimal32][rescale]"); +} typedef struct GArrowDecimal64Private_ { std::shared_ptr decimal64; @@ -1499,6 +1916,21 @@ garrow_decimal256_rescale(GArrowDecimal256 *decimal, G_END_DECLS +GArrowDecimal32 * +garrow_decimal32_new_raw(std::shared_ptr *arrow_decimal32) +{ + auto decimal32 = + g_object_new(garrow_decimal32_get_type(), "decimal32", arrow_decimal32, NULL); + return GARROW_DECIMAL32(decimal32); +} + +std::shared_ptr +garrow_decimal32_get_raw(GArrowDecimal32 *decimal32) +{ + auto priv = GARROW_DECIMAL32_GET_PRIVATE(decimal32); + return priv->decimal32; +} + GArrowDecimal64 * garrow_decimal64_new_raw(std::shared_ptr *arrow_decimal64) { diff --git a/c_glib/arrow-glib/decimal.h b/c_glib/arrow-glib/decimal.h index bb266424b4cc0..6f839a67d9b3b 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_DECIMAL32 in GArrowType. */ +/* #define GARROW_TYPE_DECIMAL32 (garrow_decimal32_get_type()) */ +GARROW_AVAILABLE_IN_19_0 +G_DECLARE_DERIVABLE_TYPE(GArrowDecimal32, garrow_decimal32, GARROW, DECIMAL32, GObject) + +struct _GArrowDecimal32Class +{ + GObjectClass parent_class; +}; + +GARROW_AVAILABLE_IN_19_0 +GArrowDecimal32 * +garrow_decimal32_new_string(const gchar *data, GError **error); +GARROW_AVAILABLE_IN_19_0 +GArrowDecimal32 * +garrow_decimal32_new_integer(const gint64 data); +GARROW_AVAILABLE_IN_19_0 +GArrowDecimal32 * +garrow_decimal32_copy(GArrowDecimal32 *decimal); +GARROW_AVAILABLE_IN_19_0 +gboolean +garrow_decimal32_equal(GArrowDecimal32 *decimal, GArrowDecimal32 *other_decimal); +GARROW_AVAILABLE_IN_19_0 +gboolean +garrow_decimal32_not_equal(GArrowDecimal32 *decimal, GArrowDecimal32 *other_decimal); +GARROW_AVAILABLE_IN_19_0 +gboolean +garrow_decimal32_less_than(GArrowDecimal32 *decimal, GArrowDecimal32 *other_decimal); +GARROW_AVAILABLE_IN_19_0 +gboolean +garrow_decimal32_less_than_or_equal(GArrowDecimal32 *decimal, + GArrowDecimal32 *other_decimal); +GARROW_AVAILABLE_IN_19_0 +gboolean +garrow_decimal32_greater_than(GArrowDecimal32 *decimal, GArrowDecimal32 *other_decimal); +GARROW_AVAILABLE_IN_19_0 +gboolean +garrow_decimal32_greater_than_or_equal(GArrowDecimal32 *decimal, + GArrowDecimal32 *other_decimal); +GARROW_AVAILABLE_IN_19_0 +gchar * +garrow_decimal32_to_string_scale(GArrowDecimal32 *decimal, gint32 scale); +GARROW_AVAILABLE_IN_19_0 +gchar * +garrow_decimal32_to_string(GArrowDecimal32 *decimal); +GARROW_AVAILABLE_IN_19_0 +GBytes * +garrow_decimal32_to_bytes(GArrowDecimal32 *decimal); +GARROW_AVAILABLE_IN_19_0 +void +garrow_decimal32_abs(GArrowDecimal32 *decimal); +GARROW_AVAILABLE_IN_19_0 +void +garrow_decimal32_negate(GArrowDecimal32 *decimal); +GARROW_AVAILABLE_IN_19_0 +gint64 +garrow_decimal32_to_integer(GArrowDecimal32 *decimal); +GARROW_AVAILABLE_IN_19_0 +GArrowDecimal32 * +garrow_decimal32_plus(GArrowDecimal32 *left, GArrowDecimal32 *right); +GARROW_AVAILABLE_IN_19_0 +GArrowDecimal32 * +garrow_decimal32_minus(GArrowDecimal32 *left, GArrowDecimal32 *right); +GARROW_AVAILABLE_IN_19_0 +GArrowDecimal32 * +garrow_decimal32_multiply(GArrowDecimal32 *left, GArrowDecimal32 *right); +GARROW_AVAILABLE_IN_19_0 +GArrowDecimal32 * +garrow_decimal32_divide(GArrowDecimal32 *left, + GArrowDecimal32 *right, + GArrowDecimal32 **remainder, + GError **error); +GARROW_AVAILABLE_IN_19_0 +GArrowDecimal32 * +garrow_decimal32_rescale(GArrowDecimal32 *decimal, + gint32 original_scale, + gint32 new_scale, + GError **error); + /* Disabled because it conflicts with GARROW_TYPE_DECIMAL64 in GArrowType. */ /* #define GARROW_TYPE_DECIMAL64 (garrow_decimal64_get_type()) */ GARROW_AVAILABLE_IN_ALL diff --git a/c_glib/arrow-glib/decimal.hpp b/c_glib/arrow-glib/decimal.hpp index dbfb7f30c60e0..09ac40a51297e 100644 --- a/c_glib/arrow-glib/decimal.hpp +++ b/c_glib/arrow-glib/decimal.hpp @@ -25,6 +25,11 @@ #include +GArrowDecimal32 * +garrow_decimal32_new_raw(std::shared_ptr *arrow_decimal32); +std::shared_ptr +garrow_decimal32_get_raw(GArrowDecimal32 *decimal); + GArrowDecimal64 * garrow_decimal64_new_raw(std::shared_ptr *arrow_decimal64); std::shared_ptr diff --git a/c_glib/test/test-decimal32.rb b/c_glib/test/test-decimal32.rb new file mode 100644 index 0000000000000..33b84ccc6b531 --- /dev/null +++ b/c_glib/test/test-decimal32.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 TestDecimal32 < Test::Unit::TestCase + def test_new_string_invalid + message = + "[decimal32][new][string]: Invalid: " + + "The string '1,1' is not a valid decimal32 number" + error = assert_raise(Arrow::Error::Invalid) do + Arrow::Decimal32.new("1,1") + end + assert_equal(message, + error.message.lines.first.chomp) + end + + def test_copy + decimal = Arrow::Decimal32.new("234.23445") + assert_equal(decimal, decimal.copy) + end + + def test_to_string_scale + integer_data = 23423445 + string_data = "234.23445" + decimal = Arrow::Decimal32.new(integer_data) + assert_equal(string_data, decimal.to_string_scale(5)) + end + + def test_to_string + string_data = "999999999" + decimal = Arrow::Decimal32.new(string_data) + assert_equal(string_data, decimal.to_s) + end + + def test_to_bytes + decimal = Arrow::Decimal32.new("12.3") + assert_equal("\x7B\x00\x00\x00", + decimal.to_bytes.to_s) + end + + def test_abs + absolute_value = "230492239" + negative_value = "-230492239" + decimal = Arrow::Decimal32.new(negative_value) + decimal.abs + assert_equal(absolute_value, decimal.to_s) + end + + def test_negate + positive_value = "230492239" + negative_value = "-230492239" + decimal = Arrow::Decimal32.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::Decimal32.new(integer_data1) + decimal2 = Arrow::Decimal32.new(integer_data2) + decimal3 = decimal1.plus(decimal2) + assert_equal((integer_data1 + integer_data2).to_s, + decimal3.to_s) + end + + def test_multiply + integer_data1 = 23423 + integer_data2 = 5443 + decimal1 = Arrow::Decimal32.new(integer_data1) + decimal2 = Arrow::Decimal32.new(integer_data2) + decimal3 = decimal1.multiply(decimal2) + assert_equal((integer_data1 * integer_data2).to_s, + decimal3.to_s) + end + + def test_divide + integer_data1 = 23423 + integer_data2 = -5443 + decimal1 = Arrow::Decimal32.new(integer_data1) + decimal2 = Arrow::Decimal32.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::Decimal32.new(23423445) + decimal2 = Arrow::Decimal32.new(0) + message = + "[decimal32][divide]: Invalid: Division by 0 in Decimal32" + assert_raise(Arrow::Error::Invalid.new(message)) do + decimal1.divide(decimal2) + end + end + + def test_equal + decimal = Arrow::Decimal32.new(10) + other_decimal1 = Arrow::Decimal32.new(10) + other_decimal2 = Arrow::Decimal32.new(11) + assert_equal([ + true, + false, + ], + [ + decimal == other_decimal1, + decimal == other_decimal2, + ]) + end + + def test_not_equal + decimal = Arrow::Decimal32.new(10) + other_decimal1 = Arrow::Decimal32.new(10) + other_decimal2 = Arrow::Decimal32.new(11) + assert_equal([ + false, + true, + ], + [ + decimal != other_decimal1, + decimal != other_decimal2, + ]) + end + + def test_less_than + decimal = Arrow::Decimal32.new(10) + other_decimal1 = Arrow::Decimal32.new(11) + other_decimal2 = Arrow::Decimal32.new(9) + assert_equal([ + true, + false, + false + ], + [ + decimal < other_decimal1, + decimal < other_decimal2, + decimal < decimal, + ]) + end + + def test_less_than_or_equal + decimal = Arrow::Decimal32.new(10) + other_decimal1 = Arrow::Decimal32.new(11) + other_decimal2 = Arrow::Decimal32.new(9) + assert_equal([ + true, + false, + true + ], + [ + decimal <= other_decimal1, + decimal <= other_decimal2, + decimal <= decimal + ]) + end + + def test_greater_than + decimal = Arrow::Decimal32.new(10) + other_decimal1 = Arrow::Decimal32.new(11) + other_decimal2 = Arrow::Decimal32.new(9) + assert_equal([ + false, + true, + false + ], + [ + decimal > other_decimal1, + decimal > other_decimal2, + decimal > decimal + ]) + end + + def test_greater_than_or_equal + decimal = Arrow::Decimal32.new(10) + other_decimal1 = Arrow::Decimal32.new(11) + other_decimal2 = Arrow::Decimal32.new(9) + assert_equal([ + false, + true, + true + ], + [ + decimal >= other_decimal1, + decimal >= other_decimal2, + decimal >= decimal + ]) + end + + def test_rescale + decimal = Arrow::Decimal32.new(10) + assert_equal(Arrow::Decimal32.new(1000), + decimal.rescale(1, 3)) + end + + def test_rescale_fail + decimal = Arrow::Decimal32.new(10) + message = + "[decimal32][rescale]: Invalid: " + + "Rescaling Decimal32 value would cause data loss" + assert_raise(Arrow::Error::Invalid.new(message)) do + decimal.rescale(1, -1) + end + end +end