Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Add lessThanOrEqualAddress cairo hint #132

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cairo_programs/cairo/hints/span_multi_pop_back.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fn main() {
let mut span = array![1, 2, 3].span();
let _ = span.multi_pop_back::<2>();
}
4 changes: 4 additions & 0 deletions cairo_programs/cairo/hints/span_multi_pop_front.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fn main() {
let mut span = array![1, 2, 3].span();
let _ = span.multi_pop_front::<2>();
}
8 changes: 8 additions & 0 deletions src/hints/hintHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ import {
} from './dict/shouldSkipSquashLoop';
import { TestLessThan, testLessThan } from './math/testLessThan';
import { DivMod, divMod } from './math/divMod';
import {
TestLessThanOrEqualAddress,
testLessThanOrEqualAddress,
} from './math/testLessThanOrEqualAddress';

/**
* Map hint names to the function executing their logic.
Expand Down Expand Up @@ -130,4 +134,8 @@ export const handlers: HintHandler = {
const h = hint as DivMod;
divMod(vm, h.lhs, h.rhs, h.quotient, h.remainder);
},
[HintName.TestLessThanOrEqualAddress]: (vm, hint) => {
const h = hint as TestLessThanOrEqualAddress;
testLessThanOrEqualAddress(vm, h.lhs, h.rhs, h.dst);
},
};
1 change: 1 addition & 0 deletions src/hints/hintName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ export enum HintName {
ShouldSkipSquashLoop = 'ShouldSkipSquashLoop',
TestLessThan = 'TestLessThan',
DivMod = 'DivMod',
TestLessThanOrEqualAddress = 'TestLessThanOrEqualAddress',
}
2 changes: 2 additions & 0 deletions src/hints/hintSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { shouldContinueSquashLoopParser } from './dict/shouldContinueSquashLoop'
import { shouldSkipSquashLoopParser } from './dict/shouldSkipSquashLoop';
import { testLessThanParser } from './math/testLessThan';
import { divModParser } from './math/divMod';
import { testLessThanOrEqualAddressParser } from './math/testLessThanOrEqualAddress';

/** Zod object to parse any implemented hints */
const hint = z.union([
Expand All @@ -35,6 +36,7 @@ const hint = z.union([
shouldSkipSquashLoopParser,
testLessThanParser,
divModParser,
testLessThanOrEqualAddressParser,
]);

/** Zod object to parse an array of hints grouped on a given PC */
Expand Down
2 changes: 1 addition & 1 deletion src/hints/math/testLessThan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const testLessThanParser = z
/**
* TestLessThan hint
*
* Store true at `dst` if value at `lhs` is stricty inferior to value at `rhs`.
* Store true at `dst` if value at `lhs` is strictly inferior to value at `rhs`.
* Store false otherwise
*/
export type TestLessThan = z.infer<typeof testLessThanParser>;
Expand Down
85 changes: 85 additions & 0 deletions src/hints/math/testLessThanOrEqualAddress.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { describe, expect, test } from 'bun:test';

import { Felt } from 'primitives/felt';
import { Register } from 'vm/instruction';
import { VirtualMachine } from 'vm/virtualMachine';

import { OpType } from 'hints/hintParamsSchema';
import { HintName } from 'hints/hintName';
import { testLessThanOrEqualAddressParser } from './testLessThanOrEqualAddress';
import { Relocatable } from 'primitives/relocatable';

const TEST_LESS_THAN_OR_EQUAL_ADDRESS = {
TestLessThanOrEqualAddress: {
lhs: {
Deref: {
register: 'AP',
offset: 0,
},
},
rhs: {
Deref: {
register: 'AP',
offset: 1,
},
},
dst: {
register: 'AP',
offset: 2,
},
},
};

describe('TestLessThanOrEqualAddress', () => {
test('should properly parse TestLessThanOrEqualAddress hint', () => {
const hint = testLessThanOrEqualAddressParser.parse(
TEST_LESS_THAN_OR_EQUAL_ADDRESS
);

expect(hint).toEqual({
type: HintName.TestLessThanOrEqualAddress,
lhs: {
type: OpType.Deref,
cell: {
register: Register.Ap,
offset: 0,
},
},
rhs: {
type: OpType.Deref,
cell: {
register: Register.Ap,
offset: 1,
},
},
dst: {
register: Register.Ap,
offset: 2,
},
});
});

test.each([
[new Relocatable(1, 0), new Relocatable(1, 1), new Felt(1n)],
[new Relocatable(1, 1), new Relocatable(1, 0), new Felt(0n)],
[new Relocatable(1, 1), new Relocatable(1, 1), new Felt(1n)],
[new Relocatable(0, 0), new Relocatable(1, 0), new Felt(1n)],
])(
'should properly execute TestLessThanOrEqualAddress hint',
(lhs, rhs, expected) => {
const hint = testLessThanOrEqualAddressParser.parse(
TEST_LESS_THAN_OR_EQUAL_ADDRESS
);
const vm = new VirtualMachine();
vm.memory.addSegment();
vm.memory.addSegment();
vm.memory.assertEq(vm.ap, lhs);
vm.memory.assertEq(vm.ap.add(1), rhs);

vm.executeHint(hint);
expect(vm.memory.get(vm.cellRefToRelocatable(hint.dst))).toEqual(
expected
);
}
);
});
63 changes: 63 additions & 0 deletions src/hints/math/testLessThanOrEqualAddress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { z } from 'zod';

import { Felt } from 'primitives/felt';
import { VirtualMachine } from 'vm/virtualMachine';

import { HintName } from 'hints/hintName';
import {
cellRef,
resOperand,
CellRef,
ResOperand,
} from 'hints/hintParamsSchema';

/** Zod object to parse TestLessThanOrEqualAddress hint */
export const testLessThanOrEqualAddressParser = z
.object({
TestLessThanOrEqualAddress: z.object({
lhs: resOperand,
rhs: resOperand,
dst: cellRef,
}),
})
.transform(({ TestLessThanOrEqualAddress: { lhs, rhs, dst } }) => ({
type: HintName.TestLessThanOrEqualAddress,
lhs,
rhs,
dst,
}));

/**
* TestLessThanOrEqualAddress hint
*
* Check whether the Relocatable value at `lhs` is inferior or equal to the value at `rhs`
*/
export type TestLessThanOrEqualAddress = z.infer<
typeof testLessThanOrEqualAddressParser
>;

/**
* TestLessThanOrEqualAddress hint
*
* Check whether the Relocatable value at `lhs` is inferior or equal to the value at `rhs`
*
* @param {VirtualMachine} vm - The virtual machine instance.
* @param {ResOperand} lhs - The left-hand side operand.
* @param {ResOperand} rhs - The right-hand side operand.
* @param {CellRef} dst - The address where the result of the comparison will be stored.
*/
export const testLessThanOrEqualAddress = (
vm: VirtualMachine,
lhs: ResOperand,
rhs: ResOperand,
dst: CellRef
) => {
const lhsValue = vm.getResOperandRelocatable(lhs);
const rhsValue = vm.getResOperandRelocatable(rhs);

const isLessThanOrEqual = lhsValue <= rhsValue;

const result = new Felt(isLessThanOrEqual ? 1n : 0n);

vm.memory.assertEq(vm.cellRefToRelocatable(dst), result);
};
Loading