> For the complete documentation index, see [llms.txt](https://series-1.gitbook.io/rundot-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://series-1.gitbook.io/rundot-docs/v5.21.0/readme/bignumbers.md).

# Big Numbers API

Handle exponential economies without losing precision. The Big Numbers API wraps `break_eternity` and exposes helpers for normalization, formatting, and progression math tuned for idle or incremental games.

## Quick Start

```typescript
import RundotGameAPI from '@series-inc/rundot-game-sdk/api'

const numbers = RundotGameAPI.numbers

const currency = numbers.normalize('BE:1.23e9')
const display = numbers.format.incremental(currency) // "1.23B"
```

## Type Checking

### `numbers.isBigNumber(value): boolean`

```typescript
// Check if a value is a big number
const isBig = numbers.isBigNumber(someValue)
```

`isBigNumber` returns `true` for exactly three kinds of value:

| Input     | Returns `true` when                                                                                                 |
| --------- | ------------------------------------------------------------------------------------------------------------------- |
| `string`  | it starts with the `BE:` prefix (for example `'BE:1.23e9'`)                                                         |
| `Decimal` | it is a `Decimal` instance (from `numbers.normalize` or `new numbers.Decimal(...)`)                                 |
| `number`  | it is a plain JS number outside the safe-integer range (`> Number.MAX_SAFE_INTEGER` or `< Number.MIN_SAFE_INTEGER`) |

Every other value (a small plain number, a non-`BE:` string, `null`, `undefined`, objects) returns `false`.

### `numbers.normalize(value): Decimal`

Converts any input into a `Decimal` instance. This is the canonical way to undo the `BE:` persistence prefix recommended in [Best Practices](#best-practices).

| Input                      | Behavior                                                                                                                                        |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `Decimal`                  | returned unchanged (no copy)                                                                                                                    |
| `BE:`-prefixed string      | the first 3 characters are stripped, then the remainder is parsed with `new Decimal(...)` (for example `'BE:1.23e9'` becomes `Decimal(1.23e9)`) |
| any other string or number | passed straight to `new Decimal(value)`                                                                                                         |

```typescript
const fromStorage = numbers.normalize('BE:1.23e9') // Decimal(1.23e9)
const fromNumber = numbers.normalize(42)           // Decimal(42)
```

## Progression Math

### Geometric Series Costs

`calculateGeometricSeriesCost(baseCost, multiplier, currentQuantity, purchaseAmount)` computes the total cost of buying `purchaseAmount` upgrades, starting from `currentQuantity` already owned. It returns a **`Decimal`** instance, so feed the result into `format.incremental` / `formatDecimalCurrency` for display, or compare it with `.lt` / `.gt` / `.lte`.

```typescript
const totalCost = numbers.calculateGeometricSeriesCost(
  baseCost, // e.g., 10
  1.15,     // multiplier
  currentLevel,
  25,       // quantity to purchase
) // -> Decimal

numbers.formatDecimalCurrency(totalCost) // "$1.23K"
```

It uses the closed-form geometric series `a * (r^n - 1) / (r - 1)`, where `a = baseCost * r^currentQuantity`. When `multiplier` is exactly `1`, it short-circuits to the linear case `baseCost * purchaseAmount` (avoiding a divide-by-zero), so a flat-cost upgrade still works.

{% hint style="warning" %}
If `Decimal` math throws, the method logs a `[RUN]` `console.error` and falls back to native JavaScript math, returning a **plain `number`** instead of a `Decimal`. That fallback loses big-number precision, so treat the return value with `numbers.normalize` (or `isBigNumber`) before assuming it is always a `Decimal`.
{% endhint %}

### Max Affordable

`calculateMaxAffordableDecimal(availableCash, baseCost, multiplier, currentQuantity)` returns how many upgrades a player can buy with their current currency. It returns a plain **`number`**.

```typescript
const maxBuy = numbers.calculateMaxAffordableDecimal(
  playerCash,
  baseCost,
  multiplier,
  currentLevel,
)
```

Two bounds to know:

* Returns `0` when the player cannot afford even a single upgrade.
* The affordability search is capped at a safety limit of `1,000,000`, so the largest value it can ever return is on the order of `1,000,000`, even if more would technically be affordable. Size single purchase batches accordingly.

If the `Decimal` calculation throws, it logs a `[RUN]` `console.error` and falls back to `Math.floor(Number(availableCash) / Number(baseCost))`.

### Direct Decimal Usage

Access the underlying class for custom math:

```typescript
const { Decimal } = numbers

const a = new Decimal('1e308')
const b = new Decimal('1e308')
const result = a.times(b) // Handles numbers beyond JavaScript limits
```

`numbers.Decimal` is the [`break_eternity.js`](https://github.com/Patashu/break_eternity.js) `Decimal` class, so instances expose that library's full surface: arithmetic (`times` / `mul`, `div`, `sub`, `pow`), comparisons (`lt`, `gt`, `lte`, `eq`), `log10`, and formatters (`toFixed`, `toExponential`). `Decimal.pow(...)` is also available as a static. Refer to the `break_eternity.js` docs for the complete method list.

## Display Formatting

### `numbers.format.incremental(value): string`

Format a big-number or `Decimal` value for display with an abbreviated suffix. The output depends on the magnitude:

| Magnitude                                                                 | Output                                                              | Example               |
| ------------------------------------------------------------------------- | ------------------------------------------------------------------- | --------------------- |
| `< 1000`                                                                  | plain integer string, `toFixed(0)` (any fractional part is dropped) | `999` -> `"999"`      |
| `1000` up to the largest suffix                                           | value divided down, then `toFixed(2)` plus the suffix               | `1.23e9` -> `"1.23B"` |
| above the largest suffix (`Dc`, `1e33`); exponential begins around `1e36` | 2-digit exponential notation                                        | `1e40` -> `"1e40"`    |

The full suffix set, each step 1000x larger than the last:

`K` (1e3), `M` (1e6), `B` (1e9), `T` (1e12), `Qa` (1e15), `Qi` (1e18), `Sx` (1e21), `Sp` (1e24), `Oc` (1e27), `No` (1e30), `Dc` (1e33).

{% hint style="info" %}
Suffixed output is fixed at 2 decimal places (`"1.23B"`, `"5.00M"`); the precision is not configurable. Once a value passes `Dc` the formatter switches to exponential notation rather than inventing new suffixes. `formatDecimalCurrency` wraps this method, so it inherits the same suffix table, 2-decimal precision, and exponential ceiling.
{% endhint %}

## Currency Formatting

### `numbers.formatDecimalCurrency(decimalValue): string`

Format a big-number or `Decimal` value as a currency string with a leading `$`. This wraps `numbers.format.incremental`, so it uses the same abbreviated suffixes (`K`, `M`, `B`, and so on) and falls back to `Number(...).toLocaleString()` if formatting fails.

| Parameter      | Type                | Description                                                                                                                                                                                                                                                                 |
| -------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `decimalValue` | `Decimal \| number` | The value to format. Pass a `Decimal` (for example from `numbers.normalize`) or a plain number. `BE:`-prefixed strings must be run through `numbers.normalize()` first; `formatDecimalCurrency` does not strip the `BE:` prefix itself, so passing one directly mis-parses. |

Returns a `string`, for example `"$1.23B"`.

```typescript
const cash = numbers.normalize('BE:1.23e9')
const label = numbers.formatDecimalCurrency(cash) // "$1.23B"
```

## API Reference

| Method                                                           | Returns   | Description                                                                                                   |
| ---------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------- |
| `numbers.isBigNumber(value)`                                     | `boolean` | `true` for a `BE:` string, a `Decimal`, or a number outside the safe-integer range                            |
| `numbers.normalize(value)`                                       | `Decimal` | Convert string/number to a `Decimal` instance (strips the `BE:` prefix; passes a `Decimal` through unchanged) |
| `numbers.format.incremental(value)`                              | `string`  | Format for display: integer below 1000, 2-decimal suffix up to `Dc`, exponential above                        |
| `numbers.calculateGeometricSeriesCost(base, mult, level, qty)`   | `Decimal` | Total cost for upgrades (linear when `mult === 1`; native-math `number` on fallback)                          |
| `numbers.calculateMaxAffordableDecimal(cash, base, mult, level)` | `number`  | Max purchasable quantity (`0` if none affordable; capped at \~1,000,000)                                      |
| `numbers.formatDecimalCurrency(value)`                           | `string`  | Format a big number as a currency string (e.g. "$1.23B")                                                      |
| `numbers.Decimal`                                                | `class`   | Direct access to the `break_eternity.js` `Decimal` class                                                      |

## Best Practices

* Store values as strings (e.g., `BE:` prefix) when persisting to avoid JSON precision issues.
* Normalize before performing comparisons so you're always working with Decimal instances.
* Keep formatting on the client; send raw numbers to your backend for canonical calculations.
* When storing big numbers, use `RundotGameAPI.appStorage` with string serialization.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://series-1.gitbook.io/rundot-docs/v5.21.0/readme/bignumbers.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
