refactor(users/sterni/nix): move generic number operation into num

We omit type checks for performance reasons in most places currently, so
the library grouping is important in showing people what to use for what
sort of input. The moved functions make sense to use with floats as
well, so we'll move them to the num library. Some of the remaining
functions could theoretically be adapted and moved, but aren't for now.

Change-Id: Ifdecaa60be594f4438b2a58b9ea6445e2da080e3
Reviewed-on: https://cl.tvl.fyi/c/depot/+/9007
Tested-by: BuildkiteCI
Reviewed-by: sterni <sternenseemann@systemli.org>
This commit is contained in:
sterni 2023-08-05 15:39:56 +02:00
parent dbdb2575cf
commit 984ea69386
6 changed files with 64 additions and 45 deletions

View file

@ -10,7 +10,7 @@ let
inherit (depot.users.sterni.nix) inherit (depot.users.sterni.nix)
char char
string string
int num
fun fun
; ;
@ -18,10 +18,10 @@ let
testAllCharConversion = it "tests conversion of all chars" [ testAllCharConversion = it "tests conversion of all chars" [
(assertEq "char.chr converts to char.allChars" (assertEq "char.chr converts to char.allChars"
(builtins.genList (fun.rl char.chr (int.add 1)) 255) (builtins.genList (fun.rl char.chr (num.add 1)) 255)
charList) charList)
(assertEq "char.ord converts from char.allChars" (assertEq "char.ord converts from char.allChars"
(builtins.genList (int.add 1) 255) (builtins.genList (num.add 1) 255)
(builtins.map char.ord charList)) (builtins.map char.ord charList))
]; ];

View file

@ -2,31 +2,22 @@
let let
# TODO(sterni): implement nix.float and figure out which of these
# functions can be split out into a common nix.num
# library.
inherit (depot.users.sterni.nix) inherit (depot.users.sterni.nix)
string string
num
; ;
inherit (builtins) inherit (builtins)
bitOr bitOr
bitAnd bitAnd
bitXor bitXor
mul
div
add
sub
; ;
abs = i: if i < 0 then -i else i;
exp = base: pow: exp = base: pow:
if pow > 0 if pow > 0
then base * (exp base (pow - 1)) then base * (exp base (pow - 1))
else if pow < 0 else if pow < 0
then 1.0 / exp base (abs pow) then 1.0 / exp base (num.abs pow)
else 1; else 1;
bitShiftR = bit: count: bitShiftR = bit: count:
@ -52,7 +43,7 @@ let
in in
if int == 0 if int == 0
then "0" then "0"
else "${sign}${go (abs int)}"; else "${sign}${go (num.abs int)}";
fromHexMap = builtins.listToAttrs fromHexMap = builtins.listToAttrs
(lib.imap0 (i: c: { name = c; value = i; }) (lib.imap0 (i: c: { name = c; value = i; })
@ -94,26 +85,17 @@ let
odd = x: bitAnd x 1 == 1; odd = x: bitAnd x 1 == 1;
even = x: bitAnd x 1 == 0; even = x: bitAnd x 1 == 0;
# div and mod behave like quot and rem in Haskell, inherit (builtins) div;
# i. e. they truncate towards 0
mod = a: b: let res = a / b; in a - (res * b); mod = a: b: let res = a / b; in a - (res * b);
inRange = a: b: x: x >= a && x <= b;
sum = builtins.foldl' (a: b: a + b) 0;
in in
{ {
inherit inherit
maxBound maxBound
minBound minBound
abs
exp exp
odd odd
even even
add
sub
mul
div div
mod mod
bitShiftR bitShiftR
@ -123,7 +105,5 @@ in
bitXor bitXor
toHex toHex
fromHex fromHex
inRange
sum
; ;
} }

View file

@ -15,9 +15,6 @@ let
; ;
testBounds = it "checks minBound and maxBound" [ testBounds = it "checks minBound and maxBound" [
# this is gonna blow up in my face because
# integer overflow is undefined behavior in
# C++, so most likely anything could happen?
(assertEq "maxBound is the maxBound" true (assertEq "maxBound is the maxBound" true
(int.maxBound + 1 < int.maxBound)) (int.maxBound + 1 < int.maxBound))
(assertEq "minBound is the minBound" true (assertEq "minBound is the minBound" true
@ -321,7 +318,6 @@ let
testBasic = it "checks basic int operations" [ testBasic = it "checks basic int operations" [
(assertEq "122 is even" (int.even 122 && !(int.odd 122)) true) (assertEq "122 is even" (int.even 122 && !(int.odd 122)) true)
(assertEq "123 is odd" (int.odd 123 && !(int.even 123)) true) (assertEq "123 is odd" (int.odd 123 && !(int.even 123)) true)
(assertEq "abs -4959" (int.abs (-4959)) 4959)
]; ];
expNumbers = [ expNumbers = [
@ -374,7 +370,7 @@ let
checkShiftLMulExp = n: checkShiftLMulExp = n:
assertEq "${toString n} >> 6 == ${toString n} * 2 ^ 6" assertEq "${toString n} >> 6 == ${toString n} * 2 ^ 6"
(int.bitShiftL n 5) (int.bitShiftL n 5)
(int.mul n (int.exp 2 5)); (n * (int.exp 2 5));
testBit = it "checks bitwise operations" (lib.flatten [ testBit = it "checks bitwise operations" (lib.flatten [
(builtins.map checkShift shifts) (builtins.map checkShift shifts)

View file

@ -0,0 +1,16 @@
{ ... }:
rec {
inherit (builtins)
mul
div
add
sub
;
abs = i: if i < 0 then -i else i;
inRange = a: b: x: x >= a && x <= b;
sum = builtins.foldl' (a: b: a + b) 0;
}

View file

@ -0,0 +1,26 @@
{ depot, ... }:
let
inherit (depot.nix.runTestsuite)
runTestsuite
it
assertEq
;
inherit (depot.users.sterni.nix)
num
;
testsBasic = it "tests basic operations" [
(assertEq "abs -4959" (num.abs (-4959)) 4959)
(assertEq "sum" (num.sum [ 123 321 1.5 ]) (123 + 321 + 1.5))
(assertEq "inRange"
(builtins.map (num.inRange 1.0 5) [ 0 0.5 3 4 4.5 5.5 5 6 ])
[ false false true true true false true false ])
];
in
runTestsuite "nix.num" [
testsBasic
]

View file

@ -6,6 +6,7 @@ let
char char
flow flow
fun fun
num
int int
string string
util util
@ -46,17 +47,17 @@ let
# byte position as an index starting with 0 # byte position as an index starting with 0
pos: pos:
let let
defaultRange = int.inRange 128 191; defaultRange = num.inRange 128 191;
secondBytePredicate = flow.switch first [ secondBytePredicate = flow.switch first [
[ (int.inRange 194 223) defaultRange ] # C2..DF [ (num.inRange 194 223) defaultRange ] # C2..DF
[ 224 (int.inRange 160 191) ] # E0 [ 224 (num.inRange 160 191) ] # E0
[ (int.inRange 225 236) defaultRange ] # E1..EC [ (num.inRange 225 236) defaultRange ] # E1..EC
[ 237 (int.inRange 128 159) ] # ED [ 237 (num.inRange 128 159) ] # ED
[ (int.inRange 238 239) defaultRange ] # EE..EF [ (num.inRange 238 239) defaultRange ] # EE..EF
[ 240 (int.inRange 144 191) ] # F0 [ 240 (num.inRange 144 191) ] # F0
[ (int.inRange 241 243) defaultRange ] # F1..F3 [ (num.inRange 241 243) defaultRange ] # F1..F3
[ 244 (int.inRange 128 143) ] # F4 [ 244 (num.inRange 128 143) ] # F4
[ (fun.const true) null ] [ (fun.const true) null ]
]; ];
@ -225,10 +226,10 @@ let
# Note that this doesn't check if the Unicode codepoint is allowed, # Note that this doesn't check if the Unicode codepoint is allowed,
# but rather allows all theoretically UTF-8-encodeable ones. # but rather allows all theoretically UTF-8-encodeable ones.
count = flow.switch cp [ count = flow.switch cp [
[ (int.inRange 0 127) 1 ] # 00000000 0xxxxxxx [ (num.inRange 0 127) 1 ] # 00000000 0xxxxxxx
[ (int.inRange 128 2047) 2 ] # 00000yyy yyxxxxxx [ (num.inRange 128 2047) 2 ] # 00000yyy yyxxxxxx
[ (int.inRange 2048 65535) 3 ] # zzzzyyyy yyxxxxxx [ (num.inRange 2048 65535) 3 ] # zzzzyyyy yyxxxxxx
[ (int.inRange 65536 1114111) 4 ] # 000uuuuu zzzzyyyy yyxxxxxx, [ (num.inRange 65536 1114111) 4 ] # 000uuuuu zzzzyyyy yyxxxxxx,
# capped at U+10FFFF # capped at U+10FFFF
[ (fun.const true) (builtins.throw invalidCodepointMsg) ] [ (fun.const true) (builtins.throw invalidCodepointMsg) ]