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