diff --git a/contracts/math/uints.ral b/contracts/math/uints.ral index 4d5daf9..12bbc79 100644 --- a/contracts/math/uints.ral +++ b/contracts/math/uints.ral @@ -1,3 +1,8 @@ +struct Result { + mut value: U512, + mut error: Bool +} + struct U512 { mut higher: U256, mut lower: U256 @@ -23,7 +28,7 @@ Contract Uints () { return v.lower } - pub fn bigAdd(a: U256, b: U256) -> U512 { + pub fn bigAdd256(a: U256, b: U256) -> U512 { if (MaxU256 - a < b) { return U512 { higher: 1, @@ -37,55 +42,60 @@ Contract Uints () { } } - pub fn div(a: U512, b: U256, bDenominator: U256, up: Bool) -> U512 { - assert!(b > 0, ArithmeticErrors.NotPositiveDivisor) + pub fn bigAdd512(a: U512, b: U512) -> U512 { + let (aLowerBLower, overflow) = overflowingAdd(a.lower, b.lower) + let aHigherBHigherOverflow = wrappingAdd(wrappingAdd(a.higher, b.higher), overflow) + return U512 { lower: aLowerBLower, higher: aHigherBHigherOverflow } + } - if (b == 1) { - return a - } + pub fn bigDivWrapper(a: U512, b: U256, bDenominator: U256, up: Bool) -> U512 { + assert!(b > 0, ArithmeticErrors.NotPositiveDivisor) - // Calculate new higher - let newHigher = a.higher * bDenominator - let higher = newHigher / b - let higherRemainder = newHigher % b - // calculate higher remainder - let maxDiv = MaxU256 / b - let deltaHigherRemainder = higherRemainder * maxDiv - // Calculate lower - let newLower = a.lower * bDenominator - let lower = newLower / b - let mut deltaLower = deltaHigherRemainder + lower - - if (up) { - let lowerRemainder = newLower % b - - let higherDecimal = deltaHigherRemainder % b - if (higherDecimal + lowerRemainder != 0) { - deltaLower = deltaLower + 1 - } - } + if (b == 1) { + return a + } - return U512 { - higher: higher, - lower: deltaLower + // Calculate new higher + let newHigher = a.higher * bDenominator + let higher = newHigher / b + let higherRemainder = newHigher % b + // calculate higher remainder + let maxDiv = MaxU256 / b + let deltaHigherRemainder = higherRemainder * maxDiv + // Calculate lower + let newLower = a.lower * bDenominator + let lower = newLower / b + let mut deltaLower = deltaHigherRemainder + lower + + if (up) { + let lowerRemainder = newLower % b + + let higherDecimal = deltaHigherRemainder % b + if (higherDecimal + lowerRemainder != 0) { + deltaLower = deltaLower + 1 } } - pub fn bigDiv(a: U512, b: U256, bDenominator: U256) -> U512 { - return div(a, b, bDenominator, false) + return U512 { + higher: higher, + lower: deltaLower } + } - pub fn bigDivUp(a: U512, b: U256, bDenominator: U256) -> U512 { - return div(a, b, bDenominator, true) - } + pub fn bigDiv512(a: U512, b: U256, bDenominator: U256) -> U512 { + return bigDivWrapper(a, b, bDenominator, false) + } - pub fn bigAdd512(a: U512, b: U512) -> U512 { - let (aLowerBLower, overflow) = overflowingAdd(a.lower, b.lower) - let aHigherBHigherOverflow = wrappingAdd(wrappingAdd(a.higher, b.higher), overflow) - return U512 { lower: aLowerBLower, higher: aHigherBHigherOverflow } + pub fn bigDivUp512(a: U512, b: U256, bDenominator: U256) -> U512 { + return bigDivWrapper(a, b, bDenominator, true) + } + + pub fn bigMul512(a: U512, b: U256) -> U512 { + let result = bigMul256(a.lower, b) + return bigAdd512(result, U512 { higher: a.higher * b, lower: 0 }) } - pub fn mul(a: U256, b: U256) -> U512 { + pub fn bigMul256(a: U256, b: U256) -> U512 { let aLower = low128(a) let aHigher = high128(a) let bLower = low128(b) @@ -106,19 +116,19 @@ Contract Uints () { return U512 { higher: higher, lower: lower } } - pub fn bigMul(a: U256, b: U256, bDenominator: U256) -> U512 { - let mut result = mul(a, b) + pub fn bigMulDiv256(a: U256, b: U256, bDenominator: U256) -> U512 { + let mut result = bigMul256(a, b) - result = bigDiv(result, bDenominator, 1) + result = bigDiv512(result, bDenominator, 1) return result } - pub fn bigMulUp(a: U256, b: U256, bDenominator: U256) -> U512 { - let mut result = mul(a, b) + pub fn bigMulDivUp256(a: U256, b: U256, bDenominator: U256) -> U512 { + let mut result = bigMul256(a, b) result = bigAdd512(result, U512 { higher: 0, lower: bDenominator - 1 }) - result = bigDiv(result, bDenominator, 1) + result = bigDiv512(result, bDenominator, 1) return result } diff --git a/test/uints.test.ts b/test/uints.test.ts index e5e720a..927d8a2 100644 --- a/test/uints.test.ts +++ b/test/uints.test.ts @@ -21,29 +21,29 @@ describe('uints tests', () => { expect(castBack).toStrictEqual(v) }) - test('big add', async () => { + test('big add 256', async () => { const uints = await deployUints(sender) { const a = 115792089237316195423570985008687907853269984665640564039457584007913129639935n const b = 999n - const result = (await uints.contractInstance.methods.bigAdd({ args: { a, b } })).returns + const result = (await uints.contractInstance.methods.bigAdd256({ args: { a, b } })).returns expect(result).toStrictEqual({ higher: 1n, lower: b - 1n }) } { const a = 777n const b = 115792089237316195423570985008687907853269984665640564039457584007913129639935n - const result = (await uints.contractInstance.methods.bigAdd({ args: { a, b } })).returns + const result = (await uints.contractInstance.methods.bigAdd256({ args: { a, b } })).returns expect(result).toStrictEqual({ higher: 1n, lower: a - 1n }) } { const a = 115792089237316195423570985008687907853269984665640564039457584007913129639935n const b = 115792089237316195423570985008687907853269984665640564039457584007913129639935n - const result = (await uints.contractInstance.methods.bigAdd({ args: { a, b } })).returns + const result = (await uints.contractInstance.methods.bigAdd256({ args: { a, b } })).returns expect(result).toStrictEqual({ higher: 1n, lower: a - 1n }) } }) - test('big div', async () => { + test('big div 512 / big div up 512', async () => { const uints = await deployUints(sender) { const a = { @@ -53,7 +53,7 @@ describe('uints tests', () => { const b = 50n const bDenominator = 10n { - const result = (await uints.contractInstance.methods.bigDiv({ args: { a, b, bDenominator } })).returns + const result = (await uints.contractInstance.methods.bigDiv512({ args: { a, b, bDenominator } })).returns // expected: 486326774796728020778998137036489212983733935595690368965721000000000000000000 // received: 486326774796728020778998137036489212983733935595690368965721852833235144487731 expect(result).toStrictEqual({ @@ -62,7 +62,7 @@ describe('uints tests', () => { }) } { - const result = (await uints.contractInstance.methods.bigDivUp({ args: { a, b, bDenominator } })).returns + const result = (await uints.contractInstance.methods.bigDivUp512({ args: { a, b, bDenominator } })).returns expect(result).toStrictEqual({ higher: 4n, lower: 23158417847463239084714197001737581570653996933128112807891516801582625927988n @@ -77,7 +77,7 @@ describe('uints tests', () => { const b = 2n const bDenominator = 1n { - const result = (await uints.contractInstance.methods.bigDiv({ args: { a, b, bDenominator } })).returns + const result = (await uints.contractInstance.methods.bigDiv512({ args: { a, b, bDenominator } })).returns // expected: 2907354897182427562197295231552018137414565442749272241125960796722557152453591693304764202855054262243050086425064711734138406514458624n // received: 2907354897182427562197295231552018137414565442749272241125960796722557152453591693304764202855054262243050086425064711734138406514458624n expect(result).toStrictEqual({ @@ -86,7 +86,7 @@ describe('uints tests', () => { }) } { - const result = (await uints.contractInstance.methods.bigDivUp({ args: { a, b, bDenominator } })).returns + const result = (await uints.contractInstance.methods.bigDivUp512({ args: { a, b, bDenominator } })).returns expect(result).toStrictEqual({ higher: 25108406941546723055343157692830665664409421777856138051584n, lower: 0n @@ -101,14 +101,14 @@ describe('uints tests', () => { const b = 2n const bDenominator = 1n { - const result = (await uints.contractInstance.methods.bigDiv({ args: { a, b, bDenominator } })).returns + const result = (await uints.contractInstance.methods.bigDiv512({ args: { a, b, bDenominator } })).returns expect(result).toStrictEqual({ higher: 0n, lower: 0n }) } { - const result = (await uints.contractInstance.methods.bigDivUp({ args: { a, b, bDenominator } })).returns + const result = (await uints.contractInstance.methods.bigDivUp512({ args: { a, b, bDenominator } })).returns expect(result).toStrictEqual({ higher: 0n, lower: 1n @@ -123,14 +123,14 @@ describe('uints tests', () => { const b = 1n const bDenominator = 1000n { - const result = (await uints.contractInstance.methods.bigDiv({ args: { a, b, bDenominator } })).returns + const result = (await uints.contractInstance.methods.bigDiv512({ args: { a, b, bDenominator } })).returns expect(result).toStrictEqual({ higher: 0n, lower: 10n }) } { - const result = (await uints.contractInstance.methods.bigDivUp({ args: { a, b, bDenominator } })).returns + const result = (await uints.contractInstance.methods.bigDivUp512({ args: { a, b, bDenominator } })).returns expect(result).toStrictEqual({ higher: 0n, lower: 10n @@ -146,14 +146,14 @@ describe('uints tests', () => { const b = 3n * 10n ** 3n const bDenominator = 1000n { - const result = (await uints.contractInstance.methods.bigDiv({ args: { a, b, bDenominator } })).returns + const result = (await uints.contractInstance.methods.bigDiv512({ args: { a, b, bDenominator } })).returns expect(result).toStrictEqual({ higher: 0n, lower: 20n }) } { - const result = (await uints.contractInstance.methods.bigDivUp({ args: { a, b, bDenominator } })).returns + const result = (await uints.contractInstance.methods.bigDivUp512({ args: { a, b, bDenominator } })).returns expect(result).toStrictEqual({ higher: 0n, lower: 20n @@ -178,12 +178,67 @@ describe('uints tests', () => { } }) - test('big mul', async () => { + test('big mul 512', async () => { + const uints = await deployUints(sender) + { + const a = { higher: 0n, lower: 123n } + const b = 2n + const result = (await uints.contractInstance.methods.bigMul512({ args: { a, b } })).returns + expect(result).toStrictEqual({ higher: 0n, lower: 246n }) + // expected: 246 + // real: 246 + } + { + const a = { higher: 0n, lower: 340282366920938463463374607431768211457n } + const b = 340282366920938463463374607431768211457n + const result = (await uints.contractInstance.methods.bigMul512({ args: { a, b } })).returns + expect(result).toStrictEqual({ higher: 1n, lower: 680564733841876926926749214863536422913n }) + // expected: 115792089237316195423570985008687907853950549399482440966384333222776666062849 + // real: 115792089237316195423570985008687907853950549399482440966384333222776666062849 + } + { + const a = { higher: 0n, lower: 115792089237316195423570985008687907853269984665640564039457584007913129639935n } + const b = 115792089237316195423570985008687907853269984665640564039457584007913129639935n + const result = (await uints.contractInstance.methods.bigMul512({ args: { a, b } })).returns + expect(result).toStrictEqual({ + higher: 115792089237316195423570985008687907853269984665640564039457584007913129639934n, + lower: 1n + }) + // expected: 13407807929942597099574024998205846127479365820592393377723561443721764030073315392623399665776056285720014482370779510884422601683867654778417822746804225 + // real: 13407807929942597099574024998205846127479365820592393377723561443721764030073315392623399665776056285720014482370779510884422601683867654778417822746804225 + } + { + const a = { higher: 0n, lower: 500n } + const b = 0n + const result = (await uints.contractInstance.methods.bigMul512({ args: { a, b } })).returns + expect(result).toStrictEqual({ higher: 0n, lower: 0n }) + } + { + const a = { higher: 1n, lower: 115792089237316195423570985008687907853269984665640564039457584007913129639935n } + const b = 100n + const result = (await uints.contractInstance.methods.bigMul512({ args: { a, b } })).returns + expect(result).toStrictEqual({ + higher: 199n, + lower: 115792089237316195423570985008687907853269984665640564039457584007913129639836n + }) + } + { + const a = { higher: 340282366920938463463374607431768211455n, lower: 340282366920938463463374607431768211455n } + const b = 340282366920938463463374607431768211455n + const result = (await uints.contractInstance.methods.bigMul512({ args: { a, b } })).returns + expect(result).toStrictEqual({ + higher: 115792089237316195423570985008687907852589419931798687112530834793049593217025n, + lower: 115792089237316195423570985008687907852589419931798687112530834793049593217025n + }) + } + }) + + test('big mul div 256', async () => { const uints = await deployUints(sender) { const a = 123n const b = 2n - const result = (await uints.contractInstance.methods.bigMul({ args: { a, b, bDenominator: 1n } })).returns + const result = (await uints.contractInstance.methods.bigMulDiv256({ args: { a, b, bDenominator: 1n } })).returns expect(result).toStrictEqual({ higher: 0n, lower: 246n }) // expected: 246 // real: 246 @@ -191,7 +246,7 @@ describe('uints tests', () => { { const a = 340282366920938463463374607431768211457n const b = 340282366920938463463374607431768211457n - const result = (await uints.contractInstance.methods.bigMul({ args: { a, b, bDenominator: 1n } })).returns + const result = (await uints.contractInstance.methods.bigMulDiv256({ args: { a, b, bDenominator: 1n } })).returns expect(result).toStrictEqual({ higher: 1n, lower: 680564733841876926926749214863536422913n }) // expected: 115792089237316195423570985008687907853950549399482440966384333222776666062849 // real: 115792089237316195423570985008687907853950549399482440966384333222776666062849 @@ -199,7 +254,7 @@ describe('uints tests', () => { { const a = 115792089237316195423570985008687907853269984665640564039457584007913129639935n const b = 115792089237316195423570985008687907853269984665640564039457584007913129639935n - const result = (await uints.contractInstance.methods.bigMul({ args: { a, b, bDenominator: 1n } })).returns + const result = (await uints.contractInstance.methods.bigMulDiv256({ args: { a, b, bDenominator: 1n } })).returns expect(result).toStrictEqual({ higher: 115792089237316195423570985008687907853269984665640564039457584007913129639934n, lower: 1n @@ -210,41 +265,42 @@ describe('uints tests', () => { { const a = 500n const b = 0n - const result = (await uints.contractInstance.methods.bigMul({ args: { a, b, bDenominator: 1n } })).returns + const result = (await uints.contractInstance.methods.bigMulDiv256({ args: { a, b, bDenominator: 1n } })).returns expect(result).toStrictEqual({ higher: 0n, lower: 0n }) } { const a = 100n const b = 100n - const result = (await uints.contractInstance.methods.bigMul({ args: { a, b, bDenominator: 100n } })).returns + const result = (await uints.contractInstance.methods.bigMulDiv256({ args: { a, b, bDenominator: 100n } })).returns expect(result).toStrictEqual({ higher: 0n, lower: 100n }) } { const a = 30n const b = 1n - const result = (await uints.contractInstance.methods.bigMul({ args: { a, b, bDenominator: 10n } })).returns + const result = (await uints.contractInstance.methods.bigMulDiv256({ args: { a, b, bDenominator: 10n } })).returns expect(result).toStrictEqual({ higher: 0n, lower: 3n }) } { const a = 500n const b = 4000n - const result = (await uints.contractInstance.methods.bigMul({ args: { a, b, bDenominator: 1000n } })).returns + const result = (await uints.contractInstance.methods.bigMulDiv256({ args: { a, b, bDenominator: 1000n } })) + .returns expect(result).toStrictEqual({ higher: 0n, lower: 2000n }) } { const a = 10n const b = 37n - const result = (await uints.contractInstance.methods.bigMul({ args: { a, b, bDenominator: 100n } })).returns + const result = (await uints.contractInstance.methods.bigMulDiv256({ args: { a, b, bDenominator: 100n } })).returns expect(result).toStrictEqual({ higher: 0n, lower: 3n }) } }) - test('big mul up', async () => { + test('big mul div up 256', async () => { const uints = await deployUints(sender) { const a = 123n const b = 2n - const result = (await uints.contractInstance.methods.bigMulUp({ args: { a, b, bDenominator: 1n } })).returns + const result = (await uints.contractInstance.methods.bigMulDivUp256({ args: { a, b, bDenominator: 1n } })).returns expect(result).toStrictEqual({ higher: 0n, lower: 246n }) // expected: 246 // real: 246 @@ -252,7 +308,7 @@ describe('uints tests', () => { { const a = 340282366920938463463374607431768211457n const b = 340282366920938463463374607431768211457n - const result = (await uints.contractInstance.methods.bigMulUp({ args: { a, b, bDenominator: 1n } })).returns + const result = (await uints.contractInstance.methods.bigMulDivUp256({ args: { a, b, bDenominator: 1n } })).returns expect(result).toStrictEqual({ higher: 1n, lower: 680564733841876926926749214863536422913n }) // expected: 115792089237316195423570985008687907853950549399482440966384333222776666062849 // real: 115792089237316195423570985008687907853950549399482440966384333222776666062849 @@ -260,7 +316,7 @@ describe('uints tests', () => { { const a = 115792089237316195423570985008687907853269984665640564039457584007913129639935n const b = 115792089237316195423570985008687907853269984665640564039457584007913129639935n - const result = (await uints.contractInstance.methods.bigMulUp({ args: { a, b, bDenominator: 1n } })).returns + const result = (await uints.contractInstance.methods.bigMulDivUp256({ args: { a, b, bDenominator: 1n } })).returns expect(result).toStrictEqual({ higher: 115792089237316195423570985008687907853269984665640564039457584007913129639934n, lower: 1n @@ -271,31 +327,35 @@ describe('uints tests', () => { { const a = 500n const b = 0n - const result = (await uints.contractInstance.methods.bigMulUp({ args: { a, b, bDenominator: 1n } })).returns + const result = (await uints.contractInstance.methods.bigMulDivUp256({ args: { a, b, bDenominator: 1n } })).returns expect(result).toStrictEqual({ higher: 0n, lower: 0n }) } { const a = 100n const b = 100n - const result = (await uints.contractInstance.methods.bigMulUp({ args: { a, b, bDenominator: 100n } })).returns + const result = (await uints.contractInstance.methods.bigMulDivUp256({ args: { a, b, bDenominator: 100n } })) + .returns expect(result).toStrictEqual({ higher: 0n, lower: 100n }) } { const a = 30n const b = 1n - const result = (await uints.contractInstance.methods.bigMulUp({ args: { a, b, bDenominator: 10n } })).returns + const result = (await uints.contractInstance.methods.bigMulDivUp256({ args: { a, b, bDenominator: 10n } })) + .returns expect(result).toStrictEqual({ higher: 0n, lower: 3n }) } { const a = 500n const b = 4000n - const result = (await uints.contractInstance.methods.bigMulUp({ args: { a, b, bDenominator: 1000n } })).returns + const result = (await uints.contractInstance.methods.bigMulDivUp256({ args: { a, b, bDenominator: 1000n } })) + .returns expect(result).toStrictEqual({ higher: 0n, lower: 2000n }) } { const a = 10n const b = 37n - const result = (await uints.contractInstance.methods.bigMulUp({ args: { a, b, bDenominator: 100n } })).returns + const result = (await uints.contractInstance.methods.bigMulDivUp256({ args: { a, b, bDenominator: 100n } })) + .returns expect(result).toStrictEqual({ higher: 0n, lower: 4n }) } })