From 7609332671d8251734cba851c475f23b88820e87 Mon Sep 17 00:00:00 2001 From: SomeGuyWhoLovesCoding Date: Sat, 9 Nov 2024 08:56:58 -0500 Subject: [PATCH] Implement Int512 Yeah I'm done --- src/macro/macroApi.ml | 2 +- src/syntax/reification.ml | 2 + src/typing/typer.ml | 35 ++- std/haxe/Int128.hx | 2 +- std/haxe/Int128Helper.hx | 2 +- std/haxe/Int256.hx | 6 +- std/haxe/Int256Helper.hx | 4 +- std/haxe/Int512.hx | 467 ++++++++++++++++++++++++++++++++++++++ std/haxe/Int512Helper.hx | 197 ++++++++++++++++ 9 files changed, 706 insertions(+), 11 deletions(-) create mode 100644 std/haxe/Int512.hx create mode 100644 std/haxe/Int512Helper.hx diff --git a/src/macro/macroApi.ml b/src/macro/macroApi.ml index ec2b9424d16..ac200bcf08b 100644 --- a/src/macro/macroApi.ml +++ b/src/macro/macroApi.ml @@ -672,7 +672,7 @@ let decode_const c = | 0, [s;suffix] -> let decoded_suffix = opt decode_string suffix in (match decoded_suffix with - | None | Some "i32" | Some "i64" | Some "i128" | Some "i256" | Some "u32" -> + | None | Some "i32" | Some "i64" | Some "i128" | Some "i256" | Some "i512" | Some "u32" -> Int (decode_string s, decoded_suffix) | Some other -> raise Invalid_expr) diff --git a/src/syntax/reification.ml b/src/syntax/reification.ml index 03474416c89..f5270aca70e 100644 --- a/src/syntax/reification.ml +++ b/src/syntax/reification.ml @@ -370,6 +370,8 @@ let reify in_macro = expr "EConst" [mk_enum "Constant" "CInt" [ (EConst(String (s, SDoubleQuotes)),(pos e1)); (EConst(String ("i128", SDoubleQuotes)),(pos e1)) ] (pos e1)] | EConst (Int (s, Some "i256")) -> expr "EConst" [mk_enum "Constant" "CInt" [ (EConst(String (s, SDoubleQuotes)),(pos e1)); (EConst(String ("i256", SDoubleQuotes)),(pos e1)) ] (pos e1)] + | EConst (Int (s, Some "i512")) -> + expr "EConst" [mk_enum "Constant" "CInt" [ (EConst(String (s, SDoubleQuotes)),(pos e1)); (EConst(String ("i512", SDoubleQuotes)),(pos e1)) ] (pos e1)] | _ -> (ECall ((efield ((efield ((efield ((EConst (Ident "haxe"),p),"macro"),p),"Context"),p),"makeExpr"),p),[e1; to_enc_pos (pos e1)]),p) end diff --git a/src/typing/typer.ml b/src/typing/typer.ml index 7221afc675a..edd7fcbdb25 100644 --- a/src/typing/typer.ml +++ b/src/typing/typer.ml @@ -1856,7 +1856,7 @@ and type_expr ?(mode=MGet) ctx (e,p) (with_type:WithType.t) = let arg_high = EConst (Int (Int64.to_string(high), Some "i64")), p in let arg_low = EConst (Int (Int64.to_string(low), Some "i64")), p in let call = ECall (field, [ arg_high; arg_low ]), p in - type_expr ctx call with_type + type_expr ctx call with_type | "i256" -> if String.length s > 66 && String.sub s 0 2 = "0x" then raise_typing_error "Invalid hexadecimal integer" p; @@ -1866,14 +1866,43 @@ and type_expr ?(mode=MGet) ctx (e,p) (with_type:WithType.t) = let low_low = Int64.of_string (String.sub s 50 66) in let ident = EConst (Ident "haxe"), p in - let field = efield ((efield (ident, "Int128"), p), "make"), p in + let field = efield ((efield (ident, "Int256"), p), "make"), p in + let field2 = efield ((efield (ident, "Int128"), p), "make"), p in let arg_high_high = EConst (Int (Int64.to_string(high_high), Some "i64")), p in let arg_high_low = EConst (Int (Int64.to_string(high_low), Some "i64")), p in let arg_low_high = EConst (Int (Int64.to_string(low_high), Some "i64")), p in let arg_low_low = EConst (Int (Int64.to_string(low_low), Some "i64")), p in - let call = ECall ( field, [ ECall ( field, [ arg_high_high, arg_high_low ] ), ECall ( field, [ arg_low_high, arg_low_low ] ) ]), p in + let call = ECall ( field, [ ECall ( field2, [ arg_high_high, arg_high_low ] ), ECall ( field2, [ arg_low_high, arg_low_low ] ) ]), p in + type_expr ctx call with_type + | "i512" -> + if String.length s > 130 && String.sub s 0 2 = "0x" then raise_typing_error "Invalid hexadecimal integer" p; + + let high_high_high = Int64.of_string (String.sub s 2 18) in + let high_high_low = Int64.of_string (String.sub s 20 34) in + let high_low_high = Int64.of_string (String.sub s 34 50) in + let high_low_low = Int64.of_string (String.sub s 50 66) in + let low_high_high = Int64.of_string (String.sub s 66 82) in + let low_high_low = Int64.of_string (String.sub s 82 98) in + let low_low_high = Int64.of_string (String.sub s 98 114) in + let low_low_low = Int64.of_string (String.sub s 114 130) in + + let ident = EConst (Ident "haxe"), p in + let field = efield ((efield (ident, "Int512"), p), "make"), p in + let field2 = efield ((efield (ident, "Int256"), p), "make"), p in + let field3 = efield ((efield (ident, "Int128"), p), "make"), p in + + let arg_high_high_high = EConst (Int (Int64.to_string(high_high_high), Some "i64")), p in + let arg_high_high_low = EConst (Int (Int64.to_string(high_high_low), Some "i64")), p in + let arg_high_low_high = EConst (Int (Int64.to_string(high_low_high), Some "i64")), p in + let arg_high_low_low = EConst (Int (Int64.to_string(high_low_low), Some "i64")), p in + let arg_low_high_high = EConst (Int (Int64.to_string(low_high_high), Some "i64")), p in + let arg_low_high_low = EConst (Int (Int64.to_string(low_high_low), Some "i64")), p in + let arg_low_low_high = EConst (Int (Int64.to_string(low_low_high), Some "i64")), p in + let arg_low_low_low = EConst (Int (Int64.to_string(low_low_low), Some "i64")), p in + + let call = ECall ( field, [ ECall ( field2, [ ECall ( field3, [ arg_high_high_high, arg_high_high_low ] ), ECall ( field3, [ arg_high_low_high, arg_high_low_low ] ) ] ), ECall ( field2, [ ECall ( field3, [ arg_low_high_high, arg_low_high_low ] ), ECall ( field3, [ arg_low_low_high, arg_low_low_low ] ) ] ) ] ), p in type_expr ctx call with_type | "u32" -> let check = ECheckType ((EConst (Int (s, None)), p), (make_ptp_th (mk_type_path ([],"UInt")) p)), p in diff --git a/std/haxe/Int128.hx b/std/haxe/Int128.hx index f48e0c84217..cbfd53ad88a 100644 --- a/std/haxe/Int128.hx +++ b/std/haxe/Int128.hx @@ -78,7 +78,7 @@ abstract Int128(__Int128) from __Int128 to __Int128 { public static inline function toInt64(x:Int128):Int64 { var res:Int64 = x.low; - // This is a completely different and overflow check because we're using Int128's. + // This is a completely different and overflow check because we're using Int256's. // It can only be triggered if you input an Int128 as the function parameter. if ((!isNeg(x) && Int128.isNeg(res)) || (x.high != x.low >> 63)) throw "Overflow"; diff --git a/std/haxe/Int128Helper.hx b/std/haxe/Int128Helper.hx index b2d5955ca43..ef59f98f95c 100644 --- a/std/haxe/Int128Helper.hx +++ b/std/haxe/Int128Helper.hx @@ -157,5 +157,5 @@ class Int128Helper { The maximum unsigned `Int32` value with the type `Int128`. This is handy for type comparison. */ - public static var maxValue32U:Int128 = Int128.make(0, -1); + public static var maxValue32U:Int128 = Int64.make(0, -1); } diff --git a/std/haxe/Int256.hx b/std/haxe/Int256.hx index 379d7f07254..029f218740e 100644 --- a/std/haxe/Int256.hx +++ b/std/haxe/Int256.hx @@ -80,7 +80,7 @@ abstract Int256(__Int256) from __Int256 to __Int256 { /** Returns an Int with the value of the Int256 `x`. - Throws an exception if `x` cannot be represented in 32 bits. + Throws an exception if `x` cannot be represented in 64 bits. **/ public static inline function toInt64(x:Int256):Int64 { return Int128.toInt64(x.low); @@ -88,7 +88,7 @@ abstract Int256(__Int256) from __Int256 to __Int256 { /** Returns an Int64 with the value of the Int256 `x`. - Throws an exception if `x` cannot be represented in 64 bits. + Throws an exception if `x` cannot be represented in 128 bits. **/ public static inline function toInt128(x:Int256):Int128 { var res:Int128 = x.low; @@ -159,7 +159,7 @@ abstract Int256(__Int256) from __Int256 to __Int256 { var neg = false; if (i.isNeg()) { neg = true; - // i = -i; cannot negate here as --170141183460469231731687303715884105728 = -170141183460469231731687303715884105728 + // i = -i; cannot negate here as --57896044618658097711785492504343953926634992332820282019728792003956564819968 = -57896044618658097711785492504343953926634992332820282019728792003956564819968 } var ten:Int256 = Int256.ofInt(10); while (i != 0) { diff --git a/std/haxe/Int256Helper.hx b/std/haxe/Int256Helper.hx index 6bcd4abd78a..b04ab95b57a 100644 --- a/std/haxe/Int256Helper.hx +++ b/std/haxe/Int256Helper.hx @@ -172,8 +172,8 @@ class Int256Helper { public static var minValue32:Int256 = ~maxValue32; /** - The maximum unsigned `Int32` value with the type `Int128`. + The maximum unsigned `Int32` value with the type `Int256`. This is handy for type comparison. */ - public static var maxValue32U:Int256 = Int128.make(0, -1); + public static var maxValue32U:Int256 = Int64.make(0, -1); } diff --git a/std/haxe/Int512.hx b/std/haxe/Int512.hx new file mode 100644 index 00000000000..b8c4f33a9eb --- /dev/null +++ b/std/haxe/Int512.hx @@ -0,0 +1,467 @@ +/* + * Copyright (C)2005-2019 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package haxe; + +using haxe.Int512; + +/** + A cross-platform signed 512-bit integer. + Int512 instances can be created from two 256-bit words using `Int512.make()`. + NOTE: This class is a beta. +**/ +#if flash +@:notNull +#end +@:transitive +abstract Int512(__Int512) from __Int512 to __Int512 { + private inline function new(x:__Int512) + this = x; + + /** + Makes a copy of `this` Int512. + **/ + public inline function copy():Int512 + return Int512.make(high, low); + + /** + Construct an Int512 from two 256-bit words `high` and `low`. + **/ + public static inline function make(high:Int256, low:Int256):Int512 + return new Int512(new __Int512(high, low)); + + /** + Returns an Int512 with the value of the Int `x`. + `x` is sign-extended to fill 256 bits. + **/ + @:from public static inline function ofInt(x:Int):Int512 + #if lua return make((x : Int32) >> 31, (x : Int32)); #else return make(x >> 31, x); #end + + /** + Returns an Int512 with the value of the Int64 `x`. + `x` is sign-extended to fill 256 bits. + **/ + @:from public static inline function ofInt64(x:Int64):Int512 + #if lua return make((x : Int64) >> 63, (x : Int64)); #else return make(x >> 63, x); #end + + /** + Returns an Int512 with the value of the 128 `x`. + `x` is sign-extended to fill 256 bits. + **/ + @:from public static inline function ofInt128(x:Int128):Int512 + #if lua return make((x : Int128) >> 127, (x : Int128)); #else return make(x >> 127, x); #end + + /** + Returns an Int512 with the value of the 128 `x`. + `x` is sign-extended to fill 256 bits. + **/ + @:from public static inline function ofInt256(x:Int256):Int512 + #if lua return make((x : Int256) >> 255, (x : Int256)); #else return make(x >> 255, x); #end + + /** + Returns an Int with the value of the Int512 `x`. + Throws an exception if `x` cannot be represented in 32 bits. + **/ + public static inline function toInt(x:Int512):Int { + return Int256.toInt(x.low); + } + + /** + Returns an Int with the value of the Int512 `x`. + Throws an exception if `x` cannot be represented in 32 bits. + **/ + public static inline function toInt64(x:Int512):Int64 { + return Int256.toInt64(x.low); + } + + /** + Returns an Int128 with the value of the Int512 `x`. + Throws an exception if `x` cannot be represented in 128 bits. + **/ + public static inline function toInt128(x:Int512):Int128 { + return Int256.toInt128(x.low); + } + + /** + Returns an Int64 with the value of the Int512 `x`. + Throws an exception if `x` cannot be represented in 256 bits. + **/ + public static inline function toInt256(x:Int512):Int256 { + var res:Int256 = x.low; + + // This is a completely different and overflow check because we're using Int512's. + // It can only be triggered if you input an Int512 as the function parameter. + if ((!isNeg(x) && Int512.isNeg(res)) || (x.high != x.low >> 255)) + throw "Overflow"; + + return res.copy(); + } + + @:deprecated('haxe.Int512.is() is deprecated. Use haxe.Int512.isInt512() instead') + inline public static function is(val:Dynamic):Bool { + return isInt512(val); + } + + /** + Returns whether the value `val` is of type `haxe.Int512` + **/ + inline public static function isInt512(val:Dynamic):Bool + return Std.isOfType(val, __Int512); + + /** + Returns `true` if `x` is less than zero. + **/ + public static inline function isNeg(x:Int512):Bool + return x.high < 0 && x.high.high < 0; + + /** + Returns `true` if `x` is exactly zero. + **/ + public static inline function isZero(x:Int512):Bool + return x == 0; + + /** + Compares `a` and `b` in signed mode. + Returns a negative value if `a < b`, positive if `a > b`, + or 0 if `a == b`. + **/ + public static inline function compare(a:Int512, b:Int512):Int256 { + var v = a.high - b.high; + v = if (v != 0) v else Int256.ucompare(a.low, b.low); + return a.high < 0 ? (b.high < 0 ? v : -1) : (b.high >= 0 ? v : 1); + } + + /** + Compares `a` and `b` in unsigned mode. + Returns a negative value if `a < b`, positive if `a > b`, + or 0 if `a == b`. + **/ + public static inline function ucompare(a:Int512, b:Int512):Int256 { + var v = Int256.ucompare(a.high, b.high); + return if (v != 0) v else Int256.ucompare(a.low, b.low); + } + + /** + Returns a signed decimal `String` representation of `x`. + **/ + public static inline function toStr(x:Int512):String + return x.toString(); + + function toString():String { + var i:Int512 = cast this; + if (i == 0) + return "0"; + var str = ""; + var neg = false; + if (i.isNeg()) { + neg = true; + // i = -i; cannot negate here as --57896044618658097711785492504343953926634992332820282019728792003956564819968 = -57896044618658097711785492504343953926634992332820282019728792003956564819968 + } + var ten:Int512 = Int512.ofInt(10); + while (i != 0) { + var r = i.divMod(ten); + if (r.modulus.isNeg()) { + str = -(r.modulus).low + str; + i = -r.quotient; + } else { + str = r.modulus.low + str; + i = r.quotient; + } + } + if (neg) + str = "-" + str; + return str; + } + + public static inline function parseString(sParam:String):Int512 { + return Int512Helper.parseString(sParam); + } + + public static inline function fromFloat(f:Float):Int512 { + return Int512Helper.fromFloat(f); + } + + /** + Performs signed integer divison of `dividend` by `divisor`. + Returns `{ quotient : Int512, modulus : Int512 }`. + **/ + public static function divMod(dividend:Int512, divisor:Int512):{quotient:Int512, modulus:Int512} { + // Handle special cases of 0 and 1 + if (divisor.high == 0) { + switch (Int256.toInt(divisor.low)) { + case 0: + throw "divide by zero"; + case 1: + return {quotient: dividend.copy(), modulus: 0}; + } + } + + var divSign = dividend.isNeg() != divisor.isNeg(); + + var modulus = dividend.isNeg() ? -dividend : dividend.copy(); + divisor = divisor.isNeg() ? -divisor : divisor; + + var quotient:Int512 = 0; + var mask:Int512 = 1; + + while (!divisor.isNeg()) { + var cmp = ucompare(divisor, modulus); + divisor <<= 1; + mask <<= 1; + if (cmp >= 0) + break; + } + + while (mask != 0) { + if (ucompare(modulus, divisor) >= 0) { + quotient |= mask; + modulus -= divisor; + } + mask >>>= 1; + divisor >>>= 1; + } + + if (divSign) + quotient = -quotient; + if (dividend.isNeg()) + modulus = -modulus; + + return { + quotient: quotient, + modulus: modulus + }; + } + + /** + Returns the negative of `x`. + **/ + @:op(-A) public static inline function neg(x:Int512):Int512 { + var high = ~x.high; + var low = -x.low; + if (low == 0) + high++; + return make(high, low); + } + + @:op(++A) private inline function preIncrement():Int512 { + this = copy(); + this.low++; + if (this.low == 0) + this.high++; + return cast this; + } + + @:op(A++) private inline function postIncrement():Int512 { + var ret = this; + preIncrement(); + return ret; + } + + @:op(--A) private inline function preDecrement():Int512 { + this = copy(); + if (this.low == 0) + this.high--; + this.low--; + return cast this; + } + + @:op(A--) private inline function postDecrement():Int512 { + var ret = this; + preDecrement(); + return ret; + } + + /** + Returns the sum of `a` and `b`. + **/ + @:op(A + B) public static inline function add(a:Int512, b:Int512):Int512 { + var high = a.high + b.high; + var low = a.low + b.low; + if (Int256.ucompare(low, a.low) < 0) + high++; + return make(high, low); + } + + /** + Returns `a` minus `b`. + **/ + @:op(A - B) public static inline function sub(a:Int512, b:Int512):Int512 { + var high = a.high - b.high; + var low = a.low - b.low; + if (Int256.ucompare(a.low, b.low) < 0) + high--; + return make(high, low); + } + + /** + Returns the product of `a` and `b`. + **/ + @:op(A * B) + public static #if !lua inline #end function mul(a:Int512, b:Int512):Int512 { + var mask = Int256Helper.maxValue128U; + var aLow = a.low & mask, aHigh = a.low >>> 128; + var bLow = b.low & mask, bHigh = b.low >>> 128; + var part00 = aLow * bLow; + var part10 = aHigh * bLow; + var part01 = aLow * bHigh; + var part11 = aHigh * bHigh; + var low = part00; + var high = part11 + (part01 >>> 128) + (part10 >>> 128); + part01 <<= 128; + low += part01; + if (Int256.ucompare(low, part01) < 0) + high++; + part10 <<= 128; + low += part10; + if (Int256.ucompare(low, part10) < 0) + high++; + high += a.low * b.high + a.high * b.low; + return make(high, low); + } + + /** + Returns the quotient of `a` divided by `b`. + **/ + @:op(A / B) public static inline function div(a:Int512, b:Int512):Int512 + return divMod(a, b).quotient; + + /** + Returns the modulus of `a` divided by `b`. + **/ + @:op(A % B) public static inline function mod(a:Int512, b:Int512):Int512 + return divMod(a, b).modulus; + + /** + Returns `true` if `a` is equal to `b`. + **/ + @:op(A == B) public static inline function eq(a:Int512, b:Int512):Bool + return a.high == b.high && a.low == b.low; + + /** + Returns `true` if `a` is not equal to `b`. + **/ + @:op(A != B) public static inline function neq(a:Int512, b:Int512):Bool + return a.high != b.high || a.low != b.low; + + @:op(A < B) private static inline function lt(a:Int512, b:Int512):Bool + return compare(a, b) < 0; + + @:op(A <= B) private static inline function lte(a:Int512, b:Int512):Bool + return compare(a, b) <= 0; + + @:op(A > B) private static inline function gt(a:Int512, b:Int512):Bool + return compare(a, b) > 0; + + @:op(A >= B) private static inline function gte(a:Int512, b:Int512):Bool + return compare(a, b) >= 0; + + /** + Returns the bitwise NOT of `a`. + **/ + @:op(~A) private static inline function complement(a:Int512):Int512 + return make(~a.high, ~a.low); + + /** + Returns the bitwise AND of `a` and `b`. + **/ + @:op(A & B) public static inline function and(a:Int512, b:Int512):Int512 + return make(a.high & b.high, a.low & b.low); + + /** + Returns the bitwise OR of `a` and `b`. + **/ + @:op(A | B) public static inline function or(a:Int512, b:Int512):Int512 + return make(a.high | b.high, a.low | b.low); + + /** + Returns the bitwise XOR of `a` and `b`. + **/ + @:op(A ^ B) public static inline function xor(a:Int512, b:Int512):Int512 + return make(a.high ^ b.high, a.low ^ b.low); + + /** + Returns `a` left-shifted by `b` bits. + **/ + @:op(A << B) public static inline function shl(a:Int512, b:Int):Int512 { + b &= 511; + return if (b == 0) a.copy() else if (b < 256) make((a.high << b) | (a.low >>> (256 - b)), a.low << b) else make(a.low << (b - 256), 0); + } + + /** + Returns `a` right-shifted by `b` bits in signed mode. + `a` is sign-extended. + **/ + @:op(A >> B) public static inline function shr(a:Int512, b:Int):Int512 { + b &= 511; + return if (b == 0) a.copy() else if (b < 256) make(a.high >> b, (a.high << (256 - b)) | (a.low >>> b)); else make(a.high >> 255, a.high >> (b - 256)); + } + + /** + Returns `a` right-shifted by `b` bits in unsigned mode. + `a` is padded with zeroes. + **/ + @:op(A >>> B) public static inline function ushr(a:Int512, b:Int):Int512 { + b &= 511; + return if (b == 0) a.copy() else if (b < 256) make(a.high >>> b, (a.high << (256 - b)) | (a.low >>> b)); else make(0, a.high >>> (b - 256)); + } + + public var high(get, never):Int256; + + private inline function get_high() + return this.high; + + private inline function set_high(x) + return this.high = x; + + public var low(get, never):Int256; + + private inline function get_low() + return this.low; + + private inline function set_low(x) + return this.low = x; +} + +/** + This typedef will fool `@:coreApi` into thinking that we are using + the same underlying type, even though it might be different on + specific platforms. +**/ +private typedef __Int512 = ___Int512; + +private class ___Int512 { + public var high:Int256; + public var low:Int256; + + public inline function new(high, low) { + this.high = high; + this.low = low; + } + + /** + We also define toString here to ensure we always get a pretty string + when tracing or calling `Std.string`. This tends not to happen when + `toString` is only in the abstract. + **/ + public function toString():String + return Int512.toStr(cast this); +} diff --git a/std/haxe/Int512Helper.hx b/std/haxe/Int512Helper.hx new file mode 100644 index 00000000000..366b4ae9d4a --- /dev/null +++ b/std/haxe/Int512Helper.hx @@ -0,0 +1,197 @@ +/* + * Copyright (C)2005-2019 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package haxe; + +using haxe.Int512; + +/** + Helper for parsing to `Int512` instances. +**/ +class Int512Helper { + /** + Create `Int256` from given string. + **/ + public static function parseString(sParam:String):Int512 { + var base = Int512.ofInt(10); + var current = Int512.ofInt(0); + var multiplier = Int512.ofInt(1); + var sIsNegative = false; + + var s = StringTools.trim(sParam); + if (s.charAt(0) == "-") { + sIsNegative = true; + s = s.substring(1, s.length); + } + var len = s.length; + + for (i in 0...len) { + var digitInt = s.charCodeAt(len - 1 - i) - '0'.code; + + if (digitInt < 0 || digitInt > 9) { + throw "NumberFormatError"; + } + + if (digitInt != 0) { + var digit:Int512 = Int512.ofInt(digitInt); + if (sIsNegative) { + current = Int512.sub(current, Int512.mul(multiplier, digit)); + if (!Int512.isNeg(current)) { + throw "NumberFormatError: Underflow"; + } + } else { + current = Int512.add(current, Int512.mul(multiplier, digit)); + if (Int512.isNeg(current)) { + throw "NumberFormatError: Overflow"; + } + } + } + + multiplier = Int512.mul(multiplier, base); + } + return current; + } + + /** + Create `Int512` from given float. + **/ + public static function fromFloat(f:Float):Int512 { + if (Math.isNaN(f) || !Math.isFinite(f)) { + throw "Number is NaN or Infinite"; + } + + var noFractions = f - (f % 1); + + // 2^53-1 and -2^53+1: these are parseable without loss of precision. + // In theory 2^53 and -2^53 are parseable too, but then there's no way to + // distinguish 2^53 from 2^53+1 + // (i.e. trace(9007199254740992. + 1. > 9007199254740992.); // false!) + if (noFractions > 9007199254740991) { + throw "Conversion overflow"; + } + if (noFractions < -9007199254740991) { + throw "Conversion underflow"; + } + + var result = Int512.ofInt(0); + var neg = noFractions < 0; + var rest = neg ? -noFractions : noFractions; + + var i = 0; + while (rest >= 1) { + var curr = rest % 2; + rest = rest / 2; + if (curr >= 1) { + result = Int512.add(result, Int512.shl(Int512.ofInt(1), i)); + } + i++; + } + + if (neg) { + result = Int512.neg(result); + } + + return result; + } + + /** + The maximum `Int512` value. + */ + public static var maxValue:Int512 = Int512.make(Int256Helper.maxValue, -1); + + /** + The minimum `Int512` value. + */ + public static var minValue:Int512 = ~maxValue; + + /** + The maximum `Int64` value with the type `Int512`. + This is handy for type comparison. + */ + public static var maxValue64:Int512 = Int512.ofInt64(Int64Helper.maxValue); + + /** + The minimum `Int64` value with the type `Int512`. + This is handy for type comparison. + */ + public static var minValue64:Int512 = ~maxValue64; + + /** + The maximum unsigned `Int64` value with the type `Int512`. + This is handy for type comparison. + */ + public static var maxValue64U:Int512 = Int512.make(1, 0); + + /** + The maximum `Int128` value with the type `Int512`. + This is handy for type comparison. + */ + public static var maxValue128:Int512 = Int512.ofInt128(Int128Helper.maxValue); + + /** + The minimum `Int128` value with the type `Int512`. + This is handy for type comparison. + */ + public static var minValue128:Int512 = ~maxValue128; + + /** + The maximum unsigned `Int128` value with the type `Int512`. + This is handy for type comparison. + */ + public static var maxValue128U:Int512 = Int256.make(0, -1); + + /** + The maximum `Int256` value with the type `Int512`. + This is handy for type comparison. + */ + public static var maxValue256:Int512 = Int512.ofInt256(Int256Helper.maxValue); + + /** + The minimum `Int256` value with the type `Int512`. + This is handy for type comparison. + */ + public static var minValue256:Int512 = ~maxValue256; + + /** + The maximum unsigned `Int256` value with the type `Int512`. + This is handy for type comparison. + */ + public static var maxValue256U:Int512 = Int512.make(0, -1); + + /** + The maximum `Int32` value with the type `Int512`. + This is handy for type comparison. + */ + public static var maxValue32:Int512 = Int512.ofInt256(Int256Helper.maxValue32); + + /** + The minimum `Int32` value with the type `Int512`. + This is handy for type comparison. + */ + public static var minValue32:Int512 = ~maxValue32; + + /** + The maximum unsigned `Int32` value with the type `Int512`. + This is handy for type comparison. + */ + public static var maxValue32U:Int512 = Int64.make(0, -1); +}