288 lines
9.7 KiB
Nix
288 lines
9.7 KiB
Nix
{ debug, expression, float, integer, intrinsics, list, string, type, ... }:
|
||
let
|
||
assertNumber#: int | float -> int | float | !
|
||
= value:
|
||
matchNumber value
|
||
{
|
||
float = value;
|
||
int = value;
|
||
};
|
||
|
||
matchNumber#: T -> { int: R, float: R } -> R | !
|
||
# where T, R: Any
|
||
= value:
|
||
{ int, float } @ select:
|
||
type.matchPrimitiveOrDefault
|
||
value
|
||
select
|
||
( debug.panic "matchNumber" "Value is not a number: Neither int nor float!" );
|
||
|
||
abs#: int | float -> int | float
|
||
= value:
|
||
let
|
||
value' = assertNumber value;
|
||
in
|
||
if value' < 0
|
||
then
|
||
( 0 - value' )
|
||
else
|
||
value';
|
||
|
||
add#: int | float -> int | float -> int | float
|
||
= intrinsics.add or (a: b: ( assertNumber a ) + ( assertNumber b ));
|
||
|
||
and#: int -> int -> int
|
||
= intrinsics.bitAnd;
|
||
|
||
ceil#: int | float -> int
|
||
= intrinsics.ceil;
|
||
|
||
div#: int | float -> int | float -> int | float
|
||
= intrinsics.div or (a: b: ( assertNumber a ) / ( assertNumber b ));
|
||
|
||
floor#: int | float -> int
|
||
= intrinsics.floor;
|
||
|
||
isInstanceOf
|
||
= value:
|
||
integer.isInstanceOf value
|
||
|| float.isInstanceOf value;
|
||
|
||
lessThan#: int | float -> int | float -> int | float
|
||
= intrinsics.lessThan or (a: b: ( assertNumber a ) < ( assertNumber b ));
|
||
|
||
moreThan#: int | float -> int | float -> int | float
|
||
= a: b: ( assertNumber a ) > ( assertNumber b );
|
||
|
||
mul#: int | float -> int | float -> int | float
|
||
= intrinsics.mul or (a: b: ( assertNumber a ) * ( assertNumber b ));
|
||
|
||
neg#: int | float -> int | float
|
||
= value: ( 0 - ( assertNumber value ) );
|
||
|
||
or#: int -> int -> int
|
||
= intrinsics.bitOr;
|
||
|
||
orNull
|
||
= value:
|
||
isInstanceOf value || value == null;
|
||
|
||
pow#: int -> int | float -> int | float
|
||
= let
|
||
pow#: int -> int | float -> int | float
|
||
= base:
|
||
exp:
|
||
list.fold
|
||
(y: x: x*y)
|
||
1.0
|
||
(list.generate (x: base) exp);
|
||
in
|
||
base:
|
||
exp:
|
||
if exp < 0.0
|
||
then
|
||
pow ( 1.0 / base ) ( 0 - exp )
|
||
else
|
||
pow ( 1.0 * base ) exp;
|
||
|
||
round#: int | float -> int
|
||
= value:
|
||
#debug.info "round" { data = { inherit value; value' = value + 0.5; round = floor ( value + 0.5 ); }; }
|
||
matchNumber value
|
||
{
|
||
int = value;
|
||
float = round' value;
|
||
};
|
||
|
||
round'#: float -> int
|
||
= value: floor ( value + 0.5 );
|
||
|
||
sub#: int | float -> int | float -> int | float
|
||
= intrinsics.sub or (a: b: (assertNumber a) - (assertNumber b));
|
||
|
||
sum#: [ int | float ] -> int | float
|
||
= list.fold (result: value: result + value) 0;
|
||
|
||
toFloat#: int | float | string -> float
|
||
= value:
|
||
type.matchPrimitiveOrDefault value
|
||
{
|
||
int = 1.0 * value;
|
||
float = value;
|
||
/*string
|
||
= let
|
||
parts = string.match "([0-9]*)[.](0*)([0-9]*)" value;
|
||
len = string.length parts.dec;
|
||
in
|
||
( ( 1.0 * ( toInteger parts.dec ) ) / ( pow 10 len ) )
|
||
+ ( toInteger parts.int );*/
|
||
}
|
||
( debug.panic "toFloat" "Cannot convert ${type.getPrimitive value} to float." );
|
||
|
||
toInteger#: int | float | string -> int
|
||
= value:
|
||
type.matchPrimitiveOrDefault value
|
||
{
|
||
float = round' value;
|
||
int = value;
|
||
string
|
||
= let
|
||
result = toInteger' value;
|
||
in
|
||
if result != null
|
||
then
|
||
result
|
||
else
|
||
debug.panic "toInteger" "Could not convert string ${value} to int!";
|
||
}
|
||
( debug.panic "toInteger" "Could not convert type ${type value} to int!" );
|
||
|
||
toInteger'#: string -> int | null
|
||
= value:
|
||
let
|
||
value' = string.match "([+-])?0*([0-9.][0-9]+)" value;
|
||
result = expression.fromJSON ( list.get value' 1);
|
||
sign = list.head value';
|
||
in
|
||
if value' != null
|
||
&& integer.isInstanceOf result
|
||
then
|
||
if sign == "-"
|
||
then
|
||
( - result )
|
||
else
|
||
result
|
||
else
|
||
null;
|
||
|
||
toStringWithMaximumPrecision#: int | float -> int -> string
|
||
= value:
|
||
precision:
|
||
if precision > 0
|
||
then
|
||
list.foldReversed
|
||
(
|
||
state:
|
||
character:
|
||
if state != null then "${character}${state}"
|
||
else if character == "." then ""
|
||
else if character != "0" then character
|
||
else null
|
||
)
|
||
null
|
||
( string.toCharacters ( toStringWithPrecision value precision ) )
|
||
else
|
||
toStringWithPrecision value precision;
|
||
|
||
splitFloat
|
||
= value:
|
||
precision:
|
||
let
|
||
factor = pow 10 precision;
|
||
value' = string ( round ( value * factor ) );
|
||
length = string.length value';
|
||
padding = string.concat (list.generate (_: "0") (precision - length));
|
||
in
|
||
debug.debug "splitFloat"
|
||
{
|
||
text = "called with/calculated:";
|
||
data
|
||
= {
|
||
inherit value precision factor value' length;
|
||
};
|
||
}
|
||
(
|
||
if length > precision
|
||
then
|
||
let
|
||
mid = length - precision;
|
||
integer = string.slice 0 mid value';
|
||
decimal = string.slice mid precision value';
|
||
in
|
||
debug.debug "splitFloat"
|
||
{
|
||
text = "length > precision";
|
||
data = { inherit integer decimal mid; };
|
||
}
|
||
{ inherit decimal integer; }
|
||
else
|
||
let
|
||
integer = "0";
|
||
decimal = "${padding}${value'}";
|
||
in
|
||
debug.debug "splitFloat"
|
||
{
|
||
text = "length <= precision";
|
||
data = { inherit integer decimal padding; };
|
||
}
|
||
{ inherit decimal integer; }
|
||
);
|
||
|
||
toStringWithPrecision#: int | float -> int -> string
|
||
= value:
|
||
precision:
|
||
let
|
||
precision'
|
||
= type.matchPrimitiveOrDefault precision
|
||
{
|
||
int = precision;
|
||
null = getPrecision value;
|
||
}
|
||
( debug.panic "toStringWithPrecision" "Invalid Precision: Int or null expected!" );
|
||
valuePos = splitFloat (0.0 + value) precision';
|
||
valueNeg = splitFloat (0.0 - value) precision';
|
||
valueWithPrecision
|
||
= debug.info "toStringWithPrecision" { text = "value"; data = value; }
|
||
debug.info "toStringWithPrecision" { text = "precision"; data = [ precision precision' ]; }
|
||
(
|
||
if precision' == 0
|
||
then
|
||
"${string (round value)}"
|
||
else if value >= 0
|
||
then
|
||
"${valuePos.integer}.${valuePos.decimal}"
|
||
else
|
||
"-${valueNeg.integer}.${valueNeg.decimal}"
|
||
);
|
||
in
|
||
type.matchPrimitiveOrDefault value
|
||
{
|
||
float = valueWithPrecision;
|
||
int = valueWithPrecision;
|
||
list = string.concatMappedWith (x: toStringWithPrecision x precision) ", " value;
|
||
set
|
||
= (
|
||
{ from, till }:
|
||
"${toStringWithPrecision from precision}–${toStringWithPrecision till precision}"
|
||
)
|
||
value;
|
||
}
|
||
( debug.panic "toStringWithPrecision" "Value must be a numeric value like int or float!" );
|
||
|
||
toSignificantString#: int | float -> string
|
||
= value:
|
||
toStringWithPrecision value null;
|
||
|
||
getPrecision
|
||
= value:
|
||
debug.info "getPrecision" { text = "value"; data = value; }
|
||
(
|
||
if value == 0.0
|
||
|| value >= 2.0
|
||
|| value <= -2.0
|
||
then
|
||
0
|
||
else
|
||
(getPrecision (value * 10)) + 1
|
||
);
|
||
|
||
xor#: int -> int -> int
|
||
= intrinsics.bitXor;
|
||
in
|
||
type "number"
|
||
{
|
||
inherit isInstanceOf orNull;
|
||
inherit abs add and ceil div floor lessThan moreThan mul neg or pow round round' sub sum xor;
|
||
inherit toFloat toInteger toInteger' toSignificantString toStringWithMaximumPrecision toStringWithPrecision;
|
||
}
|