Skip to content

Commit

Permalink
Daml-LF: Add CAST_NUMERIC and SHIFT_NUMERIC (digital-asset#2919)
Browse files Browse the repository at this point in the history
* daml-lf: add CAST_NUMERIC and SHIFT_NUMERIC internally

* daml-lf: add CAST_NUMERIC and SHIFT_NUMERIC to archive proto

* daml-lf: update spec with CAST_NUMERIC and SHIFT_NUMERIC

* update release notes

* fix spec

* Address comments from Fran and Gerolf

* fix unrel
  • Loading branch information
remyhaemmerle-da authored and mergify[bot] committed Sep 17, 2019
1 parent b2b711c commit dc9429b
Show file tree
Hide file tree
Showing 12 changed files with 152 additions and 20 deletions.
2 changes: 2 additions & 0 deletions compiler/daml-lf-proto/src/DA/Daml/LF/Proto3/DecodeV1.hs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ decodeBuiltinFunction = pure . \case
LF1.BuiltinFunctionMUL_NUMERIC -> BEMulNumeric
LF1.BuiltinFunctionDIV_NUMERIC -> BEDivNumeric
LF1.BuiltinFunctionROUND_NUMERIC -> BERoundNumeric
LF1.BuiltinFunctionCAST_NUMERIC -> error "CAST_NUMERIC not implemented"
LF1.BuiltinFunctionSHIFT_NUMERIC -> error "SHIFT_DECIMAL not implemented"

LF1.BuiltinFunctionADD_INT64 -> BEAddInt64
LF1.BuiltinFunctionSUB_INT64 -> BESubInt64
Expand Down
4 changes: 3 additions & 1 deletion daml-lf/archive/da/daml_lf_1.proto
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,8 @@ enum BuiltinFunction {
MUL_NUMERIC = 109; // *Available in versions >= 1.dev*
DIV_NUMERIC = 110; // *Available in versions >= 1.dev*
ROUND_NUMERIC = 111; // *Available in versions >= 1.dev*
CAST_NUMERIC = 121; // *Available in versions >= 1.dev*
SHIFT_NUMERIC = 122; // *Available in versions >= 1.dev*

ADD_INT64 = 7;
SUB_INT64 = 8;
Expand Down Expand Up @@ -443,7 +445,7 @@ enum BuiltinFunction {

TEXT_FROM_CODE_POINTS = 105; // *Available in versions >= 1.6*
TEXT_TO_CODE_POINTS = 106; // *Available in versions >= 1.6*
// Next id is 121. 120 is EQUAL_NUMERIC.
// Next id is 123. 122 is SHIFT_NUMERIC.
}

// Builtin literals
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,8 @@ private[lf] object DecodeV1 {
BuiltinFunctionInfo(MUL_NUMERIC, BMulNumeric, minVersion = numeric),
BuiltinFunctionInfo(DIV_NUMERIC, BDivNumeric, minVersion = numeric),
BuiltinFunctionInfo(ROUND_NUMERIC, BRoundNumeric, minVersion = numeric),
BuiltinFunctionInfo(CAST_NUMERIC, BCastNumeric, minVersion = numeric),
BuiltinFunctionInfo(SHIFT_NUMERIC, BShiftNumeric, minVersion = numeric),
BuiltinFunctionInfo(ADD_INT64, BAddInt64),
BuiltinFunctionInfo(SUB_INT64, BSubInt64),
BuiltinFunctionInfo(MUL_INT64, BMulInt64),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ final case class Compiler(packages: PackageId PartialFunction Package) {
case BMulNumeric => SBMulNumeric
case BDivNumeric => SBDivNumeric
case BRoundNumeric => SBRoundNumeric
case BCastNumeric => SBCastNumeric
case BShiftNumeric => SBShiftNumeric

// Int64 arithmetic
case BAddInt64 => SBAddInt64
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,54 @@ object SBuiltin {
final case object SBMulNumeric extends SBBinaryOpNumeric(multiply)
final case object SBDivNumeric extends SBBinaryOpNumeric(divide)

final case object SBRoundNumeric extends SBuiltin(3) {
def execute(args: util.ArrayList[SValue], machine: Machine): Unit = {
val scale = args.get(0).asInstanceOf[STNat].n
val prec = args.get(1).asInstanceOf[SInt64].value
val x = args.get(2).asInstanceOf[SNumeric].value
// FixMe: https://github.com/digital-asset/daml/issues/2289
// drop this double check
assert(x.scale == scale)
machine.ctrl = CtrlValue(
SNumeric(
rightOrArithmeticError(s"Error while rounding (Numeric $scale)", Numeric.round(prec, x)))
)
}
}

final case object SBCastNumeric extends SBuiltin(3) {
def execute(args: util.ArrayList[SValue], machine: Machine): Unit = {
val inputScale = args.get(0).asInstanceOf[STNat].n
val outputScale = args.get(1).asInstanceOf[STNat].n
val x = args.get(2).asInstanceOf[SNumeric].value
// FixMe: https://github.com/digital-asset/daml/issues/2289
// drop this double check
assert(x.scale == inputScale)
machine.ctrl = CtrlValue(
SNumeric(
rightOrArithmeticError(
s"Error while casting (Numeric $inputScale) to (Numeric $outputScale)",
Numeric.fromBigDecimal(outputScale, x))))
}
}

final case object SBShiftNumeric extends SBuiltin(3) {
def execute(args: util.ArrayList[SValue], machine: Machine): Unit = {
val inputScale = args.get(0).asInstanceOf[STNat].n
val outputScale = args.get(1).asInstanceOf[STNat].n
val x = args.get(2).asInstanceOf[SNumeric].value
// FixMe: https://github.com/digital-asset/daml/issues/2289
// drop this double check
assert(x.scale == inputScale)
machine.ctrl = CtrlValue(
SNumeric(
rightOrArithmeticError(
s"Error while shifting (Numeric $inputScale) to (Numeric $outputScale)",
Numeric.fromBigDecimal(outputScale, x.scaleByPowerOfTen(inputScale - outputScale))
)))
}
}

//
// Text functions
//
Expand Down Expand Up @@ -518,21 +566,6 @@ object SBuiltin {
}
}

final case object SBRoundNumeric extends SBuiltin(3) {
def execute(args: util.ArrayList[SValue], machine: Machine): Unit = {
val scale = args.get(0).asInstanceOf[STNat].n
val prec = args.get(1).asInstanceOf[SInt64].value
val x = args.get(2).asInstanceOf[SNumeric].value
// FixMe: https://github.com/digital-asset/daml/issues/2289
// drop this double check
assert(x.scale == scale)
machine.ctrl = CtrlValue(
SNumeric(
rightOrArithmeticError(s"Error while rounding (Numeric $scale)", Numeric.round(prec, x)))
)
}
}

//
// Equality and comparisons
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class SBuiltinTest extends FreeSpec with Matchers with TableDrivenPropertyChecks

private def tenPowerOf(i: Int, scale: Int = 10) =
if (i == 0)
"0." + "0" * scale
"1." + "0" * scale
else if (i > 0)
"1" + "0" * i + "." + "0" * scale
else
Expand Down Expand Up @@ -371,6 +371,73 @@ class SBuiltinTest extends FreeSpec with Matchers with TableDrivenPropertyChecks
}
}

"CAST_NUMERIC" - {
"throws an error in case of overflow" in {
val testCases = Table[Int, Int, String](
("input scale", "output scale", "x"),
(0, 1, s(0, Numeric.maxValue(0))),
(0, 37, "10."),
(20, 30, tenPowerOf(15, 20)),
(36, 37, s(36, Numeric.minValue(36))),
)

forEvery(testCases) { (inputScale, outputScale, x) =>
eval(e"CAST_NUMERIC @$inputScale @$outputScale $x") shouldBe 'left
}

}

"throws an error in case of precision loss" in {
val testCases = Table[Int, Int, String](
("input scale", "output scale", "x"),
(1, 0, tenPowerOf(-1, 1)),
(37, 0, tenPowerOf(-37, 37)),
(20, 10, "-" + tenPowerOf(-15, 20)),
(37, 36, tenPowerOf(-37, 37)),
)

forEvery(testCases) { (inputScale, outputScale, x) =>
eval(e"CAST_NUMERIC @$inputScale @$outputScale $x") shouldBe 'left
}
}

"returns proper result" in {
val testCases = Table[Int, Int, String](
("input scale", "output scale", "x"),
(1, 0, "1.0"),
(10, 20, tenPowerOf(-5, 10)),
(20, 10, tenPowerOf(-5, 20)),
(10, 20, tenPowerOf(10, 10)),
(20, 10, tenPowerOf(10, 20)),
)
forEvery(testCases) { (inputScale, outputScale, x) =>
eval(e"CAST_NUMERIC @$inputScale @$outputScale $x") shouldBe Right(
SNumeric(n(outputScale, x)))
}
}
}

"SHIFT_NUMERIC" - {

"returns proper result" in {
val testCases = Table[Int, Int, String, String](
("input scale", "output scale", "input", "output"),
(0, 1, s(0, Numeric.maxValue(0)), s(1, Numeric.maxValue(1))),
(0, 37, tenPowerOf(1, 0), tenPowerOf(-36, 37)),
(20, 30, tenPowerOf(15, 20), tenPowerOf(5, 30)),
(20, 10, tenPowerOf(15, 20), tenPowerOf(25, 10)),
(10, 20, tenPowerOf(-5, 10), tenPowerOf(-15, 20)),
(20, 10, tenPowerOf(-5, 20), tenPowerOf(5, 10)),
(10, 20, tenPowerOf(10, 10), tenPowerOf(0, 20)),
(20, 10, tenPowerOf(10, 20), tenPowerOf(20, 10)),
)
forEvery(testCases) { (inputScale, outputScale, input, output) =>
eval(e"SHIFT_NUMERIC @$inputScale @$outputScale $input") shouldBe Right(
SNumeric(n(outputScale, output)))
}
}
}

}

"Text operations" - {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,8 @@ object Ast {
final case object BMulNumeric extends BuiltinFunction(2) // : ∀s. Numeric s → Numeric s → Numeric s
final case object BDivNumeric extends BuiltinFunction(2) // : ∀s. Numeric s → Numeric s → Numeric s
final case object BRoundNumeric extends BuiltinFunction(2) // : ∀s. Integer → Numeric s → Numeric s
final case object BCastNumeric extends BuiltinFunction(1) // : ∀s1 s2. Numeric s1 → Numeric s2
final case object BShiftNumeric extends BuiltinFunction(1) // : ∀s1 s2. Numeric s1 → Numeric s2

// Int64 arithmetic
final case object BAddInt64 extends BuiltinFunction(2) // : Int64 → Int64 → Int64
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,8 @@ private[parser] class ExprParser[P](parserParameters: ParserParameters[P]) {
"MUL_NUMERIC" -> BMulNumeric,
"DIV_NUMERIC" -> BDivNumeric,
"ROUND_NUMERIC" -> BRoundNumeric,
"CAST_NUMERIC" -> BCastNumeric,
"SHIFT_NUMERIC" -> BShiftNumeric,
"ADD_INT64" -> BAddInt64,
"SUB_INT64" -> BSubInt64,
"MUL_INT64" -> BMulInt64,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ class ParsersSpec extends WordSpec with TableDrivenPropertyChecks with Matchers
"MUL_NUMERIC" -> BMulNumeric,
"DIV_NUMERIC" -> BDivNumeric,
"ROUND_NUMERIC" -> BRoundNumeric,
"CAST_NUMERIC" -> BCastNumeric,
"SHIFT_NUMERIC" -> BShiftNumeric,
"ADD_INT64" -> BAddInt64,
"SUB_INT64" -> BSubInt64,
"MUL_INT64" -> BMulInt64,
Expand Down
15 changes: 14 additions & 1 deletion daml-lf/spec/daml-lf-1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ We can now define all the literals that a program can handle::
n ∈ \d+

64-bit integer literals:
LitInt64 ∈ (-?)\d+ -- LitInt64:
LitInt64 ∈ (-?)\d+ -- LitInt64

Numeric literals:
LitNumeric ∈ ([+-]?)([1-9]\d+|0).\d* -- LitNumeric
Expand Down Expand Up @@ -2148,6 +2148,19 @@ Numeric functions
`α`. Throws an exception if the integer is not between `α-37` and
`α` inclusive.


* ``CAST_NUMERIC : ∀ (α₁, α₂: nat) . 'Numeric' α₁ → 'Numeric' α₂``

Converts a decimal of scale `α₁` to a decimal scale `α₂` while
keeping the value the same. Throws an exception in case of
overflow or precision loss.

* ``SHIFT_NUMERIC : ∀ (α₁, α₂: nat) . 'Int64' → 'Numeric' α₁ → 'Numeric' α₂``

Converts a decimal of scale `α₁` to a decimal scale `α₂` to another
by shifting the decimal point. Thus the ouput will be equal to the input
multiplied by `1E(α₁-α₂)`.

* ``LESS_EQ_NUMERIC : ∀ (α : nat) . 'Numeric' α → 'Numeric' α → 'Bool'``

Returns ``'True'`` if the first numeric is less or equal than the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@ private[validation] object Typing {
val alpha = TVar(Name.assertFromString("$alpha$"))
val beta = TVar(Name.assertFromString("$beta$"))
def tBinop(typ: Type): Type = typ ->: typ ->: typ
def tNumBinop = TForall(alpha.name -> KNat, tBinop(TNumeric(alpha)))
val tNumBinop = TForall(alpha.name -> KNat, tBinop(TNumeric(alpha)))
val tNumConversion =
TForall(alpha.name -> KNat, TForall(beta.name -> KNat, TNumeric(alpha) ->: TNumeric(beta)))
def tComparison(bType: BuiltinType): Type = TBuiltin(bType) ->: TBuiltin(bType) ->: TBool
def tNumComparison = TForall(alpha.name -> KNat, TNumeric(alpha) ->: TNumeric(alpha) ->: TBool)
val tNumComparison = TForall(alpha.name -> KNat, TNumeric(alpha) ->: TNumeric(alpha) ->: TBool)

Map[BuiltinFunction, Type](
BTrace -> TForall(alpha.name -> KStar, TText ->: alpha ->: alpha),
Expand All @@ -55,6 +57,8 @@ private[validation] object Typing {
BMulNumeric -> tNumBinop,
BDivNumeric -> tNumBinop,
BRoundNumeric -> TForall(alpha.name -> KNat, TInt64 ->: TNumeric(alpha) ->: TNumeric(alpha)),
BCastNumeric -> tNumConversion,
BShiftNumeric -> tNumConversion,
// Int64 arithmetic
BAddInt64 -> tBinop(TInt64),
BSubInt64 -> tBinop(TInt64),
Expand Down
1 change: 1 addition & 0 deletions unreleased.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ This page contains release notes for the SDK.
HEAD — ongoing
--------------

+ [DAML-LF] add CAST_NUMERIC and SHIFT_NUMERIC in DAML-LF 1.dev

0 comments on commit dc9429b

Please sign in to comment.