mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 09:17:43 +03:00
Daml-LF: make MUL_NUMERIC and DIV_NUMERIC multi-scale (#2921)
* daml-lf: Make MUL_NUMERIC and DIV_NUMERIC multi-scale * update release notes * compiler: fix with type change
This commit is contained in:
parent
f32f1b975e
commit
220a03c9e8
@ -134,15 +134,17 @@ mkAnds [x] = x
|
|||||||
mkAnds (x:xs) = mkAnd x $ mkAnds xs
|
mkAnds (x:xs) = mkAnd x $ mkAnds xs
|
||||||
|
|
||||||
|
|
||||||
alpha, beta :: TypeVarName
|
alpha, beta, gamma :: TypeVarName
|
||||||
-- NOTE(MH): We want to avoid shadowing variables in the environment. That's
|
-- NOTE(MH): We want to avoid shadowing variables in the environment. That's
|
||||||
-- what the weird names are for.
|
-- what the weird names are for.
|
||||||
alpha = TypeVarName "::alpha::"
|
alpha = TypeVarName "::alpha::"
|
||||||
beta = TypeVarName "::beta::"
|
beta = TypeVarName "::beta::"
|
||||||
|
gamma = TypeVarName "::gamma::"
|
||||||
|
|
||||||
tAlpha, tBeta :: Type
|
tAlpha, tBeta, tGamma :: Type
|
||||||
tAlpha = TVar alpha
|
tAlpha = TVar alpha
|
||||||
tBeta = TVar beta
|
tBeta = TVar beta
|
||||||
|
tGamma = TVar gamma
|
||||||
|
|
||||||
|
|
||||||
infixr 1 :->
|
infixr 1 :->
|
||||||
|
@ -177,8 +177,8 @@ typeOfBuiltin = \case
|
|||||||
BEGreaterEqNumeric -> pure $ TForall (alpha, KNat) $ TNumeric tAlpha :-> TNumeric tAlpha :-> TBool
|
BEGreaterEqNumeric -> pure $ TForall (alpha, KNat) $ TNumeric tAlpha :-> TNumeric tAlpha :-> TBool
|
||||||
BEAddNumeric -> pure $ TForall (alpha, KNat) $ TNumeric tAlpha :-> TNumeric tAlpha :-> TNumeric tAlpha
|
BEAddNumeric -> pure $ TForall (alpha, KNat) $ TNumeric tAlpha :-> TNumeric tAlpha :-> TNumeric tAlpha
|
||||||
BESubNumeric -> pure $ TForall (alpha, KNat) $ TNumeric tAlpha :-> TNumeric tAlpha :-> TNumeric tAlpha
|
BESubNumeric -> pure $ TForall (alpha, KNat) $ TNumeric tAlpha :-> TNumeric tAlpha :-> TNumeric tAlpha
|
||||||
BEMulNumeric -> pure $ TForall (alpha, KNat) $ TNumeric tAlpha :-> TNumeric tAlpha :-> TNumeric tAlpha
|
BEMulNumeric -> pure $ TForall (alpha, KNat) $ TForall (beta, KNat) $ TForall (gamma, KNat) $ TNumeric tAlpha :-> TNumeric tBeta :-> TNumeric tGamma
|
||||||
BEDivNumeric -> pure $ TForall (alpha, KNat) $ TNumeric tAlpha :-> TNumeric tAlpha :-> TNumeric tAlpha
|
BEDivNumeric -> pure $ TForall (alpha, KNat) $ TForall (beta, KNat) $ TForall (gamma, KNat) $ TNumeric tAlpha :-> TNumeric tBeta :-> TNumeric tGamma
|
||||||
BERoundNumeric -> pure $ TForall (alpha, KNat) $ TInt64 :-> TNumeric tAlpha :-> TNumeric tAlpha
|
BERoundNumeric -> pure $ TForall (alpha, KNat) $ TInt64 :-> TNumeric tAlpha :-> TNumeric tAlpha
|
||||||
BEInt64ToNumeric -> pure $ TForall (alpha, KNat) $ TInt64 :-> TNumeric tAlpha
|
BEInt64ToNumeric -> pure $ TForall (alpha, KNat) $ TInt64 :-> TNumeric tAlpha
|
||||||
BENumericToInt64 -> pure $ TForall (alpha, KNat) $ TNumeric tAlpha :-> TInt64
|
BENumericToInt64 -> pure $ TForall (alpha, KNat) $ TNumeric tAlpha :-> TInt64
|
||||||
|
@ -164,9 +164,9 @@ convertPrim _ "BEAddDecimal" (TNumeric10 :-> TNumeric10 :-> TNumeric10) =
|
|||||||
convertPrim _ "BESubDecimal" (TNumeric10 :-> TNumeric10 :-> TNumeric10) =
|
convertPrim _ "BESubDecimal" (TNumeric10 :-> TNumeric10 :-> TNumeric10) =
|
||||||
ETyApp (EBuiltin BESubNumeric) (TNat 10)
|
ETyApp (EBuiltin BESubNumeric) (TNat 10)
|
||||||
convertPrim _ "BEMulDecimal" (TNumeric10 :-> TNumeric10 :-> TNumeric10) =
|
convertPrim _ "BEMulDecimal" (TNumeric10 :-> TNumeric10 :-> TNumeric10) =
|
||||||
ETyApp (EBuiltin BEMulNumeric) (TNat 10)
|
ETyApp (ETyApp (ETyApp (EBuiltin BEMulNumeric) (TNat 10)) (TNat 10)) (TNat 10)
|
||||||
convertPrim _ "BEDivDecimal" (TNumeric10 :-> TNumeric10 :-> TNumeric10) =
|
convertPrim _ "BEDivDecimal" (TNumeric10 :-> TNumeric10 :-> TNumeric10) =
|
||||||
ETyApp (EBuiltin BEDivNumeric) (TNat 10)
|
ETyApp (ETyApp (ETyApp (EBuiltin BEDivNumeric) (TNat 10)) (TNat 10)) (TNat 10)
|
||||||
convertPrim _ "BERoundDecimal" (TInt64 :-> TNumeric10 :-> TNumeric10) =
|
convertPrim _ "BERoundDecimal" (TInt64 :-> TNumeric10 :-> TNumeric10) =
|
||||||
ETyApp (EBuiltin BERoundNumeric) (TNat 10)
|
ETyApp (EBuiltin BERoundNumeric) (TNat 10)
|
||||||
convertPrim _ "BEEqual" (TNumeric10 :-> TNumeric10 :-> TBool) =
|
convertPrim _ "BEEqual" (TNumeric10 :-> TNumeric10 :-> TBool) =
|
||||||
|
@ -14,6 +14,7 @@ import com.digitalasset.daml.lf.language.{LanguageVersion => LV}
|
|||||||
import com.digitalasset.daml_lf.{DamlLf1 => PLF}
|
import com.digitalasset.daml_lf.{DamlLf1 => PLF}
|
||||||
import com.google.protobuf.CodedInputStream
|
import com.google.protobuf.CodedInputStream
|
||||||
|
|
||||||
|
import scala.annotation.tailrec
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
import scala.collection.{breakOut, mutable}
|
import scala.collection.{breakOut, mutable}
|
||||||
|
|
||||||
@ -398,17 +399,11 @@ private[archive] class DecodeV1(minor: LV.Minor) extends Decode.OfPackage[PLF.Pa
|
|||||||
case PLF.Expr.SumCase.BUILTIN =>
|
case PLF.Expr.SumCase.BUILTIN =>
|
||||||
val info = DecodeV1.builtinInfoMap(lfExpr.getBuiltin)
|
val info = DecodeV1.builtinInfoMap(lfExpr.getBuiltin)
|
||||||
assertSince(info.minVersion, lfExpr.getBuiltin.getValueDescriptor.getFullName)
|
assertSince(info.minVersion, lfExpr.getBuiltin.getValueDescriptor.getFullName)
|
||||||
|
info.maxVersion.foreach(assertUntil(_, lfExpr.getBuiltin.getValueDescriptor.getFullName))
|
||||||
if (info.handleLegacyDecimal) {
|
ntimes[Expr](
|
||||||
// FixMe: https://github.com/digital-asset/daml/issues/2289
|
info.implicitDecimalScaleParameters,
|
||||||
// enable the check once the compiler produces proper DAML-LF 1.dev
|
ETyApp(_, TDecimalScale),
|
||||||
// info.maxVersion.foreach(assertUntil(_, lfExpr.getBuiltin.getValueDescriptor.getFullName))
|
EBuiltin(info.builtin))
|
||||||
ETyApp(EBuiltin(info.builtin), TDecimalScale)
|
|
||||||
} else {
|
|
||||||
info.maxVersion.foreach(
|
|
||||||
assertUntil(_, lfExpr.getBuiltin.getValueDescriptor.getFullName))
|
|
||||||
EBuiltin(info.builtin)
|
|
||||||
}
|
|
||||||
|
|
||||||
case PLF.Expr.SumCase.REC_CON =>
|
case PLF.Expr.SumCase.REC_CON =>
|
||||||
val recCon = lfExpr.getRecCon
|
val recCon = lfExpr.getRecCon
|
||||||
@ -791,6 +786,10 @@ private[archive] class DecodeV1(minor: LV.Minor) extends Decode.OfPackage[PLF.Pa
|
|||||||
|
|
||||||
private[lf] object DecodeV1 {
|
private[lf] object DecodeV1 {
|
||||||
|
|
||||||
|
@tailrec
|
||||||
|
private def ntimes[A](n: Int, f: A => A, a: A): A =
|
||||||
|
if (n == 0) a else ntimes(n - 1, f, f(a))
|
||||||
|
|
||||||
case class BuiltinTypeInfo(
|
case class BuiltinTypeInfo(
|
||||||
proto: PLF.PrimType,
|
proto: PLF.PrimType,
|
||||||
bTyp: BuiltinType,
|
bTyp: BuiltinType,
|
||||||
@ -830,7 +829,7 @@ private[lf] object DecodeV1 {
|
|||||||
builtin: BuiltinFunction,
|
builtin: BuiltinFunction,
|
||||||
minVersion: LV = LV.Features.default, // first version that does support the builtin
|
minVersion: LV = LV.Features.default, // first version that does support the builtin
|
||||||
maxVersion: Option[LV] = None, // first version that does not support the builtin
|
maxVersion: Option[LV] = None, // first version that does not support the builtin
|
||||||
handleLegacyDecimal: Boolean = false
|
implicitDecimalScaleParameters: Int = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
val builtinFunctionInfos: List[BuiltinFunctionInfo] = {
|
val builtinFunctionInfos: List[BuiltinFunctionInfo] = {
|
||||||
@ -840,27 +839,32 @@ private[lf] object DecodeV1 {
|
|||||||
ADD_DECIMAL,
|
ADD_DECIMAL,
|
||||||
BAddNumeric,
|
BAddNumeric,
|
||||||
maxVersion = Some(numeric),
|
maxVersion = Some(numeric),
|
||||||
handleLegacyDecimal = true),
|
implicitDecimalScaleParameters = 1
|
||||||
|
),
|
||||||
BuiltinFunctionInfo(
|
BuiltinFunctionInfo(
|
||||||
SUB_DECIMAL,
|
SUB_DECIMAL,
|
||||||
BSubNumeric,
|
BSubNumeric,
|
||||||
maxVersion = Some(numeric),
|
maxVersion = Some(numeric),
|
||||||
handleLegacyDecimal = true),
|
implicitDecimalScaleParameters = 1
|
||||||
|
),
|
||||||
BuiltinFunctionInfo(
|
BuiltinFunctionInfo(
|
||||||
MUL_DECIMAL,
|
MUL_DECIMAL,
|
||||||
BMulNumeric,
|
BMulNumeric,
|
||||||
maxVersion = Some(numeric),
|
maxVersion = Some(numeric),
|
||||||
handleLegacyDecimal = true),
|
implicitDecimalScaleParameters = 3
|
||||||
|
),
|
||||||
BuiltinFunctionInfo(
|
BuiltinFunctionInfo(
|
||||||
DIV_DECIMAL,
|
DIV_DECIMAL,
|
||||||
BDivNumeric,
|
BDivNumeric,
|
||||||
maxVersion = Some(numeric),
|
maxVersion = Some(numeric),
|
||||||
handleLegacyDecimal = true),
|
implicitDecimalScaleParameters = 3
|
||||||
|
),
|
||||||
BuiltinFunctionInfo(
|
BuiltinFunctionInfo(
|
||||||
ROUND_DECIMAL,
|
ROUND_DECIMAL,
|
||||||
BRoundNumeric,
|
BRoundNumeric,
|
||||||
maxVersion = Some(numeric),
|
maxVersion = Some(numeric),
|
||||||
handleLegacyDecimal = true),
|
implicitDecimalScaleParameters = 1
|
||||||
|
),
|
||||||
BuiltinFunctionInfo(ADD_NUMERIC, BAddNumeric, minVersion = numeric),
|
BuiltinFunctionInfo(ADD_NUMERIC, BAddNumeric, minVersion = numeric),
|
||||||
BuiltinFunctionInfo(SUB_NUMERIC, BSubNumeric, minVersion = numeric),
|
BuiltinFunctionInfo(SUB_NUMERIC, BSubNumeric, minVersion = numeric),
|
||||||
BuiltinFunctionInfo(MUL_NUMERIC, BMulNumeric, minVersion = numeric),
|
BuiltinFunctionInfo(MUL_NUMERIC, BMulNumeric, minVersion = numeric),
|
||||||
@ -878,12 +882,14 @@ private[lf] object DecodeV1 {
|
|||||||
INT64_TO_DECIMAL,
|
INT64_TO_DECIMAL,
|
||||||
BInt64ToNumeric,
|
BInt64ToNumeric,
|
||||||
maxVersion = Some(numeric),
|
maxVersion = Some(numeric),
|
||||||
handleLegacyDecimal = true),
|
implicitDecimalScaleParameters = 1
|
||||||
|
),
|
||||||
BuiltinFunctionInfo(
|
BuiltinFunctionInfo(
|
||||||
DECIMAL_TO_INT64,
|
DECIMAL_TO_INT64,
|
||||||
BNumericToInt64,
|
BNumericToInt64,
|
||||||
maxVersion = Some(numeric),
|
maxVersion = Some(numeric),
|
||||||
handleLegacyDecimal = true),
|
implicitDecimalScaleParameters = 1
|
||||||
|
),
|
||||||
BuiltinFunctionInfo(INT64_TO_NUMERIC, BInt64ToNumeric, minVersion = numeric),
|
BuiltinFunctionInfo(INT64_TO_NUMERIC, BInt64ToNumeric, minVersion = numeric),
|
||||||
BuiltinFunctionInfo(NUMERIC_TO_INT64, BNumericToInt64, minVersion = numeric),
|
BuiltinFunctionInfo(NUMERIC_TO_INT64, BNumericToInt64, minVersion = numeric),
|
||||||
BuiltinFunctionInfo(FOLDL, BFoldl),
|
BuiltinFunctionInfo(FOLDL, BFoldl),
|
||||||
@ -901,7 +907,8 @@ private[lf] object DecodeV1 {
|
|||||||
LEQ_DECIMAL,
|
LEQ_DECIMAL,
|
||||||
BLessEqNumeric,
|
BLessEqNumeric,
|
||||||
maxVersion = Some(numeric),
|
maxVersion = Some(numeric),
|
||||||
handleLegacyDecimal = true),
|
implicitDecimalScaleParameters = 1
|
||||||
|
),
|
||||||
BuiltinFunctionInfo(LEQ_NUMERIC, BLessEqNumeric, minVersion = numeric),
|
BuiltinFunctionInfo(LEQ_NUMERIC, BLessEqNumeric, minVersion = numeric),
|
||||||
BuiltinFunctionInfo(LEQ_TEXT, BLessEqText),
|
BuiltinFunctionInfo(LEQ_TEXT, BLessEqText),
|
||||||
BuiltinFunctionInfo(LEQ_TIMESTAMP, BLessEqTimestamp),
|
BuiltinFunctionInfo(LEQ_TIMESTAMP, BLessEqTimestamp),
|
||||||
@ -911,7 +918,8 @@ private[lf] object DecodeV1 {
|
|||||||
GEQ_DECIMAL,
|
GEQ_DECIMAL,
|
||||||
BGreaterEqNumeric,
|
BGreaterEqNumeric,
|
||||||
maxVersion = Some(numeric),
|
maxVersion = Some(numeric),
|
||||||
handleLegacyDecimal = true),
|
implicitDecimalScaleParameters = 1
|
||||||
|
),
|
||||||
BuiltinFunctionInfo(GEQ_NUMERIC, BGreaterEqNumeric, minVersion = numeric),
|
BuiltinFunctionInfo(GEQ_NUMERIC, BGreaterEqNumeric, minVersion = numeric),
|
||||||
BuiltinFunctionInfo(GEQ_TEXT, BGreaterEqText),
|
BuiltinFunctionInfo(GEQ_TEXT, BGreaterEqText),
|
||||||
BuiltinFunctionInfo(GEQ_TIMESTAMP, BGreaterEqTimestamp),
|
BuiltinFunctionInfo(GEQ_TIMESTAMP, BGreaterEqTimestamp),
|
||||||
@ -921,7 +929,8 @@ private[lf] object DecodeV1 {
|
|||||||
LESS_DECIMAL,
|
LESS_DECIMAL,
|
||||||
BLessNumeric,
|
BLessNumeric,
|
||||||
maxVersion = Some(numeric),
|
maxVersion = Some(numeric),
|
||||||
handleLegacyDecimal = true),
|
implicitDecimalScaleParameters = 1
|
||||||
|
),
|
||||||
BuiltinFunctionInfo(LESS_NUMERIC, BLessNumeric, minVersion = numeric),
|
BuiltinFunctionInfo(LESS_NUMERIC, BLessNumeric, minVersion = numeric),
|
||||||
BuiltinFunctionInfo(LESS_TEXT, BLessText),
|
BuiltinFunctionInfo(LESS_TEXT, BLessText),
|
||||||
BuiltinFunctionInfo(LESS_TIMESTAMP, BLessTimestamp),
|
BuiltinFunctionInfo(LESS_TIMESTAMP, BLessTimestamp),
|
||||||
@ -931,7 +940,8 @@ private[lf] object DecodeV1 {
|
|||||||
GREATER_DECIMAL,
|
GREATER_DECIMAL,
|
||||||
BGreaterNumeric,
|
BGreaterNumeric,
|
||||||
maxVersion = Some(numeric),
|
maxVersion = Some(numeric),
|
||||||
handleLegacyDecimal = true),
|
implicitDecimalScaleParameters = 1
|
||||||
|
),
|
||||||
BuiltinFunctionInfo(GREATER_NUMERIC, BGreaterNumeric, minVersion = numeric),
|
BuiltinFunctionInfo(GREATER_NUMERIC, BGreaterNumeric, minVersion = numeric),
|
||||||
BuiltinFunctionInfo(GREATER_TEXT, BGreaterText),
|
BuiltinFunctionInfo(GREATER_TEXT, BGreaterText),
|
||||||
BuiltinFunctionInfo(GREATER_TIMESTAMP, BGreaterTimestamp),
|
BuiltinFunctionInfo(GREATER_TIMESTAMP, BGreaterTimestamp),
|
||||||
@ -941,7 +951,8 @@ private[lf] object DecodeV1 {
|
|||||||
TO_TEXT_DECIMAL,
|
TO_TEXT_DECIMAL,
|
||||||
BToTextNumeric,
|
BToTextNumeric,
|
||||||
maxVersion = Some(numeric),
|
maxVersion = Some(numeric),
|
||||||
handleLegacyDecimal = true),
|
implicitDecimalScaleParameters = 1
|
||||||
|
),
|
||||||
BuiltinFunctionInfo(TO_TEXT_NUMERIC, BToTextNumeric, minVersion = numeric),
|
BuiltinFunctionInfo(TO_TEXT_NUMERIC, BToTextNumeric, minVersion = numeric),
|
||||||
BuiltinFunctionInfo(TO_TEXT_TIMESTAMP, BToTextTimestamp),
|
BuiltinFunctionInfo(TO_TEXT_TIMESTAMP, BToTextTimestamp),
|
||||||
BuiltinFunctionInfo(TO_TEXT_PARTY, BToTextParty, minVersion = partyTextConversions),
|
BuiltinFunctionInfo(TO_TEXT_PARTY, BToTextParty, minVersion = partyTextConversions),
|
||||||
@ -953,7 +964,7 @@ private[lf] object DecodeV1 {
|
|||||||
BuiltinFunctionInfo(
|
BuiltinFunctionInfo(
|
||||||
FROM_TEXT_DECIMAL,
|
FROM_TEXT_DECIMAL,
|
||||||
BFromTextNumeric,
|
BFromTextNumeric,
|
||||||
handleLegacyDecimal = true,
|
implicitDecimalScaleParameters = 1,
|
||||||
minVersion = numberParsing,
|
minVersion = numberParsing,
|
||||||
maxVersion = Some(numeric)),
|
maxVersion = Some(numeric)),
|
||||||
BuiltinFunctionInfo(FROM_TEXT_NUMERIC, BFromTextNumeric, minVersion = numeric),
|
BuiltinFunctionInfo(FROM_TEXT_NUMERIC, BFromTextNumeric, minVersion = numeric),
|
||||||
@ -975,7 +986,8 @@ private[lf] object DecodeV1 {
|
|||||||
EQUAL_DECIMAL,
|
EQUAL_DECIMAL,
|
||||||
BEqualNumeric,
|
BEqualNumeric,
|
||||||
maxVersion = Some(numeric),
|
maxVersion = Some(numeric),
|
||||||
handleLegacyDecimal = true),
|
implicitDecimalScaleParameters = 1
|
||||||
|
),
|
||||||
BuiltinFunctionInfo(EQUAL_NUMERIC, BEqualNumeric, minVersion = numeric),
|
BuiltinFunctionInfo(EQUAL_NUMERIC, BEqualNumeric, minVersion = numeric),
|
||||||
BuiltinFunctionInfo(EQUAL_TEXT, BEqualText),
|
BuiltinFunctionInfo(EQUAL_TEXT, BEqualText),
|
||||||
BuiltinFunctionInfo(EQUAL_TIMESTAMP, BEqualTimestamp),
|
BuiltinFunctionInfo(EQUAL_TIMESTAMP, BEqualTimestamp),
|
||||||
|
@ -7,11 +7,11 @@ import java.math.BigDecimal
|
|||||||
import java.nio.file.{Files, Paths}
|
import java.nio.file.{Files, Paths}
|
||||||
|
|
||||||
import com.digitalasset.daml.bazeltools.BazelRunfiles._
|
import com.digitalasset.daml.bazeltools.BazelRunfiles._
|
||||||
import com.digitalasset.daml.lf.archive.DecodeV1.BuiltinFunctionInfo
|
|
||||||
import com.digitalasset.daml.lf.archive.Reader.ParseError
|
import com.digitalasset.daml.lf.archive.Reader.ParseError
|
||||||
import com.digitalasset.daml.lf.data.{ImmArray, Ref}
|
import com.digitalasset.daml.lf.data.{ImmArray, Ref}
|
||||||
import com.digitalasset.daml.lf.language.Util._
|
import com.digitalasset.daml.lf.language.Util._
|
||||||
import com.digitalasset.daml.lf.language.{Ast, LanguageVersion => LV}
|
import com.digitalasset.daml.lf.language.{Ast, LanguageMinorVersion, LanguageVersion => LV}
|
||||||
|
import LanguageMinorVersion.Implicits._
|
||||||
import com.digitalasset.daml_lf.DamlLf1
|
import com.digitalasset.daml_lf.DamlLf1
|
||||||
import org.scalatest.prop.TableDrivenPropertyChecks
|
import org.scalatest.prop.TableDrivenPropertyChecks
|
||||||
import org.scalatest.{Inside, Matchers, OptionValues, WordSpec}
|
import org.scalatest.{Inside, Matchers, OptionValues, WordSpec}
|
||||||
@ -217,8 +217,8 @@ class DecodeV1Spec
|
|||||||
|
|
||||||
"decodeExpr" should {
|
"decodeExpr" should {
|
||||||
|
|
||||||
def toProto(b: BuiltinFunctionInfo) =
|
def toProtoExpr(b: DamlLf1.BuiltinFunction) =
|
||||||
DamlLf1.Expr.newBuilder().setBuiltin(b.proto).build()
|
DamlLf1.Expr.newBuilder().setBuiltin(b).build()
|
||||||
|
|
||||||
def toDecimalProto(s: String) =
|
def toDecimalProto(s: String) =
|
||||||
DamlLf1.Expr.newBuilder().setPrimLit(DamlLf1.PrimLit.newBuilder().setDecimal(s)).build()
|
DamlLf1.Expr.newBuilder().setPrimLit(DamlLf1.PrimLit.newBuilder().setDecimal(s)).build()
|
||||||
@ -226,39 +226,102 @@ class DecodeV1Spec
|
|||||||
def toNumericProto(s: String) =
|
def toNumericProto(s: String) =
|
||||||
DamlLf1.Expr.newBuilder().setPrimLit(DamlLf1.PrimLit.newBuilder().setNumeric(s)).build()
|
DamlLf1.Expr.newBuilder().setPrimLit(DamlLf1.PrimLit.newBuilder().setNumeric(s)).build()
|
||||||
|
|
||||||
def toScala(b: BuiltinFunctionInfo) =
|
val decimalBuiltinTestCases = Table[DamlLf1.BuiltinFunction, LanguageMinorVersion, Ast.Expr](
|
||||||
Ast.EBuiltin(b.builtin)
|
("decimal builtins", "minVersion", "expected output"),
|
||||||
|
(
|
||||||
|
DamlLf1.BuiltinFunction.ADD_DECIMAL,
|
||||||
|
"1",
|
||||||
|
Ast.ETyApp(Ast.EBuiltin(Ast.BAddNumeric), TDecimalScale)),
|
||||||
|
(
|
||||||
|
DamlLf1.BuiltinFunction.SUB_DECIMAL,
|
||||||
|
"1",
|
||||||
|
Ast.ETyApp(Ast.EBuiltin(Ast.BSubNumeric), TDecimalScale)),
|
||||||
|
(
|
||||||
|
DamlLf1.BuiltinFunction.MUL_DECIMAL,
|
||||||
|
"1",
|
||||||
|
Ast.ETyApp(
|
||||||
|
Ast.ETyApp(Ast.ETyApp(Ast.EBuiltin(Ast.BMulNumeric), TDecimalScale), TDecimalScale),
|
||||||
|
TDecimalScale)),
|
||||||
|
(
|
||||||
|
DamlLf1.BuiltinFunction.DIV_DECIMAL,
|
||||||
|
"1",
|
||||||
|
Ast.ETyApp(
|
||||||
|
Ast.ETyApp(Ast.ETyApp(Ast.EBuiltin(Ast.BDivNumeric), TDecimalScale), TDecimalScale),
|
||||||
|
TDecimalScale)),
|
||||||
|
(
|
||||||
|
DamlLf1.BuiltinFunction.ROUND_DECIMAL,
|
||||||
|
"1",
|
||||||
|
Ast.ETyApp(Ast.EBuiltin(Ast.BRoundNumeric), TDecimalScale)),
|
||||||
|
(
|
||||||
|
DamlLf1.BuiltinFunction.LEQ_DECIMAL,
|
||||||
|
"1",
|
||||||
|
Ast.ETyApp(Ast.EBuiltin(Ast.BLessEqNumeric), TDecimalScale)),
|
||||||
|
(
|
||||||
|
DamlLf1.BuiltinFunction.LESS_DECIMAL,
|
||||||
|
"1",
|
||||||
|
Ast.ETyApp(Ast.EBuiltin(Ast.BLessNumeric), TDecimalScale)),
|
||||||
|
(
|
||||||
|
DamlLf1.BuiltinFunction.GEQ_DECIMAL,
|
||||||
|
"1",
|
||||||
|
Ast.ETyApp(Ast.EBuiltin(Ast.BGreaterEqNumeric), TDecimalScale)),
|
||||||
|
(
|
||||||
|
DamlLf1.BuiltinFunction.GREATER_DECIMAL,
|
||||||
|
"1",
|
||||||
|
Ast.ETyApp(Ast.EBuiltin(Ast.BGreaterNumeric), TDecimalScale)),
|
||||||
|
(
|
||||||
|
DamlLf1.BuiltinFunction.TO_TEXT_DECIMAL,
|
||||||
|
"1",
|
||||||
|
Ast.ETyApp(Ast.EBuiltin(Ast.BToTextNumeric), TDecimalScale)),
|
||||||
|
(
|
||||||
|
DamlLf1.BuiltinFunction.FROM_TEXT_DECIMAL,
|
||||||
|
"5",
|
||||||
|
Ast.ETyApp(Ast.EBuiltin(Ast.BFromTextNumeric), TDecimalScale)),
|
||||||
|
(
|
||||||
|
DamlLf1.BuiltinFunction.INT64_TO_DECIMAL,
|
||||||
|
"1",
|
||||||
|
Ast.ETyApp(Ast.EBuiltin(Ast.BInt64ToNumeric), TDecimalScale)),
|
||||||
|
(
|
||||||
|
DamlLf1.BuiltinFunction.DECIMAL_TO_INT64,
|
||||||
|
"1",
|
||||||
|
Ast.ETyApp(Ast.EBuiltin(Ast.BNumericToInt64), TDecimalScale)),
|
||||||
|
(
|
||||||
|
DamlLf1.BuiltinFunction.EQUAL_DECIMAL,
|
||||||
|
"1",
|
||||||
|
Ast.ETyApp(Ast.EBuiltin(Ast.BEqualNumeric), TDecimalScale)),
|
||||||
|
)
|
||||||
|
|
||||||
// All the legacy decimal bultins.
|
val numericBuiltinTestCases = Table(
|
||||||
val decimalBuilttins =
|
"numeric builtins" -> "expected output",
|
||||||
DecodeV1.builtinFunctionInfos.filter(_.handleLegacyDecimal)
|
DamlLf1.BuiltinFunction.ADD_NUMERIC -> Ast.EBuiltin(Ast.BAddNumeric),
|
||||||
|
DamlLf1.BuiltinFunction.SUB_NUMERIC -> Ast.EBuiltin(Ast.BSubNumeric),
|
||||||
|
DamlLf1.BuiltinFunction.MUL_NUMERIC -> Ast.EBuiltin(Ast.BMulNumeric),
|
||||||
|
DamlLf1.BuiltinFunction.DIV_NUMERIC -> Ast.EBuiltin(Ast.BDivNumeric),
|
||||||
|
DamlLf1.BuiltinFunction.ROUND_NUMERIC -> Ast.EBuiltin(Ast.BRoundNumeric),
|
||||||
|
DamlLf1.BuiltinFunction.LEQ_NUMERIC -> Ast.EBuiltin(Ast.BLessEqNumeric),
|
||||||
|
DamlLf1.BuiltinFunction.LESS_NUMERIC -> Ast.EBuiltin(Ast.BLessNumeric),
|
||||||
|
DamlLf1.BuiltinFunction.GEQ_NUMERIC -> Ast.EBuiltin(Ast.BGreaterEqNumeric),
|
||||||
|
DamlLf1.BuiltinFunction.GREATER_NUMERIC -> Ast.EBuiltin(Ast.BGreaterNumeric),
|
||||||
|
DamlLf1.BuiltinFunction.TO_TEXT_NUMERIC -> Ast.EBuiltin(Ast.BToTextNumeric),
|
||||||
|
DamlLf1.BuiltinFunction.FROM_TEXT_NUMERIC -> Ast.EBuiltin(Ast.BFromTextNumeric),
|
||||||
|
DamlLf1.BuiltinFunction.INT64_TO_NUMERIC -> Ast.EBuiltin(Ast.BInt64ToNumeric),
|
||||||
|
DamlLf1.BuiltinFunction.NUMERIC_TO_INT64 -> Ast.EBuiltin(Ast.BNumericToInt64),
|
||||||
|
DamlLf1.BuiltinFunction.EQUAL_NUMERIC -> Ast.EBuiltin(Ast.BEqualNumeric),
|
||||||
|
)
|
||||||
|
|
||||||
// All the numeric versions of the former.
|
val negativeBuiltinTestCases = Table(
|
||||||
val numericBuilttins = {
|
"other builtins" -> "expected output",
|
||||||
val isNumeric = decimalBuilttins.map(_.builtin).toSet
|
// We do not need to test all other builtin
|
||||||
DecodeV1.builtinFunctionInfos.filter(info =>
|
DamlLf1.BuiltinFunction.ADD_INT64 -> Ast.EBuiltin(Ast.BAddInt64),
|
||||||
!info.handleLegacyDecimal && isNumeric(info.builtin))
|
DamlLf1.BuiltinFunction.APPEND_TEXT -> Ast.EBuiltin(Ast.BAppendText)
|
||||||
}
|
)
|
||||||
|
|
||||||
// Two other unrelated builtins, no need to test more.
|
|
||||||
val otherBuiltins =
|
|
||||||
DecodeV1.builtinFunctionInfos.filter(
|
|
||||||
info =>
|
|
||||||
info.proto == DamlLf1.BuiltinFunction.ADD_INT64 ||
|
|
||||||
info.proto == DamlLf1.BuiltinFunction.APPEND_TEXT)
|
|
||||||
assert(otherBuiltins.length == 2)
|
|
||||||
|
|
||||||
val decimalBuiltinTestCases = Table("decimal builtinInfo", numericBuilttins: _*)
|
|
||||||
val numericBuiltinTestCases = Table("numeric builtinInfo", numericBuilttins: _*)
|
|
||||||
val negativeBuiltinTestCases = Table("other builtinInfo", otherBuiltins: _*)
|
|
||||||
|
|
||||||
"translate non numeric/decimal builtin as is for any version" in {
|
"translate non numeric/decimal builtin as is for any version" in {
|
||||||
val allVersions = Table("all versions", preNumericMinVersions ++ postNumericMinVersions: _*)
|
val allVersions = Table("all versions", preNumericMinVersions ++ postNumericMinVersions: _*)
|
||||||
|
|
||||||
forEvery(allVersions) { version =>
|
forEvery(allVersions) { version =>
|
||||||
val decoder = moduleDecoder(version)
|
val decoder = moduleDecoder(version)
|
||||||
forEvery(negativeBuiltinTestCases) { info =>
|
forEvery(negativeBuiltinTestCases) { (proto, scala) =>
|
||||||
decoder.decodeExpr(toProto(info), "test") shouldBe toScala(info)
|
decoder.decodeExpr(toProtoExpr(proto), "test") shouldBe scala
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -268,10 +331,9 @@ class DecodeV1Spec
|
|||||||
forEvery(preNumericMinVersions) { version =>
|
forEvery(preNumericMinVersions) { version =>
|
||||||
val decoder = moduleDecoder(version)
|
val decoder = moduleDecoder(version)
|
||||||
|
|
||||||
forEvery(decimalBuiltinTestCases) { info =>
|
forEvery(decimalBuiltinTestCases) { (proto, minVersion, scala) =>
|
||||||
if (LV.ordering.gteq(LV(LV.Major.V1, version), info.minVersion))
|
if (LV.Major.V1.minorVersionOrdering.gteq(version, minVersion))
|
||||||
decoder.decodeExpr(toProto(info), "test") shouldBe Ast
|
decoder.decodeExpr(toProtoExpr(proto), "test") shouldBe scala
|
||||||
.ETyApp(toScala(info), Ast.TNat(10))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -281,20 +343,19 @@ class DecodeV1Spec
|
|||||||
forEvery(preNumericMinVersions) { version =>
|
forEvery(preNumericMinVersions) { version =>
|
||||||
val decoder = moduleDecoder(version)
|
val decoder = moduleDecoder(version)
|
||||||
|
|
||||||
forEvery(numericBuiltinTestCases) { info =>
|
forEvery(numericBuiltinTestCases) { (proto, _) =>
|
||||||
an[ParseError] shouldBe thrownBy(decoder.decodeExpr(toProto(info), "test"))
|
an[ParseError] shouldBe thrownBy(decoder.decodeExpr(toProtoExpr(proto), "test"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"translate Numeric builtins as is if version >= 1.dev" in {
|
"translate Numeric builtins as is if version >= 1.dev" in {
|
||||||
|
|
||||||
forEvery(preNumericMinVersions) { version =>
|
forEvery(postNumericMinVersions) { version =>
|
||||||
val decoder = moduleDecoder(version)
|
val decoder = moduleDecoder(version)
|
||||||
|
|
||||||
forEvery(numericBuiltinTestCases) { info =>
|
forEvery(numericBuiltinTestCases) { (proto, scala) =>
|
||||||
if (LV.ordering.gteq(LV(LV.Major.V1, version), info.minVersion))
|
decoder.decodeExpr(toProtoExpr(proto), "test") shouldBe scala
|
||||||
decoder.decodeExpr(toProto(info), "test") shouldBe toScala(info)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -303,11 +364,11 @@ class DecodeV1Spec
|
|||||||
// reactive the test once the decoder is not so lenient
|
// reactive the test once the decoder is not so lenient
|
||||||
"reject Decimal builtins if version >= 1.dev" ignore {
|
"reject Decimal builtins if version >= 1.dev" ignore {
|
||||||
|
|
||||||
forEvery(preNumericMinVersions) { version =>
|
forEvery(postNumericMinVersions) { version =>
|
||||||
val decoder = moduleDecoder(version)
|
val decoder = moduleDecoder(version)
|
||||||
|
|
||||||
forEvery(decimalBuiltinTestCases) { info =>
|
forEvery(decimalBuiltinTestCases) { (proto, _, _) =>
|
||||||
an[ParseError] shouldBe thrownBy(decoder.decodeExpr(toProto(info), "test"))
|
an[ParseError] shouldBe thrownBy(decoder.decodeExpr(toProtoExpr(proto), "test"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,29 +98,24 @@ abstract class NumericModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Multiplies `x` by `y`. The output has the same scale as the inputs. If rounding must be
|
* Multiplies `x` by `y`. The output has the scale `scale`. If rounding must be
|
||||||
* performed, the [[https://en.wikipedia.org/wiki/Rounding#Round_half_to_even> banker's rounding convention]]
|
* performed, the [[https://en.wikipedia.org/wiki/Rounding#Round_half_to_even> banker's rounding convention]]
|
||||||
* is applied.
|
* is applied.
|
||||||
* In case of overflow, returns an error message instead.
|
* In case of overflow, returns an error message instead.
|
||||||
*
|
|
||||||
* ```Requires the scale of `x` and `y` are the same.```
|
|
||||||
*/
|
*/
|
||||||
final def multiply(x: Numeric, y: Numeric): Either[String, Numeric] = {
|
final def multiply(scale: Int, x: Numeric, y: Numeric): Either[String, Numeric] = {
|
||||||
assert(x.scale == y.scale)
|
checkForOverflow((x multiply y).setScale(scale, ROUND_HALF_EVEN))
|
||||||
checkForOverflow((x multiply y).setScale(x.scale, ROUND_HALF_EVEN))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Divides `x` by `y`. The output has the same scale as the inputs. If rounding must be
|
* Divides `x` by `y`. The output has the scale `scale`. If rounding must be
|
||||||
* performed, the [[https://en.wikipedia.org/wiki/Rounding#Round_half_to_even> banker's rounding convention]]
|
* performed, the [[https://en.wikipedia.org/wiki/Rounding#Round_half_to_even> banker's rounding convention]]
|
||||||
* is applied.
|
* is applied.
|
||||||
* In case of overflow, returns an error message instead.
|
* In case of overflow, returns an error message instead.
|
||||||
*
|
*
|
||||||
* ```Requires the scale of `x` and `y` are the same.```
|
|
||||||
*/
|
*/
|
||||||
final def divide(x: Numeric, y: Numeric): Either[String, Numeric] = {
|
final def divide(scale: Int, x: Numeric, y: Numeric): Either[String, Numeric] = {
|
||||||
assert(x.scale == y.scale)
|
checkForOverflow(x.divide(y, scale, ROUND_HALF_EVEN))
|
||||||
checkForOverflow(x.divide(y, x.scale, ROUND_HALF_EVEN))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -193,76 +193,85 @@ class NumericSpec
|
|||||||
Numeric.assertFromString(s)
|
Numeric.assertFromString(s)
|
||||||
|
|
||||||
"return an error in case of overflow" in {
|
"return an error in case of overflow" in {
|
||||||
val testCases = Table[Numeric, Numeric](
|
val testCases = Table[Int, Numeric, Numeric](
|
||||||
("input1", "input2"),
|
("scale", "input1", "input2"),
|
||||||
("10000000000000000000.", "10000000000000000000."),
|
(0, "10000000000000000000.", "10000000000000000000."),
|
||||||
("10000000000000000000.0", "-1000000000000000000.0"),
|
(1, "10000000000000000000.0", "-1000000000000000000.0"),
|
||||||
("-1000000000000000000.00", "1000000000000000000.00"),
|
(2, "-1000000000000000000.00", "1000000000000000000.00"),
|
||||||
("-100000000000000000.000", "-1000000000000000000.000"),
|
(3, "-100000000000000000.000", "-1000000000000000000.000"),
|
||||||
("10.000000000000000000000000000000000000", "10.000000000000000000000000000000000000"),
|
(36, "10.000000000000000000000000000000000000", "10.000000000000000000000000000000000000"),
|
||||||
("5678901234567890.12345678901234", "-5678901234567890.12345678901234"),
|
(14, "5678901234567890.12345678901234", "-5678901234567890.12345678901234"),
|
||||||
)
|
)
|
||||||
|
|
||||||
multiply("10000000000000000000.", "1000000000000000000.") shouldBe 'right
|
multiply(0, "10000000000000000000.", "1000000000000000000.") shouldBe 'right
|
||||||
|
|
||||||
forEvery(testCases) { (x, y) =>
|
forEvery(testCases) { (scale, x, y) =>
|
||||||
multiply(x, y) shouldBe 'left
|
multiply(scale, x, y) shouldBe 'left
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"multiply two numeric properly" in {
|
"multiply two numeric properly" in {
|
||||||
val testCases = Table[Numeric, Numeric, Numeric](
|
val testCases = Table[Int, Numeric, Numeric, Numeric](
|
||||||
("input1", "input2", "result"),
|
("scale", "input1", "input2", "result"),
|
||||||
("0.00000", "0.00000", "0.00000"),
|
(5, "0.00000", "0.00000", "0.00000"),
|
||||||
(
|
(
|
||||||
|
38,
|
||||||
"0.00000000000000000010000000000000000000",
|
"0.00000000000000000010000000000000000000",
|
||||||
"0.00000000000000000010000000000000000000",
|
"0.00000000000000000010000000000000000000",
|
||||||
"0.00000000000000000000000000000000000001"
|
"0.00000000000000000000000000000000000001"
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
37,
|
||||||
"1.0000000000000000000000000000000000000",
|
"1.0000000000000000000000000000000000000",
|
||||||
"-0.0000000000000000000000000000000000001",
|
"-0.0000000000000000000000000000000000001",
|
||||||
"-0.0000000000000000000000000000000000001"
|
"-0.0000000000000000000000000000000000001"
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
18,
|
||||||
"-1000000000000000000.000000000000000000",
|
"-1000000000000000000.000000000000000000",
|
||||||
"0.000000000000000001",
|
"0.000000000000000001",
|
||||||
"-1.000000000000000000"
|
"-1.000000000000000000"
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
37,
|
||||||
"3.1415926535897932384626433832795028842",
|
"3.1415926535897932384626433832795028842",
|
||||||
"2.7182818284590452353602874713526624978",
|
"2.7182818284590452353602874713526624978",
|
||||||
"8.5397342226735670654635508695465744952"
|
"8.5397342226735670654635508695465744952"
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
1,
|
||||||
"0.5",
|
"0.5",
|
||||||
"0.1",
|
"0.1",
|
||||||
"0.0",
|
"0.0",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
2,
|
||||||
"0.15",
|
"0.15",
|
||||||
"0.10",
|
"0.10",
|
||||||
"0.02",
|
"0.02",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
3,
|
||||||
"1.006",
|
"1.006",
|
||||||
"0.100",
|
"0.100",
|
||||||
"0.101"
|
"0.101"
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
4,
|
||||||
"2.1003",
|
"2.1003",
|
||||||
"0.1000",
|
"0.1000",
|
||||||
"0.2100"
|
"0.2100"
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
0,
|
||||||
"5555555555555555555.",
|
"5555555555555555555.",
|
||||||
"4444444444444444444.",
|
"4444444444444444444.",
|
||||||
"24691358024691358019753086419753086420."
|
"24691358024691358019753086419753086420."
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
forEvery(testCases) { (x, y, z) =>
|
forEvery(testCases) { (scale, x, y, z) =>
|
||||||
multiply(x, y) shouldBe Right(z)
|
multiply(scale, x, y) shouldBe Right(z)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -274,77 +283,82 @@ class NumericSpec
|
|||||||
implicit def toNumeric(s: String): Numeric = Numeric.assertFromString(s)
|
implicit def toNumeric(s: String): Numeric = Numeric.assertFromString(s)
|
||||||
|
|
||||||
"return an error in case of overflow" in {
|
"return an error in case of overflow" in {
|
||||||
val testCases = Table[Numeric, Numeric](
|
val testCases = Table[Int, Numeric, Numeric](
|
||||||
("input1", "input2"),
|
("scale", "input1", "input2"),
|
||||||
("1000000000000000000.0000000000", "0.0000000001"),
|
(10, "1000000000000000000.0000000000", "0.0000000001"),
|
||||||
("-1000000000000000000000000000000000000.0", "0.1"),
|
(1, "-1000000000000000000000000000000000000.0", "0.1"),
|
||||||
(
|
(
|
||||||
|
38,
|
||||||
"0.10000000000000000000000000000000000000",
|
"0.10000000000000000000000000000000000000",
|
||||||
"-0.10000000000000000000000000000000000000",
|
"-0.10000000000000000000000000000000000000",
|
||||||
),
|
),
|
||||||
("5678901234567890.12345678901234", "-0.00000000001234"),
|
(14, "5678901234567890.12345678901234", "-0.00000000001234"),
|
||||||
)
|
)
|
||||||
|
|
||||||
divide("100000000000000000.0000000000", "0.0000000001") shouldBe 'right
|
divide(10, "100000000000000000.0000000000", "0.0000000001") shouldBe 'right
|
||||||
|
|
||||||
forEvery(testCases) { (x, y) =>
|
forEvery(testCases) { (scale, x, y) =>
|
||||||
divide(x, y) shouldBe 'left
|
divide(scale, x, y) shouldBe 'left
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"divide two numerics properly" in {
|
"divide two numerics properly" in {
|
||||||
val testCases = Table[Numeric, Numeric, Numeric](
|
val testCases = Table[Int, Numeric, Numeric, Numeric](
|
||||||
("input1", "input2", "result"),
|
("scale", "input1", "input2", "result"),
|
||||||
("0.00000", "1.00000", "0.00000"),
|
(5, "0.00000", "1.00000", "0.00000"),
|
||||||
(
|
(
|
||||||
|
38,
|
||||||
"0.00000000000000000010000000000000000000",
|
"0.00000000000000000010000000000000000000",
|
||||||
"-0.00000000000000000100000000000000000000",
|
"-0.00000000000000000100000000000000000000",
|
||||||
"-0.10000000000000000000000000000000000000"
|
"-0.10000000000000000000000000000000000000"
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
37,
|
||||||
"0.0000000000000000000000000000000000001",
|
"0.0000000000000000000000000000000000001",
|
||||||
"-0.1000000000000000000000000000000000001",
|
"-0.1000000000000000000000000000000000001",
|
||||||
"-0.0000000000000000000000000000000000010"
|
"-0.0000000000000000000000000000000000010"
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
18,
|
||||||
"1.000000000000000000",
|
"1.000000000000000000",
|
||||||
"-0.000000000000000001",
|
"-0.000000000000000001",
|
||||||
"-1000000000000000000.000000000000000000",
|
"-1000000000000000000.000000000000000000",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
37,
|
||||||
"3.1415926535897932384626433832795028842",
|
"3.1415926535897932384626433832795028842",
|
||||||
"2.7182818284590452353602874713526624978",
|
"2.7182818284590452353602874713526624978",
|
||||||
"1.1557273497909217179100931833126962991"
|
"1.1557273497909217179100931833126962991"
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
1,
|
||||||
"1.0",
|
"1.0",
|
||||||
"4.0",
|
"4.0",
|
||||||
"0.2",
|
"0.2",
|
||||||
),
|
),
|
||||||
|
(1, "6.0", "8.0", "0.8"),
|
||||||
(
|
(
|
||||||
"6.0",
|
3,
|
||||||
"8.0",
|
|
||||||
"0.8",
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"1.006",
|
"1.006",
|
||||||
"10.000",
|
"10.000",
|
||||||
"0.101"
|
"0.101"
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
4,
|
||||||
"2.1003",
|
"2.1003",
|
||||||
"10.0000",
|
"10.0000",
|
||||||
"0.2100"
|
"0.2100"
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
19,
|
||||||
"5555555555555555555.5555555555555555555",
|
"5555555555555555555.5555555555555555555",
|
||||||
"4343434343434343434.4343434343434343434",
|
"4343434343434343434.4343434343434343434",
|
||||||
"1.2790697674418604651"
|
"1.2790697674418604651"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
forEvery(testCases) { (x, y, z) =>
|
forEvery(testCases) { (scale, x, y, z) =>
|
||||||
divide(x, y) shouldBe Right(z)
|
divide(scale, x, y) shouldBe Right(z)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,8 +157,7 @@ private[digitalasset] class EncodeV1(val minor: LV.Minor) {
|
|||||||
case TApp(fun, arg) => fun -> arg
|
case TApp(fun, arg) => fun -> arg
|
||||||
})
|
})
|
||||||
|
|
||||||
private def ignoreFirstTNatForDecimalLegacy(typs: ImmArray[Type]): ImmArray[Type] =
|
private def ignoreOneDecimalScaleParameter(typs: ImmArray[Type]): ImmArray[Type] =
|
||||||
// BTNumeric must be applied to a TNat that we should ignore
|
|
||||||
typs match {
|
typs match {
|
||||||
case ImmArrayCons(TNat(_), tail) => tail
|
case ImmArrayCons(TNat(_), tail) => tail
|
||||||
case _ =>
|
case _ =>
|
||||||
@ -196,7 +195,7 @@ private[digitalasset] class EncodeV1(val minor: LV.Minor) {
|
|||||||
case TBuiltin(bType) =>
|
case TBuiltin(bType) =>
|
||||||
val (proto, typs) =
|
val (proto, typs) =
|
||||||
if (bType == BTNumeric && versionIsOlderThan(LV.Features.numeric))
|
if (bType == BTNumeric && versionIsOlderThan(LV.Features.numeric))
|
||||||
PLF.PrimType.DECIMAL -> ignoreFirstTNatForDecimalLegacy(args)
|
PLF.PrimType.DECIMAL -> ignoreOneDecimalScaleParameter(args)
|
||||||
else
|
else
|
||||||
builtinTypeInfoMap(bType).proto -> args
|
builtinTypeInfoMap(bType).proto -> args
|
||||||
builder.setPrim(
|
builder.setPrim(
|
||||||
@ -405,10 +404,10 @@ private[digitalasset] class EncodeV1(val minor: LV.Minor) {
|
|||||||
case ETyAbs(binder, body) => binder -> body
|
case ETyAbs(binder, body) => binder -> body
|
||||||
})
|
})
|
||||||
|
|
||||||
private def isLegacyDecimalBuiltin(expr: Expr) =
|
private def implicitDecimalScaleParameters(expr: Expr) =
|
||||||
expr match {
|
expr match {
|
||||||
case EBuiltin(f) => builtinFunctionMap(f).handleLegacyDecimal
|
case EBuiltin(f) => builtinFunctionMap(f).implicitDecimalScaleParameters
|
||||||
case _ => false
|
case _ => 0
|
||||||
}
|
}
|
||||||
|
|
||||||
private def encodeExprBuilder(expr0: Expr): PLF.Expr.Builder = {
|
private def encodeExprBuilder(expr0: Expr): PLF.Expr.Builder = {
|
||||||
@ -460,11 +459,8 @@ private[digitalasset] class EncodeV1(val minor: LV.Minor) {
|
|||||||
case EApps(fun, args) =>
|
case EApps(fun, args) =>
|
||||||
newBuilder.setApp(PLF.Expr.App.newBuilder().setFun(fun).accumulateLeft(args)(_ addArgs _))
|
newBuilder.setApp(PLF.Expr.App.newBuilder().setFun(fun).accumulateLeft(args)(_ addArgs _))
|
||||||
case ETyApps(expr, typs1) =>
|
case ETyApps(expr, typs1) =>
|
||||||
val typs: ImmArray[Type] =
|
val typs =
|
||||||
if (isLegacyDecimalBuiltin(expr) && versionIsOlderThan(LV.Features.numeric))
|
ntimes(implicitDecimalScaleParameters(expr), ignoreOneDecimalScaleParameter, typs1)
|
||||||
ignoreFirstTNatForDecimalLegacy(typs1)
|
|
||||||
else
|
|
||||||
typs1
|
|
||||||
newBuilder.setTyApp(
|
newBuilder.setTyApp(
|
||||||
PLF.Expr.TyApp.newBuilder().setExpr(expr).accumulateLeft(typs)(_ addTypes _))
|
PLF.Expr.TyApp.newBuilder().setExpr(expr).accumulateLeft(typs)(_ addTypes _))
|
||||||
case ETyApps(expr, typs) =>
|
case ETyApps(expr, typs) =>
|
||||||
@ -602,6 +598,10 @@ private[digitalasset] class EncodeV1(val minor: LV.Minor) {
|
|||||||
|
|
||||||
object EncodeV1 {
|
object EncodeV1 {
|
||||||
|
|
||||||
|
@tailrec
|
||||||
|
private def ntimes[A](n: Int, f: A => A, a: A): A =
|
||||||
|
if (n == 0) a else ntimes(n - 1, f, f(a))
|
||||||
|
|
||||||
private sealed abstract class LeftRecMatcher[Left, Right] {
|
private sealed abstract class LeftRecMatcher[Left, Right] {
|
||||||
def unapply(arg: Left): Option[(Left, ImmArray[Right])]
|
def unapply(arg: Left): Option[(Left, ImmArray[Right])]
|
||||||
}
|
}
|
||||||
|
@ -134,28 +134,28 @@ object SBuiltin {
|
|||||||
Numeric.add(x, y)
|
Numeric.add(x, y)
|
||||||
)
|
)
|
||||||
|
|
||||||
private def divide(x: Numeric, y: Numeric): Numeric =
|
|
||||||
if (y.signum() == 0)
|
|
||||||
throw DamlEArithmeticError(
|
|
||||||
s"Attempt to divide ${Numeric.toString(x)} by ${Numeric.toString(y)}.")
|
|
||||||
else
|
|
||||||
rightOrArithmeticError(
|
|
||||||
s"(Numeric ${x.scale}) overflow when dividing ${Numeric.toString(x)} by ${Numeric.toString(y)}.",
|
|
||||||
Numeric.divide(x, y)
|
|
||||||
)
|
|
||||||
|
|
||||||
private def multiply(x: Numeric, y: Numeric): Numeric =
|
|
||||||
rightOrArithmeticError(
|
|
||||||
s"(Numeric ${x.scale}) overflow when multiplying ${Numeric.toString(x)} by ${Numeric.toString(y)}.",
|
|
||||||
Numeric.multiply(x, y)
|
|
||||||
)
|
|
||||||
|
|
||||||
private def subtract(x: Numeric, y: Numeric): Numeric =
|
private def subtract(x: Numeric, y: Numeric): Numeric =
|
||||||
rightOrArithmeticError(
|
rightOrArithmeticError(
|
||||||
s"(Numeric ${x.scale}) overflow when subtracting ${Numeric.toString(y)} from ${Numeric.toString(x)}.",
|
s"(Numeric ${x.scale}) overflow when subtracting ${Numeric.toString(y)} from ${Numeric.toString(x)}.",
|
||||||
Numeric.subtract(x, y)
|
Numeric.subtract(x, y)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private def multiply(scale: Int, x: Numeric, y: Numeric): Numeric =
|
||||||
|
rightOrArithmeticError(
|
||||||
|
s"(Numeric ${scale}) overflow when multiplying ${Numeric.toString(x)} by ${Numeric.toString(y)}.",
|
||||||
|
Numeric.multiply(scale, x, y)
|
||||||
|
)
|
||||||
|
|
||||||
|
private def divide(scale: Int, x: Numeric, y: Numeric): Numeric =
|
||||||
|
if (y.signum() == 0)
|
||||||
|
throw DamlEArithmeticError(
|
||||||
|
s"Attempt to divide ${Numeric.toString(x)} by ${Numeric.toString(y)}.")
|
||||||
|
else
|
||||||
|
rightOrArithmeticError(
|
||||||
|
s"(Numeric ${scale}) overflow when dividing ${Numeric.toString(x)} by ${Numeric.toString(y)}.",
|
||||||
|
Numeric.divide(scale, x, y)
|
||||||
|
)
|
||||||
|
|
||||||
sealed abstract class SBBinaryOpNumeric(op: (Numeric, Numeric) => Numeric) extends SBuiltin(3) {
|
sealed abstract class SBBinaryOpNumeric(op: (Numeric, Numeric) => Numeric) extends SBuiltin(3) {
|
||||||
final def execute(args: util.ArrayList[SValue], machine: Machine): Unit = {
|
final def execute(args: util.ArrayList[SValue], machine: Machine): Unit = {
|
||||||
val scale = args.get(0).asInstanceOf[STNat].n
|
val scale = args.get(0).asInstanceOf[STNat].n
|
||||||
@ -166,10 +166,23 @@ object SBuiltin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed abstract class SBBinaryOpNumeric2(op: (Int, Numeric, Numeric) => Numeric)
|
||||||
|
extends SBuiltin(5) {
|
||||||
|
final def execute(args: util.ArrayList[SValue], machine: Machine): Unit = {
|
||||||
|
val scaleA = args.get(0).asInstanceOf[STNat].n
|
||||||
|
val scaleB = args.get(1).asInstanceOf[STNat].n
|
||||||
|
val scale = args.get(2).asInstanceOf[STNat].n
|
||||||
|
val a = args.get(3).asInstanceOf[SNumeric].value
|
||||||
|
val b = args.get(4).asInstanceOf[SNumeric].value
|
||||||
|
assert(a.scale == scaleA && b.scale == scaleB)
|
||||||
|
machine.ctrl = CtrlValue(SNumeric(op(scale, a, b)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final case object SBAddNumeric extends SBBinaryOpNumeric(add)
|
final case object SBAddNumeric extends SBBinaryOpNumeric(add)
|
||||||
final case object SBSubNumeric extends SBBinaryOpNumeric(subtract)
|
final case object SBSubNumeric extends SBBinaryOpNumeric(subtract)
|
||||||
final case object SBMulNumeric extends SBBinaryOpNumeric(multiply)
|
final case object SBMulNumeric extends SBBinaryOpNumeric2(multiply)
|
||||||
final case object SBDivNumeric extends SBBinaryOpNumeric(divide)
|
final case object SBDivNumeric extends SBBinaryOpNumeric2(divide)
|
||||||
|
|
||||||
final case object SBRoundNumeric extends SBuiltin(3) {
|
final case object SBRoundNumeric extends SBuiltin(3) {
|
||||||
def execute(args: util.ArrayList[SValue], machine: Machine): Unit = {
|
def execute(args: util.ArrayList[SValue], machine: Machine): Unit = {
|
||||||
|
@ -263,15 +263,16 @@ class SBuiltinTest extends FreeSpec with Matchers with TableDrivenPropertyChecks
|
|||||||
val overSqrtOfTen = "3.1622776601683793319988935444327185338"
|
val overSqrtOfTen = "3.1622776601683793319988935444327185338"
|
||||||
|
|
||||||
"throws exception in case of overflow" in {
|
"throws exception in case of overflow" in {
|
||||||
eval(e"$builtin @0 1${"0" * 18}. 1${"0" * 19}.") shouldBe 'right
|
eval(e"$builtin @0 @0 @0 1${"0" * 18}. 1${"0" * 19}.") shouldBe 'right
|
||||||
eval(e"$builtin @0 1${"0" * 19}. 1${"0" * 19}.") shouldBe 'left
|
eval(e"$builtin @0 @0 @0 1${"0" * 19}. 1${"0" * 19}.") shouldBe 'left
|
||||||
eval(e"$builtin @37 $underSqrtOfTen $underSqrtOfTen") shouldBe 'right
|
eval(e"$builtin @37 @37 @37 $underSqrtOfTen $underSqrtOfTen") shouldBe 'right
|
||||||
eval(e"$builtin @37 $overSqrtOfTen $underSqrtOfTen") shouldBe 'left
|
eval(e"$builtin @37 @37 @37 $overSqrtOfTen $underSqrtOfTen") shouldBe 'left
|
||||||
eval(e"$builtin @10 1.1000000000 2.2000000000") shouldBe Right(SNumeric(n(10, 2.42)))
|
eval(e"$builtin @10 @10 @10 1.1000000000 2.2000000000") shouldBe Right(
|
||||||
eval(e"$builtin @10 ${tenPowerOf(13)} ${tenPowerOf(14)}") shouldBe Right(
|
SNumeric(n(10, 2.42)))
|
||||||
|
eval(e"$builtin @10 @10 @10 ${tenPowerOf(13)} ${tenPowerOf(14)}") shouldBe Right(
|
||||||
SNumeric(n(10, "1E27")))
|
SNumeric(n(10, "1E27")))
|
||||||
eval(e"$builtin @10 ${tenPowerOf(14)} ${tenPowerOf(14)}") shouldBe 'left
|
eval(e"$builtin @10 @10 @10 ${tenPowerOf(14)} ${tenPowerOf(14)}") shouldBe 'left
|
||||||
eval(e"$builtin @10 ${s(10, bigBigDecimal)} ${bigBigDecimal - 1}") shouldBe Left(
|
eval(e"$builtin @10 @10 @10 ${s(10, bigBigDecimal)} ${bigBigDecimal - 1}") shouldBe Left(
|
||||||
DamlEArithmeticError(
|
DamlEArithmeticError(
|
||||||
s"(Numeric 10) overflow when multiplying ${s(10, bigBigDecimal)} by ${s(10, bigBigDecimal - 1)}.")
|
s"(Numeric 10) overflow when multiplying ${s(10, bigBigDecimal)} by ${s(10, bigBigDecimal - 1)}.")
|
||||||
)
|
)
|
||||||
@ -281,25 +282,25 @@ class SBuiltinTest extends FreeSpec with Matchers with TableDrivenPropertyChecks
|
|||||||
"DIV_NUMERIC" - {
|
"DIV_NUMERIC" - {
|
||||||
val builtin = "DIV_NUMERIC"
|
val builtin = "DIV_NUMERIC"
|
||||||
"throws exception in case of overflow" in {
|
"throws exception in case of overflow" in {
|
||||||
eval(e"$builtin @38 ${s(38, "1E-18")} ${s(38, "-1E-17")}") shouldBe 'right
|
eval(e"$builtin @38 @38 @38 ${s(38, "1E-18")} ${s(38, "-1E-17")}") shouldBe 'right
|
||||||
eval(e"$builtin @38 ${s(38, "1E-18")} ${s(38, "-1E-18")}") shouldBe 'left
|
eval(e"$builtin @38 @38 @38 ${s(38, "1E-18")} ${s(38, "-1E-18")}") shouldBe 'left
|
||||||
eval(e"$builtin @1 ${s(1, "1E36")} 0.2") shouldBe 'right
|
eval(e"$builtin @1 @1 @1 ${s(1, "1E36")} 0.2") shouldBe 'right
|
||||||
eval(e"$builtin @1 ${s(1, "1E36")} 0.1") shouldBe 'left
|
eval(e"$builtin @1 @1 @1 ${s(1, "1E36")} 0.1") shouldBe 'left
|
||||||
eval(e"$builtin @10 1.1000000000 2.2000000000") shouldBe Right(SNumeric(n(10, 0.5)))
|
eval(e"$builtin @10 @10 @10 1.1000000000 2.2000000000") shouldBe Right(SNumeric(n(10, 0.5)))
|
||||||
eval(e"$builtin @10 ${s(10, bigBigDecimal)} ${tenPowerOf(-10)}") shouldBe 'left
|
eval(e"$builtin @10 @10 @10 ${s(10, bigBigDecimal)} ${tenPowerOf(-10)}") shouldBe 'left
|
||||||
eval(e"$builtin @10 ${tenPowerOf(17)} ${tenPowerOf(-10)}") shouldBe Right(
|
eval(e"$builtin @10 @10 @10 ${tenPowerOf(17)} ${tenPowerOf(-10)}") shouldBe Right(
|
||||||
SNumeric(n(10, "1E27")))
|
SNumeric(n(10, "1E27")))
|
||||||
eval(e"$builtin @10 ${tenPowerOf(18)} ${tenPowerOf(-10)}") shouldBe Left(
|
eval(e"$builtin @10 @10 @10 ${tenPowerOf(18)} ${tenPowerOf(-10)}") shouldBe Left(
|
||||||
DamlEArithmeticError(
|
DamlEArithmeticError(
|
||||||
s"(Numeric 10) overflow when dividing ${tenPowerOf(18)} by ${tenPowerOf(-10)}.")
|
s"(Numeric 10) overflow when dividing ${tenPowerOf(18)} by ${tenPowerOf(-10)}.")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
"throws exception when divided by 0" in {
|
"throws exception when divided by 0" in {
|
||||||
eval(e"$builtin @10 ${s(10, one)} ${tenPowerOf(-10)}") shouldBe Right(
|
eval(e"$builtin @10 @10 @10 ${s(10, one)} ${tenPowerOf(-10)}") shouldBe Right(
|
||||||
SNumeric(n(10, tenPowerOf(10))))
|
SNumeric(n(10, tenPowerOf(10))))
|
||||||
eval(e"$builtin @10 ${s(10, one)} ${s(10, zero)}") shouldBe 'left
|
eval(e"$builtin @10 @10 @10 ${s(10, one)} ${s(10, zero)}") shouldBe 'left
|
||||||
eval(e"$builtin @10 ${s(10, bigBigDecimal)} ${s(10, zero)}") shouldBe Left(
|
eval(e"$builtin @10 @10 @10 ${s(10, bigBigDecimal)} ${s(10, zero)}") shouldBe Left(
|
||||||
DamlEArithmeticError(s"Attempt to divide ${s(10, bigBigDecimal)} by 0.0000000000.")
|
DamlEArithmeticError(s"Attempt to divide ${s(10, bigBigDecimal)} by 0.0000000000.")
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -340,23 +341,23 @@ class SBuiltinTest extends FreeSpec with Matchers with TableDrivenPropertyChecks
|
|||||||
|
|
||||||
val testCases = Table[String, (Numeric, Numeric) => Either[Any, SValue]](
|
val testCases = Table[String, (Numeric, Numeric) => Either[Any, SValue]](
|
||||||
("builtin", "reference"),
|
("builtin", "reference"),
|
||||||
("ADD_NUMERIC", (a, b) => Right(SNumeric(n(10, a add b)))),
|
("ADD_NUMERIC @10", (a, b) => Right(SNumeric(n(10, a add b)))),
|
||||||
("SUB_NUMERIC", (a, b) => Right(SNumeric(n(10, a subtract b)))),
|
("SUB_NUMERIC @10", (a, b) => Right(SNumeric(n(10, a subtract b)))),
|
||||||
("MUL_NUMERIC", (a, b) => Right(SNumeric(round(a multiply b)))),
|
("MUL_NUMERIC @10 @10 @10 ", (a, b) => Right(SNumeric(round(a multiply b)))),
|
||||||
(
|
(
|
||||||
"DIV_NUMERIC",
|
"DIV_NUMERIC @10 @10 @10",
|
||||||
(a, b) => Either.cond(b.signum != 0, SNumeric(round(BigDecimal(a) / BigDecimal(b))), ())),
|
(a, b) => Either.cond(b.signum != 0, SNumeric(round(BigDecimal(a) / BigDecimal(b))), ())),
|
||||||
("LESS_EQ_NUMERIC", (a, b) => Right(SBool(BigDecimal(a) <= BigDecimal(b)))),
|
("LESS_EQ_NUMERIC @10", (a, b) => Right(SBool(BigDecimal(a) <= BigDecimal(b)))),
|
||||||
("GREATER_EQ_NUMERIC", (a, b) => Right(SBool(BigDecimal(a) >= BigDecimal(b)))),
|
("GREATER_EQ_NUMERIC @10", (a, b) => Right(SBool(BigDecimal(a) >= BigDecimal(b)))),
|
||||||
("LESS_NUMERIC", (a, b) => Right(SBool(BigDecimal(a) < BigDecimal(b)))),
|
("LESS_NUMERIC @10", (a, b) => Right(SBool(BigDecimal(a) < BigDecimal(b)))),
|
||||||
("GREATER_NUMERIC", (a, b) => Right(SBool(BigDecimal(a) > BigDecimal(b)))),
|
("GREATER_NUMERIC @10", (a, b) => Right(SBool(BigDecimal(a) > BigDecimal(b)))),
|
||||||
("EQUAL_NUMERIC", (a, b) => Right(SBool(BigDecimal(a) == BigDecimal(b)))),
|
("EQUAL_NUMERIC @10", (a, b) => Right(SBool(BigDecimal(a) == BigDecimal(b)))),
|
||||||
)
|
)
|
||||||
|
|
||||||
forEvery(testCases) { (builtin, ref) =>
|
forEvery(testCases) { (builtin, ref) =>
|
||||||
forEvery(decimals) { a =>
|
forEvery(decimals) { a =>
|
||||||
forEvery(decimals) { b =>
|
forEvery(decimals) { b =>
|
||||||
eval(e"$builtin @10 ${s(10, a)} ${s(10, b)}").left
|
eval(e"$builtin ${s(10, a)} ${s(10, b)}").left
|
||||||
.map(_ => ()) shouldBe ref(n(10, a), n(10, b))
|
.map(_ => ()) shouldBe ref(n(10, a), n(10, b))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -314,8 +314,8 @@ object Ast {
|
|||||||
// Numeric arithmetic
|
// Numeric arithmetic
|
||||||
final case object BAddNumeric extends BuiltinFunction(2) // : ∀s. Numeric s → Numeric s → Numeric s
|
final case object BAddNumeric extends BuiltinFunction(2) // : ∀s. Numeric s → Numeric s → Numeric s
|
||||||
final case object BSubNumeric extends BuiltinFunction(2) // : ∀s. Numeric s → Numeric s → Numeric s
|
final case object BSubNumeric extends BuiltinFunction(2) // : ∀s. Numeric s → Numeric s → Numeric s
|
||||||
final case object BMulNumeric extends BuiltinFunction(2) // : ∀s. Numeric s → Numeric s → Numeric s
|
final case object BMulNumeric extends BuiltinFunction(2) // : ∀s1 s2 s. Numeric s1 → Numeric s2 → Numeric s
|
||||||
final case object BDivNumeric extends BuiltinFunction(2) // : ∀s. Numeric s → Numeric s → Numeric s
|
final case object BDivNumeric extends BuiltinFunction(2) // : ∀s1 s2 s. Numeric s1 → Numeric s2 → Numeric s
|
||||||
final case object BRoundNumeric extends BuiltinFunction(2) // : ∀s. Integer → 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 BCastNumeric extends BuiltinFunction(1) // : ∀s1 s2. Numeric s1 → Numeric s2
|
||||||
final case object BShiftNumeric extends BuiltinFunction(1) // : ∀s1 s2. Numeric s1 → Numeric s2
|
final case object BShiftNumeric extends BuiltinFunction(1) // : ∀s1 s2. Numeric s1 → Numeric s2
|
||||||
|
@ -2156,33 +2156,23 @@ Numeric functions
|
|||||||
scale of the inputs and the output is given by the type parameter
|
scale of the inputs and the output is given by the type parameter
|
||||||
`α`. Throws an error if overflow.
|
`α`. Throws an error if overflow.
|
||||||
|
|
||||||
* ``MUL_NUMERIC : ∀ (α : nat) . 'Numeric' α → 'Numeric' α → 'Numeric' α``
|
* ``MUL_NUMERIC : ∀ (α₁ α₂ α : nat) . 'Numeric' α₁ → 'Numeric' α₂ → 'Numeric' α``
|
||||||
|
|
||||||
Multiplies the two decimals and rounds the result to the closest
|
Multiplies the two numerics and rounds the result to the closest
|
||||||
multiple of ``10⁻ᵅ`` using `banker's rounding convention
|
multiple of ``10⁻ᵅ`` using `banker's rounding convention
|
||||||
<https://en.wikipedia.org/wiki/Rounding#Round_half_to_even>`_. The
|
<https://en.wikipedia.org/wiki/Rounding#Round_half_to_even>`_.
|
||||||
scale of the inputs and the output is given by the type parameter
|
The type parameters `α₁`, `α₂`, `α` define the scale of the first
|
||||||
`α`. Throws an error in case of overflow.
|
input, the second input, and the output, respectively. Throws an
|
||||||
|
error in case of overflow.
|
||||||
|
|
||||||
* ``DIV_NUMERIC : ∀ (α : nat) . 'Numeric' α → 'Numeric' α → 'Numeric' α``
|
* ``DIV_NUMERIC : ∀ (α₁ α₂ α : nat) . 'Numeric' α₁ → 'Numeric' α₂ → 'Numeric' α``
|
||||||
|
|
||||||
Divides the first decimal by the second one and rounds the result to
|
Divides the first decimal by the second one and rounds the result to
|
||||||
the closest multiple of ``10⁻ᵅ`` using `banker's rounding convention
|
the closest multiple of ``10⁻ᵅ`` using `banker's rounding convention
|
||||||
<https://en.wikipedia.org/wiki/Rounding#Round_half_to_even>`_ (where
|
<https://en.wikipedia.org/wiki/Rounding#Round_half_to_even>`_ (where
|
||||||
`n` is given as the type parameter). The scale of the inputs and
|
`n` is given as the type parameter). The type parameters `α₁`,
|
||||||
the output is given by the type parameter `α`. Throws an error in
|
`α₂`, `α` define the scale of the first input, the second input, and
|
||||||
case of overflow.
|
the output, respectively. Throws an error in case of overflow.
|
||||||
|
|
||||||
* ``ROUND_NUMERIC : ∀ (α : nat) . 'Int64' → 'Numeric' α → 'Numeric' α``
|
|
||||||
|
|
||||||
Rounds the decimal to the closest multiple of ``10ⁱ`` where ``i`` is
|
|
||||||
integer argument. In case the value to be rounded is exactly
|
|
||||||
half-way between two multiples, rounds toward the even one,
|
|
||||||
following the `banker's rounding convention
|
|
||||||
<https://en.wikipedia.org/wiki/Rounding#Round_half_to_even>`_. The
|
|
||||||
scale of the inputs and the output is given by the type parameter
|
|
||||||
`α`. Throws an exception if the integer is not between `α-37` and
|
|
||||||
`α` inclusive.
|
|
||||||
|
|
||||||
|
|
||||||
* ``CAST_NUMERIC : ∀ (α₁, α₂: nat) . 'Numeric' α₁ → 'Numeric' α₂``
|
* ``CAST_NUMERIC : ∀ (α₁, α₂: nat) . 'Numeric' α₁ → 'Numeric' α₂``
|
||||||
|
@ -42,8 +42,15 @@ private[validation] object Typing {
|
|||||||
protected[validation] lazy val typeOfBuiltinFunction = {
|
protected[validation] lazy val typeOfBuiltinFunction = {
|
||||||
val alpha = TVar(Name.assertFromString("$alpha$"))
|
val alpha = TVar(Name.assertFromString("$alpha$"))
|
||||||
val beta = TVar(Name.assertFromString("$beta$"))
|
val beta = TVar(Name.assertFromString("$beta$"))
|
||||||
|
val gamma = TVar(Name.assertFromString("$beta$"))
|
||||||
def tBinop(typ: Type): Type = typ ->: typ ->: typ
|
def tBinop(typ: Type): Type = typ ->: typ ->: typ
|
||||||
val tNumBinop = TForall(alpha.name -> KNat, tBinop(TNumeric(alpha)))
|
val tNumBinop = TForall(alpha.name -> KNat, tBinop(TNumeric(alpha)))
|
||||||
|
val tMultiNumBinop =
|
||||||
|
TForall(
|
||||||
|
alpha.name -> KNat,
|
||||||
|
TForall(
|
||||||
|
beta.name -> KNat,
|
||||||
|
TForall(gamma.name -> KNat, TNumeric(alpha) ->: TNumeric(beta) ->: TNumeric(gamma))))
|
||||||
val tNumConversion =
|
val tNumConversion =
|
||||||
TForall(alpha.name -> KNat, TForall(beta.name -> KNat, TNumeric(alpha) ->: TNumeric(beta)))
|
TForall(alpha.name -> KNat, TForall(beta.name -> KNat, TNumeric(alpha) ->: TNumeric(beta)))
|
||||||
def tComparison(bType: BuiltinType): Type = TBuiltin(bType) ->: TBuiltin(bType) ->: TBool
|
def tComparison(bType: BuiltinType): Type = TBuiltin(bType) ->: TBuiltin(bType) ->: TBool
|
||||||
@ -54,8 +61,8 @@ private[validation] object Typing {
|
|||||||
// Numeric arithmetic
|
// Numeric arithmetic
|
||||||
BAddNumeric -> tNumBinop,
|
BAddNumeric -> tNumBinop,
|
||||||
BSubNumeric -> tNumBinop,
|
BSubNumeric -> tNumBinop,
|
||||||
BMulNumeric -> tNumBinop,
|
BMulNumeric -> tMultiNumBinop,
|
||||||
BDivNumeric -> tNumBinop,
|
BDivNumeric -> tMultiNumBinop,
|
||||||
BRoundNumeric -> TForall(alpha.name -> KNat, TInt64 ->: TNumeric(alpha) ->: TNumeric(alpha)),
|
BRoundNumeric -> TForall(alpha.name -> KNat, TInt64 ->: TNumeric(alpha) ->: TNumeric(alpha)),
|
||||||
BCastNumeric -> tNumConversion,
|
BCastNumeric -> tNumConversion,
|
||||||
BShiftNumeric -> tNumConversion,
|
BShiftNumeric -> tNumConversion,
|
||||||
|
@ -12,3 +12,4 @@ HEAD — ongoing
|
|||||||
* [DAML Docs] suppress instance documentation when `--data-only` mode is requested
|
* [DAML Docs] suppress instance documentation when `--data-only` mode is requested
|
||||||
+ [DAML-LF] add CAST_NUMERIC and SHIFT_NUMERIC in DAML-LF 1.dev
|
+ [DAML-LF] add CAST_NUMERIC and SHIFT_NUMERIC in DAML-LF 1.dev
|
||||||
+ [Sandbox] Dramatically increased performance of the ActiveContractService by only loading the contracts that the parties in the transaction filter are allowed to see.
|
+ [Sandbox] Dramatically increased performance of the ActiveContractService by only loading the contracts that the parties in the transaction filter are allowed to see.
|
||||||
|
+ [DAML-LF] change signature of MUL_NUMERIC and DIV_NUMERIC
|
||||||
|
Loading…
Reference in New Issue
Block a user