From 39f7ab83a8f1a4eb78db5dfe7d4c4dd6afbef087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Araya=20Jim=C3=A9nez?= Date: Wed, 7 Aug 2024 02:15:30 -0600 Subject: [PATCH 01/10] feat: add DivMod to hintName.ts --- src/hints/hintName.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hints/hintName.ts b/src/hints/hintName.ts index f00a024a..d18bcac0 100644 --- a/src/hints/hintName.ts +++ b/src/hints/hintName.ts @@ -15,4 +15,5 @@ export enum HintName { ShouldContinueSquashLoop = 'ShouldContinueSquashLoop', ShouldSkipSquashLoop = 'ShouldSkipSquashLoop', TestLessThan = 'TestLessThan', + DivMod = 'DivMod', } From 77efecc07c2f7c1c2743937b60083eedf820b7ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Araya=20Jim=C3=A9nez?= Date: Wed, 7 Aug 2024 02:21:13 -0600 Subject: [PATCH 02/10] feat: add parser in hintSchema.ts --- src/hints/hintSchema.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hints/hintSchema.ts b/src/hints/hintSchema.ts index 892737db..aacdc5f7 100644 --- a/src/hints/hintSchema.ts +++ b/src/hints/hintSchema.ts @@ -15,6 +15,7 @@ import { initSquashDataParser } from './dict/initSquashData'; import { shouldContinueSquashLoopParser } from './dict/shouldContinueSquashLoop'; import { shouldSkipSquashLoopParser } from './dict/shouldSkipSquashLoop'; import { testLessThanParser } from './math/testLessThan'; +import { divModParser } from './math/divMod'; /** Zod object to parse any implemented hints */ const hint = z.union([ @@ -33,6 +34,7 @@ const hint = z.union([ shouldContinueSquashLoopParser, shouldSkipSquashLoopParser, testLessThanParser, + divModParser ]); /** Zod object to parse an array of hints grouped on a given PC */ From f9d7d48e48b485754d0215f091f0318d49f2df83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Araya=20Jim=C3=A9nez?= Date: Wed, 7 Aug 2024 02:25:57 -0600 Subject: [PATCH 03/10] feat: add divMod hint in progress --- src/hints/math/divMod.ts | 58 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/hints/math/divMod.ts diff --git a/src/hints/math/divMod.ts b/src/hints/math/divMod.ts new file mode 100644 index 00000000..ba0715ce --- /dev/null +++ b/src/hints/math/divMod.ts @@ -0,0 +1,58 @@ +import { z } from 'zod'; + +import { VirtualMachine } from 'vm/virtualMachine'; + +import { resOperand, ResOperand, cellRef, CellRef, } from 'hints/hintParamsSchema'; +import { HintName } from 'hints/hintName'; +import { Felt } from 'primitives/felt'; + +/** Zod object to parse DivMod hint */ +export const divModParser = z + .object({ + DivMod: z.object({ + lhs: resOperand, + rhs: resOperand, + quotient: cellRef, + remainder: cellRef, + }), + }) + .transform(({ DivMod: { lhs, rhs, quotient, remainder } }) => ({ + type: HintName.DivMod, + leftHandSide: lhs, + rightHandSide: rhs, + quotientAddress: quotient, + remainderAddress: remainder, + })); + + +/** + * DivMod hint type + */ +export type DivMod = z.infer; + +/** + * Perform division and modulus operations. + * + * @param {VirtualMachine} vm - The virtual machine instance + * @param {ResOperand} leftHandSide - The left-hand side operand + * @param {ResOperand} rightHandSide - The right-hand side operand + * @param {CellRef} quotientAddress - The address to store the quotient + * @param {CellRef} remainderAddress - The address to store the remainder + */ + +export const divMod = ( + vm: VirtualMachine, + leftHandSide: ResOperand, + rightHandSide: ResOperand, + quotientAddress: CellRef, + remainderAddress: CellRef + ) => { + const lhsValue = vm.getResOperandValue(leftHandSide).toBigInt(); + const rhsValue = vm.getResOperandValue(rightHandSide).toBigInt(); + + const quotientValue = new Felt(lhsValue / rhsValue); + const remainderValue = new Felt(lhsValue % rhsValue); + + const quotientAddr = vm.cellRefToRelocatable(quotientAddress); + const remainderAddr = vm.cellRefToRelocatable(remainderAddress); + }; \ No newline at end of file From fde21d7deecbaf0c7853c6776638cc580eaf3df6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Araya=20Jim=C3=A9nez?= Date: Wed, 7 Aug 2024 02:27:48 -0600 Subject: [PATCH 04/10] feat: add code in hintHandler.ts --- src/hints/hintHandler.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/hints/hintHandler.ts b/src/hints/hintHandler.ts index 020aeeab..7c141d62 100644 --- a/src/hints/hintHandler.ts +++ b/src/hints/hintHandler.ts @@ -48,6 +48,7 @@ import { shouldSkipSquashLoop, } from './dict/shouldSkipSquashLoop'; import { TestLessThan, testLessThan } from './math/testLessThan'; +import { DivMod, divMod } from './math/divMod'; /** * Map hint names to the function executing their logic. @@ -125,4 +126,8 @@ export const handlers: HintHandler = { const h = hint as TestLessThan; testLessThan(vm, h.lhs, h.rhs, h.dst); }, + [HintName.DivMod]: (vm, hint) => { + const h = hint as DivMod; + divMod(vm, h.leftHandSide, h.rightHandSide, h.quotientAddress, h.remainderAddress); + }, }; From 3dc46e37c8e3be90c6b4a982f4843130b2ec9c55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Araya=20Jim=C3=A9nez?= Date: Thu, 8 Aug 2024 01:45:32 -0600 Subject: [PATCH 05/10] feat: add unit test and complete hint --- src/hints/math/divMod.test.ts | 92 +++++++++++++++++++++++++++++++++++ src/hints/math/divMod.ts | 7 +++ 2 files changed, 99 insertions(+) create mode 100644 src/hints/math/divMod.test.ts diff --git a/src/hints/math/divMod.test.ts b/src/hints/math/divMod.test.ts new file mode 100644 index 00000000..5c53ed25 --- /dev/null +++ b/src/hints/math/divMod.test.ts @@ -0,0 +1,92 @@ +import { describe, expect, test } from 'bun:test'; + +import { Felt } from 'primitives/felt'; +import { VirtualMachine } from 'vm/virtualMachine'; +import { Relocatable } from 'primitives/relocatable'; +import { HintName } from 'hints/hintName'; +import { OpType, ResOperand, CellRef } from 'hints/hintParamsSchema'; +import { divModParser } from './divMod'; +import { divMod } from './divMod'; +import { Register } from 'vm/instruction'; + +const DIV_MOD_HINT = { + DivMod: { + lhs: { + Deref: { + register: 'AP', + offset: 0, + }, + }, + rhs: { + Deref: { + register: 'AP', + offset: 1, + }, + }, + quotient: { + register: 'AP', + offset: 2, + }, + remainder: { + register: 'AP', + offset: 3, + }, + }, +}; + +describe('DivMod', () => { + test('should properly parse DivMod hint', () => { + const hint = divModParser.parse(DIV_MOD_HINT); + expect(hint).toEqual({ + type: HintName.DivMod, + leftHandSide: { + type: OpType.Deref, + cell: { + register: Register.Ap, + offset: 0, + }, + }, + rightHandSide: { + type: OpType.Deref, + cell: { + register: Register.Ap, + offset: 1, + }, + }, + quotientAddress: { + register: Register.Ap, + offset: 2, + }, + remainderAddress: { + register: Register.Ap, + offset: 3, + }, + }); + }); + + test('should properly execute DivMod hint', () => { + const hint = divModParser.parse(DIV_MOD_HINT); + const vm = new VirtualMachine(); + vm.memory.addSegment(); + vm.memory.addSegment(); + + const lhsValue = new Felt(17n); + const rhsValue = new Felt(5n); + const quotientExpected = new Felt(3n); + const remainderExpected = new Felt(2n); + + const lhsAddr = new Relocatable(0, 0); + const rhsAddr = new Relocatable(0, 1); + const quotientAddr = new Relocatable(0, 2); + const remainderAddr = new Relocatable(0, 3); + + vm.memory.assertEq(lhsAddr, lhsValue); + vm.memory.assertEq(rhsAddr, rhsValue); + vm.ap = new Relocatable(0, 0); + + divMod(vm, hint.leftHandSide, hint.rightHandSide, hint.quotientAddress, hint.remainderAddress); + + expect(vm.memory.get(quotientAddr)).toEqual(quotientExpected); + expect(vm.memory.get(remainderAddr)).toEqual(remainderExpected); + }); +}); diff --git a/src/hints/math/divMod.ts b/src/hints/math/divMod.ts index ba0715ce..8a11727a 100644 --- a/src/hints/math/divMod.ts +++ b/src/hints/math/divMod.ts @@ -50,9 +50,16 @@ export const divMod = ( const lhsValue = vm.getResOperandValue(leftHandSide).toBigInt(); const rhsValue = vm.getResOperandValue(rightHandSide).toBigInt(); + if (rhsValue === 0n) { + throw new Error("Division by zero"); + } + const quotientValue = new Felt(lhsValue / rhsValue); const remainderValue = new Felt(lhsValue % rhsValue); const quotientAddr = vm.cellRefToRelocatable(quotientAddress); const remainderAddr = vm.cellRefToRelocatable(remainderAddress); + + vm.memory.assertEq(quotientAddr, quotientValue); + vm.memory.assertEq(remainderAddr, remainderValue); }; \ No newline at end of file From fcbbd459c6c280871f78c84085ac5bcc2205fb04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Araya=20Jim=C3=A9nez?= Date: Thu, 8 Aug 2024 11:03:16 -0600 Subject: [PATCH 06/10] feat: requested changes applied in unit test divMod.test.ts, hint divMod.ts and hintHandler.ts --- src/hints/hintHandler.ts | 2 +- src/hints/hintSchema.ts | 2 +- src/hints/math/divMod.test.ts | 57 ++++++++++++++++---------------- src/hints/math/divMod.ts | 61 ++++++++++++++++++----------------- 4 files changed, 61 insertions(+), 61 deletions(-) diff --git a/src/hints/hintHandler.ts b/src/hints/hintHandler.ts index 7c141d62..48fbc69b 100644 --- a/src/hints/hintHandler.ts +++ b/src/hints/hintHandler.ts @@ -128,6 +128,6 @@ export const handlers: HintHandler = { }, [HintName.DivMod]: (vm, hint) => { const h = hint as DivMod; - divMod(vm, h.leftHandSide, h.rightHandSide, h.quotientAddress, h.remainderAddress); + divMod(vm, h.lhs, h.rhs, h.quotient, h.remainder); }, }; diff --git a/src/hints/hintSchema.ts b/src/hints/hintSchema.ts index aacdc5f7..d3675b50 100644 --- a/src/hints/hintSchema.ts +++ b/src/hints/hintSchema.ts @@ -34,7 +34,7 @@ const hint = z.union([ shouldContinueSquashLoopParser, shouldSkipSquashLoopParser, testLessThanParser, - divModParser + divModParser, ]); /** Zod object to parse an array of hints grouped on a given PC */ diff --git a/src/hints/math/divMod.test.ts b/src/hints/math/divMod.test.ts index 5c53ed25..5a5d37e4 100644 --- a/src/hints/math/divMod.test.ts +++ b/src/hints/math/divMod.test.ts @@ -1,10 +1,8 @@ import { describe, expect, test } from 'bun:test'; - import { Felt } from 'primitives/felt'; import { VirtualMachine } from 'vm/virtualMachine'; -import { Relocatable } from 'primitives/relocatable'; import { HintName } from 'hints/hintName'; -import { OpType, ResOperand, CellRef } from 'hints/hintParamsSchema'; +import { OpType } from 'hints/hintParamsSchema'; import { divModParser } from './divMod'; import { divMod } from './divMod'; import { Register } from 'vm/instruction'; @@ -39,54 +37,55 @@ describe('DivMod', () => { const hint = divModParser.parse(DIV_MOD_HINT); expect(hint).toEqual({ type: HintName.DivMod, - leftHandSide: { + lhs: { type: OpType.Deref, cell: { register: Register.Ap, offset: 0, }, }, - rightHandSide: { + rhs: { type: OpType.Deref, cell: { register: Register.Ap, offset: 1, }, }, - quotientAddress: { + quotient: { register: Register.Ap, offset: 2, }, - remainderAddress: { + remainder: { register: Register.Ap, offset: 3, }, }); }); - test('should properly execute DivMod hint', () => { - const hint = divModParser.parse(DIV_MOD_HINT); - const vm = new VirtualMachine(); - vm.memory.addSegment(); - vm.memory.addSegment(); - - const lhsValue = new Felt(17n); - const rhsValue = new Felt(5n); - const quotientExpected = new Felt(3n); - const remainderExpected = new Felt(2n); + test.each([ + [new Felt(17n), new Felt(5n), new Felt(3n), new Felt(2n)], + [new Felt(10n), new Felt(3n), new Felt(3n), new Felt(1n)], + [new Felt(20n), new Felt(4n), new Felt(5n), new Felt(0n)], + [new Felt(15n), new Felt(6n), new Felt(2n), new Felt(3n)], + ])( + 'should properly execute DivMod hint', + (lhsValue, rhsValue, quotientExpected, remainderExpected) => { + const hint = divModParser.parse(DIV_MOD_HINT); + const vm = new VirtualMachine(); + vm.memory.addSegment(); + vm.memory.addSegment(); - const lhsAddr = new Relocatable(0, 0); - const rhsAddr = new Relocatable(0, 1); - const quotientAddr = new Relocatable(0, 2); - const remainderAddr = new Relocatable(0, 3); + vm.memory.assertEq(vm.ap, lhsValue); + vm.memory.assertEq(vm.ap.add(1), rhsValue); - vm.memory.assertEq(lhsAddr, lhsValue); - vm.memory.assertEq(rhsAddr, rhsValue); - vm.ap = new Relocatable(0, 0); + divMod(vm, hint.lhs, hint.rhs, hint.quotient, hint.remainder); - divMod(vm, hint.leftHandSide, hint.rightHandSide, hint.quotientAddress, hint.remainderAddress); - - expect(vm.memory.get(quotientAddr)).toEqual(quotientExpected); - expect(vm.memory.get(remainderAddr)).toEqual(remainderExpected); - }); + expect(vm.memory.get(vm.cellRefToRelocatable(hint.quotient))).toEqual( + quotientExpected + ); + expect(vm.memory.get(vm.cellRefToRelocatable(hint.remainder))).toEqual( + remainderExpected + ); + } + ); }); diff --git a/src/hints/math/divMod.ts b/src/hints/math/divMod.ts index 8a11727a..135e3292 100644 --- a/src/hints/math/divMod.ts +++ b/src/hints/math/divMod.ts @@ -2,7 +2,12 @@ import { z } from 'zod'; import { VirtualMachine } from 'vm/virtualMachine'; -import { resOperand, ResOperand, cellRef, CellRef, } from 'hints/hintParamsSchema'; +import { + resOperand, + ResOperand, + cellRef, + CellRef, +} from 'hints/hintParamsSchema'; import { HintName } from 'hints/hintName'; import { Felt } from 'primitives/felt'; @@ -18,13 +23,12 @@ export const divModParser = z }) .transform(({ DivMod: { lhs, rhs, quotient, remainder } }) => ({ type: HintName.DivMod, - leftHandSide: lhs, - rightHandSide: rhs, - quotientAddress: quotient, - remainderAddress: remainder, + lhs, + rhs, + quotient, + remainder, })); - /** * DivMod hint type */ @@ -34,32 +38,29 @@ export type DivMod = z.infer; * Perform division and modulus operations. * * @param {VirtualMachine} vm - The virtual machine instance - * @param {ResOperand} leftHandSide - The left-hand side operand - * @param {ResOperand} rightHandSide - The right-hand side operand - * @param {CellRef} quotientAddress - The address to store the quotient - * @param {CellRef} remainderAddress - The address to store the remainder + * @param {ResOperand} lhs - The left-hand side operand + * @param {ResOperand} rhs - The right-hand side operand + * @param {CellRef} quotient - The address to store the quotient + * @param {CellRef} remainder - The address to store the remainder */ export const divMod = ( - vm: VirtualMachine, - leftHandSide: ResOperand, - rightHandSide: ResOperand, - quotientAddress: CellRef, - remainderAddress: CellRef - ) => { - const lhsValue = vm.getResOperandValue(leftHandSide).toBigInt(); - const rhsValue = vm.getResOperandValue(rightHandSide).toBigInt(); - - if (rhsValue === 0n) { - throw new Error("Division by zero"); - } + vm: VirtualMachine, + lhs: ResOperand, + rhs: ResOperand, + quotient: CellRef, + remainder: CellRef +) => { + const lhsValue = vm.getResOperandValue(lhs).toBigInt(); + const rhsValue = vm.getResOperandValue(rhs).toBigInt(); + + if (rhsValue === 0n) { + throw new Error('Division by zero'); + } - const quotientValue = new Felt(lhsValue / rhsValue); - const remainderValue = new Felt(lhsValue % rhsValue); - - const quotientAddr = vm.cellRefToRelocatable(quotientAddress); - const remainderAddr = vm.cellRefToRelocatable(remainderAddress); + const quotientValue = new Felt(lhsValue / rhsValue); + const remainderValue = new Felt(lhsValue % rhsValue); - vm.memory.assertEq(quotientAddr, quotientValue); - vm.memory.assertEq(remainderAddr, remainderValue); - }; \ No newline at end of file + vm.memory.assertEq(vm.cellRefToRelocatable(quotient), quotientValue); + vm.memory.assertEq(vm.cellRefToRelocatable(remainder), remainderValue); +}; From 44f76726f9314ff4cbc0e6d796fa4681b1b9b08a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Araya=20Jim=C3=A9nez?= Date: Thu, 8 Aug 2024 18:56:26 -0600 Subject: [PATCH 07/10] test: add integration test for DivMod hint --- cairo_programs/cairo/hints/divmod.cairo | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 cairo_programs/cairo/hints/divmod.cairo diff --git a/cairo_programs/cairo/hints/divmod.cairo b/cairo_programs/cairo/hints/divmod.cairo new file mode 100644 index 00000000..bee66af1 --- /dev/null +++ b/cairo_programs/cairo/hints/divmod.cairo @@ -0,0 +1,4 @@ +fn main() { + let _ = 17_u32 / 12_u32; + let _ = 17_u32 % 12_u32; +} \ No newline at end of file From 76a008af6d03faa21832a0d0751876e851c8dbc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Araya=20Jim=C3=A9nez?= Date: Fri, 9 Aug 2024 13:07:49 -0600 Subject: [PATCH 08/10] test: changes applied and new edge case test added --- src/hints/math/divMod.test.ts | 17 +++++++++++++++++ src/hints/math/divMod.ts | 5 ++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/hints/math/divMod.test.ts b/src/hints/math/divMod.test.ts index 5a5d37e4..7bb77af5 100644 --- a/src/hints/math/divMod.test.ts +++ b/src/hints/math/divMod.test.ts @@ -88,4 +88,21 @@ describe('DivMod', () => { ); } ); + + test('should throw an error when rhs is zero', () => { + const hint = divModParser.parse(DIV_MOD_HINT); + const vm = new VirtualMachine(); + vm.memory.addSegment(); + vm.memory.addSegment(); + + const lhsValue = new Felt(17n); + const rhsValue = new Felt(0n); + + vm.memory.assertEq(vm.ap, lhsValue); + vm.memory.assertEq(vm.ap.add(1), rhsValue); + + expect(() => { + divMod(vm, hint.lhs, hint.rhs, hint.quotient, hint.remainder); + }); + }); }); diff --git a/src/hints/math/divMod.ts b/src/hints/math/divMod.ts index 135e3292..9e4ba9fb 100644 --- a/src/hints/math/divMod.ts +++ b/src/hints/math/divMod.ts @@ -10,6 +10,7 @@ import { } from 'hints/hintParamsSchema'; import { HintName } from 'hints/hintName'; import { Felt } from 'primitives/felt'; +import { CannotDivideByZero } from 'errors/primitives'; /** Zod object to parse DivMod hint */ export const divModParser = z @@ -54,9 +55,7 @@ export const divMod = ( const lhsValue = vm.getResOperandValue(lhs).toBigInt(); const rhsValue = vm.getResOperandValue(rhs).toBigInt(); - if (rhsValue === 0n) { - throw new Error('Division by zero'); - } + if (!rhsValue) throw new CannotDivideByZero(); const quotientValue = new Felt(lhsValue / rhsValue); const remainderValue = new Felt(lhsValue % rhsValue); From 5330daf27495873512868349405c9a1aab09ffe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Araya=20Jim=C3=A9nez?= Date: Wed, 14 Aug 2024 06:50:54 -0600 Subject: [PATCH 09/10] test: fix and add multiple cases for division by zero in DivMod hint --- src/hints/math/divMod.test.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/hints/math/divMod.test.ts b/src/hints/math/divMod.test.ts index 7bb77af5..394c2b8f 100644 --- a/src/hints/math/divMod.test.ts +++ b/src/hints/math/divMod.test.ts @@ -6,6 +6,7 @@ import { OpType } from 'hints/hintParamsSchema'; import { divModParser } from './divMod'; import { divMod } from './divMod'; import { Register } from 'vm/instruction'; +import { CannotDivideByZero } from 'errors/primitives'; const DIV_MOD_HINT = { DivMod: { @@ -89,20 +90,22 @@ describe('DivMod', () => { } ); - test('should throw an error when rhs is zero', () => { + test.each([ + [new Felt(17n), new Felt(0n)], + [new Felt(100n), new Felt(0n)], + [new Felt(5n), new Felt(0n)], + [new Felt(0n), new Felt(0n)], + ])('should throw an error when rhs is zero', (lhsValue, rhsValue) => { const hint = divModParser.parse(DIV_MOD_HINT); const vm = new VirtualMachine(); vm.memory.addSegment(); vm.memory.addSegment(); - const lhsValue = new Felt(17n); - const rhsValue = new Felt(0n); - vm.memory.assertEq(vm.ap, lhsValue); vm.memory.assertEq(vm.ap.add(1), rhsValue); expect(() => { divMod(vm, hint.lhs, hint.rhs, hint.quotient, hint.remainder); - }); + }).toThrow(CannotDivideByZero); }); }); From 0471f2f07476faef6c36c5f08a6000e0f98403d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Araya=20Jim=C3=A9nez?= Date: Thu, 29 Aug 2024 03:33:15 -0600 Subject: [PATCH 10/10] test: update DivMod hint test to check for specific error instance --- src/hints/math/divMod.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hints/math/divMod.test.ts b/src/hints/math/divMod.test.ts index 394c2b8f..84914dad 100644 --- a/src/hints/math/divMod.test.ts +++ b/src/hints/math/divMod.test.ts @@ -106,6 +106,6 @@ describe('DivMod', () => { expect(() => { divMod(vm, hint.lhs, hint.rhs, hint.quotient, hint.remainder); - }).toThrow(CannotDivideByZero); + }).toThrow(new CannotDivideByZero()); }); });