├── static ├── uint256.png ├── github-mark.png ├── uint256-2023.png ├── uint256-no-clear.png └── uint256-tuweni.png ├── scratch.clj ├── project.clj ├── .gitignore ├── .circleci └── config.yml ├── juint ├── src │ ├── test │ │ └── java │ │ │ └── io │ │ │ └── nervous │ │ │ └── juint │ │ │ ├── TestUInt128.java │ │ │ ├── TestUInt256.java │ │ │ └── Properties.java │ └── main │ │ └── java │ │ └── io │ │ └── nervous │ │ └── juint │ │ ├── StringUtil.java │ │ ├── UInt256.java │ │ ├── UInt128.java │ │ ├── Division.java │ │ ├── UInt.java │ │ └── Arrays.java └── pom.xml ├── bench ├── src │ └── main │ │ └── java │ │ └── io │ │ └── nervous │ │ └── juint │ │ ├── UIntState.java │ │ ├── BigState.java │ │ ├── Not.java │ │ ├── Add.java │ │ ├── Pow.java │ │ ├── Shift.java │ │ ├── Misc.java │ │ ├── Or.java │ │ ├── And.java │ │ ├── Xor.java │ │ ├── Divide.java │ │ ├── Multiply.java │ │ ├── Subtract.java │ │ └── Bits.java └── pom.xml ├── graph.py ├── split-graph.py ├── README.md ├── pom.xml ├── biginteger-performance.md └── LICENSE /static/uint256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nervous-systems/java-unsigned-integers/HEAD/static/uint256.png -------------------------------------------------------------------------------- /static/github-mark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nervous-systems/java-unsigned-integers/HEAD/static/github-mark.png -------------------------------------------------------------------------------- /static/uint256-2023.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nervous-systems/java-unsigned-integers/HEAD/static/uint256-2023.png -------------------------------------------------------------------------------- /static/uint256-no-clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nervous-systems/java-unsigned-integers/HEAD/static/uint256-no-clear.png -------------------------------------------------------------------------------- /static/uint256-tuweni.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nervous-systems/java-unsigned-integers/HEAD/static/uint256-tuweni.png -------------------------------------------------------------------------------- /scratch.clj: -------------------------------------------------------------------------------- 1 | (ns scratch 2 | (:import [org.openjdk.jol.info ClassLayout])) 3 | 4 | (defn layout [inst] 5 | (println (.toPrintable (ClassLayout/parseClass (class inst)) inst))) 6 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject playground "0.1.0-SNAPSHOT" 2 | :description "Introspection" 3 | :url "http://example.com/FIXME" 4 | :dependencies [[org.clojure/clojure "1.11.1"] 5 | [org.openjdk.jol/jol-core "0.16"]] 6 | ) 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.ear 17 | *.zip 18 | *.tar.gz 19 | *.rar 20 | 21 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 22 | hs_err_pid* 23 | 24 | target 25 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | - image: circleci/openjdk:8-jdk 6 | environment: 7 | - MAVEN_OPTS: -Xmx2048m 8 | - JUINT_SAMPLE_FACTOR: 0.25 9 | steps: 10 | - checkout 11 | - restore_cache: 12 | keys: 13 | - deps-{{ checksum "pom.xml" }} 14 | - deps- 15 | - run: cd juint/ && mvn dependency:go-offline 16 | - save_cache: 17 | paths: 18 | - ~/.m2 19 | key: deps-{{ checksum "pom.xml" }} 20 | - run: 21 | command: mvn -Dtest=TestUInt128 -e test 22 | working_directory: juint 23 | - run: 24 | command: mvn -Dtest=TestUInt256 -e test 25 | working_directory: juint 26 | -------------------------------------------------------------------------------- /juint/src/test/java/io/nervous/juint/TestUInt128.java: -------------------------------------------------------------------------------- 1 | package io.nervous.juint; 2 | 3 | import java.math.BigInteger; 4 | 5 | import org.junit.Test; 6 | import static org.junit.Assert.assertEquals; 7 | import static org.junit.Assert.assertArrayEquals; 8 | 9 | public class TestUInt128 extends Properties { 10 | UInt128 construct(int[] ints) { return new UInt128(ints); } 11 | UInt128 construct(byte[] bytes) { return new UInt128(bytes); } 12 | UInt128 construct(BigInteger b) { return new UInt128(b); } 13 | UInt128 construct(long v) { return new UInt128(v); } 14 | 15 | UInt128 construct(String s, int radix) { 16 | return new UInt128(s, radix); 17 | } 18 | 19 | int maxWidth() { 20 | return UInt128.MAX_WIDTH; 21 | } 22 | 23 | @Test 24 | public void copyCtor() { 25 | for(int i = 0; i < SAMPLE_SMALL; i++) { 26 | int[] ints = randomints(UInt256.MAX_WIDTH); 27 | UInt256 large = new UInt256(ints); 28 | assertEquals(new UInt128(large.toBigInteger()), new UInt128(large)); 29 | assertArrayEquals( 30 | Arrays.stripLeadingZeroes(ints, UInt256.MAX_WIDTH - UInt128.MAX_WIDTH), 31 | new UInt128(large).ints); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /juint/src/test/java/io/nervous/juint/TestUInt256.java: -------------------------------------------------------------------------------- 1 | package io.nervous.juint; 2 | 3 | import java.math.BigInteger; 4 | 5 | import org.junit.BeforeClass; 6 | 7 | import org.junit.Test; 8 | import static org.junit.Assert.assertEquals; 9 | import static org.junit.Assert.assertArrayEquals; 10 | 11 | public class TestUInt256 extends Properties { 12 | UInt256 construct(int[] ints) { return new UInt256(ints); } 13 | UInt256 construct(byte[] bytes) { return new UInt256(bytes); } 14 | UInt256 construct(BigInteger b) { return new UInt256(b); } 15 | UInt256 construct(long v) { return new UInt256(v); } 16 | 17 | UInt256 construct(String s, int radix) { 18 | return new UInt256(s, radix); 19 | } 20 | 21 | int maxWidth() { 22 | return UInt256.MAX_WIDTH; 23 | } 24 | 25 | @Test 26 | public void copyCtor() { 27 | for(int i = 0; i < SAMPLE_SMALL; i++) { 28 | int[] ints = randomints(UInt128.MAX_WIDTH); 29 | UInt128 small = new UInt128(ints); 30 | assertEquals(new UInt256(small.toBigInteger()), new UInt256(small)); 31 | assertEquals(small, new UInt128(new UInt256(small))); 32 | assertArrayEquals(ints, new UInt256(small).ints); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /bench/src/main/java/io/nervous/juint/UIntState.java: -------------------------------------------------------------------------------- 1 | package io.nervous.juint; 2 | 3 | import org.openjdk.jmh.annotations.*; 4 | 5 | @State(Scope.Thread) 6 | public class UIntState { 7 | int bits = 256; 8 | 9 | UInt256 max = new UInt256(new int[]{-1, -1, -1, -1, -1, -1, -1, -1}); 10 | UInt256[] full = new UInt256[]{ 11 | new UInt256(new int[]{-1, Short.MAX_VALUE, -1, Integer.MAX_VALUE, 12 | Integer.MIN_VALUE, 77, 3, -1}), 13 | new UInt256(new int[]{Short.MAX_VALUE, -1, -1, Integer.MAX_VALUE, 14 | Short.MIN_VALUE, 13, 2, 0}), 15 | new UInt256(new int[]{Short.MIN_VALUE, 0, 0, Integer.MAX_VALUE, 16 | Integer.MAX_VALUE, 11, 1, Short.MIN_VALUE})}; 17 | 18 | UInt256 one = UInt256.ONE; 19 | UInt256 zero = UInt256.ZERO; 20 | UInt256[] onew = new UInt256[]{ 21 | new UInt256(new int[]{-1}), 22 | new UInt256(new int[]{Integer.MIN_VALUE}), 23 | new UInt256(new int[]{1})}; 24 | 25 | UInt256[] twow = new UInt256[]{ 26 | new UInt256(new int[]{1, 1}), 27 | new UInt256(new int[]{Integer.MAX_VALUE, -1}), 28 | new UInt256(new int[]{Short.MIN_VALUE, 9})}; 29 | 30 | UInt256[] half = new UInt256[]{ 31 | new UInt256(new int[]{1, Short.MAX_VALUE, -1, Integer.MAX_VALUE}), 32 | new UInt256(new int[]{Short.MAX_VALUE, -1, -1, Integer.MAX_VALUE}), 33 | new UInt256(new int[]{Short.MIN_VALUE, 0, 0, Integer.MAX_VALUE})}; 34 | } 35 | -------------------------------------------------------------------------------- /bench/src/main/java/io/nervous/juint/BigState.java: -------------------------------------------------------------------------------- 1 | package io.nervous.juint; 2 | 3 | import java.math.BigInteger; 4 | 5 | import org.openjdk.jmh.annotations.*; 6 | 7 | @State(Scope.Thread) 8 | public class BigState { 9 | int bits = 256; 10 | 11 | static BigInteger b(int[] vs) { 12 | return new UInt256(vs).toBigInteger(); 13 | } 14 | 15 | BigInteger max = b(new int[]{-1, -1, -1, -1, -1, -1, -1, -1}); 16 | BigInteger[] full = new BigInteger[]{ 17 | b(new int[]{-1, Short.MAX_VALUE, -1, Integer.MAX_VALUE, 18 | Integer.MIN_VALUE, 77, 3, -1}), 19 | b(new int[]{Short.MAX_VALUE, -1, -1, Integer.MAX_VALUE, 20 | Short.MIN_VALUE, 13, 2, 0}), 21 | b(new int[]{Short.MIN_VALUE, 0, 0, Integer.MAX_VALUE, 22 | Integer.MAX_VALUE, 11, 1, Short.MIN_VALUE})}; 23 | 24 | BigInteger one = BigInteger.ONE; 25 | BigInteger zero = b(new int[0]); 26 | BigInteger[] onew = new BigInteger[]{ 27 | b(new int[]{-1}), 28 | b(new int[]{Integer.MIN_VALUE}), 29 | b(new int[]{1})}; 30 | 31 | BigInteger[] twow = new BigInteger[]{ 32 | b(new int[]{1, 1}), 33 | b(new int[]{Integer.MAX_VALUE, -1}), 34 | b(new int[]{Short.MIN_VALUE, 9})}; 35 | 36 | BigInteger[] half = new BigInteger[]{ 37 | b(new int[]{-1, Short.MAX_VALUE, -1, Integer.MAX_VALUE}), 38 | b(new int[]{Short.MAX_VALUE, -1, -1, Integer.MAX_VALUE}), 39 | b(new int[]{Short.MIN_VALUE, 0, 0, Integer.MAX_VALUE})}; 40 | } 41 | -------------------------------------------------------------------------------- /bench/src/main/java/io/nervous/juint/Not.java: -------------------------------------------------------------------------------- 1 | package io.nervous.juint; 2 | 3 | import org.openjdk.jmh.annotations.*; 4 | import org.openjdk.jmh.infra.Blackhole; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | 8 | @BenchmarkMode({Mode.Throughput}) 9 | @OutputTimeUnit(TimeUnit.MICROSECONDS) 10 | public class Not { 11 | @Benchmark 12 | public void not_max(UIntState s, Blackhole b) { 13 | b.consume(s.max.not()); 14 | } 15 | 16 | @Benchmark 17 | public void not_max_ref(BigState s, Blackhole b) { 18 | b.consume(s.max.not()); 19 | } 20 | 21 | @Benchmark 22 | public void not_zero(UIntState s, Blackhole b) { 23 | b.consume(s.zero.not()); 24 | } 25 | 26 | @Benchmark 27 | public void not_zero_ref(BigState s, Blackhole b) { 28 | b.consume(s.zero.not()); 29 | } 30 | 31 | @Benchmark 32 | public void not_half(UIntState s, Blackhole b) { 33 | for(int i = 0; i < s.half.length; i++) 34 | b.consume(s.half[i].not()); 35 | } 36 | 37 | @Benchmark 38 | public void not_half_ref(BigState s, Blackhole b) { 39 | for(int i = 0; i < s.half.length; i++) 40 | b.consume(s.half[i].not()); 41 | } 42 | 43 | @Benchmark 44 | public void not_1w(UIntState s, Blackhole b) { 45 | for(int i = 0; i < s.onew.length; i++) 46 | b.consume(s.onew[i].not()); 47 | } 48 | 49 | @Benchmark 50 | public void not_1w_ref(BigState s, Blackhole b) { 51 | for(int i = 0; i < s.onew.length; i++) 52 | b.consume(s.onew[i].not()); 53 | } 54 | 55 | @Benchmark 56 | public void not_2w(UIntState s, Blackhole b) { 57 | for(int i = 0; i < s.onew.length; i++) 58 | b.consume(s.twow[i].not()); 59 | } 60 | 61 | @Benchmark 62 | public void not_2w_ref(BigState s, Blackhole b) { 63 | for(int i = 0; i < s.twow.length; i++) 64 | b.consume(s.twow[i].not()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /bench/src/main/java/io/nervous/juint/Add.java: -------------------------------------------------------------------------------- 1 | package io.nervous.juint; 2 | 3 | import org.openjdk.jmh.annotations.*; 4 | import org.openjdk.jmh.infra.Blackhole; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | 8 | @BenchmarkMode({Mode.Throughput}) 9 | @OutputTimeUnit(TimeUnit.MICROSECONDS) 10 | public class Add { 11 | @Benchmark 12 | public void add_halfHalf(UIntState s, Blackhole b) { 13 | for(int i = 0; i < s.half.length; i++) 14 | b.consume(s.half[i].add(s.half[0])); 15 | } 16 | 17 | @Benchmark 18 | public void add_halfHalf_ref(BigState s, Blackhole b) { 19 | for(int i = 0; i < s.half.length; i++) 20 | b.consume(s.half[i].add(s.half[0])); 21 | } 22 | 23 | @Benchmark 24 | public void add_maxMax(UIntState s, Blackhole b) { 25 | b.consume(s.max.add(s.max)); 26 | } 27 | 28 | @Benchmark 29 | public void add_maxMax_ref(BigState s, Blackhole b) { 30 | b.consume(s.max.add(s.max).and(s.max)); 31 | } 32 | 33 | @Benchmark 34 | public void add_max1w(UIntState s, Blackhole b) { 35 | b.consume(s.max.add(s.onew[0])); 36 | } 37 | 38 | @Benchmark 39 | public void add_max1w_ref(BigState s, Blackhole b) { 40 | b.consume(s.max.add(s.onew[0]).and(s.max)); 41 | } 42 | 43 | @Benchmark 44 | public void add_half1w(UIntState s, Blackhole b) { 45 | for(int i = 0; i < s.onew.length; i++) 46 | b.consume(s.half[i].add(s.onew[i])); 47 | } 48 | 49 | @Benchmark 50 | public void add_half1w_ref(BigState s, Blackhole b) { 51 | for(int i = 0; i < s.onew.length; i++) 52 | b.consume(s.half[i].add(s.onew[i])); 53 | } 54 | 55 | @Benchmark 56 | public void add_1w1w(UIntState s, Blackhole b) { 57 | for(int i = 0; i < s.onew.length; i++) 58 | b.consume(s.onew[i].add(s.onew[0])); 59 | } 60 | 61 | @Benchmark 62 | public void add_1w1w_ref(BigState s, Blackhole b) { 63 | for(int i = 0; i < s.onew.length; i++) 64 | b.consume(s.onew[i].add(s.onew[0])); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /bench/src/main/java/io/nervous/juint/Pow.java: -------------------------------------------------------------------------------- 1 | package io.nervous.juint; 2 | 3 | import org.openjdk.jmh.annotations.*; 4 | import org.openjdk.jmh.infra.Blackhole; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | 8 | @BenchmarkMode({Mode.Throughput}) 9 | @OutputTimeUnit(TimeUnit.MILLISECONDS) 10 | public class Pow { 11 | @Benchmark 12 | public void pow_1w256(UIntState s, Blackhole b) { 13 | for(int i = 0; i < s.onew.length; i++) 14 | b.consume(s.onew[i].pow(256)); 15 | } 16 | 17 | @Benchmark 18 | public void pow_1w256_ref(BigState s, Blackhole b) { 19 | for(int i = 0; i < s.onew.length; i++) 20 | b.consume(s.onew[i].pow(256).and(s.max)); 21 | } 22 | 23 | @Benchmark 24 | public void pow_1w16(UIntState s, Blackhole b) { 25 | for(int i = 0; i < s.onew.length; i++) 26 | b.consume(s.onew[i].pow(16)); 27 | } 28 | 29 | @Benchmark 30 | public void pow_1w16_ref(BigState s, Blackhole b) { 31 | for(int i = 0; i < s.onew.length; i++) 32 | b.consume(s.onew[i].pow(16)); 33 | } 34 | 35 | @Benchmark 36 | public void pow_2w8(UIntState s, Blackhole b) { 37 | for(int i = 0; i < s.twow.length; i++) 38 | b.consume(s.twow[i].pow(8)); 39 | } 40 | 41 | @Benchmark 42 | public void pow_2w8_ref(BigState s, Blackhole b) { 43 | for(int i = 0; i < s.twow.length; i++) 44 | b.consume(s.twow[i].pow(8)); 45 | } 46 | 47 | @Benchmark 48 | public void pow_2w17(UIntState s, Blackhole b) { 49 | for(int i = 0; i < s.twow.length; i++) 50 | b.consume(s.twow[i].pow(17)); 51 | } 52 | 53 | @Benchmark 54 | public void pow_2w17_ref(BigState s, Blackhole b) { 55 | for(int i = 0; i < s.twow.length; i++) 56 | b.consume(s.twow[i].pow(17)); 57 | } 58 | 59 | @Benchmark 60 | public void pow_half2(UIntState s, Blackhole b) { 61 | for(int i = 0; i < s.half.length; i++) 62 | b.consume(s.half[i].pow(2)); 63 | } 64 | 65 | @Benchmark 66 | public void pow_half2_ref(BigState s, Blackhole b) { 67 | for(int i = 0; i < s.half.length; i++) 68 | b.consume(s.half[i].pow(2)); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /bench/src/main/java/io/nervous/juint/Shift.java: -------------------------------------------------------------------------------- 1 | package io.nervous.juint; 2 | 3 | import org.openjdk.jmh.annotations.*; 4 | import org.openjdk.jmh.infra.Blackhole; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | 8 | @BenchmarkMode({Mode.Throughput}) 9 | @OutputTimeUnit(TimeUnit.MICROSECONDS) 10 | public class Shift { 11 | @Benchmark 12 | public void shiftLeft_1wBy1w(UIntState s, Blackhole b) { 13 | for(int i = 0; i < s.onew.length; i++) 14 | b.consume(s.onew[i].shiftLeft(32)); 15 | } 16 | 17 | @Benchmark 18 | public void shiftLeft_1wBy1w_ref(BigState s, Blackhole b) { 19 | for(int i = 0; i < s.onew.length; i++) 20 | b.consume(s.onew[i].shiftLeft(32)); 21 | } 22 | 23 | @Benchmark 24 | public void shiftRight_1wBy1w(UIntState s, Blackhole b) { 25 | for(int i = 0; i < s.onew.length; i++) 26 | b.consume(s.onew[i].shiftRight(32)); 27 | } 28 | 29 | @Benchmark 30 | public void shiftRight_1wBy1w_ref(BigState s, Blackhole b) { 31 | for(int i = 0; i < s.onew.length; i++) 32 | b.consume(s.onew[i].shiftRight(32)); 33 | } 34 | 35 | @Benchmark 36 | public void shiftLeft_maxByHalf(UIntState s, Blackhole b) { 37 | b.consume(s.max.shiftLeft(128)); 38 | } 39 | 40 | @Benchmark 41 | public void shiftLeft_maxByHalf_ref(BigState s, Blackhole b) { 42 | b.consume(s.max.shiftLeft(128)); 43 | } 44 | 45 | @Benchmark 46 | public void shiftRight_maxByHalf(UIntState s, Blackhole b) { 47 | b.consume(s.max.shiftRight(128)); 48 | } 49 | 50 | @Benchmark 51 | public void shiftRight_maxByHalf_ref(BigState s, Blackhole b) { 52 | b.consume(s.max.shiftRight(128)); 53 | } 54 | 55 | @Benchmark 56 | public void shiftLeft_maxByAlmostHalf(UIntState s, Blackhole b) { 57 | b.consume(s.max.shiftLeft(97)); 58 | } 59 | 60 | @Benchmark 61 | public void shiftLeft_maxByAlmostHalf_ref(BigState s, Blackhole b) { 62 | b.consume(s.max.shiftLeft(97)); 63 | } 64 | 65 | @Benchmark 66 | public void shiftRight_maxByAlmostHalf(UIntState s, Blackhole b) { 67 | b.consume(s.max.shiftRight(97)); 68 | } 69 | 70 | @Benchmark 71 | public void shiftRight_maxByAlmostHalf_ref(BigState s, Blackhole b) { 72 | b.consume(s.max.shiftRight(97)); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /bench/src/main/java/io/nervous/juint/Misc.java: -------------------------------------------------------------------------------- 1 | package io.nervous.juint; 2 | 3 | import org.openjdk.jmh.annotations.*; 4 | import org.openjdk.jmh.infra.Blackhole; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | 8 | @BenchmarkMode({Mode.Throughput}) 9 | @OutputTimeUnit(TimeUnit.MILLISECONDS) 10 | public class Misc { 11 | @Benchmark 12 | public void compareTo_full(UIntState s, Blackhole b) { 13 | for(int i = 0; i < s.full.length; i++) 14 | b.consume(s.full[0].compareTo(s.full[i])); 15 | } 16 | 17 | @Benchmark 18 | public void compareTo_full_ref(BigState s, Blackhole b) { 19 | for(int i = 0; i < s.full.length; i++) 20 | b.consume(s.full[0].compareTo(s.full[i])); 21 | } 22 | 23 | @Benchmark 24 | public void toString_max10(UIntState s, Blackhole b) { 25 | b.consume(s.max.toString()); 26 | } 27 | 28 | @Benchmark 29 | public void toString_max10_ref(BigState s, Blackhole b) { 30 | b.consume(s.max.toString()); 31 | } 32 | 33 | @Benchmark 34 | public void toString_max2(UIntState s, Blackhole b) { 35 | b.consume(s.max.toString(2)); 36 | } 37 | 38 | @Benchmark 39 | public void toString_max2_ref(BigState s, Blackhole b) { 40 | b.consume(s.max.toString(2)); 41 | } 42 | 43 | @Benchmark 44 | public void toString_half16(UIntState s, Blackhole b) { 45 | b.consume(s.half[0].toString(16)); 46 | } 47 | 48 | @Benchmark 49 | public void toString_half6_ref(BigState s, Blackhole b) { 50 | b.consume(s.half[0].toString(16)); 51 | } 52 | 53 | @Benchmark 54 | public void toString_1w10(UIntState s, Blackhole b) { 55 | for(int i = 0; i < s.onew.length; i++) 56 | b.consume(s.onew[i].toString()); 57 | } 58 | 59 | @Benchmark 60 | public void toString_1w10_ref(BigState s, Blackhole b) { 61 | for(int i = 0; i < s.onew.length; i++) 62 | b.consume(s.onew[i].toString()); 63 | } 64 | 65 | @Benchmark 66 | public void toString_2w10(UIntState s, Blackhole b) { 67 | for(int i = 0; i < s.twow.length; i++) 68 | b.consume(s.twow[i].toString()); 69 | } 70 | 71 | @Benchmark 72 | public void toString_2w10_ref(BigState s, Blackhole b) { 73 | for(int i = 0; i < s.twow.length; i++) 74 | b.consume(s.twow[i].toString()); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /bench/src/main/java/io/nervous/juint/Or.java: -------------------------------------------------------------------------------- 1 | package io.nervous.juint; 2 | 3 | import org.openjdk.jmh.annotations.*; 4 | import org.openjdk.jmh.infra.Blackhole; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | 8 | @BenchmarkMode({Mode.Throughput}) 9 | @OutputTimeUnit(TimeUnit.MICROSECONDS) 10 | public class Or { 11 | @Benchmark 12 | public void or_halfHalf(UIntState s, Blackhole b) { 13 | for(int i = 0; i < s.half.length; i++) 14 | b.consume(s.half[i].or(s.half[0])); 15 | } 16 | 17 | @Benchmark 18 | public void or_halfHalf_ref(BigState s, Blackhole b) { 19 | for(int i = 0; i < s.half.length; i++) 20 | b.consume(s.half[i].or(s.half[0])); 21 | } 22 | 23 | @Benchmark 24 | public void or_maxHalf(UIntState s, Blackhole b) { 25 | for(int i = 0; i < s.half.length; i++) 26 | b.consume(s.max.or(s.half[i])); 27 | } 28 | 29 | @Benchmark 30 | public void or_maxHalf_ref(BigState s, Blackhole b) { 31 | for(int i = 0; i < s.half.length; i++) 32 | b.consume(s.max.or(s.half[i])); 33 | } 34 | 35 | @Benchmark 36 | public void or_maxMax(UIntState s, Blackhole b) { 37 | b.consume(s.max.or(s.max)); 38 | } 39 | 40 | @Benchmark 41 | public void or_maxMax_ref(BigState s, Blackhole b) { 42 | b.consume(s.max.or(s.max)); 43 | } 44 | 45 | @Benchmark 46 | public void or_max1w(UIntState s, Blackhole b) { 47 | for(int i = 0; i < s.onew.length; i++) 48 | b.consume(s.max.or(s.onew[i])); 49 | } 50 | 51 | @Benchmark 52 | public void or_max1w_ref(BigState s, Blackhole b) { 53 | for(int i = 0; i < s.onew.length; i++) 54 | b.consume(s.max.or(s.onew[i])); 55 | } 56 | 57 | @Benchmark 58 | public void or_half1w(UIntState s, Blackhole b) { 59 | for(int i = 0; i < s.half.length; i++) 60 | b.consume(s.half[i].or(s.onew[i])); 61 | } 62 | 63 | @Benchmark 64 | public void or_half1w_ref(BigState s, Blackhole b) { 65 | for(int i = 0; i < s.half.length; i++) 66 | b.consume(s.half[i].or(s.onew[i])); 67 | } 68 | 69 | @Benchmark 70 | public void or_1w1w(UIntState s, Blackhole b) { 71 | for(int i = 0; i < s.onew.length; i++) 72 | b.consume(s.onew[i].or(s.onew[0])); 73 | } 74 | 75 | @Benchmark 76 | public void or_1w1w_ref(BigState s, Blackhole b) { 77 | for(int i = 0; i < s.onew.length; i++) 78 | b.consume(s.onew[i].or(s.onew[0])); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /bench/src/main/java/io/nervous/juint/And.java: -------------------------------------------------------------------------------- 1 | package io.nervous.juint; 2 | 3 | import org.openjdk.jmh.annotations.*; 4 | import org.openjdk.jmh.infra.Blackhole; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | 8 | @BenchmarkMode({Mode.Throughput}) 9 | @OutputTimeUnit(TimeUnit.MICROSECONDS) 10 | public class And { 11 | @Benchmark 12 | public void and_halfHalf(UIntState s, Blackhole b) { 13 | for(int i = 0; i < s.half.length; i++) 14 | b.consume(s.half[0].and(s.half[i])); 15 | } 16 | 17 | @Benchmark 18 | public void and_halfHalf_ref(BigState s, Blackhole b) { 19 | for(int i = 0; i < s.half.length; i++) 20 | b.consume(s.half[0].and(s.half[i])); 21 | } 22 | 23 | @Benchmark 24 | public void and_maxHalf(UIntState s, Blackhole b) { 25 | for(int i = 0; i < s.half.length; i++) 26 | b.consume(s.half[i].and(s.max)); 27 | } 28 | 29 | @Benchmark 30 | public void and_maxHalf_ref(BigState s, Blackhole b) { 31 | for(int i = 0; i < s.half.length; i++) 32 | b.consume(s.half[i].and(s.max)); 33 | } 34 | 35 | @Benchmark 36 | public void and_maxMax(UIntState s, Blackhole b) { 37 | b.consume(s.max.and(s.max)); 38 | } 39 | 40 | @Benchmark 41 | public void and_maxMax_ref(BigState s, Blackhole b) { 42 | b.consume(s.max.and(s.max)); 43 | } 44 | 45 | @Benchmark 46 | public void and_max1w(UIntState s, Blackhole b) { 47 | for(int i = 0; i < s.onew.length; i++) 48 | b.consume(s.max.and(s.onew[i])); 49 | } 50 | 51 | @Benchmark 52 | public void and_max1w_ref(BigState s, Blackhole b) { 53 | for(int i = 0; i < s.onew.length; i++) 54 | b.consume(s.max.and(s.onew[i])); 55 | } 56 | 57 | @Benchmark 58 | public void and_half1w(UIntState s, Blackhole b) { 59 | for(int i = 0; i < s.half.length; i++) 60 | b.consume(s.half[i].and(s.onew[i])); 61 | } 62 | 63 | @Benchmark 64 | public void and_half1w_ref(BigState s, Blackhole b) { 65 | for(int i = 0; i < s.half.length; i++) 66 | b.consume(s.half[i].and(s.onew[i])); 67 | } 68 | 69 | @Benchmark 70 | public void and_1w1w(UIntState s, Blackhole b) { 71 | for(int i = 0; i < s.onew.length; i++) 72 | b.consume(s.onew[0].and(s.onew[i])); 73 | } 74 | 75 | @Benchmark 76 | public void and_1w1w_ref(BigState s, Blackhole b) { 77 | for(int i = 0; i < s.onew.length; i++) 78 | b.consume(s.onew[0].and(s.onew[i])); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /bench/src/main/java/io/nervous/juint/Xor.java: -------------------------------------------------------------------------------- 1 | package io.nervous.juint; 2 | 3 | import org.openjdk.jmh.annotations.*; 4 | import org.openjdk.jmh.infra.Blackhole; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | 8 | @BenchmarkMode({Mode.Throughput}) 9 | @OutputTimeUnit(TimeUnit.MICROSECONDS) 10 | public class Xor { 11 | @Benchmark 12 | public void xor_halfHalf(UIntState s, Blackhole b) { 13 | for(int i = 0; i < s.half.length; i++) 14 | b.consume(s.half[i].xor(s.half[0])); 15 | } 16 | 17 | @Benchmark 18 | public void xor_halfHalf_ref(BigState s, Blackhole b) { 19 | for(int i = 0; i < s.half.length; i++) 20 | b.consume(s.half[i].xor(s.half[0])); 21 | } 22 | 23 | @Benchmark 24 | public void xor_maxHalf(UIntState s, Blackhole b) { 25 | for(int i = 0; i < s.half.length; i++) 26 | b.consume(s.max.xor(s.half[i])); 27 | } 28 | 29 | @Benchmark 30 | public void xor_maxHalf_ref(BigState s, Blackhole b) { 31 | for(int i = 0; i < s.half.length; i++) 32 | b.consume(s.max.xor(s.half[i])); 33 | } 34 | 35 | @Benchmark 36 | public void xor_maxMax(UIntState s, Blackhole b) { 37 | b.consume(s.max.xor(s.max)); 38 | } 39 | 40 | @Benchmark 41 | public void xor_maxMax_ref(BigState s, Blackhole b) { 42 | b.consume(s.max.xor(s.max)); 43 | } 44 | 45 | @Benchmark 46 | public void xor_max1w(UIntState s, Blackhole b) { 47 | for(int i = 0; i < s.onew.length; i++) 48 | b.consume(s.max.xor(s.onew[i])); 49 | } 50 | 51 | @Benchmark 52 | public void xor_max1w_ref(BigState s, Blackhole b) { 53 | for(int i = 0; i < s.onew.length; i++) 54 | b.consume(s.max.xor(s.onew[i])); 55 | } 56 | 57 | @Benchmark 58 | public void xor_half1w(UIntState s, Blackhole b) { 59 | for(int i = 0; i < s.half.length; i++) 60 | b.consume(s.half[i].xor(s.onew[i])); 61 | } 62 | 63 | @Benchmark 64 | public void xor_half1w_ref(BigState s, Blackhole b) { 65 | for(int i = 0; i < s.half.length; i++) 66 | b.consume(s.half[i].xor(s.onew[i])); 67 | } 68 | 69 | @Benchmark 70 | public void xor_1w1w(UIntState s, Blackhole b) { 71 | for(int i = 0; i < s.onew.length; i++) 72 | b.consume(s.onew[i].xor(s.onew[0])); 73 | } 74 | 75 | @Benchmark 76 | public void xor_1w1w_ref(BigState s, Blackhole b) { 77 | for(int i = 0; i < s.onew.length; i++) 78 | b.consume(s.onew[i].xor(s.onew[0])); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /graph.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import csv, sys 3 | from decimal import Decimal 4 | 5 | def avg(d): 6 | return {k: sum(v)/len(v) for (k, v) in d.items()} 7 | 8 | def calculate(path): 9 | scores = {} 10 | refs = {} 11 | 12 | with open(path, 'r') as f: 13 | reader = csv.reader(f) 14 | next(reader) 15 | for row in reader: 16 | segments = tuple(row[0].split('.')[-1].split('_')) 17 | d = refs if segments[-1] == 'ref' else scores 18 | d.setdefault(segments[0], []).append(Decimal(row[-3])) 19 | 20 | scores = avg(scores) 21 | refs = avg(refs) 22 | 23 | for (meth, mean) in scores.items(): 24 | change = (mean - refs[meth]) / refs[meth] * 100 25 | yield (meth, change) 26 | 27 | if __name__ == '__main__': 28 | import numpy as np 29 | import matplotlib.pyplot as plt 30 | from matplotlib import ticker, rc 31 | import pprint, itertools 32 | 33 | data = {sys.argv[i]: sorted(calculate(sys.argv[i+1]), key=lambda t: t[0]) 34 | for i in range(1, len(sys.argv), 2)} 35 | 36 | mmax = float(max(list(zip(*itertools.chain(*data.values())))[1])) 37 | mmin = float(min(list(zip(*itertools.chain(*data.values())))[1])) 38 | 39 | pprint.pprint(data) 40 | 41 | N = len(list(data.values())[0]) 42 | 43 | ind = np.arange(N) 44 | width = 0.35 45 | colors = itertools.cycle(('#9edeff', '#ff9ede')) 46 | FONT = 'Droid Sans Mono' 47 | 48 | rc('font', family=FONT) 49 | 50 | (fig, ax) = plt.subplots() 51 | 52 | ax.tick_params(labeltop=False, top=False, bottom=False) 53 | 54 | rects = [] 55 | for (i, (tag, xs)) in enumerate(sorted(data.items(), key=lambda t: t[0])): 56 | color = next(colors) 57 | r = ax.bar(ind + (i * width), list(zip(*xs))[1], width, color=color) 58 | rects.append((r, tag)) 59 | 60 | ax.set_title("UInt256 Throughput Increase\nvs. OpenJDK's BigInteger") 61 | labels = list(zip(*list(data.values())[0]))[0] 62 | ax.set_xticks(ind, top=False) 63 | ax.set_xticklabels(labels, rotation=90) 64 | ax.tick_params(axis='x', top=False, labeltop=False) 65 | 66 | def yformat(v, pos): 67 | return '%s%d%% ' % ('+' if 0 < v else '', v) 68 | 69 | ax.yaxis.set_major_formatter(ticker.FuncFormatter(yformat)) 70 | ax.spines[['top']].set_visible(False) 71 | 72 | ax.legend( 73 | *zip(*tuple((r[0], l) for (r, l) in rects)), 74 | frameon=False) 75 | 76 | ax.axhline(0, color='k') 77 | 78 | plt.tight_layout() 79 | plt.savefig('out.png', transparent=True) 80 | -------------------------------------------------------------------------------- /bench/src/main/java/io/nervous/juint/Divide.java: -------------------------------------------------------------------------------- 1 | package io.nervous.juint; 2 | 3 | import org.openjdk.jmh.annotations.*; 4 | import org.openjdk.jmh.infra.Blackhole; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | 8 | @BenchmarkMode({Mode.Throughput}) 9 | @OutputTimeUnit(TimeUnit.MICROSECONDS) 10 | public class Divide { 11 | @Benchmark 12 | public void divmod_halfHalf(UIntState s, Blackhole b) { 13 | for(int i = 0; i < s.half.length; i++) 14 | b.consume(s.half[0].divmod(s.half[i])); 15 | } 16 | 17 | @Benchmark 18 | public void divmod_halfHalf_ref(BigState s, Blackhole b) { 19 | for(int i = 0; i < s.half.length; i++) 20 | b.consume(s.half[0].divideAndRemainder(s.half[i])); 21 | } 22 | 23 | @Benchmark 24 | public void divmod_maxHalf(UIntState s, Blackhole b) { 25 | for(int i = 0; i < s.half.length; i++) 26 | b.consume(s.max.divmod(s.half[i])); 27 | } 28 | 29 | @Benchmark 30 | public void divmod_maxHalf_ref(BigState s, Blackhole b) { 31 | for(int i = 0; i < s.half.length; i++) 32 | b.consume(s.max.divideAndRemainder(s.half[i])); 33 | } 34 | 35 | @Benchmark 36 | public void divmod_maxMax(UIntState s, Blackhole b) { 37 | b.consume(s.max.divmod(s.max.subtract(s.one))); 38 | } 39 | 40 | @Benchmark 41 | public void divmod_maxMax_ref(BigState s, Blackhole b) { 42 | b.consume(s.max.divideAndRemainder(s.max.subtract(s.one))); 43 | } 44 | 45 | @Benchmark 46 | public void divmod_max1w(UIntState s, Blackhole b) { 47 | for(int i = 0; i < s.onew.length; i++) 48 | b.consume(s.max.divmod(s.onew[i])); 49 | } 50 | 51 | @Benchmark 52 | public void divmod_max1w_ref(BigState s, Blackhole b) { 53 | for(int i = 0; i < s.onew.length; i++) 54 | b.consume(s.max.divideAndRemainder(s.onew[i])); 55 | } 56 | 57 | @Benchmark 58 | public void divmod_max2w(UIntState s, Blackhole b) { 59 | for(int i = 0; i < s.twow.length; i++) 60 | b.consume(s.max.divmod(s.twow[i])); 61 | } 62 | 63 | @Benchmark 64 | public void divmod_max2w_ref(BigState s, Blackhole b) { 65 | for(int i = 0; i < s.twow.length; i++) 66 | b.consume(s.max.divideAndRemainder(s.twow[i])); 67 | } 68 | 69 | @Benchmark 70 | public void divmod_half1w(UIntState s, Blackhole b) { 71 | for(int i = 0; i < s.half.length; i++) 72 | b.consume(s.half[i].divmod(s.onew[i])); 73 | } 74 | 75 | @Benchmark 76 | public void divmod_half1w_ref(BigState s, Blackhole b) { 77 | for(int i = 0; i < s.half.length; i++) 78 | b.consume(s.half[i].divideAndRemainder(s.onew[i])); 79 | } 80 | 81 | @Benchmark 82 | public void divmod_1w1w(UIntState s, Blackhole b) { 83 | for(int i = 0; i < s.onew.length; i++) 84 | b.consume(s.onew[i].divmod(s.onew[i].subtract(s.one))); 85 | } 86 | 87 | @Benchmark 88 | public void divmod_1w1_ref(BigState s, Blackhole b) { 89 | for(int i = 0; i < s.onew.length; i++) 90 | b.consume(s.onew[i].divideAndRemainder(s.onew[i].subtract(s.one))); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /bench/src/main/java/io/nervous/juint/Multiply.java: -------------------------------------------------------------------------------- 1 | package io.nervous.juint; 2 | 3 | import org.openjdk.jmh.annotations.*; 4 | import org.openjdk.jmh.infra.Blackhole; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | 8 | @BenchmarkMode({Mode.Throughput}) 9 | @OutputTimeUnit(TimeUnit.MILLISECONDS) 10 | public class Multiply { 11 | @Benchmark 12 | public void multiply_halfSquare(UIntState s, Blackhole b) { 13 | for(int i = 0; i < s.half.length; i++) 14 | b.consume(s.half[i].multiply(s.half[i])); 15 | } 16 | 17 | @Benchmark 18 | public void multiply_halfSquare_ref(BigState s, Blackhole b) { 19 | for(int i = 0; i < s.half.length; i++) 20 | b.consume(s.half[i].multiply(s.half[i])); 21 | } 22 | 23 | @Benchmark 24 | public void multiply_halfHalf(UIntState s, Blackhole b) { 25 | for(int i = 1; i < s.half.length; i++) 26 | b.consume(s.half[0].multiply(s.half[i])); 27 | } 28 | 29 | @Benchmark 30 | public void multiply_halfHalf_ref(BigState s, Blackhole b) { 31 | for(int i = 1; i < s.half.length; i++) 32 | b.consume(s.half[0].multiply(s.half[i])); 33 | } 34 | 35 | @Benchmark 36 | public void multiply_half2w(UIntState s, Blackhole b) { 37 | for(int i = 0; i < s.half.length; i++) 38 | b.consume(s.half[i].multiply(s.twow[i])); 39 | } 40 | 41 | @Benchmark 42 | public void multiply_half2w_ref(BigState s, Blackhole b) { 43 | for(int i = 0; i < s.half.length; i++) 44 | b.consume(s.half[i].multiply(s.twow[i])); 45 | } 46 | 47 | @Benchmark 48 | public void multiply_fullFull(UIntState s, Blackhole b) { 49 | for(int i = 0; i < s.full.length; i++) 50 | b.consume(s.full[0].multiply(s.full[i])); 51 | } 52 | 53 | @Benchmark 54 | public void multiply_fullFull_ref(BigState s, Blackhole b) { 55 | for(int i = 0; i < s.full.length; i++) 56 | b.consume(s.full[0].multiply(s.full[i]).and(s.max)); 57 | } 58 | 59 | @Benchmark 60 | public void multiply_half1w(UIntState s, Blackhole b) { 61 | for(int i = 0; i < s.half.length; i++) 62 | b.consume(s.half[i].multiply(s.onew[i])); 63 | } 64 | 65 | @Benchmark 66 | public void multiply_half1w_ref(BigState s, Blackhole b) { 67 | for(int i = 0; i < s.half.length; i++) 68 | b.consume(s.half[i].multiply(s.onew[i])); 69 | } 70 | 71 | @Benchmark 72 | public void multiply_2w1w(UIntState s, Blackhole b) { 73 | for(int i = 0; i < s.twow.length; i++) 74 | b.consume(s.twow[i].multiply(s.onew[i])); 75 | } 76 | 77 | @Benchmark 78 | public void multiply_2w1w_ref(BigState s, Blackhole b) { 79 | for(int i = 0; i < s.twow.length; i++) 80 | b.consume(s.twow[i].multiply(s.onew[i])); 81 | } 82 | 83 | @Benchmark 84 | public void multiply_2w2w(UIntState s, Blackhole b) { 85 | for(int i = 0; i < s.twow.length; i++) 86 | b.consume(s.twow[i].multiply(s.twow[i])); 87 | } 88 | 89 | @Benchmark 90 | public void multiply_2w2w_ref(BigState s, Blackhole b) { 91 | for(int i = 0; i < s.twow.length; i++) 92 | b.consume(s.twow[i].multiply(s.twow[i])); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /split-graph.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import csv, sys 3 | from decimal import Decimal 4 | 5 | def avg(d): 6 | return {k: sum(v)/len(v) for (k, v) in d.items()} 7 | 8 | def calculate(path): 9 | scores = {} 10 | refs = {} 11 | 12 | with open(path, 'r') as f: 13 | reader = csv.reader(f) 14 | next(reader) 15 | for row in reader: 16 | segments = tuple(row[0].split('.')[-1].split('_')) 17 | d = refs if segments[-1] == 'ref' else scores 18 | d.setdefault(segments[0], []).append(Decimal(row[-3])) 19 | 20 | scores = avg(scores) 21 | refs = avg(refs) 22 | 23 | for (meth, mean) in scores.items(): 24 | change = (mean - refs[meth]) / refs[meth] * 100 25 | yield (meth, change) 26 | 27 | if __name__ == '__main__': 28 | import numpy as np 29 | import matplotlib.pyplot as plt 30 | from matplotlib import ticker, rc 31 | import pprint, itertools 32 | 33 | data = {sys.argv[i]: sorted(calculate(sys.argv[i+1]), key=lambda t: t[0]) 34 | for i in range(1, len(sys.argv), 2)} 35 | 36 | mmax = float(max(list(zip(*itertools.chain(*data.values())))[1])) 37 | mmin = float(min(list(zip(*itertools.chain(*data.values())))[1])) 38 | 39 | pprint.pprint(data) 40 | 41 | N = len(list(data.values())[0]) 42 | 43 | ind = np.arange(N) 44 | width = 0.35 45 | colors = itertools.cycle(('#9edeff', '#ff9ede')) 46 | FONT = 'Droid Sans Mono' 47 | 48 | rc('font', family=FONT) 49 | 50 | (fig, (axhi, axlo)) = plt.subplots(2, 1, sharex=True) 51 | 52 | for ax in (axhi, axlo): 53 | ax.spines['bottom'].set_visible(False) 54 | ax.spines['top'] .set_visible(False) 55 | 56 | axhi.tick_params(labeltop=False, top=False, bottom=False) 57 | 58 | axlo.set_ylim((mmin, 75)) 59 | axhi.set_ylim((300, mmax)) 60 | 61 | rects = [] 62 | for (i, (tag, xs)) in enumerate(sorted(data.items(), key=lambda t: t[0])): 63 | color = next(colors) 64 | r = axlo.bar(ind + (i * width), list(zip(*xs))[1], width, color=color) 65 | axhi.bar(ind + (i * width), list(zip(*xs))[1], width, color=color) 66 | rects.append((r, tag)) 67 | 68 | axhi.set_title("UInt256 Throughput Increase\nvs. Apache Tuweni's UInt256") 69 | axlo.set_xticks(ind) 70 | axlo.set_xticklabels([k for (k, _) in list(data.values())[0]], rotation=90) 71 | 72 | def yformat(v, pos): 73 | return '%s%d%% ' % ('+' if 0 < v else '', v) 74 | 75 | for ax in (axlo, axhi): 76 | ax.yaxis.set_major_formatter(ticker.FuncFormatter(yformat)) 77 | ax.tick_params(axis='y', left='off') 78 | 79 | axlo.tick_params(axis='x', bottom=False, top=False) 80 | 81 | axlo.axhline(0, color='k') 82 | axlo.axhline(75, linestyle=':', color='silver') 83 | axhi.axhline(300, linestyle=':', color='silver') 84 | 85 | d = .015 86 | kw = dict(transform=axhi.transAxes, color='k', clip_on=False) 87 | axhi.plot((-d, +d), (-d, +d), **kw) 88 | axhi.plot((1 - d, 1 + d), (-d, +d), **kw) 89 | 90 | kw.update(transform=axlo.transAxes) 91 | axlo.plot((-d, +d), (1 - d, 1 + d), **kw) 92 | axlo.plot((1 - d, 1 + d), (1 - d, 1 + d), **kw) 93 | 94 | plt.tight_layout() 95 | plt.savefig('out.png', transparent=True) 96 | -------------------------------------------------------------------------------- /bench/src/main/java/io/nervous/juint/Subtract.java: -------------------------------------------------------------------------------- 1 | package io.nervous.juint; 2 | 3 | import org.openjdk.jmh.annotations.*; 4 | import org.openjdk.jmh.infra.Blackhole; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | 8 | @BenchmarkMode({Mode.Throughput}) 9 | @OutputTimeUnit(TimeUnit.MICROSECONDS) 10 | public class Subtract { 11 | @Benchmark 12 | public void subtract_zeroMax(UIntState s, Blackhole b) { 13 | b.consume(s.zero.subtract(s.max)); 14 | } 15 | 16 | @Benchmark 17 | public void subtract_zeroMax_ref(BigState s, Blackhole b) { 18 | b.consume(s.zero.subtract(s.max).and(s.max)); 19 | } 20 | 21 | @Benchmark 22 | public void subtract_halfHalf(UIntState s, Blackhole b) { 23 | for(int i = 0; i < s.half.length; i++) 24 | b.consume(s.half[0].subtract(s.half[i])); 25 | } 26 | 27 | @Benchmark 28 | public void subtract_halfHalf_ref(BigState s, Blackhole b) { 29 | for(int i = 0; i < s.half.length; i++) 30 | b.consume(s.half[0].subtract(s.half[i]).and(s.max)); 31 | } 32 | 33 | @Benchmark 34 | public void subtract_maxHalf(UIntState s, Blackhole b) { 35 | b.consume(s.max.subtract(s.half[0])); 36 | } 37 | 38 | @Benchmark 39 | public void subtract_maxHalf_ref(BigState s, Blackhole b) { 40 | b.consume(s.max.subtract(s.half[0])); 41 | } 42 | 43 | @Benchmark 44 | public void subtract_halfMax(UIntState s, Blackhole b) { 45 | for(int i = 0; i < s.half.length; i++) 46 | b.consume(s.half[i].subtract(s.max)); 47 | } 48 | 49 | @Benchmark 50 | public void subtract_halfMax_ref(BigState s, Blackhole b) { 51 | for(int i = 0; i < s.half.length; i++) 52 | b.consume(s.half[i].subtract(s.max).and(s.max)); 53 | } 54 | 55 | @Benchmark 56 | public void subtract_maxMax(UIntState s, Blackhole b) { 57 | b.consume(s.max.subtract(s.max)); 58 | } 59 | 60 | @Benchmark 61 | public void subtract_maxMax_ref(BigState s, Blackhole b) { 62 | b.consume(s.max.subtract(s.max)); 63 | } 64 | 65 | @Benchmark 66 | public void subtract_max1w(UIntState s, Blackhole b) { 67 | for(int i = 0; i < s.onew.length; i++) 68 | b.consume(s.max.subtract(s.onew[i])); 69 | } 70 | 71 | @Benchmark 72 | public void subtract_max1w_ref(BigState s, Blackhole b) { 73 | for(int i = 0; i < s.onew.length; i++) 74 | b.consume(s.max.subtract(s.onew[i])); 75 | } 76 | 77 | @Benchmark 78 | public void subtract_1wmax(UIntState s, Blackhole b) { 79 | for(int i = 0; i < s.onew.length; i++) 80 | b.consume(s.onew[i].subtract(s.max)); 81 | } 82 | 83 | @Benchmark 84 | public void subtract_1wmax_ref(BigState s, Blackhole b) { 85 | for(int i = 0; i < s.onew.length; i++) 86 | b.consume(s.onew[i].subtract(s.max).and(s.max)); 87 | } 88 | 89 | 90 | @Benchmark 91 | public void subtract_half1w(UIntState s, Blackhole b) { 92 | for(int i = 0; i < s.half.length; i++) 93 | b.consume(s.half[i].subtract(s.onew[i])); 94 | } 95 | 96 | @Benchmark 97 | public void subtract_half1w_ref(BigState s, Blackhole b) { 98 | for(int i = 0; i < s.half.length; i++) 99 | b.consume(s.half[i].subtract(s.onew[i])); 100 | } 101 | 102 | @Benchmark 103 | public void subtract_1w1w(UIntState s, Blackhole b) { 104 | for(int i = 0; i < s.onew.length; i++) 105 | b.consume(s.onew[0].subtract(s.onew[i])); 106 | } 107 | 108 | @Benchmark 109 | public void subtract_1w1w_ref(BigState s, Blackhole b) { 110 | for(int i = 0; i < s.onew.length; i++) 111 | b.consume(s.onew[0].subtract(s.onew[i])); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JUInt [![CircleCI](https://circleci.com/gh/nervous-systems/juint/tree/master.png?style=shield&circle-token=6f13ee621838019658e21ab69a65f3fe7743401f)](https://circleci.com/gh/nervous-systems/juint/tree/master) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.nervous/juint/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.nervous/juint) 2 | 3 | Optimized, immutable Java implementations of fixed-width, unsigned integers. 4 | Currently 128 bit 5 | ([UInt128](https://nervous.io/doc/juint/io/nervous/juint/UInt128.html)) and 256 6 | bit ([UInt256](https://nervous.io/doc/juint/io/nervous/juint/UInt256.html)) 7 | variants are available (with identical, `BigInteger`-style interfaces). It'd be 8 | trivial to offer differently sized integers with the same semantics, as all 9 | operations are implemented statically in terms of arrays. 10 | 11 | `juint` requires Java 8. 12 | 13 | ## Documentation 14 | ### Javadoc 15 | - [UInt128](https://nervous.io/doc/juint/io/nervous/juint/UInt128.html) 16 | - [UInt256](https://nervous.io/doc/juint/io/nervous/juint/UInt256.html) 17 | 18 | ## Performance 19 | 20 | Ignoring constant overhead, per-instance memory consumption is identical to 21 | `BigInteger`'s best case - i.e. an array holding the minimum number of integers 22 | required to represent a given number - never zero prefixed. 23 | 24 | Operation throughput is expected to be significantly higher than OpenJDK's 25 | `BigInteger` for most operations (often dramatically so). There are exhaustive 26 | (around 170) JMH micro-benchmarks per operation & magnitude in the `bench` 27 | subproject. 28 | 29 | ![graph](static/uint256.png) 30 | 31 | and some updated 2023 benchmarks: 32 | 33 | ![graph](static/uint256-2023.png) 34 | 35 | ![graph](static/uint256-tuweni.png) 36 | 37 | Each operation's bar represents the average throughput across all of its 38 | magnitude-specific benchmark cases, relative to identical benchmarks which 39 | operate on `BigInteger`. Typically, in the case of `UInt256`, there'd be 40 | separate cases for one word, two words, four words (half) and 8 words (full), 41 | each operating on a range of similarly wide values. 42 | 43 | Erring on the side of fairness, the `BigInteger` reference benchmarks only 44 | include the cost of constraining the result within the maximum width if the 45 | operation _will definitely_ overflow. As this approach requires a degree of 46 | foreknowledge present in few real-life use cases, relative throughput 47 | may be significantly improved from that depicted. 48 | 49 | The `multiply` disparity above is due to HotSpot's intrinsification of 50 | `BigInteger.multiplyToLen`, a private method used by `BigInteger.multiply` (and 51 | `pow`, by association) - obviously not an optimization strategy available to 52 | library code. 53 | 54 | ## License 55 | 56 | Like OpenJDK itself, `juint` is distributed under the terms of the _GNU General 57 | Public License_ (version 2) **with the classpath exception**: 58 | 59 | > ...The copyright holders of this library give you permission to link this 60 | > library with independent modules to produce an executable, regardless of the 61 | > license terms of these independent modules... 62 | 63 | Please see the accompanying `LICENSE` file for details. 64 | 65 | ### GPL Notes 66 | 67 | As mentioned, `juint` offers an interface compatible with `BigInteger`, and 68 | follows a similar strategy to OpenJDK for division and the `(String, int)` 69 | constructor. While I don't consider it a _derived work_, I don't want to have 70 | to explain that to Gavin Belson in a courtroom. 71 | 72 | Despite a strong personal preference for Public Domain software, retaining 73 | OpenJDK's license seems the prudent choice, and doesn't place additional burden 74 | on those consuming this project. 75 | -------------------------------------------------------------------------------- /bench/src/main/java/io/nervous/juint/Bits.java: -------------------------------------------------------------------------------- 1 | package io.nervous.juint; 2 | 3 | import org.openjdk.jmh.annotations.*; 4 | import org.openjdk.jmh.infra.Blackhole; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | 8 | @BenchmarkMode({Mode.Throughput}) 9 | @OutputTimeUnit(TimeUnit.MILLISECONDS) 10 | public class Bits { 11 | @Benchmark 12 | public void testBit_max(UIntState s, Blackhole b) { 13 | for(int i = 0; i < s.bits; i++) 14 | b.consume(s.max.testBit(i)); 15 | } 16 | 17 | @Benchmark 18 | public void testBit_max_ref(BigState s, Blackhole b) { 19 | for(int i = 0; i < s.bits; i++) 20 | b.consume(s.max.testBit(i)); 21 | } 22 | 23 | @Benchmark 24 | public void testBit_half(UIntState s, Blackhole b) { 25 | for(int i = 0; i < s.bits; i++) 26 | b.consume(s.half[0].testBit(i)); 27 | } 28 | 29 | @Benchmark 30 | public void testBit_half_ref(BigState s, Blackhole b) { 31 | for(int i = 0; i < s.bits; i++) 32 | b.consume(s.half[0].testBit(i)); 33 | } 34 | 35 | @Benchmark 36 | public void testBit_zero(UIntState s, Blackhole b) { 37 | for(int i = 0; i < s.bits; i++) 38 | b.consume(s.zero.testBit(i)); 39 | } 40 | 41 | @Benchmark 42 | public void testBit_zero_ref(BigState s, Blackhole b) { 43 | for(int i = 0; i < s.bits; i++) 44 | b.consume(s.zero.testBit(i)); 45 | } 46 | 47 | @Benchmark 48 | public void flipBit_max(UIntState s, Blackhole b) { 49 | for(int i = 0; i < s.bits; i++) 50 | b.consume(s.max.flipBit(i)); 51 | } 52 | 53 | @Benchmark 54 | public void flipBit_max_ref(BigState s, Blackhole b) { 55 | for(int i = 0; i < s.bits; i++) 56 | b.consume(s.max.flipBit(i)); 57 | } 58 | 59 | @Benchmark 60 | public void flipBit_half(UIntState s, Blackhole b) { 61 | for(int i = 0; i < s.bits; i++) 62 | b.consume(s.half[0].flipBit(i)); 63 | } 64 | 65 | @Benchmark 66 | public void flipBit_half_ref(BigState s, Blackhole b) { 67 | for(int i = 0; i < s.bits; i++) 68 | b.consume(s.half[0].flipBit(i)); 69 | } 70 | 71 | @Benchmark 72 | public void flipBit_zero(UIntState s, Blackhole b) { 73 | for(int i = 0; i < s.bits; i++) 74 | b.consume(s.zero.flipBit(i)); 75 | } 76 | 77 | @Benchmark 78 | public void flipBit_zero_ref(BigState s, Blackhole b) { 79 | for(int i = 0; i < s.bits; i++) 80 | b.consume(s.zero.flipBit(i)); 81 | } 82 | 83 | @Benchmark 84 | public void setBit_halfAll(UIntState s, Blackhole b) { 85 | for(int i = 0; i < s.bits; i++) 86 | b.consume(s.half[0].setBit(i)); 87 | } 88 | 89 | @Benchmark 90 | public void setBit_halfAll_ref(BigState s, Blackhole b) { 91 | for(int i = 0; i < s.bits; i++) 92 | b.consume(s.half[0].setBit(i)); 93 | } 94 | 95 | @Benchmark 96 | public void setBit_zero(UIntState s, Blackhole b) { 97 | for(int i = 0; i < s.bits; i++) 98 | b.consume(s.zero.setBit(i)); 99 | } 100 | 101 | @Benchmark 102 | public void setBit_zero_ref(BigState s, Blackhole b) { 103 | for(int i = 0; i < s.bits; i++) 104 | b.consume(s.zero.setBit(i)); 105 | } 106 | 107 | @Benchmark 108 | public void clearBit_max(UIntState s, Blackhole b) { 109 | for(int i = 0; i < s.bits; i++) 110 | b.consume(s.max.clearBit(i)); 111 | } 112 | 113 | @Benchmark 114 | public void clearBit_max_ref(BigState s, Blackhole b) { 115 | for(int i = 0; i < s.bits; i++) 116 | b.consume(s.max.clearBit(i)); 117 | } 118 | 119 | @Benchmark 120 | public void clearBit_zero(UIntState s, Blackhole b) { 121 | for(int i = 0; i < s.bits; i++) 122 | b.consume(s.zero.clearBit(i)); 123 | } 124 | 125 | @Benchmark 126 | public void clearBit_zero_ref(BigState s, Blackhole b) { 127 | for(int i = 0; i < s.bits; i++) 128 | b.consume(s.zero.clearBit(i)); 129 | } 130 | 131 | @Benchmark 132 | public void clearBit_half(UIntState s, Blackhole b) { 133 | for(int i = 0; i < s.bits; i++) 134 | b.consume(s.half[0].clearBit(i)); 135 | } 136 | 137 | @Benchmark 138 | public void clearBit_half_ref(BigState s, Blackhole b) { 139 | for(int i = 0; i < s.bits; i++) 140 | b.consume(s.half[0].clearBit(i)); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 4.0.0 7 | 8 | io.nervous 9 | juint-parent 10 | 0.1.0 11 | pom 12 | Optimized, immutable, unsigned wide integer types - Parent. 13 | https://github.com/nervous-systems/juint 14 | 15 | ${project.groupId}:${project.artifactId} 16 | 17 | 18 | 19 | GPL 2.0 with OpenJDK classpath exception 20 | http://openjdk.java.net/legal/gplv2+ce.html 21 | 22 | 23 | 24 | 25 | 26 | Moe Aboulkheir 27 | moe@nervous.io 28 | Nervous Systems, Ltd. 29 | https://nervous.io 30 | 31 | 32 | 33 | 34 | scm:git:git://github.com/nervous-systems/juint.git 35 | 36 | scm:git:ssh://github.com:nervous-systems/juint.git 37 | 38 | https://github.com/nervous-systems/juint 39 | 40 | 41 | 42 | 43 | ossrh 44 | https://oss.sonatype.org/content/repositories/snapshots 45 | 46 | 47 | ossrh 48 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 49 | 50 | 51 | 52 | 53 | UTF-8 54 | 55 | 56 | 57 | juint 58 | bench 59 | 60 | 61 | 62 | 63 | 64 | org.codehaus.mojo 65 | cobertura-maven-plugin 66 | 2.7 67 | 68 | 69 | html 70 | xml 71 | 72 | 73 | 74 | 75 | 76 | 77 | org.apache.maven.plugins 78 | maven-compiler-plugin 79 | 3.3 80 | 81 | 1.8 82 | 1.8 83 | -Xlint:unchecked 84 | 85 | 86 | 87 | 88 | org.apache.maven.plugins 89 | maven-source-plugin 90 | 2.2.1 91 | 92 | 93 | attach-sources 94 | 95 | jar-no-fork 96 | 97 | 98 | 99 | 100 | 101 | 102 | org.apache.maven.plugins 103 | maven-javadoc-plugin 104 | 2.9.1 105 | 106 | 107 | attach-javadocs 108 | 109 | jar 110 | 111 | 112 | 113 | 114 | 115 | 116 | org.apache.maven.plugins 117 | maven-gpg-plugin 118 | 1.5 119 | 120 | 121 | sign-artifacts 122 | verify 123 | 124 | sign 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /juint/pom.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 4.0.0 7 | 8 | 9 | io.nervous 10 | juint-parent 11 | 0.1.0 12 | 13 | 14 | io.nervous 15 | juint 16 | 0.1.0 17 | ${project.groupId}:${project.artifactId} 18 | Optimized, immutable, unsigned wide integer types. 19 | https://github.com/nervous-systems/juint 20 | 21 | 22 | 23 | GPL 2.0 with OpenJDK classpath exception 24 | http://openjdk.java.net/legal/gplv2+ce.html 25 | 26 | 27 | 28 | 29 | 30 | Moe Aboulkheir 31 | moe@nervous.io 32 | Nervous Systems, Ltd. 33 | https://nervous.io 34 | 35 | 36 | 37 | 38 | scm:git:git://github.com/nervous-systems/juint.git 39 | 40 | scm:git:ssh://github.com:nervous-systems/juint.git 41 | 42 | https://github.com/nervous-systems/juint 43 | 44 | 45 | 46 | 47 | ossrh 48 | https://oss.sonatype.org/content/repositories/snapshots 49 | 50 | 51 | ossrh 52 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 53 | 54 | 55 | 56 | 57 | 58 | junit 59 | junit 60 | 4.12 61 | test 62 | 63 | 64 | 65 | 66 | 67 | 68 | org.codehaus.mojo 69 | cobertura-maven-plugin 70 | 2.7 71 | 72 | 73 | html 74 | xml 75 | 76 | 77 | 78 | 79 | 80 | 81 | org.apache.maven.plugins 82 | maven-compiler-plugin 83 | 3.3 84 | 85 | 1.8 86 | 1.8 87 | -Xlint:unchecked 88 | 89 | 90 | 91 | 92 | org.apache.maven.plugins 93 | maven-source-plugin 94 | 2.2.1 95 | 96 | 97 | attach-sources 98 | 99 | jar-no-fork 100 | 101 | 102 | 103 | 104 | 105 | 106 | org.apache.maven.plugins 107 | maven-javadoc-plugin 108 | 2.9.1 109 | 110 | 111 | attach-javadocs 112 | 113 | jar 114 | 115 | 116 | 117 | 118 | 119 | 120 | org.apache.maven.plugins 121 | maven-gpg-plugin 122 | 1.5 123 | 124 | 125 | sign-artifacts 126 | verify 127 | 128 | sign 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /bench/pom.xml: -------------------------------------------------------------------------------- 1 | 5 | 4.0.0 6 | 7 | 8 | io.nervous 9 | juint-parent 10 | 0.1.0 11 | 12 | 13 | io.nervous.juint 14 | bench 15 | 0.1.0 16 | jar 17 | 18 | 19 | 20 | org.openjdk.jmh 21 | jmh-core 22 | ${jmh.version} 23 | 24 | 25 | org.openjdk.jmh 26 | jmh-generator-annprocess 27 | ${jmh.version} 28 | provided 29 | 30 | 31 | io.nervous 32 | juint 33 | 0.1.0 34 | 35 | 36 | 37 | 38 | 39 | UTF-8 40 | 1.25 41 | 1.8 42 | benchmarks 43 | 44 | 45 | 46 | 47 | 48 | org.apache.maven.plugins 49 | maven-compiler-plugin 50 | 3.1 51 | 52 | ${javac.target} 53 | ${javac.target} 54 | ${javac.target} 55 | 56 | 57 | 58 | 59 | maven-deploy-plugin 60 | 2.8.1 61 | 62 | true 63 | 64 | 65 | 66 | 67 | org.apache.maven.plugins 68 | maven-shade-plugin 69 | 2.2 70 | 71 | 72 | package 73 | 74 | shade 75 | 76 | 77 | ${uberjar.name} 78 | 79 | 80 | org.openjdk.jmh.Main 81 | 82 | 83 | 84 | 85 | *:* 86 | 87 | META-INF/*.SF 88 | META-INF/*.DSA 89 | META-INF/*.RSA 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | maven-clean-plugin 102 | 2.5 103 | 104 | 105 | maven-deploy-plugin 106 | 2.8.1 107 | 108 | 109 | maven-install-plugin 110 | 2.5.1 111 | 112 | 113 | maven-jar-plugin 114 | 2.4 115 | 116 | 117 | maven-javadoc-plugin 118 | 2.9.1 119 | 120 | 121 | maven-resources-plugin 122 | 2.6 123 | 124 | 125 | maven-site-plugin 126 | 3.3 127 | 128 | 129 | maven-source-plugin 130 | 2.2.1 131 | 132 | 133 | maven-surefire-plugin 134 | 2.17 135 | 136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /juint/src/main/java/io/nervous/juint/StringUtil.java: -------------------------------------------------------------------------------- 1 | package io.nervous.juint; 2 | 3 | import static io.nervous.juint.Arrays.LONG; 4 | 5 | /** 6 | * These constants are basically ripped from OpenJDK. 7 | */ 8 | final class StringUtil { 9 | private static int[] BITS_PER_DIGIT = { 10 | 1024, 1624, 2048, 2378, 2648, 2875, 3072, 3247, 3402, 3543, 3672, 11 | 3790, 3899, 4001, 4096, 4186, 4271, 4350, 4426, 4498, 4567, 4633, 12 | 4696, 4756, 4814, 4870, 4923, 4975, 5025, 5074, 5120, 5166, 5210, 13 | 5253, 5295}; 14 | 15 | private static int[] DIGITS_PER_INT = { 16 | 30, 19, 15, 13, 11, 11, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 17 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5}; 18 | 19 | private static int[] RADIX_LENGTH_LONG = { 20 | 62, 39, 31, 27, 24, 22, 20, 19, 18, 18, 17, 17, 16, 16, 15, 15, 15, 21 | 14, 14, 14, 14, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 12}; 22 | 23 | private static int[] RADIX_INT = { 24 | 0x40000000, 0x4546b3db, 0x40000000, 0x48c27395, 0x159fd800, 0x75db9c97, 25 | 0x40000000, 0x17179149, 0x3b9aca00, 0xcc6db61, 0x19a10000, 0x309f1021, 26 | 0x57f6c100, 0xa2f1b6f, 0x10000000, 0x18754571, 0x247dbc80, 0x3547667b, 27 | 0x4c4b4000, 0x6b5a6e1d, 0x6c20a40, 0x8d2d931, 0xb640000, 0xe8d4a51, 28 | 0x1269ae40, 0x17179149, 0x1cb91000, 0x23744899, 0x2b73a840, 0x34e63b41, 29 | 0x40000000, 0x4cfa3cc1, 0x5c13d840, 0x6d91b519, 0x39aa400}; 30 | 31 | private static long[] RADIX_LONG = { 32 | 0x4000000000000000L, 0x383d9170b85ff80bL, 0x4000000000000000L, 0x6765c793fa10079dL, 33 | 0x41c21cb8e1000000L, 0x3642798750226111L, 0x1000000000000000L, 0x12bf307ae81ffd59L, 34 | 0xde0b6b3a7640000L, 0x4d28cb56c33fa539L, 0x1eca170c00000000L, 0x780c7372621bd74dL, 35 | 0x1e39a5057d810000L, 0x5b27ac993df97701L, 0x1000000000000000L, 0x27b95e997e21d9f1L, 36 | 0x5da0e1e53c5c8000L, 0xb16a458ef403f19L, 0x16bcc41e90000000L, 0x2d04b7fdd9c0ef49L, 37 | 0x5658597bcaa24000L, 0x6feb266931a75b7L, 0xc29e98000000000L, 0x14adf4b7320334b9L, 38 | 0x226ed36478bfa000L, 0x383d9170b85ff80bL, 0x5a3c23e39c000000L, 0x4e900abb53e6b71L, 39 | 0x7600ec618141000L, 0xaee5720ee830681L, 0x1000000000000000L, 0x172588ad4f5f0981L, 40 | 0x211e44f7d02c1000L, 0x2ee56725f06e5c71L, 0x41c21cb8e1000000L}; 41 | 42 | static String ZEROES = "000000000000000000000000000000000000000000000000000000000000000"; 43 | 44 | static int[] fromString(final String s, final int radix, final int maxWidth) { 45 | if(radix < Character.MIN_RADIX || Character.MAX_RADIX < radix) 46 | throw new NumberFormatException("Radix out of range"); 47 | 48 | if(-1 < s.lastIndexOf('-')) 49 | throw new NumberFormatException("Invalid sign"); 50 | 51 | int pos = 0; 52 | final int len = s.length(); 53 | final int signi = s.lastIndexOf('+'); 54 | 55 | if(-1 < signi) { 56 | if(0 < signi) 57 | throw new NumberFormatException("Illegal embedded sign character"); 58 | pos++; 59 | } 60 | 61 | if(len == pos) 62 | throw new NumberFormatException("Zero-length"); 63 | 64 | while(pos < len && Character.digit(s.charAt(pos), radix) == 0) 65 | pos++; 66 | 67 | if(pos == len) 68 | return Arrays.ZERO; 69 | 70 | final int digits = len - pos, perint = DIGITS_PER_INT[radix - 2]; 71 | final long bits = ((digits * BITS_PER_DIGIT[radix - 2]) >>> 10) + 1; 72 | final int words = Math.min((int)(bits + 31) >>> 5, maxWidth); 73 | final int[] ints = new int[words]; 74 | 75 | int firstlen = digits % perint; 76 | if(firstlen == 0) 77 | firstlen = perint; 78 | 79 | String group = s.substring(pos, pos += firstlen); 80 | if((ints[words - 1] = Integer.parseInt(group, radix)) < 0) 81 | throw new NumberFormatException("Illegal digit"); 82 | 83 | final int superradix = RADIX_INT[radix - 2]; 84 | int groupv = 0; 85 | while(pos < len) { 86 | group = s.substring(pos, pos += perint); 87 | if((groupv = Integer.parseInt(group, radix)) < 0) 88 | throw new NumberFormatException("Illegal digit"); 89 | muladd(ints, superradix, groupv); 90 | } 91 | 92 | return Arrays.stripLeadingZeroes(ints); 93 | } 94 | 95 | private static void muladd(final int[] out, final int mul, final int add) { 96 | final long lmul = mul & LONG, ladd = add & LONG; 97 | final int len = out.length; 98 | 99 | long carry = 0; 100 | for(int outi = len - 1; 0 <= outi; outi--) { 101 | final long prod = lmul * (out[outi] & LONG) + carry; 102 | out[outi] = (int)prod; 103 | carry = prod >>> 32; 104 | } 105 | 106 | long sum = (out[len - 1] & LONG) + ladd; 107 | out[len - 1] = (int)sum; 108 | carry = sum >>> 32; 109 | for(int outi = len - 2; carry != 0L && 0 <= outi; outi--) { 110 | sum = (out[outi] & LONG) + carry; 111 | out[outi] = (int)sum; 112 | carry = sum >>> 32; 113 | } 114 | } 115 | 116 | static String toString(final int[] ints, final int radix) { 117 | final String[] groups = new String[(4 * ints.length + 6) / 7]; 118 | final long divisor = RADIX_LONG[radix - 2]; 119 | 120 | int group = 0; 121 | int[] q = ints, r = null; 122 | int[][] tmp; 123 | 124 | do { 125 | tmp = Arrays.divmod(q, divisor); 126 | q = tmp[0]; 127 | r = tmp[1]; 128 | if(r.length == 0) 129 | groups[group++] = "0"; 130 | else { 131 | final long rl = r.length == 1 ? 132 | (r[0] & LONG) : ((r[0] & LONG) << 32) | (r[1] & LONG); 133 | groups[group++] = Long.toString(rl, radix); 134 | } 135 | } while(0 < q.length); 136 | 137 | final int rlen = RADIX_LENGTH_LONG[radix - 2]; 138 | final StringBuilder sb = new StringBuilder(group * rlen); 139 | sb.append(groups[group - 1]); 140 | 141 | int zeroes; 142 | for(int i = group - 2; 0 <= i; i--) { 143 | if((zeroes = rlen - groups[i].length()) != 0) 144 | sb.append(ZEROES.substring(0, zeroes)); 145 | sb.append(groups[i]); 146 | } 147 | return sb.toString(); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /juint/src/main/java/io/nervous/juint/UInt256.java: -------------------------------------------------------------------------------- 1 | package io.nervous.juint; 2 | 3 | import java.math.BigInteger; 4 | 5 | /** 6 | * Represents unsigned values less than {@code 2**256}. 7 | * 8 | * As indicated by the type signatures, arithmetic operations are not applicable 9 | * to types of other widths in this package. Copy constructors can be used to 10 | * explicitly promote or truncate values for the purposes of interoperability. 11 | */ 12 | public final class UInt256 extends UInt { 13 | static final int MAX_WIDTH = 8; 14 | 15 | /** 16 | * Maximum representable value. 17 | */ 18 | public static UInt256 MAX_VALUE = new UInt256(Arrays.maxValue(MAX_WIDTH)); 19 | 20 | public static UInt256 ZERO = new UInt256(Arrays.ZERO); 21 | public static UInt256 ONE = new UInt256(Arrays.ONE); 22 | public static UInt256 TWO = new UInt256(Arrays.TWO); 23 | 24 | /** 25 | * Construct from a big-endian {@code int} array. 26 | * 27 | * If {@code ints} exceeds {@link MAX_VALUE}, only the maximum prefix 28 | * will be considered. Leaves {@code ints} untouched. 29 | */ 30 | public UInt256(final int[] ints) { 31 | super(ints, MAX_WIDTH); 32 | } 33 | 34 | /** 35 | * Construct from a big-endian {@code byte} array. 36 | * 37 | * If {@code bytes} exceeds {@link MAX_VALUE}, only the maximum prefix 38 | * will be considered. Leaves {@code bytes} untouched. 39 | */ 40 | public UInt256(final byte[] bytes) { 41 | super(bytes, MAX_VALUE); 42 | } 43 | 44 | /** 45 | * Construct from a {@link UInt128}. 46 | */ 47 | public UInt256(final UInt128 other) { 48 | super(other, MAX_WIDTH); 49 | } 50 | 51 | /** 52 | * Construct from a base ten string. 53 | * 54 | * Excessively wide numbers will be truncated. 55 | * 56 | * @throws NumberFormatException Negative, invalid or zero-length number. 57 | */ 58 | public UInt256(final String s) { 59 | this(s, 10); 60 | } 61 | 62 | /** 63 | * Construct from a string in the given radix. 64 | * 65 | * Excessively wide numbers will be truncated. 66 | * 67 | * @throws NumberFormatException Negative, invalid or zero-length number. 68 | */ 69 | public UInt256(final String s, final int radix) { 70 | super(s, radix, MAX_WIDTH); 71 | } 72 | 73 | /** 74 | * Construct from a {@link BigInteger}. 75 | * 76 | * If {@code b} exceeds {@link MAX_VALUE}, it's truncated. 77 | */ 78 | public UInt256(final BigInteger b) { super(b, MAX_WIDTH); } 79 | 80 | /** 81 | * Construct from a {@code long}, when considered unsigned. 82 | * 83 | * For low values of {@code v}, an array cache may be used. 84 | */ 85 | public UInt256(final long v) { super(v); } 86 | 87 | public UInt256 not() { 88 | return new UInt256(Arrays.not(ints, MAX_VALUE.ints)); 89 | } 90 | 91 | public UInt256 and(final UInt256 other) { 92 | return new UInt256(Arrays.and(ints, other.ints)); 93 | } 94 | 95 | public UInt256 or(final UInt256 other) { 96 | return new UInt256(Arrays.or(ints, other.ints)); 97 | } 98 | 99 | public UInt256 xor(final UInt256 other) { 100 | return new UInt256(Arrays.xor(ints, other.ints)); 101 | } 102 | 103 | public UInt256 setBit(final int bit) { 104 | if(bit < 0) 105 | throw new ArithmeticException("Negative bit address"); 106 | return ((MAX_WIDTH <= bit >>> 5) ? this : 107 | new UInt256(Arrays.setBit(ints, bit))); 108 | } 109 | 110 | public UInt256 clearBit(final int bit) { 111 | if(bit < 0) 112 | throw new ArithmeticException("Negative bit address"); 113 | return ((ints.length <= bit >>> 5) ? this : 114 | new UInt256(Arrays.clearBit(ints, bit))); 115 | } 116 | 117 | public UInt256 flipBit(final int bit) { 118 | if(bit < 0) 119 | throw new ArithmeticException("Negative bit address"); 120 | return ((MAX_WIDTH <= bit >>> 5) ? this : 121 | new UInt256(Arrays.flipBit(ints, bit))); 122 | } 123 | 124 | public UInt256 shiftLeft(final int places) { 125 | return new UInt256( 126 | 0 < places ? 127 | Arrays.lshift(ints, places, MAX_WIDTH) : 128 | Arrays.rshift(ints, -places, MAX_WIDTH)); 129 | } 130 | 131 | public UInt256 shiftRight(final int places) { 132 | return new UInt256( 133 | 0 < places ? 134 | Arrays.rshift(ints, places, MAX_WIDTH) : 135 | Arrays.lshift(ints, -places, MAX_WIDTH)); 136 | } 137 | 138 | public UInt256 inc() { 139 | return new UInt256(Arrays.inc(ints, MAX_WIDTH)); 140 | } 141 | 142 | public UInt256 dec() { 143 | return isZero() ? MAX_VALUE : new UInt256(Arrays.dec(ints)); 144 | } 145 | 146 | public UInt256 add(final UInt256 other) { 147 | return (isZero() ? other : 148 | (other.isZero() ? this : 149 | new UInt256(Arrays.add(ints, other.ints, MAX_WIDTH)))); 150 | } 151 | 152 | public UInt256 addmod(final UInt256 add, final UInt256 mod) { 153 | if(mod.isZero()) 154 | throw new ArithmeticException("div/mod by zero"); 155 | if(isZero() && add.isZero()) 156 | return ZERO; 157 | return new UInt256(Arrays.addmod(ints, add.ints, mod.ints)); 158 | } 159 | 160 | public UInt256 subtract(final UInt256 other) { 161 | if(other.isZero()) 162 | return this; 163 | final int cmp = compareTo(other); 164 | return (cmp == 0 ? ZERO : 165 | new UInt256( 166 | cmp < 0 ? 167 | Arrays.subgt(ints, other.ints, MAX_VALUE.ints) : 168 | Arrays.sub (ints, other.ints))); 169 | } 170 | 171 | public UInt256 multiply(final UInt256 other) { 172 | if(ints.length == 0 || other.ints.length == 0) 173 | return ZERO; 174 | return new UInt256(Arrays.multiply(ints, other.ints, MAX_WIDTH)); 175 | } 176 | 177 | public UInt256 mulmod(final UInt256 mul, final UInt256 mod) { 178 | if(mod.isZero()) 179 | throw new ArithmeticException("div/mod by zero"); 180 | return new UInt256(Arrays.mulmod(ints, mul.ints, mod.ints)); 181 | } 182 | 183 | public UInt256 pow(final int exp) { 184 | if(exp < 0) 185 | throw new ArithmeticException("Negative exponent"); 186 | if(exp == 0) 187 | return ONE; 188 | if(isZero()) 189 | return this; 190 | return (exp == 1 ? this : 191 | new UInt256(Arrays.pow(ints, getLowestSetBit(), exp, MAX_WIDTH))); 192 | } 193 | 194 | public UInt256 divide(final UInt256 other) { 195 | if(other.isZero()) 196 | throw new ArithmeticException("div/mod by zero"); 197 | if(isZero()) 198 | return ZERO; 199 | final int cmp = compareTo(other); 200 | return (cmp < 0 ? ZERO : 201 | (cmp == 0 ? ONE : 202 | new UInt256(Arrays.divide(ints, other.ints)))); 203 | } 204 | 205 | public UInt256 mod(final UInt256 other) { 206 | if(other.isZero()) 207 | throw new ArithmeticException("div/mod by zero"); 208 | if(isZero()) 209 | return ZERO; 210 | final int cmp = compareTo(other); 211 | return (cmp < 0 ? this : 212 | (cmp == 0 ? ZERO : 213 | new UInt256(Arrays.mod(ints, other.ints)))); 214 | } 215 | 216 | public UInt256[] divmod(final UInt256 other) { 217 | if(other.isZero()) 218 | throw new ArithmeticException("div/mod by zero"); 219 | if(isZero()) 220 | return new UInt256[]{ZERO, ZERO}; 221 | final int cmp = compareTo(other); 222 | if(cmp < 0) 223 | return new UInt256[]{ZERO, this}; 224 | if(cmp == 0) 225 | return new UInt256[]{ONE, ZERO}; 226 | 227 | final int[][] qr = Arrays.divmod(ints, other.ints); 228 | return new UInt256[]{new UInt256(qr[0]), new UInt256(qr[1])}; 229 | } 230 | 231 | public boolean equals(final Object other) { 232 | if(other instanceof BigInteger) 233 | return Arrays.compare(ints, (BigInteger)other, MAX_WIDTH) == 0; 234 | return super.equals(other); 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /juint/src/main/java/io/nervous/juint/UInt128.java: -------------------------------------------------------------------------------- 1 | package io.nervous.juint; 2 | 3 | import java.math.BigInteger; 4 | 5 | /** 6 | * Represents unsigned values less than {@code 2**128}. 7 | * 8 | * As indicated by the type signatures, arithmetic operations are not applicable 9 | * to types of other widths in this package. Copy constructors can be used to 10 | * explicitly promote or truncate values for the purposes of interoperability. 11 | */ 12 | public final class UInt128 extends UInt { 13 | static final int MAX_WIDTH = 4; 14 | 15 | /** 16 | * Maximum representable value. 17 | */ 18 | public static UInt128 MAX_VALUE = new UInt128(Arrays.maxValue(MAX_WIDTH)); 19 | 20 | public static UInt128 ZERO = new UInt128(Arrays.ZERO); 21 | public static UInt128 ONE = new UInt128(Arrays.ONE); 22 | public static UInt128 TWO = new UInt128(Arrays.TWO); 23 | 24 | /** 25 | * Construct from a big-endian {@code int} array. 26 | * 27 | * If {@code ints} exceeds {@link MAX_VALUE}, only the maximum prefix 28 | * will be considered. Leaves {@code ints} untouched. 29 | */ 30 | public UInt128(final int[] ints) { 31 | super(ints, MAX_WIDTH); 32 | } 33 | 34 | /** 35 | * Construct from a big-endian {@code byte} array. 36 | * 37 | * If {@code bytes} exceeds {@link MAX_VALUE}, only the maximum prefix 38 | * will be considered. Leaves {@code bytes} untouched. 39 | */ 40 | public UInt128(final byte[] bytes) { 41 | super(bytes, MAX_VALUE); 42 | } 43 | 44 | /** 45 | * Construct from a {@link UInt256}. 46 | * 47 | * Excessively wide numbers will be truncated. 48 | */ 49 | public UInt128(final UInt256 other) { 50 | super(other, MAX_WIDTH); 51 | } 52 | 53 | /** 54 | * Construct from a base ten string. 55 | * 56 | * Excessively wide numbers will be truncated. 57 | * 58 | * @throws NumberFormatException Negative, invalid or zero-length number. 59 | */ 60 | public UInt128(final String s) { 61 | this(s, 10); 62 | } 63 | 64 | /** 65 | * Construct from a string in the given radix. 66 | * 67 | * Excessively wide numbers will be truncated. 68 | * 69 | * @throws NumberFormatException Negative, invalid or zero-length number. 70 | */ 71 | public UInt128(final String s, final int radix) { 72 | super(s, radix, MAX_WIDTH); 73 | } 74 | 75 | /** 76 | * Construct from a {@link BigInteger}. 77 | * 78 | * If {@code b} exceeds {@link MAX_VALUE}, it's truncated. 79 | */ 80 | public UInt128(final BigInteger b) { super(b, MAX_WIDTH); } 81 | 82 | /** 83 | * Construct from a {@code long}, when considered unsigned. 84 | * 85 | * For low values of {@code v}, an array cache may be used. 86 | */ 87 | public UInt128(final long v) { super(v); } 88 | 89 | public UInt128 not() { 90 | return new UInt128(Arrays.not(ints, MAX_VALUE.ints)); 91 | } 92 | 93 | public UInt128 and(final UInt128 other) { 94 | return new UInt128(Arrays.and(ints, other.ints)); 95 | } 96 | 97 | public UInt128 or(final UInt128 other) { 98 | return new UInt128(Arrays.or(ints, other.ints)); 99 | } 100 | 101 | public UInt128 xor(final UInt128 other) { 102 | return new UInt128(Arrays.xor(ints, other.ints)); 103 | } 104 | 105 | public UInt128 setBit(final int bit) { 106 | if(bit < 0) 107 | throw new ArithmeticException("Negative bit address"); 108 | return ((MAX_WIDTH <= bit >>> 5) ? this : 109 | new UInt128(Arrays.setBit(ints, bit))); 110 | } 111 | 112 | public UInt128 clearBit(final int bit) { 113 | if(bit < 0) 114 | throw new ArithmeticException("Negative bit address"); 115 | return ((ints.length <= bit >>> 5) ? this : 116 | new UInt128(Arrays.clearBit(ints, bit))); 117 | } 118 | 119 | public UInt128 flipBit(final int bit) { 120 | if(bit < 0) 121 | throw new ArithmeticException("Negative bit address"); 122 | return ((MAX_WIDTH <= bit >>> 5) ? this : 123 | new UInt128(Arrays.flipBit(ints, bit))); 124 | } 125 | 126 | public UInt128 shiftLeft(final int places) { 127 | return new UInt128( 128 | 0 < places ? 129 | Arrays.lshift(ints, places, MAX_WIDTH) : 130 | Arrays.rshift(ints, -places, MAX_WIDTH)); 131 | } 132 | 133 | public UInt128 shiftRight(final int places) { 134 | return new UInt128( 135 | 0 < places ? 136 | Arrays.rshift(ints, places, MAX_WIDTH) : 137 | Arrays.lshift(ints, -places, MAX_WIDTH)); 138 | } 139 | 140 | public UInt128 inc() { 141 | return new UInt128(Arrays.inc(ints, MAX_WIDTH)); 142 | } 143 | 144 | public UInt128 dec() { 145 | return isZero() ? MAX_VALUE : new UInt128(Arrays.dec(ints)); 146 | } 147 | 148 | public UInt128 add(final UInt128 other) { 149 | return (isZero() ? other : 150 | (other.isZero() ? this : 151 | new UInt128(Arrays.add(ints, other.ints, MAX_WIDTH)))); 152 | } 153 | 154 | public UInt128 addmod(final UInt128 add, final UInt128 mod) { 155 | if(mod.isZero()) 156 | throw new ArithmeticException("div/mod by zero"); 157 | return new UInt128(Arrays.addmod(ints, add.ints, mod.ints)); 158 | } 159 | 160 | public UInt128 subtract(final UInt128 other) { 161 | if(other.isZero()) 162 | return this; 163 | final int cmp = compareTo(other); 164 | return (cmp == 0 ? ZERO : 165 | new UInt128( 166 | cmp < 0 ? 167 | Arrays.subgt(ints, other.ints, MAX_VALUE.ints) : 168 | Arrays.sub (ints, other.ints))); 169 | } 170 | 171 | public UInt128 multiply(final UInt128 other) { 172 | if(ints.length == 0 || other.ints.length == 0) 173 | return ZERO; 174 | 175 | return new UInt128(Arrays.multiply(ints, other.ints, MAX_WIDTH)); 176 | } 177 | 178 | public UInt128 mulmod(final UInt128 mul, final UInt128 mod) { 179 | if(mod.isZero()) 180 | throw new ArithmeticException("div/mod by zero"); 181 | return new UInt128(Arrays.mulmod(ints, mul.ints, mod.ints)); 182 | } 183 | 184 | public UInt128 pow(final int exp) { 185 | if(exp < 0) 186 | throw new ArithmeticException("Negative exponent"); 187 | if(exp == 0) 188 | return ONE; 189 | if(isZero()) 190 | return this; 191 | return (exp == 1 ? this : 192 | new UInt128(Arrays.pow(ints, getLowestSetBit(), exp, MAX_WIDTH))); 193 | } 194 | 195 | public UInt128 divide(final UInt128 other) { 196 | if(other.isZero()) 197 | throw new ArithmeticException("div/mod by zero"); 198 | if(isZero()) 199 | return ZERO; 200 | final int cmp = compareTo(other); 201 | return (cmp < 0 ? ZERO : 202 | (cmp == 0 ? ONE : 203 | new UInt128(Arrays.divide(ints, other.ints)))); 204 | } 205 | 206 | public UInt128 mod(final UInt128 other) { 207 | if(other.isZero()) 208 | throw new ArithmeticException("div/mod by zero"); 209 | if(isZero()) 210 | return ZERO; 211 | final int cmp = compareTo(other); 212 | return (cmp < 0 ? this : 213 | (cmp == 0 ? ZERO : 214 | new UInt128(Arrays.mod(ints, other.ints)))); 215 | } 216 | 217 | public UInt128[] divmod(final UInt128 other) { 218 | if(other.isZero()) 219 | throw new ArithmeticException("div/mod by zero"); 220 | if(isZero()) 221 | return new UInt128[]{ZERO, ZERO}; 222 | final int cmp = compareTo(other); 223 | if(cmp < 0) 224 | return new UInt128[]{ZERO, this}; 225 | if(cmp == 0) 226 | return new UInt128[]{ONE, ZERO}; 227 | 228 | final int[][] qr = Arrays.divmod(ints, other.ints); 229 | return new UInt128[]{new UInt128(qr[0]), new UInt128(qr[1])}; 230 | } 231 | 232 | public boolean equals(final Object other) { 233 | if(other instanceof BigInteger) 234 | return Arrays.compare(ints, (BigInteger)other, MAX_WIDTH) == 0; 235 | return super.equals(other); 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /juint/src/main/java/io/nervous/juint/Division.java: -------------------------------------------------------------------------------- 1 | package io.nervous.juint; 2 | 3 | final class Division { 4 | private static long LONG = 0xffffffffL; 5 | 6 | static int[][] div(final int[] a, final int b) { 7 | final long bl = b & LONG; 8 | 9 | if(a.length == 1) { 10 | final long al = a[0] & LONG; 11 | return new int[][]{new int[]{(int)(al / bl)}, new int[]{(int)(al % bl)}}; 12 | } 13 | 14 | final int[] quo = java.util.Arrays.copyOf(a, a.length); 15 | final int places = Integer.numberOfLeadingZeros(b); 16 | int rem = a[0], alen = a.length; 17 | final long reml = rem & LONG; 18 | 19 | if(reml < bl) 20 | quo[0] = 0; 21 | else { 22 | quo[0] = (int)(reml / bl); 23 | rem = (int)(reml % bl); 24 | } 25 | 26 | while(0 < --alen) { 27 | final long est = ((rem & LONG) << 32) | (a[a.length - alen] & LONG); 28 | final int q; 29 | if(0 <= est) { 30 | q = (int)(est / bl); 31 | rem = (int)(est % bl); 32 | } else { 33 | long tmp = divone(est, b & LONG); 34 | q = (int)(tmp & LONG); 35 | rem = (int)(tmp >>> 32); 36 | } 37 | quo[a.length - alen] = q; 38 | } 39 | 40 | return new int[][]{quo, new int[]{0 < places ? rem % b : rem}}; 41 | } 42 | 43 | static int[][] div(final int[] a, long b) { 44 | final int alen = a.length; 45 | final int[] quo = new int[alen - 1], rem = new int[alen + 1]; 46 | 47 | System.arraycopy(a, 0, rem, 1, alen); 48 | 49 | final int places = Long.numberOfLeadingZeros(b); 50 | if(0 < places) { 51 | lshunt(rem, places); 52 | b <<= places; 53 | } 54 | 55 | final int dh = (int)(b >>> 32); 56 | final long dhl = dh & LONG; 57 | final int dl = (int)(b & LONG); 58 | 59 | int qhat; 60 | for(int i = 0; i < alen - 1; i++) 61 | if((qhat = D3(i, rem, dh, dhl, dl)) != 0) 62 | quo[i] = D4_D5(i, rem, dh, dl, qhat); 63 | 64 | if(0 < places) 65 | rshift(rem, places); 66 | 67 | return new int[][]{quo, rem}; 68 | } 69 | 70 | static int[][] div(final int[] a, final int[] b) { 71 | final int places = Integer.numberOfLeadingZeros(b[0]); 72 | final int[] div, rem; 73 | 74 | if(0 < places) { 75 | div = new int[b.length]; 76 | copyshift(b, 0, div, 0, places); 77 | 78 | if(places <= Integer.numberOfLeadingZeros(a[0])) { 79 | rem = new int[a.length + 1]; 80 | copyshift(a, 0, rem, 1, places); 81 | } else { 82 | rem = new int[a.length + 2]; 83 | final int invp = 32 - places; 84 | int c = 0; 85 | for(int i = 0; i < a.length; i++) 86 | rem[i + 1] = (c << places) | ((c = a[i]) >>> invp); 87 | rem[a.length + 1] = c << places; 88 | } 89 | } else { 90 | div = b; 91 | rem = new int[a.length + 1]; 92 | System.arraycopy(a, 0, rem, 1, a.length); 93 | } 94 | 95 | final int qints = rem.length - b.length; 96 | final int[] quo = new int[qints]; 97 | 98 | final int dh = div[0]; 99 | final int dl = div[1]; 100 | final long dhl = dh & LONG; 101 | 102 | int qhat; 103 | for(int i = 0; i < qints; i++) 104 | if((qhat = D3(i, rem, dh, dhl, dl)) != 0) 105 | quo[i] = D4_D5(i, rem, div, qhat); 106 | 107 | if(0 < places) 108 | rshift(rem, places); 109 | 110 | return new int[][]{quo, rem}; 111 | } 112 | 113 | private static int D3(final int j, final int[] rem, final int dh, final long dhl, final int dl) { 114 | int qhat, qrem; 115 | boolean correct = true; 116 | final int nh = rem[j]; 117 | final int nm = rem[j + 1]; 118 | 119 | if(nh == dh) { 120 | qhat = ~0; 121 | qrem = nh + nm; 122 | correct = nh + 0x80000000 <= qrem + 0x80000000; 123 | } else { 124 | final long chunk = (((long)nh) << 32) | (nm & LONG); 125 | if(0 <= chunk) { 126 | qhat = (int)(chunk / dhl); 127 | qrem = (int)(chunk - (qhat * dhl)); 128 | } else { 129 | final long tmp = divone(chunk, dh & LONG); 130 | qhat = (int)(tmp & LONG); 131 | qrem = (int)(tmp >>> 32); 132 | } 133 | } 134 | 135 | if(qhat != 0 && correct) { 136 | final long nl = rem[j + 2] & LONG; 137 | long rs = ((qrem & LONG) << 32) | nl; 138 | long est = (dl & LONG) * (qhat & LONG); 139 | 140 | if(0 < Long.compareUnsigned(est, rs)) { 141 | qhat--; 142 | qrem = (int)((qrem & LONG) + dhl); 143 | if(dhl <= (qrem & LONG)) { 144 | est -= (dl & LONG); 145 | rs = ((qrem & LONG) << 32) | nl; 146 | if(0 < Long.compareUnsigned(est, rs)) 147 | qhat--; 148 | } 149 | } 150 | } 151 | return qhat; 152 | } 153 | 154 | private static int D4_D5( 155 | final int i, final int[] rem, final int[] divisor, final int qhat) { 156 | 157 | final int tmp = rem[i]; 158 | rem[i] = 0; 159 | final int borrow = mulsub(rem, divisor, qhat & LONG, divisor.length, i); 160 | 161 | if(tmp + 0x80000000 < borrow + 0x80000000) { 162 | divadd(divisor, rem, i + 1); 163 | return qhat - 1; 164 | } 165 | return qhat; 166 | } 167 | 168 | private static int D4_D5( 169 | final int i, final int[] rem, final int dh, final int dl, final int qhat) { 170 | 171 | final int tmp = rem[i]; 172 | rem[i] = 0; 173 | final int borrow = mulsub(rem, dh, dl, qhat & LONG, i); 174 | 175 | if(tmp + 0x80000000 < borrow + 0x80000000) { 176 | divadd(dh, dl, rem, i + 1); 177 | return qhat - 1; 178 | } 179 | return qhat; 180 | } 181 | 182 | private static int mulsub( 183 | final int[] q, final int[] a, final long x, final int len, int off) { 184 | 185 | long carry = 0; 186 | off += len; 187 | 188 | for(int ai = len - 1; 0 <= ai; ai--) { 189 | long prod = (a[ai] & LONG) * x + carry; 190 | long diff = q[off] - prod; 191 | q[off--] = (int)diff; 192 | carry = (prod >>> 32) + (((((~(int)prod) & LONG)) < (diff & LONG)) ? 1 : 0); 193 | } 194 | return (int)carry; 195 | } 196 | 197 | private static int mulsub( 198 | final int[] q, final int dh, final int dl, final long x, final int off) { 199 | long prod = (dl & LONG) * x; 200 | long diff = q[off + 2] - prod; 201 | q[off + 2] = (int)diff; 202 | long carry = (prod >>> 32) + (((~(int)prod) & LONG) < (diff & LONG) ? 1 : 0); 203 | prod = (dh & LONG) * x + carry; 204 | diff = q[off + 1] - prod; 205 | q[off + 1] = (int)diff; 206 | return (int)(prod >>> 32) + (((~(int)prod) & LONG) < (diff & LONG) ? 1 : 0); 207 | } 208 | 209 | private static int divadd(final int[] a, final int[] result, final int offset) { 210 | long carry = 0; 211 | 212 | for(int ai = a.length - 1; 0 <= ai; ai--) { 213 | long sum = (a[ai] & LONG) + (result[ai + offset] & LONG) + carry; 214 | result[ai + offset] = (int)sum; 215 | carry = sum >>> 32; 216 | } 217 | return (int)carry; 218 | } 219 | 220 | private static int divadd(final long dh, final long dl, final int[] result, final int off) { 221 | result[off + 1] = (int)(dl + (result[off + 1] & LONG)); 222 | final long sum = dh + (result[off] & LONG); 223 | result[off] = (int)sum; 224 | return (int)(sum >>> 32); 225 | } 226 | 227 | private static long divone(final long a, final long b) { 228 | if(b == 1) 229 | return (int)a; 230 | 231 | long quo = (a >>> 1) / (b >>> 1); 232 | long rem = a - quo * b; 233 | 234 | while(rem < 0) { 235 | rem += b; 236 | quo--; 237 | } 238 | 239 | while(b <= rem) { 240 | rem -= b; 241 | quo++; 242 | } 243 | 244 | return (rem << 32) | (quo & LONG); 245 | } 246 | 247 | static void lshunt(final int[] a, final int places) { 248 | final int invplaces = 32 - places; 249 | for(int ai = 0, lim = ai + a.length- 1; ai < lim; ai++) 250 | a[ai] = (a[ai] << places) | (a[ai + 1] >>> invplaces); 251 | a[a.length - 1] <<= places; 252 | } 253 | 254 | private static void rshunt(final int[] a, final int places) { 255 | int invplaces = 32 - places; 256 | for(int ai = a.length - 1; 0 < ai; ai--) 257 | a[ai] = (a[ai - 1] << invplaces) | (a[ai] >>> places); 258 | a[0] >>>= places; 259 | } 260 | 261 | private static void rshift(final int[] a, final int places) { 262 | if(a.length == 0) 263 | return; 264 | final int bits = places & 0x1F; 265 | if(bits == 0) 266 | return; 267 | if(Integer.bitCount(a[0]) <= bits) { 268 | lshunt(a, 32 - bits); 269 | for(int ai = a.length - 1; 0 < ai; ai--) 270 | a[ai] = a[ai - 1]; 271 | a[0] = 0; 272 | } else 273 | rshunt(a, bits); 274 | } 275 | 276 | private static void copyshift( 277 | final int[] src, int srci, final int[] dst, final int dsti, final int places) { 278 | final int invplaces = 32 - places; 279 | 280 | int carry = src[srci]; 281 | for(int i = 0; i < src.length - 1; i++) 282 | dst[dsti + i] = (carry << places) | ((carry = src[++srci]) >>> invplaces); 283 | dst[dsti + src.length - 1] = carry << places; 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /juint/src/main/java/io/nervous/juint/UInt.java: -------------------------------------------------------------------------------- 1 | package io.nervous.juint; 2 | 3 | import java.math.BigInteger; 4 | import static io.nervous.juint.Arrays.LONG; 5 | 6 | public abstract class UInt 7 | extends java.lang.Number 8 | implements Comparable { 9 | 10 | final int[] ints; 11 | 12 | /* toString */ 13 | static final int DEFAULT_RADIX = 10; 14 | 15 | UInt(final long l) { 16 | this.ints = Arrays.valueOf(l); 17 | } 18 | 19 | UInt(final int[] ints) { 20 | this.ints = ints; 21 | } 22 | 23 | UInt(final int[] ints, final int maxWidth) { 24 | this(Arrays.stripLeadingZeroes(ints, Math.max(0, ints.length - maxWidth))); 25 | } 26 | 27 | UInt(final UInt other, final int maxWidth) { 28 | this(other.ints, maxWidth); 29 | } 30 | 31 | UInt(final String s, final int radix, final int maxWidth) { 32 | this.ints = StringUtil.fromString(s, radix, maxWidth); 33 | } 34 | 35 | UInt(final BigInteger b, final int maxWidth) { 36 | this(Arrays.from(b, maxWidth), maxWidth); 37 | } 38 | 39 | UInt(final byte[] bytes, final UInt maxValue) { 40 | this(Arrays.from(bytes, maxValue.ints), maxValue.ints.length); 41 | } 42 | 43 | /** 44 | * {@code this / other, this % other} 45 | */ 46 | public abstract T[] divmod(T other); 47 | /** 48 | * {@code this / other} 49 | */ 50 | public abstract T divide(T other); 51 | /** 52 | * {@code this % other} 53 | */ 54 | public abstract T mod(T other); 55 | /** 56 | * {@code this * other} 57 | */ 58 | public abstract T multiply(T other); 59 | /** 60 | * {@code (this * mul) % mod} 61 | * 62 | * The multiply operation is unbounded by the type's width. 63 | */ 64 | public abstract T mulmod(T mul, T mod); 65 | /** 66 | * {@code this ** exp} 67 | */ 68 | public abstract T pow(int exp); 69 | /** 70 | * {@code ~this} 71 | */ 72 | public abstract T not(); 73 | /** 74 | * {@code this & other} 75 | */ 76 | public abstract T and(T other); 77 | /** 78 | * {@code this | other} 79 | */ 80 | public abstract T or(T other); 81 | /** 82 | * {@code this ^ other} 83 | */ 84 | public abstract T xor(T other); 85 | /** 86 | * {@code this + 1} 87 | */ 88 | public abstract T inc(); 89 | /** 90 | * {@code this - 1} 91 | */ 92 | public abstract T dec(); 93 | /** 94 | * {@code this + other} 95 | */ 96 | public abstract T add(T other); 97 | /** 98 | * {@code (this + add) % mod} 99 | * 100 | * The add operation is unbounded by the type's width. 101 | */ 102 | public abstract T addmod(T add, T mod); 103 | /** 104 | * {@code this - other} 105 | */ 106 | public abstract T subtract(T other); 107 | /** 108 | * {@code this << places}. 109 | * 110 | * Shifts right if {@code places} is negative. 111 | */ 112 | public abstract T shiftLeft(int places); 113 | /** 114 | * {@code this >> places}. 115 | * 116 | * Shifts left if {@code places} is negative. 117 | */ 118 | public abstract T shiftRight(int places); 119 | /** 120 | * {@code this | (1 << bit)} 121 | */ 122 | public abstract T setBit(int bit); 123 | /** 124 | * {@code this & ~(1 << bit)} 125 | */ 126 | public abstract T clearBit(int bit); 127 | /** 128 | * {@code this ^ (1 << bit)} 129 | */ 130 | public abstract T flipBit(int bit); 131 | 132 | /** 133 | * {@code (this & (1 << bit)) != 0} 134 | */ 135 | public final boolean testBit(final int bit) { 136 | if(bit < 0) 137 | throw new ArithmeticException("Negative bit address"); 138 | final int i = bit >>> 5; 139 | return i < ints.length && 0 != (ints[ints.length - i - 1] & (1 << (bit & 31))); 140 | } 141 | 142 | /** 143 | * Alias for {@link #divmod}. 144 | */ 145 | public final T[] divideAndRemainder(T other) { return divmod(other); } 146 | 147 | /** 148 | * Alias for {@link #mod}. 149 | */ 150 | public final T remainder(T other) { return mod(other); }; 151 | 152 | /** 153 | * Count the number of bits required to represent this number in binary. 154 | */ 155 | public final int bitLength() { 156 | return Arrays.bitLength(ints); 157 | } 158 | 159 | /** 160 | * {@code this == 0} 161 | */ 162 | public final boolean isZero() { 163 | return ints.length == 0; 164 | } 165 | 166 | /** 167 | * Return the index of the right-most set bit, or {@code -1}. 168 | */ 169 | public final int getLowestSetBit() { 170 | final int start = ints.length - 1; 171 | for(int i = start; 0 <= i; i--) 172 | if(ints[i] != 0) 173 | return (start - i) * 32 + Integer.numberOfTrailingZeros(ints[i]); 174 | return -1; 175 | } 176 | 177 | /** 178 | * Return a hash code identical to the equivalent OpenJDK {@code BigInteger}. 179 | */ 180 | public int hashCode() { 181 | int out = 0; 182 | 183 | for(int i = 0; i < ints.length; i++) 184 | out = (int)(31*out + (ints[i] & LONG)); 185 | 186 | return out; 187 | } 188 | 189 | public boolean equals(final Object other) { 190 | if(other instanceof UInt) 191 | return Arrays.compare(ints, ((UInt)other).ints) == 0; 192 | return false; 193 | } 194 | 195 | public final int compareTo(final T other) { 196 | return Arrays.compare(ints, other.ints); 197 | } 198 | 199 | /** 200 | * {@code other < this ? this : other} 201 | */ 202 | @SuppressWarnings("unchecked") 203 | public final T max(final T other) { 204 | return 0 < compareTo(other) ? ((T)this) : other; 205 | } 206 | 207 | /** 208 | * {@code this < other ? this : other } 209 | */ 210 | @SuppressWarnings("unchecked") 211 | public final T min(final T other) { 212 | return compareTo(other) < 0 ? ((T)this) : other; 213 | } 214 | 215 | public final int intValue() { 216 | return ints.length == 0 ? 0 : ints[ints.length - 1]; 217 | } 218 | 219 | public final long longValue() { 220 | final int len = ints.length; 221 | if(len == 0) 222 | return 0; 223 | final long out = ints[len - 1] & LONG; 224 | return ints.length == 1 ? out : ((ints[len - 2] & LONG) << 32 | out); 225 | } 226 | 227 | public final float floatValue() { 228 | /* Unless somebody desperately wants this to be as efficient as possible, 229 | I don't think it's worth spending time on - as with doubleValue. */ 230 | return Float.parseFloat(toString()); 231 | } 232 | 233 | public final double doubleValue() { 234 | return Double.parseDouble(toString()); 235 | } 236 | 237 | public final int intValueExact() { 238 | if(ints.length <= 1 && bitLength() < 32) 239 | return intValue(); 240 | throw new ArithmeticException("Out of int range"); 241 | } 242 | 243 | public final long longValueExact() { 244 | if(ints.length <= 2 && bitLength() < 64) 245 | return longValue(); 246 | throw new ArithmeticException("Out of long range"); 247 | } 248 | 249 | public final short shortValueExact() { 250 | if(ints.length <= 1 && bitLength() < 32) { 251 | final int v = intValue(); 252 | if(Short.MIN_VALUE <= v && v <= Short.MAX_VALUE) 253 | return shortValue(); 254 | } 255 | throw new ArithmeticException("Out of short range"); 256 | } 257 | 258 | public final byte byteValueExact() { 259 | if(ints.length <= 1 && bitLength() < 32) { 260 | final int v = intValue(); 261 | if(Byte.MIN_VALUE <= v && v <= Byte.MAX_VALUE) 262 | return byteValue(); 263 | } 264 | throw new ArithmeticException("Out of byte range"); 265 | } 266 | 267 | public final BigInteger toBigInteger() { 268 | BigInteger out = BigInteger.ZERO; 269 | for(int i = 0; i < ints.length; i++) { 270 | out = out.shiftLeft(32).or(BigInteger.valueOf(ints[i] & LONG)); 271 | } 272 | return out; 273 | } 274 | 275 | /** 276 | * Return a big-endian byte array. 277 | */ 278 | public final byte[] toByteArray() { 279 | final int bytes = (int)Math.ceil(bitLength() / 8.0); 280 | final byte[] out = new byte[bytes]; 281 | 282 | int intsi = ints.length - 1, v = 0; 283 | for(int outi = bytes - 1, copied = 0; 0 <= outi; outi--, copied++) 284 | out[outi] = (byte)(v = (copied % 4 == 0) ? ints[intsi--] : v >>> 8); 285 | return out; 286 | } 287 | 288 | /** 289 | * Return a big-endian int array. 290 | */ 291 | public final int[] toIntArray() { 292 | return java.util.Arrays.copyOf(ints, ints.length); 293 | } 294 | 295 | /** 296 | * Decimal string representation. 297 | */ 298 | public final String toString() { 299 | return toString(DEFAULT_RADIX); 300 | } 301 | 302 | /** 303 | * String representation in the given radix. 304 | * 305 | * {@code radix} values outside {@code Character.MIN_RADIX} and 306 | * {@code Character.MAX_RADIX} are substituted with {@code 10}. 307 | */ 308 | public final String toString(int radix) { 309 | if(isZero()) 310 | return "0"; 311 | 312 | if(radix < Character.MIN_RADIX || Character.MAX_RADIX < radix) 313 | radix = DEFAULT_RADIX; 314 | 315 | if(ints.length == 1) 316 | return Integer.toUnsignedString(ints[0], radix); 317 | if(ints.length == 2) 318 | return Long.toUnsignedString(((ints[0] & LONG) << 32) | (ints[1] & LONG), radix); 319 | 320 | return StringUtil.toString(ints, radix); 321 | } 322 | } 323 | -------------------------------------------------------------------------------- /biginteger-performance.md: -------------------------------------------------------------------------------- 1 |

2 | Moe Aboulkheir
3 | 2023-01-14
4 | 5 | 6 |

7 | 8 | # Performance Pathologies in OpenJDK's BigInteger Implementation 9 | 10 | Many and many a year ago (in a kingdom by the sea), I wrote some fixed-width 11 | integer types without sign, with alacrity. 12 | 13 | In Java, nominally for a 14 | [Clojure Ethereum VM](https://github.com/nervous-systems/sputter) 15 | project, though completely unnecessary — I was on a work trip, it was the 16 | weekend, and it was either that or the minibar. I did some benchmarking, and I 17 | got some pretty ridiculous numbers, against what were two most recent major 18 | OpenJDK versions at the time: 19 | 20 | ![graph](static/uint256.png) 21 | 22 | N.B. The 23 | [project](https://github.com/nervous-systems/java-unsigned-integers) includes 24 | `UInt128` and `UInt256`, however these are trivial wrappers around a 25 | general-purpose 26 | [Arrays.java](https://github.com/nervous-systems/java-unsigned-integers/blob/master/juint/src/main/java/io/nervous/juint/Arrays.java), 27 | which can operate on arbitrarily wide integers, as long as there is an 28 | upper-bound on their length — adding smaller or larger `uint` types would not 29 | be a problem. 30 | 31 | ## About Those Benchmarks 32 | 33 | Each bar is an aggregate of around 10 JMH microbenchmarks (170 altogether) per 34 | operation, performed with numbers of different magnitudes. With the generosity 35 | of a dying aunt, I only mod 2256 the `BigInteger`instance when it's known that 36 | it's exceeeded the desired capacity — mostly not how things work in 37 | real life, when dealing with arbitrary values. So, these are worst-case 38 | speedups. 39 | 40 | - Almost every operation is twice as fast as OpenJDK 8. 41 | - `UInt256`'s concomitants of the slowest `BigInteger` operations (`clearBit` and `toString`), do not benefit from the fact that the width of the number 42 | is fixed. Something else is going on. 43 | - The only cases where `BigInteger` is faster are due to the use of intrinsics, 44 | and even then, it's basically getting nowhere. 45 | 46 | ### Updated Benchmarks 47 | 48 | I'd been meaning to write an article like this for a while, so after 5 years I re-ran 49 | the benchmarks against a recent GraalVM (since that's the JVM I was using), and OpenJDK 19. 50 | 51 | ![graph](static/uint256-2023.png) 52 | 53 | Basically everything is at OpenJDK 9 levels, except the intrinsics are faster. 54 | Let's take a tour of the source of `UInt256` and OpenJDK 8 and 19's `BigInteger` 55 | to see if we can't get a handle on some of these larger discrepancies. 56 | 57 | # `clearBit` 58 | 59 | Here's my `UInt256.clearBit` 60 | 61 | ```java 62 | public final class UInt256 extends UInt { 63 | ... 64 | public UInt256 clearBit(final int bit) { 65 | if(bit < 0) 66 | throw new ArithmeticException("Negative bit address"); 67 | return ((ints.length <= bit >>> 5) ? this : 68 | new UInt256(Arrays.clearBit(ints, bit))); 69 | } 70 | } 71 | ``` 72 | 73 | (`ints` is a little endian array of integers, with no leading zeroes). 74 | 75 | The 76 | only logic here is that we don't do any work if the bit falls outside the bounds 77 | of `ints`. `Arrays.clearBit`: 78 | 79 | ```java 80 | final class Arrays { 81 | ... 82 | static int[] clearBit(final int[] a, final int bit) { 83 | final int alen = a.length; 84 | final int i = alen - (bit >>> 5) - 1; 85 | final int v = a[i] & ~(1 << (bit & 31)); 86 | 87 | if(v == a[i]) 88 | return a; 89 | 90 | if(i != 0 || v != 0) { 91 | final int[] out = copyOf(a, alen); 92 | out[i] = v; 93 | return out; 94 | } 95 | return stripLeadingZeroes(a, 1); 96 | } 97 | } 98 | ``` 99 | 100 | ## Trivial Optimizations 101 | - Return the unmolested array if the bit is already clear. 102 | - If it's not the zeroth int, or clearing the bit won't zero the int, don't 103 | bother looking for leading zeroes. 104 | - Otherwise, remove the first int without inspecting it (second arg to 105 | `stripLeadingZeroes`), as we know it's zero, and scan forward for the first 106 | non-zero value before copying: 107 | 108 | ```java 109 | final class Arrays { 110 | ... 111 | static int[] stripLeadingZeroes(final int[] ints, int strip) { 112 | final int len = ints.length; 113 | 114 | for(; strip < len && ints[strip] == 0; strip++) 115 | ; 116 | return strip == 0 ? ints : copyOfRange(ints, strip, len); 117 | } 118 | } 119 | ``` 120 | 121 | I'm _dying_ to know what OpenJDK 8 is doing. 122 | 123 | ```java 124 | public class BigInteger extends Number implements Comparable { 125 | ... 126 | public BigInteger clearBit(int n) { 127 | if (n < 0) 128 | throw new ArithmeticException("Negative bit address"); 129 | 130 | int intNum = n >>> 5; 131 | int[] result = new int[Math.max(intLength(), ((n + 1) >>> 5) + 1)]; 132 | 133 | for (int i=0; i < result.length; i++) 134 | result[result.length-i-1] = getInt(i); 135 | 136 | result[result.length-intNum-1] &= ~(1 << (n & 31)); 137 | 138 | return valueOf(result); 139 | } 140 | } 141 | ``` 142 | 143 | If the bit is out of bounds, and I'm reading right, it's _growing_ the integer 144 | array to a size bounded by 232 / 32 (approx 108, the biblical lower 145 | bound on the number of angels[^1]), to 146 | "clear" a bit which wouldn't otherwise exist — then stripping all of the leading 147 | zeroes in the `int[]` constructor used by `valueOf`, resulting in exactly the 148 | same number! You don't see this kind of thing every day, top class stuff. I was keen to see how OpenJDK 19 had sped 149 | this up, but it's doing _exactly_ the same thing! The speedup must come from 150 | some ancillary method/s (e.g. `getInt`, `valueOf`, etc.), overall runtime 151 | optimizations, or some combination. Pandaemonium. Let's take a look at throughput graph 152 | with `clearBit` thrown out: 153 | 154 | ![Graph](static/uint256-no-clear.png) 155 | 156 | [^1]: Proof in [Revelation 5:11](https://bible.bibleask.org/#/search/Revelation%205:11/KJV). 157 | 158 | # `setBit` 159 | 160 | While not as dramatic, my `setBit` implementation is 200% faster. Let's take a look 161 | at what's happening with `BigInteger`. 162 | 163 | ```java 164 | public class BigInteger extends Number implements Comparable { 165 | ... 166 | public BigInteger setBit(int n) { 167 | if (n < 0) 168 | throw new ArithmeticException("Negative bit address"); 169 | 170 | int intNum = n >>> 5; 171 | int[] result = new int[Math.max(intLength(), intNum+2)]; 172 | 173 | for (int i=0; i < result.length; i++) 174 | result[result.length-i-1] = getInt(i); 175 | 176 | result[result.length-intNum-1] |= (1 << (n & 31)); 177 | 178 | return valueOf(result); 179 | } 180 | } 181 | ``` 182 | 183 | This is a little goofy — if the corresponding integer exists 184 | in the integer array, why not check if the bit is _already_ set before doing any 185 | allocating? Compare: 186 | 187 | ```java 188 | final class Arrays { 189 | ... 190 | static int[] setBit(final int[] a, final int bit) { 191 | final int i = bit >>> 5; 192 | final int alen = a.length; 193 | 194 | if(i <= alen - 1) { 195 | final int j = alen - i - 1; 196 | final int v = a[j] | 1 << (bit & 31); 197 | if(v == a[j]) 198 | return a; 199 | final int[] out = copyOf(a, alen); 200 | out[j] = v; 201 | return out; 202 | } 203 | 204 | final int[] out = new int[i + 1]; 205 | System.arraycopy(a, 0, out, out.length - alen, alen); 206 | 207 | out[0] = 1 << (bit & 31); 208 | return out; 209 | } 210 | } 211 | ``` 212 | 213 | # `flipBit` 214 | 215 | In general, there are some speedups to be had by closely coupling the 216 | zero-stripping code with the array arithmetic, as we often know accidentally 217 | (e.g. if there's a carry left over), or with a cheap check, whether we need to 218 | scan for leading zeroes or not (as we saw with `clearBit`. We use this to our 219 | advantage in `flipBit`, also, and gain a `150%+` speedup: 220 | 221 | ```java 222 | final class Arrays { 223 | ... 224 | static int[] flipBit(final int[] a, final int bit) { 225 | final int i = bit >>> 5; 226 | final int alen = a.length; 227 | 228 | if(i < alen - 1) { 229 | final int[] out = copyOf(a, alen); 230 | final int j = alen - i - 1; 231 | out[j] ^= (1 << (bit & 31)); 232 | return out; 233 | } 234 | 235 | final int[] out = new int[i + 1]; 236 | System.arraycopy(a, 0, out, out.length - alen, alen); 237 | 238 | return (out[0] ^= (1 << (bit & 31))) == 0 ? stripLeadingZeroes(out, 1) : out; 239 | } 240 | } 241 | ``` 242 | 243 | This method could be further optimized. We also get a marginal gain in 244 | `UInt256.flipBit`, as we can ignore bits which fall out of bounds, though this 245 | wouldn't be exercised by the benchmarks. Anyway, enough with the bits. Let's 246 | take a look at `toString`. 247 | 248 | # `toString` 249 | 250 | `toString` accepts a radix in which to represent the number, so it does a bunch 251 | of `divideAndRemainder` calls, and despite both implementing Knuth's Algorithm 252 | D, division is substantially faster in `UInt256` — which probably accounts for a 253 | large degree of the disparity. 254 | 255 | ```java 256 | public abstract class UInt 257 | extends Number 258 | implements Comparable { 259 | ... 260 | public final String toString(int radix) { 261 | if(isZero()) 262 | return "0"; 263 | 264 | if(radix < Character.MIN_RADIX || Character.MAX_RADIX < radix) 265 | radix = DEFAULT_RADIX; 266 | 267 | if(ints.length == 1) 268 | return Integer.toUnsignedString(ints[0], radix); 269 | if(ints.length == 2) 270 | return Long.toUnsignedString(((ints[0] & LONG) << 32) | (ints[1] & LONG), radix); 271 | 272 | return StringUtil.toString(ints, radix); 273 | } 274 | } 275 | ``` 276 | 277 | I think these length checks (absent in `BigInteger`) probably account for the 278 | rest, in the lower-magnitude test cases. `BigInteger` immediately falls back to 279 | Schoenhage radix conversion if the number within some range (2256 is 280 | comfortably within this range), which is what `StringUtil.toString` does, above. 281 | Anything we can do to avoid that'll be a big win. 282 | 283 | # Other Fixed-width Integer Implementations 284 | 285 | Someone recently pointed me at [Apache Tuewni's UInt256 286 | implementation](https://github.com/apache/incubator-tuweni/blob/main/units/src/main/java/org/apache/tuweni/units/bigints/UInt256.java). 287 | It looked a little eccentric, but I wanted to benchmark it regardless. 288 | 289 | ![graph](static/uint256-tuweni.png) 290 | 291 | Unlike [java-unsigned-integers](https://github.com/nervous-systems/java-unsigned-integers) (the project containing my implementation of `UInt256`, etc.), Tuweni's `UInt256` is less 292 | concerned about genericity, and performs arithmetic directly in its class, rather than 293 | deferring to something like `Arrays.java` as shown above. This means its loops, e.g.: 294 | 295 | ```java 296 | public final class UInt256 implements UInt256Value { 297 | ... 298 | public UInt256 xor(UInt256 value) { 299 | int[] result = new int[INTS_SIZE]; 300 | for (int i = INTS_SIZE - 1; i >= 0; --i) { 301 | result[i] = this.ints[i] ^ value.ints[i]; 302 | } 303 | return new UInt256(result); 304 | } 305 | } 306 | ``` 307 | 308 | are trivial to unroll, and it seems unconcerned, generally, with leading zeroes. 309 | This comes at some cost — if you wanted to implement `UInt512`, you'd have 310 | to copy and paste all of this code. Although, against that trade-off, I'll 311 | happily take some 25%-50% losses along with some 300%-450% gains. 312 | 313 | Thanks for reading! Open an issue or a PR if you have ideas about further 314 | speedups. 315 |

318 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | "CLASSPATH" EXCEPTION TO THE GPL 342 | 343 | All source files in this project are designated as subject to the "Classpath" 344 | exception: 345 | 346 | Linking this library statically or dynamically with other modules is making 347 | a combined work based on this library. Thus, the terms and conditions of 348 | the GNU General Public License cover the whole combination. 349 | 350 | As a special exception, the copyright holders of this library give you 351 | permission to link this library with independent modules to produce an 352 | executable, regardless of the license terms of these independent modules, 353 | and to copy and distribute the resulting executable under terms of your 354 | choice, provided that you also meet, for each linked independent module, 355 | the terms and conditions of the license of that module. An independent 356 | module is a module which is not derived from or based on this library. If 357 | you modify this library, you may extend this exception to your version of 358 | the library, but you are not obligated to do so. If you do not wish to do 359 | so, delete this exception statement from your version. 360 | -------------------------------------------------------------------------------- /juint/src/main/java/io/nervous/juint/Arrays.java: -------------------------------------------------------------------------------- 1 | package io.nervous.juint; 2 | 3 | import java.math.BigInteger; 4 | 5 | import static java.util.Arrays.copyOfRange; 6 | import static java.util.Arrays.copyOf; 7 | 8 | /** 9 | * These methods don't mutate their arguments or return arrays w/ leading zeroes. 10 | */ 11 | final class Arrays { 12 | static final long LONG = 0xffffffffL; 13 | static final int MAX_CACHE = 28; 14 | static final int[][] CACHE = new int[MAX_CACHE][1]; 15 | 16 | static { 17 | CACHE[0] = new int[0]; 18 | for(int i = 1; i < MAX_CACHE; i++) { 19 | CACHE[i] = new int[]{i}; 20 | } 21 | } 22 | 23 | static final int[] ZERO = CACHE[0]; 24 | static final int[] ONE = CACHE[1]; 25 | static final int[] TWO = CACHE[2]; 26 | 27 | static int[] valueOf(final long v) { 28 | if(0 <= v && v < MAX_CACHE) 29 | return CACHE[(int)v]; 30 | 31 | final int hi = (int)(v >>> 32); 32 | return hi == 0 ? new int[]{(int)v} : new int[]{hi, (int)v}; 33 | } 34 | 35 | static int compare(final int[] ints, final int[] other) { 36 | final int len = ints.length; 37 | if(len < other.length) 38 | return -1; 39 | if(len > other.length) 40 | return 1; 41 | 42 | int cmp; 43 | for(int i = 0; i < len; i++) 44 | if(ints[i] != other[i]) 45 | return Integer.compareUnsigned(ints[i], other[i]); 46 | 47 | return 0; 48 | } 49 | 50 | static int compare(final int[] ints, final BigInteger other, final int maxWidth) { 51 | final int il = bitLength(ints), bl = other.bitLength(); 52 | if(il < bl) 53 | return -1; 54 | if(il > bl) 55 | return 1; 56 | 57 | return compare(ints, from(other, maxWidth)); 58 | } 59 | 60 | static int[] stripLeadingZeroes(final int[] ints, int strip) { 61 | final int len = ints.length; 62 | 63 | for(; strip < len && ints[strip] == 0; strip++) 64 | ; 65 | return strip == 0 ? ints : copyOfRange(ints, strip, len); 66 | } 67 | 68 | static int[] stripLeadingZeroes(final int[] ints) { 69 | return stripLeadingZeroes(ints, 0); 70 | } 71 | 72 | static byte[] stripLeadingZeroes(final byte[] bs) { 73 | int strip; 74 | final int len = bs.length; 75 | 76 | for(strip = 0; strip < len && bs[strip] == 0; strip++) 77 | ; 78 | return strip == 0 ? bs : copyOfRange(bs, strip, len); 79 | } 80 | 81 | static int[] not(final int[] ints, final int[] maxValue) { 82 | int len = ints.length, maxWidth = maxValue.length; 83 | if(ints.length == 0) 84 | return maxValue; 85 | 86 | int start = 0; 87 | if(ints[0] == -1) 88 | for(start = 1; start < len && ints[start] == -1; start++) 89 | ; 90 | 91 | if(start == maxWidth) 92 | return ZERO; 93 | 94 | final int[] out = new int[len < maxWidth ? maxWidth : maxWidth - start]; 95 | int leading = maxWidth - len; 96 | 97 | java.util.Arrays.fill(out, 0, leading, -1); 98 | 99 | for(int i = out.length - 1; leading <= i; i--) 100 | out[i] = ~(ints[--len]); 101 | 102 | return out; 103 | } 104 | 105 | static int[] and(int[] longer, int[] shorter) { 106 | if(longer.length < shorter.length) { 107 | int[] tmp = longer; longer = shorter; shorter = tmp; 108 | } 109 | int shortlen = shorter.length; 110 | if(shortlen == 0) 111 | return ZERO; 112 | 113 | final int[] out = copyOf(shorter, shortlen); 114 | int longlen = longer.length; 115 | 116 | while(0 < shortlen) 117 | out[--shortlen] &= longer[--longlen]; 118 | 119 | return out[0] == 0 ? stripLeadingZeroes(out, 1) : out; 120 | } 121 | 122 | static int[] or(int[] longer, int[] shorter) { 123 | if(longer.length < shorter.length) { 124 | int[] tmp = longer; longer = shorter; shorter = tmp; 125 | } 126 | int longlen = longer.length, shortlen = shorter.length; 127 | final int[] out = copyOf(longer, longlen); 128 | 129 | while(0 < shortlen) 130 | out[--longlen] |= shorter[--shortlen]; 131 | 132 | return out; 133 | } 134 | 135 | static int[] xor(int[] longer, int[] shorter) { 136 | if(longer.length < shorter.length) { 137 | int[] tmp = longer; longer = shorter; shorter = tmp; 138 | } 139 | if(longer.length == 0) 140 | return ZERO; 141 | 142 | int longlen = longer.length, shortlen = shorter.length; 143 | final int[] out = copyOf(longer, longlen); 144 | 145 | while(0 < shortlen) 146 | out[--longlen] ^= shorter[--shortlen]; 147 | 148 | return out[0] == 0 ? stripLeadingZeroes(out, 1) : out; 149 | } 150 | 151 | static int[] setBit(final int[] a, final int bit) { 152 | final int i = bit >>> 5, alen = a.length; 153 | 154 | if(i <= alen - 1) { 155 | final int j = alen - i - 1, v = a[j] | 1 << (bit & 31); 156 | if(v == a[j]) 157 | return a; 158 | final int[] out = copyOf(a, alen); 159 | out[j] = v; 160 | return out; 161 | } 162 | 163 | final int[] out = new int[i + 1]; 164 | System.arraycopy(a, 0, out, out.length - alen, alen); 165 | 166 | out[0] = 1 << (bit & 31); 167 | return out; 168 | } 169 | 170 | static int[] clearBit(final int[] a, final int bit) { 171 | final int alen = a.length, i = alen - (bit >>> 5) - 1; 172 | final int v = a[i] & ~(1 << (bit & 31)); 173 | 174 | if(v == a[i]) 175 | return a; 176 | 177 | if(i != 0 || v != 0) { 178 | final int[] out = copyOf(a, alen); 179 | out[i] = v; 180 | return out; 181 | } 182 | return stripLeadingZeroes(a, 1); 183 | } 184 | 185 | static int[] flipBit(final int[] a, final int bit) { 186 | final int i = bit >>> 5, alen = a.length; 187 | 188 | if(i < alen - 1) { 189 | final int[] out = copyOf(a, alen); 190 | final int j = alen - i - 1; 191 | out[j] ^= (1 << (bit & 31)); 192 | return out; 193 | } 194 | 195 | final int[] out = new int[i + 1]; 196 | System.arraycopy(a, 0, out, out.length - alen, alen); 197 | 198 | return (out[0] ^= (1 << (bit & 31))) == 0 ? stripLeadingZeroes(out, 1) : out; 199 | } 200 | 201 | static int[] lshift(final int[] a, final int n, final int maxWidth) { 202 | if(n == 0) 203 | return a; 204 | 205 | final int alen = a.length, ints = n >>> 5; 206 | if(alen == 0 || maxWidth < ints) 207 | return ZERO; 208 | 209 | final int bits = n & 0x1f; 210 | int outlen = alen + ints; 211 | int ai = 0; 212 | 213 | if(maxWidth < outlen) { 214 | ai = outlen - maxWidth; 215 | outlen = maxWidth; 216 | 217 | while(ai < alen && a[ai] == 0) { 218 | ai++; outlen--; 219 | } 220 | } 221 | 222 | if(ai == alen) 223 | return ZERO; 224 | 225 | final int[] out; 226 | if(bits == 0) { 227 | out = new int[outlen]; 228 | System.arraycopy(a, ai, out, 0, alen - ai); 229 | return out; 230 | } 231 | 232 | int outi = 0; 233 | final int invbits = 32 - bits, high = a[ai] >>> invbits; 234 | 235 | if(high != 0 && outlen < maxWidth) { 236 | out = new int[outlen + 1]; 237 | out[outi++] = high; 238 | } else 239 | out = new int[outlen]; 240 | 241 | while (ai < alen - 1) 242 | out[outi++] = a[ai++] << bits | a[ai] >>> invbits; 243 | out[outi] = a[ai] << bits; 244 | 245 | return out[0] == 0 ? stripLeadingZeroes(out, 1) : out; 246 | } 247 | 248 | static int[] rshift(final int[] a, final int n, final int maxWidth) { 249 | final int alen = a.length, ints = n >>> 5; 250 | if(alen <= ints) 251 | return ZERO; 252 | 253 | final int bits = n & 0x1f, outlen = alen - ints; 254 | if(bits == 0) 255 | return copyOf(a, outlen); 256 | 257 | final int invbits = 32 - bits, high = a[0] >>> bits, out[]; 258 | int outi = 0, ai = 0; 259 | 260 | if(high != 0) { 261 | out = new int[outlen]; 262 | out[outi++] = high; 263 | } else 264 | out = new int[outlen - 1]; 265 | 266 | while (ai < outlen - 1) 267 | out[outi++] = (a[ai++] << invbits) | (a[ai] >>> bits); 268 | 269 | return out; 270 | } 271 | 272 | static int[] inc(final int[] a, final int maxWidth) { 273 | return inc(a, false, maxWidth); 274 | } 275 | 276 | static int[] inc(final int[] a, final boolean mutate, final int maxWidth) { 277 | final int len = a.length; 278 | if(len == 0) 279 | return ONE; 280 | 281 | int last = len - 1; 282 | final int[] b = mutate ? a : copyOf(a, len); 283 | 284 | while(0 <= last) 285 | if(++(b[last--]) != 0) 286 | return b; 287 | if(len == maxWidth) 288 | return stripLeadingZeroes(b); 289 | 290 | final int[] c = new int[len + 1]; 291 | System.arraycopy(b, 0, c, 1, len); 292 | c[0] = 1; 293 | return c; 294 | } 295 | 296 | static int[] dec(final int[] a) { 297 | final int len = a.length; 298 | int last = len - 1; 299 | final int[] b = copyOf(a, len); 300 | 301 | int v; 302 | 303 | if((v = --(b[last])) != -1) 304 | return (last == 0 && v == 0) ? stripLeadingZeroes(b, 1) : b; 305 | 306 | while(0 <= --last && (v = --(b[last])) == -1) 307 | ; 308 | return (v == 0 && last == 0) ? stripLeadingZeroes(b, 1) : b; 309 | } 310 | 311 | static int[] add(int[] longer, int[] shorter, final int maxWidth) { 312 | if(longer.length < shorter.length) { 313 | int[] tmp = longer; 314 | longer = shorter; 315 | shorter = tmp; 316 | } 317 | int longi = longer.length; 318 | int shorti = shorter.length; 319 | final int[] out = copyOf(longer, longi); 320 | long sum = 0; 321 | 322 | while (0 < shorti) { 323 | sum = (out[--longi] & LONG) + (shorter[--shorti] & LONG) + (sum >>> 32); 324 | out[longi] = (int)sum; 325 | } 326 | 327 | boolean carry = sum >>> 32 != 0; 328 | while (carry && 0 < longi) 329 | carry = ++(out[--longi]) == 0; 330 | 331 | if(carry && (out.length < maxWidth || maxWidth == -1)) { 332 | int grown[] = new int[out.length + 1]; 333 | grown[0] = 1; 334 | 335 | System.arraycopy(out, 0, grown, 1, out.length); 336 | return grown; 337 | } 338 | 339 | return out[0] == 0 ? stripLeadingZeroes(out, 1) : out; 340 | } 341 | 342 | static int[] subgt(final int[] a, final int[] b, final int[] maxValue) { 343 | if(a.length == 0) 344 | return inc(not(b, maxValue), true, maxValue.length); 345 | return inc(not(sub(b, a), maxValue), true, maxValue.length); 346 | } 347 | 348 | static int[] sub(final int[] a, final int[] b) { 349 | int longi = a.length, shorti = b.length; 350 | if(shorti == 0) 351 | return a; 352 | final int[] out = copyOf(a, longi); 353 | long diff = 0; 354 | 355 | while (0 < shorti) { 356 | diff = (out[--longi] & LONG) - (b[--shorti] & LONG) + (diff >> 32); 357 | out[longi] = (int)diff; 358 | } 359 | 360 | if(diff >> 32 != 0) 361 | while (0 < longi && --(out[--longi]) == -1) 362 | ; 363 | 364 | return out[0] == 0 ? stripLeadingZeroes(out, 1) : out; 365 | } 366 | 367 | static int[] mulmod(int[] a, int[] b, final int[] c) { 368 | if(a.length < b.length) { 369 | int[] tmp = a; a = b; b = tmp; 370 | } 371 | if(b.length == 0) 372 | return ZERO; 373 | final int[] mul = mul(a, a.length, b, b.length); 374 | final int cmp = compare(mul, c); 375 | return (cmp < 0 ? mul : (cmp == 0 ? ZERO : mod(mul, c))); 376 | } 377 | 378 | static int[] addmod(int[] a, int[] b, final int[] c) { 379 | if(a.length < b.length) { 380 | int[] tmp = a; a = b; b = tmp; 381 | } 382 | final int[] add = b.length == 0 ? a : add(a, b, -1); 383 | final int cmp = compare(add, c); 384 | return (cmp < 0 ? add : (cmp == 0 ? ZERO : mod(add, c))); 385 | } 386 | 387 | static int[] multiply(int[] a, int[] b, final int maxWidth) { 388 | if(a.length < b.length) { 389 | int[] tmp = a; a = b; b = tmp; 390 | } 391 | final int alen = a.length, blen = b.length; 392 | 393 | if(blen == 1) 394 | return mul(a, alen, b[0], maxWidth); 395 | if(blen == 2) 396 | return mul(a, alen, b[0], b[1], maxWidth); 397 | 398 | final int outlen = alen + blen; 399 | if(maxWidth < outlen) 400 | return mul(a, alen, b, blen, maxWidth, outlen - maxWidth); 401 | return mul(a, alen, b, blen); 402 | } 403 | 404 | static int[] mul(int[] a, final int alen, int b, int maxWidth) { 405 | if(Integer.bitCount(b) == 1) 406 | return lshift(a, Integer.numberOfTrailingZeros(b), maxWidth); 407 | 408 | final int[] out = new int[alen == maxWidth ? maxWidth : (alen + 1)]; 409 | 410 | long carry = 0; 411 | final long bl = b & LONG; 412 | 413 | for(int ai = alen - 1, outi = out.length - 1; 0 <= ai; ai--, outi--) { 414 | final long prod = (a[ai] & LONG) * bl + carry; 415 | out[outi] = (int)prod; 416 | carry = prod >>> 32; 417 | } 418 | 419 | return ((alen != maxWidth && (out[0] = (int)carry) != 0) ? out : 420 | (out[0] == 0 ? stripLeadingZeroes(out, 1) : 421 | out)); 422 | } 423 | 424 | static int[] mul( 425 | final int[] a, final int alen, final int hi, final int lo, final int maxWidth) { 426 | 427 | int outlen = alen + 2; 428 | final long lhi = hi & LONG; 429 | final long llo = lo & LONG; 430 | final int[] out = new int[outlen]; 431 | int outi = outlen - 1; 432 | long carry = 0; 433 | 434 | for(int i = alen - 1; 0 <= i; i--) { 435 | long prod = (a[i] & LONG) * llo + carry; 436 | out[outi--] = (int)prod; 437 | carry = prod >>> 32; 438 | } 439 | out[outi] = (int)carry; 440 | 441 | carry = 0; 442 | outi = out.length - 2; 443 | 444 | for(int ai = alen - 1; 0 <= ai; ai--) { 445 | long prod = (a[ai] & LONG) * lhi + (out[outi] & LONG) + carry; 446 | out[outi--] = (int)prod; 447 | carry = prod >>> 32; 448 | } 449 | out[0] = (int)carry; 450 | 451 | if(outlen <= maxWidth) 452 | return carry == 0L ? stripLeadingZeroes(out, 1) : out; 453 | 454 | return stripLeadingZeroes(out, outlen - maxWidth); 455 | } 456 | 457 | static int[] mul( 458 | final int[] a, final int alen, final int[] b, final int blen) { 459 | 460 | final int outlen = alen + blen; 461 | final int astart = alen - 1, bstart = blen - 1; 462 | final int[] out = new int[outlen]; 463 | long carry = 0; 464 | 465 | for(int bi = bstart, outi = outlen - 1; 0 <= bi; bi--, outi--) { 466 | final long prod = (b[bi] & LONG) * (a[astart] & LONG) + carry; 467 | out[outi] = (int)prod; 468 | carry = prod >>> 32; 469 | } 470 | out[astart] = (int)carry; 471 | 472 | for(int ai = astart - 1; 0 <= ai; ai--) { 473 | carry = 0; 474 | for(int bi = bstart, outi = bstart + ai + 1; 0 <= bi; bi--, outi--) { 475 | final long prod = (b[bi] & LONG) * (a[ai] & LONG) + (out[outi] & LONG) + carry; 476 | out[outi] = (int)prod; 477 | carry = prod >>> 32; 478 | } 479 | out[ai] = (int)carry; 480 | } 481 | 482 | return carry == 0L ? stripLeadingZeroes(out, 1) : out; 483 | } 484 | 485 | static int[] mul( 486 | final int[] a, final int alen, final int[] b, final int blen, 487 | final int outlen, final int trunc) { 488 | 489 | final int astart = alen - 1, bstart = blen - 1; 490 | final int[] out = new int[outlen]; 491 | long carry = 0; 492 | 493 | for(int bi = bstart, outi = outlen - 1; 0 <= bi; bi--, outi--) { 494 | final long prod = (b[bi] & LONG) * (a[astart] & LONG) + carry; 495 | out[outi] = (int)prod; 496 | carry = prod >>> 32; 497 | } 498 | if(trunc <= astart) 499 | out[astart - trunc] = (int)carry; 500 | 501 | int outend = outlen - 2; 502 | for(int ai = astart - 1; 0 <= ai; ai--) { 503 | carry = 0; 504 | for(int bi = bstart, outi = outend--; 0 <= bi && 0 <= outi; bi--, outi--) { 505 | final long prod = (b[bi] & LONG) * (a[ai] & LONG) + (out[outi] & LONG) + carry; 506 | out[outi] = (int)prod; 507 | carry = prod >>> 32; 508 | } 509 | if(trunc <= ai) 510 | out[ai - trunc] = (int)carry; 511 | } 512 | return stripLeadingZeroes(out); 513 | } 514 | 515 | static int bitLength(int a[]) { 516 | return (a.length == 0 ? 517 | 0 : 518 | ((a.length - 1) * 32) + (32 - Integer.numberOfLeadingZeros(a[0]))); 519 | } 520 | 521 | static int[] square(final int[] a, final int maxWidth) { 522 | final int alen = a.length, start; 523 | 524 | int outlen = alen << 1; 525 | if(maxWidth < outlen) { 526 | start = (outlen - maxWidth) >>> 1; 527 | outlen = maxWidth; 528 | } else 529 | start = 0; 530 | 531 | final int[] out = new int[outlen]; 532 | 533 | int last = 0; 534 | for(int ai = start, i = 0; ai < alen; ai++) { 535 | final long wl = (a[ai] & LONG); 536 | final long prod = wl * wl; 537 | out[i++] = (last << 31) | (int)(prod >>> 33); 538 | out[i++] = (int)(prod >>> 1); 539 | last = (int)prod; 540 | } 541 | 542 | for(int ai = alen, pos = 1; start < ai && pos < outlen; ai--, pos += 2) { 543 | long carry = 0; 544 | int outi = outlen - pos - 1; 545 | final long v = a[ai - 1] & LONG; 546 | 547 | for(int aj = ai - 2; 0 <= aj && 0 <= outi; aj--) { 548 | long prod = (a[aj] & LONG) * v + (out[outi] & LONG) + carry; 549 | out[outi--] = (int)prod; 550 | carry = prod >>> 32; 551 | } 552 | 553 | if(0 <= outi) { 554 | carry += (out[outi] & LONG); 555 | out[outi] = (int)carry; 556 | if((carry >> 32) != 0) 557 | for(int tmp = ai - 1; 0 <= tmp && 0 <= --outi && ++(out[outi]) == 0; tmp--) 558 | ; 559 | } 560 | } 561 | 562 | Division.lshunt(out, 1); 563 | out[outlen - 1] |= (a[alen - 1] & 1); 564 | return out[0] == 0 ? stripLeadingZeroes(out, 1) : out; 565 | } 566 | 567 | static int[] pow(int[] a, final int lo, int exp, final int maxWidth) { 568 | if(exp == 2) 569 | return square(a, maxWidth); 570 | 571 | long shift = (long)lo * exp; 572 | if(Integer.MAX_VALUE < shift) 573 | throw new ArithmeticException("Overflow"); 574 | 575 | if(0 < lo) 576 | a = rshift(a, lo, maxWidth); 577 | 578 | final int bits = bitLength(a); 579 | if(bits == 1) 580 | return 0 < lo ? lshift(ONE, lo * exp, maxWidth) : ONE; 581 | 582 | long scale = (long)bits * exp; 583 | 584 | if(a.length == 1 && scale < 63) { 585 | long out = 1, base = a[0] & LONG; 586 | 587 | while(exp != 0) { 588 | if((exp & 1) == 1) 589 | out *= base; 590 | if((exp >>>= 1) != 0) 591 | base *= base; 592 | } 593 | 594 | if(0 < lo) 595 | return ((shift + scale) < 63 ? 596 | valueOf(out << shift) : 597 | lshift(valueOf(out), (int)shift, maxWidth)); 598 | return valueOf(out); 599 | } 600 | 601 | final int lplaces = lo * exp; 602 | int[] out = ONE; 603 | 604 | while(exp != 0) { 605 | if((exp & 1) == 1) 606 | out = multiply(out, a, maxWidth); 607 | if((exp >>>= 1) != 0) 608 | a = square(a, maxWidth); 609 | } 610 | 611 | return 0 < lplaces ? lshift(out, lplaces, maxWidth) : out; 612 | } 613 | 614 | static int[] divide(final int[] a, final int[] b) { 615 | final int[] q; 616 | 617 | switch(b.length) { 618 | case 1: 619 | q = Division.div(a, b[0])[0]; 620 | break; 621 | case 2: 622 | final long divisor = ((b[0] & LONG) << 32) | (b[1] & LONG); 623 | q = Division.div(a, divisor)[0]; 624 | break; 625 | default: 626 | q = Division.div(a, b)[0]; 627 | } 628 | 629 | return q[0] == 0 ? stripLeadingZeroes(q) : q; 630 | } 631 | 632 | static int[] mod(final int[] a, final int[] b) { 633 | final int[] r; 634 | 635 | switch(b.length) { 636 | case 1: 637 | r = Division.div(a, b[0])[1]; 638 | break; 639 | case 2: 640 | final long divisor = ((b[0] & LONG) << 32) | (b[1] & LONG); 641 | r = Division.div(a, divisor)[1]; 642 | break; 643 | default: 644 | r = Division.div(a, b)[1]; 645 | } 646 | 647 | return r[0] == 0 ? stripLeadingZeroes(r) : r; 648 | } 649 | 650 | static int[][] divmod(final int[] a, final long b) { 651 | final int[][] qr = Division.div(a, b); 652 | 653 | if(0 < qr[0].length && qr[0][0] == 0) 654 | qr[0] = stripLeadingZeroes(qr[0]); 655 | if(qr[1][0] == 0) 656 | qr[1] = stripLeadingZeroes(qr[1]); 657 | 658 | return qr; 659 | } 660 | 661 | static int[][] divmod(final int[] a, final int[] b) { 662 | final int[][] qr; 663 | 664 | switch(b.length) { 665 | case 1: 666 | qr = Division.div(a, b[0]); 667 | break; 668 | case 2: 669 | final long divisor = ((b[0] & LONG) << 32) | (b[1] & LONG); 670 | qr = Division.div(a, divisor); 671 | break; 672 | default: 673 | qr = Division.div(a, b); 674 | } 675 | 676 | if(0 < qr[0].length && qr[0][0] == 0) 677 | qr[0] = stripLeadingZeroes(qr[0]); 678 | if(qr[1][0] == 0) 679 | qr[1] = stripLeadingZeroes(qr[1]); 680 | 681 | return qr; 682 | } 683 | 684 | private static BigInteger BIG_INT = BigInteger.valueOf(LONG); 685 | 686 | static int[] from(BigInteger b, final int maxWidth) { 687 | int n = Math.min((b.bitLength() >>> 5) + 1, maxWidth); 688 | final int[] ints = new int[n]; 689 | while(0 < n) { 690 | ints[--n] = b.and(BIG_INT).intValue(); 691 | b = b.shiftRight(32); 692 | } 693 | return (0 < ints.length && ints[0] == 0) ? stripLeadingZeroes(ints) : ints; 694 | } 695 | 696 | static int[] from(final byte[] bytes, final int[] maxValue) { 697 | int len = bytes.length; 698 | 699 | if(len == 0) 700 | return ZERO; 701 | 702 | int skip; 703 | for (skip = 0; skip < len && bytes[skip] == 0; skip++) 704 | ; 705 | 706 | final int ints = Math.min(maxValue.length, ((len - skip) + 3) >>> 2); 707 | final int[] out = new int[ints]; 708 | int b = len - 1; 709 | for(int i = ints - 1; 0 <= i; i--) { 710 | out[i] = bytes[b--] & 0xff; 711 | int copy = Math.min(3, b - skip + 1); 712 | for(int j = 8; j <= (copy << 3); j += 8) 713 | out[i] |= ((bytes[b--] & 0xff) << j); 714 | } 715 | return out; 716 | } 717 | 718 | static int[] maxValue(final int maxWidth) { 719 | final int[] max = new int[maxWidth]; 720 | java.util.Arrays.fill(max, -1); 721 | return max; 722 | } 723 | } 724 | -------------------------------------------------------------------------------- /juint/src/test/java/io/nervous/juint/Properties.java: -------------------------------------------------------------------------------- 1 | package io.nervous.juint; 2 | 3 | import java.security.SecureRandom; 4 | import java.math.BigInteger; 5 | import java.util.Random; 6 | 7 | import org.junit.Test; 8 | import org.junit.Before; 9 | import org.junit.After; 10 | import org.junit.BeforeClass; 11 | 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | import static org.junit.Assert.assertArrayEquals; 16 | import static org.junit.Assert.assertEquals; 17 | import static org.junit.Assert.assertNotEquals; 18 | import static org.junit.Assert.assertTrue; 19 | import static org.junit.Assert.assertFalse; 20 | 21 | public abstract class Properties> { 22 | abstract T construct(int[] ints); 23 | abstract T construct(byte[] bytes); 24 | abstract T construct(BigInteger b); 25 | abstract T construct(long v); 26 | abstract T construct(String s, int radix); 27 | abstract int maxWidth (); 28 | 29 | T zero = construct(new int[0]); 30 | T max = construct(Arrays.maxValue(maxWidth())); 31 | T one = fromInt(1), two = fromInt(2), x, y, rand1w, xcopy, ycopy, rand1wcopy; 32 | BigInteger xb, yb, rand1wb; 33 | 34 | static int SAMPLE_BIG; 35 | static int SAMPLE_MED; 36 | static int SAMPLE_SMALL; 37 | 38 | @BeforeClass 39 | public static void init() { 40 | String v = System.getenv("JUINT_SAMPLE_FACTOR"); 41 | double i = v == null ? 1.0 : Double.parseDouble(v); 42 | SAMPLE_BIG = (int)(4096 * i); 43 | SAMPLE_MED = (int)(2048 * i); 44 | SAMPLE_SMALL = (int)(1024 * i); 45 | System.out.println("Max interations: " + SAMPLE_BIG); 46 | } 47 | 48 | Random rnd = new SecureRandom(); 49 | 50 | @Before 51 | public void cycle() { 52 | x = random(); 53 | y = random(); 54 | rand1w = random(1); 55 | 56 | xb = x .toBigInteger(); 57 | yb = y .toBigInteger(); 58 | rand1wb = rand1w.toBigInteger(); 59 | 60 | xcopy = construct(x.toIntArray()); 61 | ycopy = construct(y.toIntArray()); 62 | rand1wcopy = construct(rand1w.toIntArray()); 63 | } 64 | 65 | @After 66 | public void immutable() { 67 | assertEquals(x, xcopy); 68 | assertEquals(y, ycopy); 69 | assertEquals(rand1w, rand1wcopy); 70 | } 71 | 72 | T fromInt(int i) { return i == 0 ? zero : construct(new int[]{i}); } 73 | T random() { return random(Math.abs(rnd.nextInt() % (maxWidth()+1))); } 74 | 75 | private static final int SMALL_MAX = Short.MAX_VALUE; 76 | 77 | int[] randomints(int n) { 78 | if(n == 0) 79 | return new int[0]; 80 | if(rnd.nextFloat() < 0.025) 81 | return Arrays.maxValue(n); 82 | 83 | int[] ints = new int[n]; 84 | final boolean small = rnd.nextFloat() < 0.25; 85 | do { 86 | ints[0] = small ? rnd.nextInt(SMALL_MAX) : rnd.nextInt(); 87 | } while(ints[0] == 0); 88 | for(int i = 1; i < n; i++) 89 | ints[i] = small ? rnd.nextInt(SMALL_MAX) : rnd.nextInt(); 90 | return ints; 91 | } 92 | 93 | T random(int n) { 94 | return construct(randomints(n)); 95 | } 96 | 97 | T randomNonZero() { 98 | T x; 99 | do { 100 | x = random(); 101 | } while(x.isZero()); 102 | return x; 103 | } 104 | 105 | void eq(BigInteger a, T b) { 106 | assertTrue( 107 | ("UInt<" + java.util.Arrays.toString(b.ints) + 108 | "> != Big<" + java.util.Arrays.toString(construct(a).ints) + 109 | ">\n(UInt<" + b + "> != Big<" + a + ">)"), 110 | b.equals(a)); 111 | } 112 | 113 | BigInteger big(T o) { return o.toBigInteger(); } 114 | BigInteger big(long i) { return BigInteger.valueOf(i); } 115 | BigInteger big(int[] ints) { 116 | BigInteger out = BigInteger.ZERO; 117 | for(int i = 0; i < ints.length; i++) { 118 | out = out.shiftLeft(32).or(BigInteger.valueOf(ints[i] & Arrays.LONG)); 119 | } 120 | return out; 121 | } 122 | 123 | BigInteger trunc(BigInteger u) { 124 | BigInteger mask = BigInteger.ONE.shiftLeft(32 * maxWidth()) 125 | .subtract(BigInteger.ONE); 126 | return u.and(mask); 127 | } 128 | 129 | @Test 130 | public void longCtorInvariant() { 131 | assertEquals(construct(new int[]{-1, -1}), construct(-1L)); 132 | assertEquals(zero, construct(0L)); 133 | assertEquals(one, construct(1L)); 134 | } 135 | 136 | @Test 137 | public void bytesCtor() { 138 | for(int i = 0; i < SAMPLE_MED; i++, cycle()) 139 | assertEquals(x, construct(x.toByteArray())); 140 | } 141 | 142 | @Test 143 | public void bytesCtorTruncate() { 144 | for(int i = 0; i < SAMPLE_BIG; i++) { 145 | BigInteger b = new BigInteger(32 * maxWidth() * 2, rnd); 146 | eq(trunc(b), construct(b.toByteArray())); 147 | } 148 | } 149 | 150 | @Test 151 | public void isZeroInvariant() { 152 | assertTrue (zero.isZero()); 153 | assertFalse (one .isZero()); 154 | assertFalse (two .isZero()); 155 | assertTrue (one .subtract(one).isZero()); 156 | assertFalse (max .isZero()); 157 | } 158 | 159 | @Test 160 | public void notInvariant() { 161 | assertEquals(zero.not(), max); 162 | assertEquals(zero.not().not(), zero); 163 | 164 | assertEquals(max.not(), zero); 165 | assertEquals(max.not().not(), max); 166 | 167 | assertEquals(one.not(), max.subtract(one)); 168 | assertEquals(one.not().not(), one); 169 | 170 | int[] exp = Arrays.maxValue(maxWidth()); 171 | exp[exp.length - 1] = ~1; 172 | exp[exp.length - 2] = ~-1; 173 | assertEquals(construct(exp), construct(new int[]{-1, 1}).not()); 174 | } 175 | 176 | @Test 177 | public void bitLength() { 178 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) 179 | assertEquals(xb.bitLength(), x.bitLength()); 180 | } 181 | 182 | @Test 183 | public void not() { 184 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) 185 | eq(trunc(xb.not()), x.not()); 186 | } 187 | 188 | @Test 189 | public void getLowestSetBit() { 190 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) 191 | assertEquals(xb.getLowestSetBit(), x.getLowestSetBit()); 192 | } 193 | 194 | @Test 195 | public void shiftLeftInvariant() { 196 | assertEquals(zero, zero.shiftLeft(0)); 197 | assertEquals(one, one .shiftLeft(0)); 198 | assertEquals(construct(new int[]{1, 0}), one .shiftLeft(32)); 199 | } 200 | 201 | @Test 202 | public void shiftRightInvariant() { 203 | assertEquals(zero, zero.shiftRight(0)); 204 | assertEquals(one, one .shiftRight(0)); 205 | assertEquals(zero, one .shiftRight(32)); 206 | assertEquals(one, construct(new int[]{1, 0}).shiftRight(32)); 207 | } 208 | 209 | @Test 210 | public void shiftLeftLimit() { 211 | int[] ints = new int[maxWidth()]; 212 | ints[0] = -1; 213 | 214 | T a = construct(ints); 215 | for(int places = 0; places <= 32; places++) 216 | eq(trunc(big(a).shiftLeft(places)), a.shiftLeft(places)); 217 | } 218 | 219 | @Test 220 | public void shiftLeftWord() { 221 | for(int i = 0; i < SAMPLE_SMALL; i++, cycle()) 222 | for(int places = 0; places <= x.ints.length * 32; places += 32) 223 | eq(trunc(xb.shiftLeft(places)), x.shiftLeft(places)); 224 | } 225 | 226 | @Test 227 | public void shiftRightWord() { 228 | for(int i = 0; i < SAMPLE_SMALL; i++, cycle()) 229 | for(int places = 0; places <= x.ints.length * 32; places += 32) 230 | eq(xb.shiftRight(places), x.shiftRight(places)); 231 | } 232 | 233 | @Test 234 | public void shiftLeft() { 235 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) { 236 | for(int places = 0; places <= x.ints.length * 32; places++) 237 | eq(trunc(xb.shiftLeft(places)), x.shiftLeft(places)); 238 | } 239 | } 240 | 241 | @Test 242 | public void shiftRight() { 243 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) 244 | for(int places = 0; places <= x.ints.length * 32; places++) 245 | eq(xb.shiftRight(places), x.shiftRight(places)); 246 | } 247 | 248 | @Test 249 | public void shift() { 250 | for(int i = 0; i < SAMPLE_SMALL / 2; i++, cycle()) { 251 | int w = x.ints.length * 32; 252 | for(int places = -w; places <= w; places++) { 253 | eq(trunc(xb.shiftLeft (places)), x.shiftLeft(places)); 254 | eq(trunc(xb.shiftRight(places)), x.shiftRight(places));; 255 | } 256 | } 257 | } 258 | 259 | @Test 260 | public void multiplyInvariant() { 261 | assertEquals(zero, one .multiply(zero)); 262 | assertEquals(zero, zero.multiply(one)); 263 | assertEquals(zero, zero.multiply(zero)); 264 | assertEquals(one, one .multiply(one)); 265 | 266 | assertEquals(zero, max .multiply(zero)); 267 | assertEquals(zero, zero.multiply(max)); 268 | assertEquals(max, max .multiply(one)); 269 | assertEquals(max, one .multiply(max)); 270 | 271 | assertEquals(two, two.multiply(one)); 272 | assertEquals(two, one.multiply(two)); 273 | 274 | assertEquals(max.subtract(one), max.multiply(two)); 275 | assertEquals(max.subtract(one), two.multiply(max)); 276 | 277 | T half = max.divide(fromInt(2)); 278 | assertEquals(max.subtract(one), half.multiply(two)); 279 | assertEquals(max.subtract(one), two .multiply(half)); 280 | assertEquals(one, half.multiply(half)); 281 | } 282 | 283 | @Test 284 | public void multiply1w() { 285 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) 286 | eq(trunc(xb.multiply(rand1wb)), x.multiply(rand1w)); 287 | } 288 | 289 | @Test 290 | public void multiply() { 291 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) { 292 | eq(trunc(xb.multiply(yb)), x.multiply(y)); 293 | } 294 | } 295 | 296 | @Test 297 | public void multiplySquare() { 298 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) { 299 | eq(trunc(xb.multiply(xb)), x.multiply(x)); 300 | } 301 | } 302 | 303 | @Test 304 | public void mulmod() { 305 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) { 306 | T mod = randomNonZero(); 307 | eq(xb.multiply(yb).mod(big(mod)), x.mulmod(y, mod)); 308 | } 309 | } 310 | 311 | @Test(expected=ArithmeticException.class) 312 | public void powNegative() { 313 | one.pow(-1); 314 | } 315 | 316 | @Test 317 | public void powInvariant() { 318 | assertEquals(one, one .pow(0)); 319 | assertEquals(zero, zero.pow(1)); 320 | assertEquals(two, two .pow(1)); 321 | assertEquals(one, two .pow(0)); 322 | assertEquals(fromInt(4), two .pow(2)); 323 | } 324 | 325 | @Test 326 | public void powSmall() { 327 | for(int i = 0; i < maxWidth() * 32; i++) 328 | eq(big(two).pow(i), two.pow(i)); 329 | eq(trunc(big(two).pow(256)), two.pow(256)); 330 | 331 | T six = fromInt(6); 332 | for(int i = 0; i <= maxWidth() * 32; i++) 333 | eq(trunc(big(six).pow(i)), six.pow(i)); 334 | } 335 | 336 | @Test 337 | public void powConstrained() { 338 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) { 339 | int exp = rnd.nextInt(300); 340 | eq(trunc(xb.pow(exp)), x.pow(exp)); 341 | } 342 | } 343 | 344 | @Test 345 | public void fromArrayTruncate() { 346 | for(int i = 0; i < SAMPLE_SMALL; i++) { 347 | int[] ints = randomints(maxWidth() * 2); 348 | eq(trunc(big(ints)), construct(ints)); 349 | } 350 | } 351 | 352 | @Test(expected=NumberFormatException.class) 353 | public void fromStringNeg() { construct("-1", 10); } 354 | @Test(expected=NumberFormatException.class) 355 | public void fromStringEmpty() { construct("", 10); } 356 | @Test(expected=NumberFormatException.class) 357 | public void fromStringEmpty2() { construct("+", 10); } 358 | @Test(expected=NumberFormatException.class) 359 | public void fromStringInvalid() { construct("_", 10); } 360 | 361 | @Test 362 | public void fromStringInvariant() { 363 | assertEquals(zero, construct("0", 2)); 364 | assertEquals(one, construct("1", 2)); 365 | assertEquals(two, construct("10", 2)); 366 | assertEquals(fromInt(1024), construct("+001024", 10)); 367 | } 368 | 369 | @Test 370 | public void fromString10() { 371 | for(int i = 0; i < SAMPLE_MED; i++, cycle()) { 372 | assertEquals(x, construct(x.toString(), 10)); 373 | } 374 | } 375 | 376 | @Test 377 | public void fromString() { 378 | for(int i = 0; i < SAMPLE_MED; i++, cycle()) 379 | for(int radix = Character.MIN_RADIX; radix < Character.MAX_RADIX; radix++) 380 | assertEquals(x, construct(x.toString(radix), radix)); 381 | } 382 | 383 | @Test 384 | public void fromStringTruncate() { 385 | for(int i = 0; i < SAMPLE_MED; i++) { 386 | BigInteger b = new BigInteger(32 * maxWidth() * 2, rnd); 387 | for(int radix = Character.MIN_RADIX; radix < Character.MAX_RADIX; radix++) 388 | eq(trunc(b), construct(b.toString(radix), radix)); 389 | } 390 | } 391 | 392 | @Test 393 | public void toStringDefaultsRadix() { 394 | for(int i = -5; i < Character.MIN_RADIX; i++) 395 | assertEquals(x.toString(), x.toString(i)); 396 | for(int i = 1; i < 10; i++) 397 | assertEquals(x.toString(), x.toString(Character.MAX_RADIX + i)); 398 | } 399 | 400 | @Test 401 | public void toStringDefaultRadix() { 402 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) 403 | assertEquals(xb.toString(), x.toString()); 404 | } 405 | 406 | @Test 407 | public void toStringRadix() { 408 | for(int i = 0; i < SAMPLE_MED; i++, cycle()) 409 | for(int radix = Character.MIN_RADIX; radix < Character.MAX_RADIX; radix++) 410 | assertEquals(xb.toString(radix), x.toString(radix)); 411 | } 412 | 413 | @Test(expected=ArithmeticException.class) 414 | public void divmodZero() { 415 | one.divmod(zero); 416 | } 417 | 418 | @Test(expected=ArithmeticException.class) 419 | public void divideZero() { 420 | one.divide(zero); 421 | } 422 | 423 | @Test(expected=ArithmeticException.class) 424 | public void modZero() { 425 | one.mod(zero); 426 | } 427 | 428 | public void divmodInvariant() { 429 | for(int i = 0; i < SAMPLE_MED; i++, cycle()) { 430 | T[] qr = x.divmod(x); 431 | assertEquals(one, qr[0]); 432 | assertEquals(zero, qr[1]); 433 | } 434 | } 435 | 436 | @Test 437 | public void divmod1w() { 438 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) { 439 | BigInteger[] bqr = xb.divideAndRemainder(rand1wb); 440 | T[] uqr = x.divmod(rand1w); 441 | eq(bqr[0], uqr[0]); 442 | eq(bqr[1], uqr[1]); 443 | 444 | eq(bqr[0], x.divide(rand1w)); 445 | } 446 | } 447 | 448 | @Test 449 | public void divmod2w() { 450 | if(2 <= maxWidth()) 451 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) { 452 | T b = random(2); 453 | BigInteger[] bqr = xb.divideAndRemainder(big(b)); 454 | T[] uqr = x.divmod(b); 455 | eq(bqr[0], uqr[0]); 456 | eq(bqr[1], uqr[1]); 457 | 458 | eq(bqr[0], x.divide(b)); 459 | } 460 | } 461 | 462 | @Test 463 | public void divmod() { 464 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) { 465 | T b = randomNonZero(); 466 | BigInteger[] bqr = xb.divideAndRemainder(big(b)); 467 | T[] uqr = x.divmod(b); 468 | 469 | eq(bqr[0], uqr[0]); 470 | eq(bqr[1], uqr[1]); 471 | 472 | assertEquals(uqr[0], x.divide(b)); 473 | assertEquals(uqr[1], x.mod(b)); 474 | 475 | if(!x.equals(zero)) { 476 | bqr = big(b).divideAndRemainder(xb); 477 | uqr = b.divmod(x); 478 | 479 | eq(bqr[0], uqr[0]); 480 | eq(bqr[1], uqr[1]); 481 | 482 | assertEquals(uqr[0], b.divide(x)); 483 | assertEquals(uqr[1], b.mod(x)); 484 | } 485 | } 486 | } 487 | 488 | @Test 489 | public void divmodPowersOfTwo() { 490 | for(int i = 0; i < SAMPLE_MED; i++, cycle()) 491 | for(int exp = 1; exp < maxWidth() * 32; exp++) { 492 | T b = two.pow(exp); 493 | BigInteger[] bqr = xb.divideAndRemainder(big(b)); 494 | T[] uqr = x.divmod(b); 495 | 496 | eq(bqr[0], uqr[0]); 497 | eq(bqr[1], uqr[1]); 498 | } 499 | } 500 | 501 | @Test 502 | public void divmodTiny() { 503 | for(int a = 0; a < SAMPLE_SMALL; a++) { 504 | T av = fromInt(a); 505 | for(int b = 1; b < 25; b++) { 506 | T bv = fromInt(b); 507 | BigInteger[] bqr = big(av).divideAndRemainder(big(bv)); 508 | T[] uqr = av.divmod(bv); 509 | 510 | eq(bqr[0], uqr[0]); 511 | eq(bqr[1], uqr[1]); 512 | } 513 | } 514 | } 515 | 516 | @Test 517 | public void divmodClose() { 518 | for(int i = 0; i < SAMPLE_SMALL; i++, cycle()) { 519 | T xc = x; 520 | 521 | for(int j = 0; j < 10 && !xc.isZero(); j++) { 522 | BigInteger[] bqr = xb.divideAndRemainder(big(xc)); 523 | T[] uqr = x.divmod(xc); 524 | 525 | eq(bqr[0], uqr[0]); 526 | eq(bqr[1], uqr[1]); 527 | 528 | xc = xc.dec(); 529 | } 530 | } 531 | } 532 | 533 | @Test 534 | public void toFromBigInteger() { 535 | for(int i = 0; i < SAMPLE_MED; i++, cycle()) { 536 | BigInteger b = new BigInteger(32 * maxWidth(), rnd); 537 | assertEquals(b, construct(b).toBigInteger()); 538 | } 539 | } 540 | 541 | @Test 542 | public void compareTo() { 543 | for(int i = 0; i < SAMPLE_MED; i++, cycle()) { 544 | assertEquals(xb.compareTo(yb), x.compareTo(y)); 545 | assertEquals(yb.compareTo(xb), y.compareTo(x)); 546 | } 547 | } 548 | 549 | @Test 550 | public void equalsInvariant() { 551 | assertEquals (one, one); 552 | assertEquals (zero, zero); 553 | assertNotEquals(zero, one); 554 | assertNotEquals(one, zero); 555 | assertEquals (max, max); 556 | } 557 | 558 | @Test 559 | public void equalsBigInteger() { 560 | assertTrue (one .equals(big(one))); 561 | assertFalse(zero.equals(big(one))); 562 | assertTrue (max .equals(big(max))); 563 | assertTrue (zero.equals(big(max.inc()))); 564 | assertFalse(max .equals(big(one))); 565 | } 566 | 567 | @Test 568 | public void and() { 569 | for(int i = 0; i < SAMPLE_MED; i++, cycle()) 570 | eq(xb.and(yb), x.and(y)); 571 | } 572 | 573 | @Test 574 | public void or() { 575 | for(int i = 0; i < SAMPLE_MED; i++, cycle()) 576 | eq(xb.or(yb), x.or(y)); 577 | } 578 | 579 | @Test 580 | public void xor() { 581 | for(int i = 0; i < SAMPLE_MED; i++, cycle()) 582 | eq(xb.xor(yb), x.xor(y)); 583 | } 584 | 585 | @Test(expected=ArithmeticException.class) 586 | public void setBitNegative() { 587 | zero.setBit(-1); 588 | } 589 | 590 | @Test 591 | public void setBitInvariant() { 592 | assertEquals(one, zero.setBit(0)); 593 | assertEquals(construct(new int[]{1, 0}), zero.setBit(32)); 594 | assertEquals(zero, zero.setBit(maxWidth() * 32)); 595 | } 596 | 597 | @Test 598 | public void setBit() { 599 | final int maxWidth = maxWidth(); 600 | for(int i = 0; i < SAMPLE_SMALL; i++, cycle()) { 601 | for(int bit = 0; bit < maxWidth * 32; bit++) 602 | eq(xb.setBit(bit), x.setBit(bit)); 603 | eq(trunc(xb.setBit(maxWidth * 32)), x.setBit(maxWidth * 32)); 604 | eq(trunc(xb.setBit(maxWidth * 64)), x.setBit(maxWidth * 64)); 605 | } 606 | } 607 | 608 | @Test(expected=ArithmeticException.class) 609 | public void clearBitNegative() { 610 | zero.clearBit(-1); 611 | } 612 | 613 | @Test 614 | public void clearBitInvariant() { 615 | assertEquals(zero, one .clearBit(0)); 616 | assertEquals(zero, zero.clearBit(0)); 617 | assertEquals(zero, two .clearBit(1)); 618 | assertEquals(zero, construct(new int[]{1, 0}).clearBit(32)); 619 | int[] a = new int[maxWidth()]; 620 | a[0] = 1 << 31; 621 | assertEquals(zero, construct(a).clearBit((maxWidth() * 32) - 1)); 622 | } 623 | 624 | @Test 625 | public void clearBit() { 626 | for(int i = 0; i < SAMPLE_MED; i++, cycle()) { 627 | for(int bit = 0; bit < x.ints.length * 32; bit++) 628 | eq(xb.clearBit(bit), x.clearBit(bit)); 629 | eq(xb.clearBit(x.ints.length * 32), x.clearBit(x.ints.length * 32)); 630 | eq(xb.clearBit(x.ints.length * 64), x.clearBit(x.ints.length * 64)); 631 | } 632 | } 633 | 634 | @Test(expected=ArithmeticException.class) 635 | public void flipBitNegative() { 636 | zero.flipBit(-1); 637 | } 638 | 639 | @Test 640 | public void flipBitInvariant() { 641 | assertEquals(one, zero.flipBit(0)); 642 | assertEquals(zero, one .flipBit(0)); 643 | assertEquals(zero, two .flipBit(1)); 644 | assertEquals(two, zero.flipBit(1)); 645 | } 646 | 647 | @Test 648 | public void flipBit() { 649 | int maxWidth = maxWidth(); 650 | for(int i = 0; i < SAMPLE_SMALL; i++, cycle()) { 651 | for(int bit = 0; bit < maxWidth * 32; bit++) 652 | eq(xb.flipBit(bit), x.flipBit(bit)); 653 | eq(trunc(xb.flipBit(maxWidth * 32)), x.flipBit(maxWidth * 32)); 654 | eq(trunc(xb.flipBit(maxWidth * 64)), x.flipBit(maxWidth * 64)); 655 | } 656 | } 657 | 658 | @Test(expected=ArithmeticException.class) 659 | public void testBitNegative() { 660 | zero.testBit(-1); 661 | } 662 | 663 | @Test 664 | public void testBit() { 665 | int maxWidth = maxWidth(); 666 | for(int i = 0; i < SAMPLE_SMALL; i++, cycle()) { 667 | for(int bit = 0; bit < maxWidth * 32; bit++) 668 | assertEquals(xb.testBit(bit), x.testBit(bit)); 669 | assertEquals(xb.testBit(maxWidth * 32), x.testBit(maxWidth * 32)); 670 | assertEquals(xb.testBit(maxWidth * 64), x.testBit(maxWidth * 64)); 671 | } 672 | } 673 | 674 | @Test 675 | public void add() { 676 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) 677 | eq(trunc(xb.add(yb)), x.add(y)); 678 | } 679 | 680 | @Test 681 | public void addmod() { 682 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) { 683 | T mod = randomNonZero(); 684 | eq(xb.add(yb).mod(big(mod)), x.addmod(y, mod)); 685 | } 686 | } 687 | 688 | @Test 689 | public void addSubtractInvariant() { 690 | assertEquals(zero, zero.subtract(zero)); 691 | assertEquals(max, zero.subtract(one)); 692 | assertEquals(one, one .subtract(zero)); 693 | assertEquals(zero, zero.add(zero)); 694 | assertEquals(one, zero.add(one)); 695 | assertEquals(zero, one .subtract(one)); 696 | assertEquals(max, one .subtract(one).subtract(one)); 697 | assertEquals(max, one .subtract(two)); 698 | assertEquals( 699 | max.subtract(one), 700 | one.subtract(two).subtract(one)); 701 | assertEquals( 702 | max.subtract(one), 703 | one.subtract(fromInt(3))); 704 | assertEquals(zero, max .add(one)); 705 | } 706 | 707 | @Test 708 | public void subtract() { 709 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) { 710 | eq(trunc(xb.subtract(yb)), x.subtract(y)); 711 | eq(trunc(yb.subtract(xb)), y.subtract(x)); 712 | } 713 | } 714 | 715 | @Test 716 | public void incDecInvariant() { 717 | assertEquals(zero, max.inc()); 718 | assertEquals(max, zero.dec()); 719 | assertEquals(construct(new int[]{1, 0}), construct(new int[]{-1}).inc()); 720 | assertEquals(construct(new int[]{-1}), construct(new int[]{1, 0}).dec()); 721 | assertEquals(construct(new int[]{-2, -1}), construct(new int[]{-1, 0}).dec()); 722 | } 723 | 724 | @Test 725 | public void inc() { 726 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) { 727 | eq(trunc(xb.add(BigInteger.ONE)), x.inc()); 728 | eq(trunc(xb.add(BigInteger.valueOf(2))), x.inc().inc()); 729 | } 730 | } 731 | 732 | @Test 733 | public void dec() { 734 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) { 735 | eq(trunc(xb.subtract(BigInteger.ONE)), x.dec()); 736 | eq(trunc(xb.subtract(BigInteger.valueOf(2))), x.dec().dec()); 737 | } 738 | } 739 | 740 | @Test 741 | public void toByteArray() { 742 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) { 743 | byte[] exp = xb.toByteArray(); 744 | assertArrayEquals( 745 | Arrays.stripLeadingZeroes(exp), 746 | x.toByteArray()); 747 | } 748 | } 749 | 750 | @Test 751 | public void intValue() { 752 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) 753 | assertEquals(xb.intValue(), x.intValue()); 754 | } 755 | 756 | @Test(expected=ArithmeticException.class) 757 | public void intValueExactThrows() { 758 | construct(-1).intValueExact(); 759 | } 760 | 761 | @Test 762 | public void longValue() { 763 | for(int i = 0; i < SAMPLE_MED; i++, cycle()) 764 | assertEquals(xb.longValue(), x.longValue()); 765 | } 766 | 767 | @Test 768 | public void shortValue() { 769 | for(int i = 0; i < SAMPLE_MED; i++, cycle()) 770 | assertEquals(xb.shortValue(), x.shortValue()); 771 | } 772 | 773 | @Test 774 | public void byteValue() { 775 | for(int i = 0; i < SAMPLE_MED; i++, cycle()) 776 | assertEquals(xb.byteValue(), x.byteValue()); 777 | } 778 | 779 | @Test(expected=ArithmeticException.class) 780 | public void longValueExactThrows() { 781 | construct(new int[]{-1, -1, -1}).longValueExact(); 782 | } 783 | 784 | @Test(expected=ArithmeticException.class) 785 | public void shortValueExactThrows() { 786 | construct(new int[]{Short.MAX_VALUE + 1}).shortValueExact(); 787 | } 788 | 789 | @Test(expected=ArithmeticException.class) 790 | public void byteValueExactThrows() { 791 | construct(new int[]{Byte.MAX_VALUE + 1}).byteValueExact(); 792 | } 793 | 794 | @Test 795 | public void doubleValue() { 796 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) 797 | assertEquals(xb.doubleValue(), x.doubleValue(), 0); 798 | } 799 | 800 | @Test 801 | public void floatValue() { 802 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) 803 | assertEquals(xb.floatValue(), x.floatValue(), 0); 804 | } 805 | 806 | @Test 807 | public void testHashCode() { 808 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) 809 | assertEquals(xb.hashCode(), x.hashCode()); 810 | } 811 | 812 | @Test 813 | public void minMax() { 814 | for(int i = 0; i < SAMPLE_BIG; i++, cycle()) { 815 | eq(xb.max(yb), x.max(y)); 816 | eq(xb.min(yb), x.min(y)); 817 | } 818 | } 819 | } 820 | --------------------------------------------------------------------------------