docs(changeset): Added toBaseN and fromBaseN with test cases

This commit is contained in:
Hein 2025-04-29 16:50:13 +02:00
parent 635e6957f2
commit 67a17e55a5
5 changed files with 281 additions and 0 deletions

View File

@ -0,0 +1,5 @@
---
"@warkypublic/artemis-kit": patch
---
Added toBaseN and fromBaseN with test cases

View File

@ -0,0 +1,6 @@
import { describe, it } from 'vitest';
describe('LLM Tests', () => {
it('test', () => {
})
})

View File

@ -0,0 +1,184 @@
import { describe, it, expect } from 'vitest';
import { fromBaseN, toBaseN } from './baseNumber'; // Update with actual path
// This test suite targets ES6+ environments
describe('Base-N Converters', () => {
describe('fromBaseN', () => {
// Basic conversion tests
it('should convert base 10 strings correctly', () => {
expect(fromBaseN('0', 10)).toBe(BigInt(0));
expect(fromBaseN('123', 10)).toBe(BigInt(123));
expect(fromBaseN('9999', 10)).toBe(BigInt(9999));
});
it('should convert base 2 (binary) strings correctly', () => {
expect(fromBaseN('0', 2)).toBe(BigInt(0));
expect(fromBaseN('1', 2)).toBe(BigInt(1));
expect(fromBaseN('10', 2)).toBe(BigInt(2));
expect(fromBaseN('1010', 2)).toBe(BigInt(10));
expect(fromBaseN('11111111', 2)).toBe(BigInt(255));
});
it('should convert base 16 (hex) strings correctly', () => {
expect(fromBaseN('0', 16)).toBe(BigInt(0));
expect(fromBaseN('A', 16)).toBe(BigInt(10));
expect(fromBaseN('F', 16)).toBe(BigInt(15));
expect(fromBaseN('10', 16)).toBe(BigInt(16));
expect(fromBaseN('FF', 16)).toBe(BigInt(255));
expect(fromBaseN('ABC', 16)).toBe(BigInt(2748));
expect(fromBaseN('DEADBEEF', 16)).toBe(BigInt(3735928559));
});
it('should convert base 36 strings correctly', () => {
expect(fromBaseN('0', 36)).toBe(BigInt(0));
expect(fromBaseN('Z', 36)).toBe(BigInt(35));
expect(fromBaseN('10', 36)).toBe(BigInt(36));
expect(fromBaseN('CLAUDE', 36)).toBe(BigInt(761371970));
});
// Case insensitivity
it('should be case insensitive', () => {
expect(fromBaseN('abc', 16)).toBe(BigInt(2748));
expect(fromBaseN('ABC', 16)).toBe(BigInt(2748));
expect(fromBaseN('AbC', 16)).toBe(BigInt(2748));
expect(fromBaseN('claude', 36)).toBe(BigInt(761371970));
expect(fromBaseN('CLAUDE', 36)).toBe(BigInt(761371970));
});
// Default base
it('should use base 36 by default', () => {
expect(fromBaseN('Z')).toBe(BigInt(35));
expect(fromBaseN('10')).toBe(BigInt(36));
});
// Edge cases
it('should handle empty string or null', () => {
expect(fromBaseN('')).toBe(BigInt(0));
expect(fromBaseN('', 10)).toBe(BigInt(0));
});
it('should handle very large numbers', () => {
expect(fromBaseN('ZZZZZZZZZZZZ', 36)).toBe(BigInt('4738381338321616895'));
});
// Error cases
it('should throw error for invalid base', () => {
expect(() => fromBaseN('123', 1)).toThrow('Base must be between 2 and 36');
expect(() => fromBaseN('123', 37)).toThrow('Base must be between 2 and 36');
});
it('should throw error for invalid characters', () => {
expect(() => fromBaseN('12$3', 10)).toThrow('Invalid character in input string: $');
expect(() => fromBaseN('XYZ', 10)).toThrow('Digit Z is invalid for base 10');
});
it('should throw error for digit out of range for base', () => {
expect(() => fromBaseN('129', 9)).toThrow('Digit 9 is invalid for base 9');
expect(() => fromBaseN('12A', 10)).toThrow('Digit A is invalid for base 10');
});
});
describe('toBaseN', () => {
// Basic conversion tests
it('should convert to base 10 strings correctly', () => {
expect(toBaseN(0, 10)).toBe('0');
expect(toBaseN(123, 10)).toBe('123');
expect(toBaseN(9999, 10)).toBe('9999');
});
it('should convert to base 2 (binary) strings correctly', () => {
expect(toBaseN(0, 2)).toBe('0');
expect(toBaseN(1, 2)).toBe('1');
expect(toBaseN(2, 2)).toBe('10');
expect(toBaseN(10, 2)).toBe('1010');
expect(toBaseN(255, 2)).toBe('11111111');
});
it('should convert to base 16 (hex) strings correctly', () => {
expect(toBaseN(0, 16)).toBe('0');
expect(toBaseN(10, 16)).toBe('A');
expect(toBaseN(15, 16)).toBe('F');
expect(toBaseN(16, 16)).toBe('10');
expect(toBaseN(255, 16)).toBe('FF');
expect(toBaseN(2748, 16)).toBe('ABC');
expect(toBaseN(3735928559, 16)).toBe('DEADBEEF');
});
it('should convert to base 36 strings correctly', () => {
expect(toBaseN(0, 36)).toBe('0');
expect(toBaseN(35, 36)).toBe('Z');
expect(toBaseN(36, 36)).toBe('10');
expect(toBaseN(761371970, 36)).toBe('CLAUDE');
});
// Default base
it('should use base 36 by default', () => {
expect(toBaseN(35)).toBe('Z');
expect(toBaseN(36)).toBe('10');
});
// Edge cases
it('should handle zero', () => {
expect(toBaseN(0)).toBe('0');
expect(toBaseN(BigInt(0))).toBe('0');
});
it('should handle negative numbers', () => {
expect(toBaseN(-123, 10)).toBe('-123');
expect(toBaseN(-15, 16)).toBe('-F');
expect(toBaseN(-35, 36)).toBe('-Z');
});
it('should handle BigInt inputs', () => {
expect(toBaseN(BigInt(123), 10)).toBe('123');
expect(toBaseN(BigInt('9007199254740991'), 16)).toBe('1FFFFFFFFFFFFF');
expect(toBaseN(BigInt('4738381338321616895'), 36)).toBe('ZZZZZZZZZZZZ');
});
// Error cases
it('should throw error for invalid base', () => {
expect(() => toBaseN(123, 1)).toThrow('Base must be between 2 and 36');
expect(() => toBaseN(123, 37)).toThrow('Base must be between 2 and 36');
});
});
// Round-trip tests
describe('round-trip conversions', () => {
it('should preserve value when converting back and forth', () => {
const testValues = [
0, 1, 42, 123, 255, 1000, 9999, 65535, 16777215, 2147483647,
// Large values as BigInt
BigInt('4738381338321616895'),
BigInt('761371970') // Value for 'CLAUDE' in base 36
];
const bases = [2, 8, 10, 16, 36];
for (const value of testValues) {
for (const base of bases) {
// Convert to string in base-n, then back to number
const baseStr = toBaseN(value, base);
const backToNumber = fromBaseN(baseStr, base);
// Convert BigInt to string for comparison since BigInt !== number
const originalValue = typeof value === 'bigint' ? value : BigInt(value);
expect(backToNumber).toBe(originalValue);
}
}
});
it('should handle random values in round-trip conversions', () => {
// Test with 10 random values
for (let i = 0; i < 10; i++) {
const randomValue = Math.floor(Math.random() * 1000000);
const randomBase = Math.floor(Math.random() * 35) + 2; // Random base between 2-36
const baseStr = toBaseN(randomValue, randomBase);
const backToNumber = fromBaseN(baseStr, randomBase);
expect(backToNumber).toBe(BigInt(randomValue));
}
});
});
});

85
src/strings/baseNumber.ts Normal file
View File

@ -0,0 +1,85 @@
/**
* Convert a string in a given base to a decimal number
* This implementation targets ES6+ and uses BigInt for large number support
*
* @param str The string to convert
* @param base The base to convert from (2-36)
* @returns The decimal number as BigInt
*/
export function fromBaseN(str: string, base: number = 36): bigint {
// Input validation
if (base < 2 || base > 36) {
throw new Error('Base must be between 2 and 36');
}
// Convert empty string or null to 0
if (!str) {
return BigInt(0);
}
// Use standard alphanumeric characters for conversion: 0-9, A-Z
const lookupString = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
let result = BigInt(0);
let power = BigInt(1);
// Process string right to left
for (let i = str.length - 1; i >= 0; i--) {
const char = str[i].toUpperCase();
const digit = lookupString.indexOf(char);
// Check if character is valid
if (digit === -1) {
throw new Error(`Invalid character in input string: ${char}`);
}
// Validate digit is within base range
if (digit >= base) {
throw new Error(`Digit ${char} is invalid for base ${base}`);
}
result += BigInt(digit) * power;
power *= BigInt(base);
}
return result;
}
/**
* Convert a decimal number to a string in a given base
* This implementation targets ES6+ and uses BigInt for large number support
*
* @param num The decimal number to convert
* @param base The base to convert to (2-36)
* @returns The string representation in the specified base
*/
export function toBaseN(num: number | bigint, base: number = 36): string {
// Input validation
if (base < 2 || base > 36) {
throw new Error('Base must be between 2 and 36');
}
// Handle 0 separately
if (num === 0 || num === BigInt(0)) {
return '0';
}
// Convert to BigInt to handle large numbers
const bigNum = typeof num === 'number' ? BigInt(num) : num;
// Use standard alphanumeric characters for conversion: 0-9, A-Z
const lookupString = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
let result = '';
let remaining = bigNum > BigInt(0) ? bigNum : -bigNum; // Handle negative numbers
const bigBase = BigInt(base);
while (remaining > BigInt(0)) {
const digitValue = Number(remaining % bigBase);
const digitChar = lookupString[digitValue];
result = digitChar + result;
remaining = remaining / bigBase;
}
// Add negative sign if necessary
return bigNum < BigInt(0) ? '-' + result : result;
}

View File

@ -7,3 +7,4 @@ export * from "./legacy";
export * from "./uuid"; export * from "./uuid";
export * from "./time"; export * from "./time";
export * from "./blankValue"; export * from "./blankValue";
export * from "./baseNumber";