import { describe, it, expect } from 'vitest';
import { sar } from './sar.js';
describe('SAR (0x1d)', () => {
it('shifts positive value (same as SHR)', () => {
const frame = createFrame({ stack: [4n, 0x1000n] });
expect(sar(frame)).toBeNull();
expect(frame.stack[0]).toBe(0x100n);
});
it('sign-extends negative value', () => {
const negValue = 1n << 255n; // MSB set
const frame = createFrame({ stack: [1n, negValue] });
expect(sar(frame)).toBeNull();
// Result should have both bit 255 and 254 set (sign-extended)
const expected = (1n << 255n) | (1n << 254n);
expect(frame.stack[0]).toBe(expected);
});
it('divides negative by power of 2', () => {
// -16 / 4 = -4
const minus16 = (1n << 256n) - 16n;
const frame = createFrame({ stack: [2n, minus16] });
expect(sar(frame)).toBeNull();
const minus4 = (1n << 256n) - 4n;
expect(frame.stack[0]).toBe(minus4);
});
it('returns -1 for shift >= 256 on negative', () => {
const negValue = 1n << 255n;
const frame = createFrame({ stack: [256n, negValue] });
expect(sar(frame)).toBeNull();
const minusOne = (1n << 256n) - 1n;
expect(frame.stack[0]).toBe(minusOne);
});
it('returns 0 for shift >= 256 on positive', () => {
const posValue = 1n << 254n;
const frame = createFrame({ stack: [256n, posValue] });
expect(sar(frame)).toBeNull();
expect(frame.stack[0]).toBe(0n);
});
it('handles zero shift (identity)', () => {
const value = 0x123456n;
const frame = createFrame({ stack: [0n, value] });
expect(sar(frame)).toBeNull();
expect(frame.stack[0]).toBe(value);
});
it('shifts -1 remains -1', () => {
const minusOne = (1n << 256n) - 1n;
const frame = createFrame({ stack: [100n, minusOne] });
expect(sar(frame)).toBeNull();
expect(frame.stack[0]).toBe(minusOne);
});
it('handles MIN_INT256', () => {
const MIN_INT = 1n << 255n;
const frame = createFrame({ stack: [1n, MIN_INT] });
expect(sar(frame)).toBeNull();
// -2^255 / 2 = -2^254 (sign-extended)
const expected = (1n << 255n) | (1n << 254n);
expect(frame.stack[0]).toBe(expected);
});
it('differs from SHR on negative values', () => {
const negValue = 1n << 255n;
// SHR: logical (zero-fill)
const frameSHR = createFrame({ stack: [1n, negValue] });
shr(frameSHR);
// SAR: arithmetic (sign-fill)
const frameSAR = createFrame({ stack: [1n, negValue] });
sar(frameSAR);
expect(frameSHR.stack[0]).not.toBe(frameSAR.stack[0]);
expect(frameSHR.stack[0]).toBe(1n << 254n); // Positive
expect(frameSAR.stack[0]).toBe((1n << 255n) | (1n << 254n)); // Negative
});
it('returns InvalidOpcode before Constantinople', () => {
const frame = createFrame({
stack: [4n, 0xFF00n],
hardfork: 'byzantium'
});
expect(sar(frame)).toEqual({ type: 'InvalidOpcode' });
});
it('returns StackUnderflow with insufficient stack', () => {
const frame = createFrame({ stack: [4n] });
expect(sar(frame)).toEqual({ type: 'StackUnderflow' });
});
it('returns OutOfGas when insufficient gas', () => {
const frame = createFrame({ stack: [4n, 0xFF00n], gasRemaining: 2n });
expect(sar(frame)).toEqual({ type: 'OutOfGas' });
});
});