Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decimal remainder, pow, div #9566

Merged
merged 11 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,7 @@
- [Make expand_to_rows, expand_column support Rows, Tables, Column data
types][9533]
- [Data Link for `Enso_File`.][9525]
- [Added `pow`, `remainder`, and `div` to `Decimal`.][9566]

[debug-shortcuts]:
https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug
Expand Down Expand Up @@ -933,6 +934,7 @@
[9435]: https://github.com/enso-org/enso/pull/9435
[9533]: https://github.com/enso-org/enso/pull/9533
[9525]: https://github.com/enso-org/enso/pull/9525
[9566]: https://github.com/enso-org/enso/pull/9566

#### Enso Compiler

Expand Down
129 changes: 127 additions & 2 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/Decimal.enso
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import project.Data.Numeric.Math_Context.Math_Context
import project.Data.Numeric.Rounding_Mode.Rounding_Mode
import project.Data.Text.Text
import project.Error.Error
import project.Errors.Common.Arithmetic_Error
import project.Errors.Illegal_Argument.Illegal_Argument
import project.Nothing.Nothing
import project.Panic.Panic
Expand All @@ -13,7 +12,7 @@ from project.Data.Boolean import Boolean, False, True
from project.Data.Numbers import Float, Integer, Number, Number_Parse_Error
from project.Data.Numeric.Internal.Decimal_Internal import Decimal_Comparator
from project.Data.Ordering import Comparable, Ordering
from project.Errors.Common import Loss_Of_Numeric_Precision
from project.Errors.Common import Arithmetic_Error, Loss_Of_Numeric_Precision, Unsupported_Argument_Types

polyglot java import java.lang.ArithmeticException
polyglot java import java.lang.NumberFormatException
Expand Down Expand Up @@ -519,6 +518,127 @@ type Decimal
/ : Decimal -> Decimal
/ self (that : Decimal) = self.divide that

## ALIAS modulo, modulus
GROUP Operators
ICON math
Computes the remainder when dividing this by that.

Arguments:
- that: The number to divide this by.

Remainder in Enso will undergo automatic conversions such that you need
not convert other numeric types to `Decimal` manually.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds to me like the result of the operation will undergo conversions, which as far as I understand is not the case and you mean the arguments, right?

Maybe we can rephrase it to make it clearer?

Suggested change
Remainder in Enso will undergo automatic conversions such that you need
not convert other numeric types to `Decimal` manually.
Arguments of the `remainder` operation will undergo automatic conversions such that you need
not convert other numeric types to `Decimal` manually.

maybe something like that?

  1. I don't think you need to say in Enso there - this is an Enso library so that is rather assumed,
  2. IMO it's good to point you mean arguments

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


> Example
Computing the remainder when dividing 10 by 3 (which is 1).

remainder = Decimal.new 10 . remainder 3
# => 1
remainder : Decimal -> Decimal -> Decimal
remainder self (that : Decimal) =
handle_java_exception <|
Decimal.Value (self.big_decimal.remainder that.big_decimal)

## ALIAS modulo, modulus, remainder
GROUP Operators
ICON math
Computes the remainder when dividing this by that.

Arguments:
- that: The number to divide this by.

Remainder in Enso will undergo automatic conversions such that you need
not convert other numeric types to `Decimal` manually.

> Example
Computing the remainder when dividing 10 by 3 (which is 1).

remainder = Decimal.new 10 % 3
# => 1
% : Decimal -> Decimal -> Decimal
% self (that : Decimal) = self.remainder that

## GROUP Math
ICON math
Computes the integer division of this by that.

Arguments:
- that: The number to divide this by.

Integer division of Decimals rounds down to the nearest integer.

Integer division in enso will undergo automatic conversions such that you
need not convert other numeric types to `Decimal` manually.

! Error Conditions

- If `that` is 0, `div` throws an Arithmetic_Error.

> Example
Dividing 10 by 3 to get 3.

Decimal.new "10" . div (Decimal.new "3")
# => 3

> Example
Dividing 10.28 by 3.01 to get 3.

Decimal.new "10.28" . div (Decimal.new "3.01")
# => 3
div : Decimal -> Decimal
div self that:Decimal -> Decimal =
handle_java_exception <|
Decimal.Value (self.big_decimal.divideToIntegralValue that.big_decimal)

## ALIAS power
GROUP Operators
ICON math
Compute the result of raising this to the positive integer power `exp`.

Arguments:
- exp: The exponent. Must be an integer in the range 0 through
999999999 (inclusive).

! Error Conditions

- If `exp` is outside the range 0 through
999999999 (inclusive), `Arithmetic_Error` will be thrown.

> Example
Computing 2.25 to the fifth power.

Decimal.new "2.25" . pow (Decimal.new "5")
# => 57.6650390625
pow : Integer -> Decimal
pow self exp:Integer =
## If `exp` is an integer that does not fit in a Java Integer,
UnsuppUnsupported_Argument_Types is raised, so we convert that to an
Arithmetic_Error.
handle_java_exception <| handle_unsupported_argument_types <|
Decimal.Value (self.big_decimal.pow exp)

## ALIAS power
GROUP Operators
ICON math
Compute the result of raising this to the positive integer power `exp`.

Arguments:
- exp: The exponent. Must be an integer in the range 0 through
999999999 (inclusive).

! Error Conditions

- If `exp` is outside the range 0 through
999999999 (inclusive), `Arithmetic_Error` will be thrown.

> Example
Computing 2.25 to the fifth power.

Decimal.new "2.25" ^ Decimal.new "5"
# => 57.6650390625
^ : Integer -> Decimal
^ self exp:Integer = self.pow exp

## GROUP Operators
ICON operators
Compute the negation of this.
Expand Down Expand Up @@ -578,6 +698,11 @@ handle_java_exception ~action =
Panic.catch ArithmeticException action caught_panic->
Error.throw (Arithmetic_Error.Error caught_panic.payload.getMessage)

## PRIVATE
handle_unsupported_argument_types ~action =
Panic.catch Unsupported_Argument_Types action _->
Error.throw (Arithmetic_Error.Error "Exponent out of range 0..999999999 (inclusive)")

## PRIVATE
Comparable.from (_ : Decimal) = Decimal_Comparator

Expand Down
68 changes: 68 additions & 0 deletions test/Base_Tests/src/Data/Decimal_Spec.enso
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,74 @@ add_specs suite_builder =
nd2 = -d2
nd2 . should_equal 5

suite_builder.group "remainder" group_builder->
group_builder.specify "should define remainder" <|
cases = []
+ [[5, 3, 2], [5.0, 3.0, 2.0], [3.5, 2, 1.5], [10.5, 1.0, 0.5], [3, 1, 0], [3.0, 1.0, 0]]
+ [[-5, 3, -2], [-5.0, 3.0, -2.0], [-3.5, 2, -1.5], [-10.5, 1.0, -0.5], [-3, 1, 0], [-3.0, 1.0, 0]]
+ [["9223372036854775807", 10, 7], ["9223372036854775808", 10, 8], ["922337203685477580000000000008", 10, 8]]
+ [["-9223372036854775808", 10, -8], ["-9223372036854775809", 10, -9], ["-922337203685477580000000000008", 10, -8]]
cases.map c->
base = c.at 0
modulus = c.at 1
residue = c.at 2
(Decimal.new base % modulus) . should_equal residue
(Decimal.new base % Decimal.new modulus) . should_equal residue
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd probably add a test case for something like 2.7 % 0.5. Should it work and return 0.2 or are non-integer arguments not allowed? Either way it would be good to have a test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


group_builder.specify "should define remainder (example)" <|
(Decimal.new 10 . remainder 3) . should_equal 1
(Decimal.new 10 % 3) . should_equal 1

group_builder.specify "remainder 0 should throw" <|
(Decimal.new 3 % 0) . should_fail_with Arithmetic_Error
(Decimal.new 3 % Decimal.new 0) . should_fail_with Arithmetic_Error

suite_builder.group "div" group_builder->
group_builder.specify "should define div" <|
Decimal.new "10" . div (Decimal.new "3") . should_equal 3
Decimal.new "10.28" . div (Decimal.new "3.01") . should_equal 3

Decimal.new "35" . div (Decimal.new "6") . should_equal 5
Decimal.new "-35" . div (Decimal.new "6") . should_equal -5
Decimal.new "35" . div (Decimal.new "-6") . should_equal -5
Decimal.new "-35" . div (Decimal.new "-6") . should_equal 5
Decimal.new "35.0" . div (Decimal.new "6") . should_equal 5
Decimal.new "35.3" . div (Decimal.new "6") . should_equal 5

Decimal.new "35" . div (Decimal.new "5") . should_equal 7

Decimal.new "0" . div (Decimal.new "5") . should_equal 0

Decimal.new "12" . div (Decimal.new "0") . should_fail_with Arithmetic_Error

suite_builder.group "pow" group_builder->
group_builder.specify "should define pow" <|
Decimal.new "10" . pow 3 . should_equal 1000

(Decimal.new "10" ^ 3) . should_equal 1000

(Decimal.new "-10" ^ 2) . should_equal 100
(Decimal.new "-10" ^ 3) . should_equal -1000

(Decimal.new "2" ^ 5) . should_equal 32
(Decimal.new "3" ^ 4) . should_equal 81
(Decimal.new "-2" ^ 5) . should_equal -32
(Decimal.new "-3" ^ 4) . should_equal 81

(Decimal.new "2.25" ^ 5) . should_equal 57.6650390625

(Decimal.new "10" ^ 1) . should_equal 10
(Decimal.new "2" ^ 1) . should_equal 2
(Decimal.new "10" ^ 0) . should_equal 1
(Decimal.new "2" ^ 0) . should_equal 1

(Decimal.new "23234" ^ 12) . should_equal (Decimal.new "24745020631916033678933049360198248076412726408646656")
(Decimal.new "-794" ^ 112) . should_equal (Decimal.new "6024156861645760455373941610695035918372130160062114486642167348130629475133139191309665922068844301194118714216190697635299124619812230483098091272538558376111325623532711124479664789034731517753138259958467197045503553217529624389271803885423122798234927557537822587658929087103065356345719502482054440067247048272417652736")

group_builder.specify "should throw an error for exponent out of range" <|
Decimal.new "10" . pow -1 . should_fail_with Arithmetic_Error
Decimal.new "10" . pow 99999999999 . should_fail_with Arithmetic_Error

main =
suite = Test.build suite_builder->
add_specs suite_builder
Expand Down
41 changes: 38 additions & 3 deletions test/Base_Tests/src/Data/Numbers_Spec.enso
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ add_specs suite_builder =
(3 / 4) . should_equal 0.75 epsilon=eps
(almost_max_long * 2 / almost_max_long_times_three) . should_equal 0.6666666 epsilon=eps

1/0 . is_infinite . should_be_true
-1/0 . is_infinite . should_be_true
radeusgd marked this conversation as resolved.
Show resolved Hide resolved

group_builder.specify "should support integer division" <|
(10.div 3) . should_equal 3
(10.div 0).should_fail_with Arithmetic_Error
Expand Down Expand Up @@ -342,13 +345,45 @@ add_specs suite_builder =
1.0%0 . is_nan . should_be_true
almost_max_long_times_three%0.0 . is_nan . should_be_true

1/0 . is_infinite . should_be_true
-1/0 . is_infinite . should_be_true

hundred_factorial%0 . should_fail_with Arithmetic_Error
hundred_factorial%hundred_factorial . should_equal 0
10%hundred_factorial . should_equal 10

# Pending https://github.com/enso-org/enso/issues/9553
group_builder.specify "should define modulo for negative integers and decimals" pending="large integer % is incorrect" <|
-5%3 . should_equal -2
-5%3 . should_be_a Integer

-5.0%3.0 . should_equal -2.0
-5%3.0 . should_equal -2.0
-5.0%3 . should_equal -2.0
-5.0%3.0 . should_be_a Float
-5%3.0 . should_be_a Float
-5.0%3 . should_be_a Float

-3.5%2 . should_equal -1.5
-10.5%1.0 . should_equal -0.5

-3%1 . should_equal 0
-3%1 . should_be_a Integer

-3.5%1 . should_equal -0.5

(-almost_max_long_times_three)%10 . should_equal -8
-1000%almost_max_long_times_three . should_equal -1000

-1%0 . should_fail_with Arithmetic_Error
-almost_max_long_times_three%0 . should_fail_with Arithmetic_Error

-1.0%0.0 . is_nan . should_be_true
-1%0.0 . is_nan . should_be_true
-1.0%0 . is_nan . should_be_true
-almost_max_long_times_three%0.0 . is_nan . should_be_true

-hundred_factorial%0 . should_fail_with Arithmetic_Error
-hundred_factorial%hundred_factorial . should_equal 0
-10%hundred_factorial . should_equal -10

group_builder.specify "should support less than operator" <|
(1 < 2).should_be_true
(1 < 1).should_be_false
Expand Down
Loading