Initial commit
This commit is contained in:
commit
0d2ce70dd1
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
result
|
27
flake.lock
generated
Normal file
27
flake.lock
generated
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1742206328,
|
||||
"narHash": "sha256-q+AQ///oMnyyFzzF4H9ShSRENt3Zsx37jTiRkLkXXE0=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "096478927c360bc18ea80c8274f013709cf7bdcd",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
93
flake.nix
Normal file
93
flake.nix
Normal file
@ -0,0 +1,93 @@
|
||||
{
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
|
||||
outputs = { self, nixpkgs }: let
|
||||
system = "x86_64-linux";
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
in {
|
||||
bigint-lib = pkgs.callPackage ./lib.nix { };
|
||||
packages.${system}.default = with self.bigint-lib; pkgs.writeText "bigint-test" (let
|
||||
toS' = x: if builtins.isBool x
|
||||
then if x
|
||||
then "true"
|
||||
else "false"
|
||||
else if isBigInt x then bigIntToString x
|
||||
else toString x;
|
||||
compare = name: f: x: y: "${name} ${bigIntToString x} ${bigIntToString y} = ${toS' (f x y)}";
|
||||
in ''
|
||||
${bigIntToString (toBigInt 123)}
|
||||
${bigIntToString (toBigInt (-123))}
|
||||
${bigIntToString (toBigInt (-0))}
|
||||
${bigIntToString (toBigInt 0)}
|
||||
|
||||
${compare "bigIntEquals" bigIntEquals (toBigInt (0)) (toBigInt (1))}
|
||||
${compare "bigIntEquals" bigIntEquals (toBigInt (-1)) (toBigInt (1))}
|
||||
${compare "bigIntEquals" bigIntEquals (toBigInt (2)) (toBigInt (2))}
|
||||
${compare "bigIntEquals" bigIntEquals (toBigInt (0)) (toBigInt (-0))}
|
||||
|
||||
${compare "bigIntGreaterThan" bigIntGreaterThan (toBigInt 0) (toBigInt 0)}
|
||||
${compare "bigIntGreaterThan" bigIntGreaterThan (toBigInt 12345) (toBigInt 9999)}
|
||||
${compare "bigIntGreaterThan" bigIntGreaterThan (toBigInt 9999) (toBigInt 12345)}
|
||||
${compare "bigIntGreaterThan" bigIntGreaterThan (toBigInt 99999) (toBigInt 12345)}
|
||||
${compare "bigIntGreaterThan" bigIntGreaterThan (toBigInt (-99999)) (toBigInt 12345)}
|
||||
${compare "bigIntGreaterThan" bigIntGreaterThan (toBigInt 99999) (toBigInt (-12345))}
|
||||
${compare "bigIntGreaterThan" bigIntGreaterThan (toBigInt (-99999)) (toBigInt (-12345))}
|
||||
|
||||
${compare "bigIntLessThan" bigIntLessThan (toBigInt 0) (toBigInt 0)}
|
||||
${compare "bigIntLessThan" bigIntLessThan (toBigInt 12345) (toBigInt 9999)}
|
||||
${compare "bigIntLessThan" bigIntLessThan (toBigInt 9999) (toBigInt 12345)}
|
||||
${compare "bigIntLessThan" bigIntLessThan (toBigInt 99999) (toBigInt 12345)}
|
||||
${compare "bigIntLessThan" bigIntLessThan (toBigInt (-99999)) (toBigInt 12345)}
|
||||
${compare "bigIntLessThan" bigIntLessThan (toBigInt 99999) (toBigInt (-12345))}
|
||||
${compare "bigIntLessThan" bigIntLessThan (toBigInt (-99999)) (toBigInt (-12345))}
|
||||
|
||||
${compare "bigIntAdd" bigIntAdd (toBigInt 0) (toBigInt 0)}
|
||||
${compare "bigIntAdd" bigIntAdd (toBigInt 1) (toBigInt 2)}
|
||||
${compare "bigIntAdd" bigIntAdd (toBigInt 1) (toBigInt (-2))}
|
||||
${compare "bigIntAdd" bigIntAdd (toBigInt 5) (toBigInt (-10))}
|
||||
${compare "bigIntAdd" bigIntAdd (toBigInt (-2)) (toBigInt 1)}
|
||||
${compare "bigIntAdd" bigIntAdd (toBigInt (-10)) (toBigInt 5)}
|
||||
${compare "bigIntAdd" bigIntAdd (toBigInt 100000000) (toBigInt (-1))}
|
||||
${compare "bigIntAdd" bigIntAdd (toBigInt (-1)) (toBigInt 100000000)}
|
||||
${compare "bigIntAdd" bigIntAdd (toBigInt 200000000) (toBigInt (-2))}
|
||||
${compare "bigIntAdd" bigIntAdd (toBigInt (-2)) (toBigInt 200000000)}
|
||||
${compare "bigIntAdd" bigIntAdd (toBigInt 99) (toBigInt 99)}
|
||||
${compare "bigIntAdd" bigIntAdd (toBigInt (-1)) (toBigInt (-2))}
|
||||
${compare "bigIntAdd" bigIntAdd (toBigInt (-99)) (toBigInt (-99))}
|
||||
${compare "bigIntAdd" bigIntAdd (toBigInt (99)) (toBigInt (-99))}
|
||||
${compare "bigIntAdd" bigIntAdd (toBigInt (-99)) (toBigInt (99))}
|
||||
${compare "bigIntAdd" bigIntAdd (toBigInt (-99)) (toBigInt (99))}
|
||||
${compare "bigIntAdd" bigIntAdd (toBigInt 100000000) (toBigInt 1)}
|
||||
|
||||
${compare "bigIntSub" bigIntSub (toBigInt 0) (toBigInt 0)}
|
||||
${compare "bigIntSub" bigIntSub (toBigInt 1) (toBigInt 2)}
|
||||
${compare "bigIntSub" bigIntSub (toBigInt 100000000) (toBigInt (-1))}
|
||||
${compare "bigIntSub" bigIntSub (toBigInt (-1)) (toBigInt 100000000)}
|
||||
${compare "bigIntSub" bigIntSub (toBigInt 200000000) (toBigInt (-2))}
|
||||
${compare "bigIntSub" bigIntSub (toBigInt (-2)) (toBigInt 200000000)}
|
||||
${compare "bigIntSub" bigIntSub (toBigInt 99) (toBigInt 99)}
|
||||
${compare "bigIntSub" bigIntSub (toBigInt (-1)) (toBigInt (-2))}
|
||||
${compare "bigIntSub" bigIntSub (toBigInt (-99)) (toBigInt (-99))}
|
||||
${compare "bigIntSub" bigIntSub (toBigInt (99)) (toBigInt (-99))}
|
||||
${compare "bigIntSub" bigIntSub (toBigInt (-99)) (toBigInt (99))}
|
||||
${compare "bigIntSub" bigIntSub (toBigInt (-99)) (toBigInt (99))}
|
||||
|
||||
${compare "bigIntMultiply" bigIntMultiply (toBigInt 0) (toBigInt 0)}
|
||||
${compare "bigIntMultiply" bigIntMultiply (toBigInt 100) (toBigInt 0)}
|
||||
${compare "bigIntMultiply" bigIntMultiply (toBigInt 100) (toBigInt 1)}
|
||||
${compare "bigIntMultiply" bigIntMultiply (toBigInt 100) (toBigInt 2)}
|
||||
${compare "bigIntMultiply" bigIntMultiply (toBigInt 100) (toBigInt 100)}
|
||||
${compare "bigIntMultiply" bigIntMultiply (toBigInt 1) (toBigInt 2)}
|
||||
${compare "bigIntMultiply" bigIntMultiply (toBigInt 100000000) (toBigInt (-1))}
|
||||
${compare "bigIntMultiply" bigIntMultiply (toBigInt (-1)) (toBigInt 100000000)}
|
||||
${compare "bigIntMultiply" bigIntMultiply (toBigInt 200000000) (toBigInt (-2))}
|
||||
${compare "bigIntMultiply" bigIntMultiply (toBigInt (-2)) (toBigInt 200000000)}
|
||||
${compare "bigIntMultiply" bigIntMultiply (toBigInt 99) (toBigInt 99)}
|
||||
${compare "bigIntMultiply" bigIntMultiply (toBigInt (-1)) (toBigInt (-2))}
|
||||
${compare "bigIntMultiply" bigIntMultiply (toBigInt (-99)) (toBigInt (-99))}
|
||||
${compare "bigIntMultiply" bigIntMultiply (toBigInt (99)) (toBigInt (-99))}
|
||||
${compare "bigIntMultiply" bigIntMultiply (toBigInt (-99)) (toBigInt (99))}
|
||||
${compare "bigIntMultiply" bigIntMultiply (toBigInt (-99)) (toBigInt (99))}
|
||||
'');
|
||||
};
|
||||
}
|
172
lib.nix
Normal file
172
lib.nix
Normal file
@ -0,0 +1,172 @@
|
||||
{ lib, ... }: with lib;
|
||||
rec {
|
||||
# TODO: Changing the base currently requires more modifications to the code.
|
||||
# The arithmetic works out, but `toBigInt` and `bigIntToString` does not.
|
||||
base = 10;
|
||||
|
||||
# a -> Bool
|
||||
isBigInt = x: isAttrs x
|
||||
&& x ? type
|
||||
&& x.type == "bigint"
|
||||
&& x ? isPositive
|
||||
&& x ? digits;
|
||||
|
||||
# Num -> BigInt
|
||||
toBigInt = num: {
|
||||
type = "bigint";
|
||||
isPositive = num >= 0;
|
||||
digits = pipe num [
|
||||
toString
|
||||
(removePrefix "-")
|
||||
stringToCharacters
|
||||
(map toInt)
|
||||
];
|
||||
};
|
||||
|
||||
# BigInt -> String
|
||||
bigIntToString = { isPositive, digits, ... }:
|
||||
(optionalString (!isPositive) "-") + (concatStringsSep "" (map toString digits));
|
||||
|
||||
# https://silentmatt.com/blog/2011/10/how-bigintegers-work/
|
||||
|
||||
# BigInt -> BigInt -> BigInt
|
||||
bigIntAdd = x: y:
|
||||
assert isBigInt x;
|
||||
assert isBigInt y;
|
||||
let
|
||||
# rem = lib.trivial.mod;
|
||||
# mod = a: b: a - b * (builtins.floor (a / b));
|
||||
repeat = item: times: map (const item) (range 1 times);
|
||||
zfill = len: el: xs: if length xs > len then xs else (repeat el (len - (length xs))) ++ xs;
|
||||
stripZeros = xs: if xs == [0] then [0]
|
||||
else if head xs == 0 then stripZeros (tail xs)
|
||||
else xs;
|
||||
|
||||
addDigits = xs: ys: let
|
||||
addF = { fst, snd }: { carry ? false, digits ? [] }: let
|
||||
n = if carry then 1 + fst + snd else fst + snd;
|
||||
in {
|
||||
carry = n >= base;
|
||||
digits = [(mod n base)] ++ digits;
|
||||
};
|
||||
xs' = zfill (max (length xs) (length ys)) 0 xs;
|
||||
ys' = zfill (max (length xs) (length ys)) 0 ys;
|
||||
folded = foldr addF {} (zipLists xs' ys');
|
||||
in if folded.carry then [1] ++ folded.digits else folded.digits;
|
||||
|
||||
subDigits = xs: ys: let
|
||||
subF = { fst, snd }: { borrow ? false, digits ? [] }: let
|
||||
n = if borrow then fst - snd - 1 else fst - snd;
|
||||
n' = if n < 0 then n + base else n;
|
||||
in {
|
||||
borrow = n < 0;
|
||||
digits = [n'] ++ digits;
|
||||
};
|
||||
xs' = zfill (max (length xs) (length ys)) 0 xs;
|
||||
ys' = zfill (max (length xs) (length ys)) 0 ys;
|
||||
folded = foldr subF {} (zipLists xs' ys');
|
||||
in stripZeros folded.digits;
|
||||
|
||||
in if x.isPositive && y.isPositive then {
|
||||
type = "bigint";
|
||||
isPositive = true;
|
||||
digits = addDigits x.digits y.digits;
|
||||
}
|
||||
else if !x.isPositive && !y.isPositive then {
|
||||
type = "bigint";
|
||||
isPositive = false;
|
||||
digits = addDigits x.digits y.digits;
|
||||
}
|
||||
else if x.isPositive && !y.isPositive then rec {
|
||||
type = "bigint";
|
||||
isPositive = !(bigIntLessThan x (y // { isPositive = true; }));
|
||||
digits = if isPositive
|
||||
then subDigits x.digits y.digits
|
||||
else subDigits y.digits x.digits;
|
||||
}
|
||||
else /* !x.isPositive && y.isPositive */ rec {
|
||||
type = "bigint";
|
||||
isPositive = !(bigIntLessThan y (x // { isPositive = true; }));
|
||||
digits = if isPositive
|
||||
then subDigits y.digits x.digits
|
||||
else subDigits x.digits y.digits;
|
||||
};
|
||||
|
||||
# BigInt -> BigInt -> BigInt
|
||||
bigIntSub = x: y@{ isPositive, ... }:
|
||||
bigIntAdd x (y // { isPositive = !isPositive; });
|
||||
|
||||
# https://silentmatt.com/blog/2011/10/how-bigintegers-work-part-2-multiplication/
|
||||
|
||||
# BigInt -> BigInt -> BigInt
|
||||
bigIntMultiply = x: y:
|
||||
assert isBigInt x;
|
||||
assert isBigInt y;
|
||||
let
|
||||
xor = a: b: (a && !b) || (!a && b);
|
||||
|
||||
createPartial = y_digit: { carry ? 0, digits ? [] }: x_digit: let
|
||||
n = (x_digit * y_digit) + carry;
|
||||
in {
|
||||
carry = builtins.floor (n / base);
|
||||
digits = [(mod n base)] ++ digits;
|
||||
};
|
||||
|
||||
partialProductDigits = let
|
||||
mapYs = i: y_digit: let
|
||||
partial = foldl (createPartial y_digit) { } ([0] ++ x.digits);
|
||||
partialWithCorrectPower = partial.digits ++ (builtins.genList (_: 0) i);
|
||||
in partialWithCorrectPower;
|
||||
in imap0 mapYs y.digits;
|
||||
|
||||
partialProducts =
|
||||
map (digits: { type = "bigint"; isPositive = true; inherit digits; }) partialProductDigits;
|
||||
|
||||
in foldl bigIntAdd (toBigInt 0) partialProducts // {
|
||||
isPositive = !(xor x.isPositive y.isPositive);
|
||||
};
|
||||
|
||||
# BigInt -> BigInt -> BigInt
|
||||
bigIntEquals = x: y:
|
||||
assert isBigInt x;
|
||||
assert isBigInt y;
|
||||
x == y;
|
||||
|
||||
# BigInt -> BigInt -> BigInt
|
||||
bigIntGreaterThan = x: y:
|
||||
assert isBigInt x;
|
||||
assert isBigInt y;
|
||||
if x.isPositive && !y.isPositive then true
|
||||
else if !x.isPositive && y.isPositive then false
|
||||
# Cheaper than the upcoming calculations
|
||||
else if bigIntEquals x y then false
|
||||
|
||||
else if x.isPositive && y.isPositive then
|
||||
if length x.digits < length y.digits then false
|
||||
else if length x.digits > length y.digits then true
|
||||
else let
|
||||
compareDigits = xs: ys:
|
||||
if xs == [] then false
|
||||
else if (head xs) == (head ys) then compareDigits (tail xs) (tail ys)
|
||||
else head xs > head ys;
|
||||
in compareDigits x.digits y.digits
|
||||
|
||||
/* !x.isPositive && !y.isPositive */
|
||||
else
|
||||
if length x.digits > length y.digits then false
|
||||
else if length x.digits < length y.digits then true
|
||||
else let
|
||||
compareDigits = xs: ys:
|
||||
if xs == [] then false
|
||||
else if (head xs) == (head ys) then compareDigits (tail xs) (tail ys)
|
||||
else head xs < head ys;
|
||||
in compareDigits x.digits y.digits;
|
||||
|
||||
# BigInt -> BigInt -> BigInt
|
||||
bigIntLessThan = flip bigIntGreaterThan;
|
||||
|
||||
# BigInt -> BigInt -> BigInt
|
||||
bigIntCmp = x: y: if bigIntEquals x y then 0 else
|
||||
if bigIntGreaterThan x y then 1 else
|
||||
-1;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user