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

add BigInt.fromString and BigInt.fromNumber #2399

Merged
merged 11 commits into from
Mar 25, 2024
5 changes: 5 additions & 0 deletions .changeset/sixty-news-begin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"effect": patch
---

add BigInt.fromString and BigInt.fromNumber
79 changes: 77 additions & 2 deletions packages/effect/src/BigInt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -547,14 +547,89 @@ export const multiplyAll = (collection: Iterable<bigint>): bigint => {
}

/**
* Convers a bigint into a number
* Takes a `bigint` and returns an `Option` of `number`.
*
* If the `bigint` is outside the safe integer range for JavaScript (`Number.MAX_SAFE_INTEGER`
* and `Number.MIN_SAFE_INTEGER`), it returns `Option.none()`. Otherwise, it converts the `bigint`
* to a number and returns `Option.some(number)`.
*
* @param b - The `bigint` to be converted to a `number`.
*
* @example
* import { fromBigInt } from "effect/Number"
tim-smart marked this conversation as resolved.
Show resolved Hide resolved
* import { Option } from "effect"
*
* assert.deepStrictEqual(fromBigInt(BigInt(42)), Option.some(42))
* assert.deepStrictEqual(fromBigInt(BigInt(Number.MAX_SAFE_INTEGER) + BigInt(1)), Option.none())
* assert.deepStrictEqual(fromBigInt(BigInt(Number.MIN_SAFE_INTEGER) - BigInt(1)), Option.none())
*
* @since 2.0.0
* @category conversions
* @since 2.0.0
*/
export const toNumber = (b: bigint): Option.Option<number> => {
if (b > BigInt(Number.MAX_SAFE_INTEGER) || b < BigInt(Number.MIN_SAFE_INTEGER)) {
return Option.none()
}
return Option.some(Number(b))
}

/**
* Takes a string and returns an `Option` of `bigint`.
*
* If the string is empty or contains characters that cannot be converted into a `bigint`,
* it returns `Option.none()`, otherwise, it returns `Option.some(bigint)`.
*
* @param s - The string to be converted to a `bigint`.
*
* @example
* import { fromString } from "effect/BigInt"
* import { Option } from "effect"
*
* assert.deepStrictEqual(fromString("42"), Option.some(BigInt(42)))
* assert.deepStrictEqual(fromString(" "), Option.none())
* assert.deepStrictEqual(fromString("a"), Option.none())
*
* @category conversions
* @since 2.4.12
*/
export const fromString = (s: string): Option.Option<bigint> => {
try {
return s.trim() === ""
? Option.none()
: Option.some(BigInt(s))
} catch (_) {
return Option.none()
}
}

/**
* Takes a number and returns an `Option` of `bigint`.
*
* If the number is outside the safe integer range for JavaScript (`Number.MAX_SAFE_INTEGER`
* and `Number.MIN_SAFE_INTEGER`), it returns `Option.none()`. Otherwise, it attempts to
* convert the number to a `bigint` and returns `Option.some(bigint)`.
*
* @param n - The number to be converted to a `bigint`.
*
* @example
* import { fromNumber } from "effect/BigInt"
* import { Option } from "effect"
*
* assert.deepStrictEqual(fromNumber(42), Option.some(BigInt(42)))
* assert.deepStrictEqual(fromNumber(Number.MAX_SAFE_INTEGER + 1), Option.none())
* assert.deepStrictEqual(fromNumber(Number.MIN_SAFE_INTEGER - 1), Option.none())
*
* @category conversions
* @since 2.4.12
*/
export const fromNumber = (n: number): Option.Option<bigint> => {
if (n > Number.MAX_SAFE_INTEGER || n < Number.MIN_SAFE_INTEGER) {
return Option.none()
}

try {
return Option.some(BigInt(n))
} catch (_) {
return Option.none()
}
}
31 changes: 30 additions & 1 deletion packages/effect/src/Number.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ export const sumAll = (collection: Iterable<number>): number => {
* @param collection - The collection of `number`s to multiply.
*
* @example
* import { multiplyAll } from 'effect/Number'
* import { multiplyAll } from "effect/Number"
*
* assert.deepStrictEqual(multiplyAll([2, 3, 4]), 24)
*
Expand Down Expand Up @@ -493,3 +493,32 @@ export const parse = (s: string): Option<number> => {
? option.none
: option.some(n)
}

/**
* Takes a string and returns an `Option` of `number`.
*
* If the string is empty or invalid characters,
* it returns `Option.none()`. Otherwise, it attempts to convert the string to a number
* and returns `Option.some(number)`.
*
* @param s - The string to be converted to a `number`.
*
* @example
* import { fromString } from "effect/Number"
* import { Option } from "effect"
*
* assert.deepStrictEqual(fromString("42"), Option.some(42))
* assert.deepStrictEqual(fromString(""), Option.none())
* assert.deepStrictEqual(fromString("not a number"), Option.none())
*
* @category conversions
* @since 2.4.12
*/
export const fromString = (s: string): Option<number> => {
tim-smart marked this conversation as resolved.
Show resolved Hide resolved
const n = Number(s)
return s.trim() === ""
? option.none
: Number.isNaN(n)
? option.none
: option.some(n)
}
40 changes: 38 additions & 2 deletions packages/effect/test/BigInt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,43 @@ describe("BigInt", () => {
})

it("toNumber", () => {
assert.deepStrictEqual(BigInt_.toNumber(1n), Option.some(1))
assert.deepStrictEqual(BigInt_.toNumber(BigInt(Number.MAX_SAFE_INTEGER) + 1n), Option.none())
assert.deepStrictEqual(BigInt_.toNumber(BigInt(Number.MAX_SAFE_INTEGER)), Option.some(Number.MAX_SAFE_INTEGER))
assert.deepStrictEqual(BigInt_.toNumber(BigInt(Number.MAX_SAFE_INTEGER) + BigInt(1)), Option.none())
assert.deepStrictEqual(BigInt_.toNumber(BigInt(Number.MIN_SAFE_INTEGER)), Option.some(Number.MIN_SAFE_INTEGER))
assert.deepStrictEqual(BigInt_.toNumber(BigInt(Number.MIN_SAFE_INTEGER) - BigInt(1)), Option.none())
assert.deepStrictEqual(BigInt_.toNumber(BigInt(0)), Option.some(0))
assert.deepStrictEqual(BigInt_.toNumber(BigInt(42)), Option.some(42))
assert.deepStrictEqual(BigInt_.toNumber(BigInt(-42)), Option.some(-42))
})

it("fromString", () => {
assert.deepStrictEqual(BigInt_.fromString("NaN"), Option.none())
assert.deepStrictEqual(BigInt_.fromString("Infinity"), Option.none())
assert.deepStrictEqual(BigInt_.fromString("-Infinity"), Option.none())
assert.deepStrictEqual(BigInt_.fromString("3.14"), Option.none())
assert.deepStrictEqual(BigInt_.fromString("-3.14"), Option.none())
assert.deepStrictEqual(BigInt_.fromString("1e3"), Option.none())
assert.deepStrictEqual(BigInt_.fromString("1e-3"), Option.none())
assert.deepStrictEqual(BigInt_.fromString(""), Option.none())
assert.deepStrictEqual(BigInt_.fromString("a"), Option.none())
assert.deepStrictEqual(BigInt_.fromString("42"), Option.some(BigInt(42)))
assert.deepStrictEqual(BigInt_.fromString("\n\r\t 42 \n\r\t"), Option.some(BigInt(42)))
})

it("fromNumber", () => {
assert.deepStrictEqual(BigInt_.fromNumber(Number.MAX_SAFE_INTEGER), Option.some(BigInt(Number.MAX_SAFE_INTEGER)))
assert.deepStrictEqual(BigInt_.fromNumber(Number.MAX_SAFE_INTEGER + 1), Option.none())
assert.deepStrictEqual(BigInt_.fromNumber(Number.MIN_SAFE_INTEGER), Option.some(BigInt(Number.MIN_SAFE_INTEGER)))
assert.deepStrictEqual(BigInt_.fromNumber(Number.MIN_SAFE_INTEGER - 1), Option.none())
assert.deepStrictEqual(BigInt_.fromNumber(Infinity), Option.none())
assert.deepStrictEqual(BigInt_.fromNumber(-Infinity), Option.none())
assert.deepStrictEqual(BigInt_.fromNumber(NaN), Option.none())
assert.deepStrictEqual(BigInt_.fromNumber(1e100), Option.none())
assert.deepStrictEqual(BigInt_.fromNumber(-1e100), Option.none())
assert.deepStrictEqual(BigInt_.fromNumber(3.14), Option.none())
assert.deepStrictEqual(BigInt_.fromNumber(-3.14), Option.none())
assert.deepStrictEqual(BigInt_.fromNumber(0), Option.some(BigInt(0)))
assert.deepStrictEqual(BigInt_.fromNumber(42), Option.some(BigInt(42)))
assert.deepStrictEqual(BigInt_.fromNumber(-42), Option.some(BigInt(-42)))
})
})
13 changes: 13 additions & 0 deletions packages/effect/test/Number.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,17 @@ describe("Number", () => {
assert.deepStrictEqual(Number.parse("42"), Option.some(42))
assert.deepStrictEqual(Number.parse("a"), Option.none())
})

it("fromString", () => {
assert.deepStrictEqual(Number.fromString("NaN"), Option.none())
assert.deepStrictEqual(Number.fromString("Infinity"), Option.some(Infinity))
assert.deepStrictEqual(Number.fromString("-Infinity"), Option.some(-Infinity))
assert.deepStrictEqual(Number.fromString("42"), Option.some(42))
assert.deepStrictEqual(Number.fromString(" 42 \n\r\t"), Option.some(42))
assert.deepStrictEqual(Number.fromString("3.14"), Option.some(3.14))
assert.deepStrictEqual(Number.fromString("1e3"), Option.some(1000))
assert.deepStrictEqual(Number.fromString("1e-3"), Option.some(0.001))
assert.deepStrictEqual(Number.fromString(""), Option.none())
assert.deepStrictEqual(Number.fromString("a"), Option.none())
})
})
Loading