Skip to content

Latest commit

 

History

History
126 lines (96 loc) · 39.7 KB

lib-spec.md

File metadata and controls

126 lines (96 loc) · 39.7 KB

Prime Math Library Specification

Introduction

The Prime Math Library is a JavaScript library that implements the Prime Framework for numbers. It provides a comprehensive system for representing and manipulating integers in a base-independent way, ensuring unique, factorization-based representations and geometric consistency. The library introduces Universal Numbers – integers encoded by their complete digit expansions across all bases – and supports operations on these numbers while preserving their intrinsic prime factor structure. It is designed to resemble standard JavaScript math interfaces (like Math and BigInt) in usability, but with far greater precision and structural insight. The specification below details each component of the library, ensuring no ambiguity for implementors and full compliance with Prime Framework principles.

Universal Number Representation

Universal Numbers are the cornerstone of the library. Each natural number ( N ) is represented in a universal coordinate system that encodes all its positional digit expansions (for every base ( b \ge 2 )) simultaneously. In practice, this means that the library does not store a number solely as a binary, decimal, or any single-base value. Instead, it maintains a representation that captures the full set of base-specific digits of ( N ). Formally, the universal embedding of ( N ) is the collection of its digit sequences in every base. This collection can be thought of as a graded tuple of digits: one sequence of digits for base-2, another for base-3, base-4, and so on, all unified into one object.

Internally, the library encodes this universal representation as a structured object, UniversalNumber. Each UniversalNumber contains data corresponding to the number’s expansion in various bases. In a practical implementation, it is sufficient to store the number’s prime factorization as the canonical universal coordinate, since prime factors uniquely determine all base expansions. The library treats the list of prime-exponent pairs (the fundamental theorem of arithmetic representation) as the number’s intrinsic coordinates. This captures the “arithmetic essence” of the number: from these universal coordinates, one can derive the number’s digits in any base, and conversely any standard representation (binary, decimal, etc.) can be converted into this prime-based form via factorization.

Key properties of Universal Numbers include:

  • Base-Invariance: A UniversalNumber is base-agnostic. It acts as a "universal descriptor" for the integer, from which any base-specific representation can be obtained if needed. This ensures interoperability across numeral systems – the universal representation subsumes all others.
  • Canonical Encoding: For each natural number ( N ), there is a unique canonical embedded form ( \hat{N} ) in the library’s internal algebra. All possible representations of ( N ) (in any base) collapse to this one object, enforced by the framework’s coherence constraints (see below). The library will always produce this unique form for a given integer, avoiding duplicates or ambiguous encodings.
  • Large Number Handling: The universal representation can efficiently handle extremely large integers. By storing structural information (like prime factors) rather than just a raw bit string, the library can manipulate numbers with magnitude well beyond typical bounds. There is no inherent limit to the size of ( N ) that can be represented, apart from memory and time constraints. This goes beyond JavaScript’s native BigInt in that it keeps factored information, enabling higher precision operations and insights into the number’s structure.

Intrinsic Primes and Primality Testing

An intrinsic prime is a number that cannot be factored into smaller natural numbers within the Prime Framework’s algebraic structure. Formally, an embedded number ( \hat{N} ) (for ( N > 1 )) is intrinsically prime if whenever it is expressed as a product ( \hat{N} = \hat{A} \cdot \hat{B} ) (with ( \hat{A}, \hat{B} ) being embeddings of some natural numbers ( A, B > 1 )), then either ( A = 1 ) or ( B = 1 ). In plain terms, ( \hat{N} ) has no non-trivial factors in the framework’s algebra — it is “prime” in the intrinsic sense, mirroring the concept of an indivisible prime number in standard arithmetic.

The library will implement functionality to identify and work with intrinsic primes:

  • Intrinsic Primality Test: A method UniversalNumber.isIntrinsicPrime() will determine if a given UniversalNumber represents an intrinsic prime. This will typically involve checking if the underlying natural number has any divisors other than 1 and itself. By the Prime Framework definition, this is equivalent to the usual primality test on the natural number ( N ) because the framework’s intrinsic primes correspond to classical prime numbers. The library should use an efficient primality testing algorithm (e.g., Miller-Rabin or other probabilistic tests for large numbers) or leverage its prime factor data (if already computed) to decide primality.
  • Intrinsic Prime Encoding: When an integer is identified as prime, its UniversalNumber representation is marked or flagged internally as an intrinsic prime. This may simply be a byproduct of storing its prime factorization – a prime number ( p ) will have a factorization consisting of just ( p^1 ). The library ensures that such representations cannot be further factored.
  • Prime Generation (Optional): For convenience, the library may provide an iterator or generator for intrinsic primes (e.g., PrimeMath.nextPrime(after: UniversalNumber) to get the next intrinsic prime after a given value). This would utilize classical prime generation techniques but yield results as UniversalNumber objects.

Unique Factorization of Numbers

Every non-unit UniversalNumber (natural number greater than 1) in the library can be decomposed uniquely into a product of intrinsic primes. This is the Prime Framework’s analog of the Fundamental Theorem of Arithmetic: given any embedded number ( \hat{N} ), there exist intrinsic primes ( \hat{p}_1, \hat{p}_2, \ldots, \hat{p}_k ) such that ( \hat{N} = \hat{p}_1 \cdot \hat{p}_2 \cdot \cdots \hat{p}_k ), and this factorization is unique up to the order of factors. The library must enforce and leverage this unique factorization property:

  • Factorization Method: A core feature is UniversalNumber.factorize(), which returns the unique set of prime factors of the number. The output can be an array of UniversalNumber instances each representing an intrinsic prime, or a list of pairs ((p, e)) indicating prime base p with exponent e. For example, factoring the universal number for 360 might return ({(2,3), (3,2), (5,1)}), since (360 = 2^3 \cdot 3^2 \cdot 5^1). This corresponds to the universal coordinates of 360. Internally, this factorization can be derived from the stored universal representation (if it’s stored as prime factors already, this is trivial; if not, an integer factorization algorithm will be applied).
  • Uniqueness Guarantee: The library ensures that repeated calls to factorization yield the same result consistently (the factors may be sorted by size to have a deterministic order). Thanks to the coherence inner product enforcement (see below), the representation ( \hat{N} ) is canonical and thus there is no ambiguity in what the prime factors are. Even if a number were constructed in two different ways, the library will reconcile it to the same prime factor set. Unique factorization is not just assumed but is a consequence of how numbers are embedded.
  • Composite Number Construction: The library can also take a set of intrinsic primes and compose them into a composite UniversalNumber. For example, a function UniversalNumber.fromPrimeFactors([{p: 2, e: 3}, {p: 3, e: 2}, {p: 5, e: 1}]) would output the UniversalNumber for 360. Internally, it will multiply the given prime powers to form the new number’s representation. This multiplication should adhere to the intrinsic algebra’s multiplication (which for our purposes is the same as normal integer multiplication, but done via combining exponents). The result is then automatically kept in canonical form (which it will be, since multiplying primes yields a unique factor set).
  • Factorization Depth: The library’s factorization routine must handle very large numbers robustly. For extremely large integers (far beyond standard 64-bit ranges), advanced algorithms like general number field sieve or Pollard’s rho might be necessary. Implementors should carefully choose algorithms or heuristics to balance performance and correctness. The goal is to support factorizing numbers as large as those encountered in universal coordinates for large objects (potentially hundreds or thousands of digits, if not more).

Topological and Geometric Framework

One distinguishing aspect of the Prime Framework is that it situates numbers in a geometric and algebraic context. Each number’s universal representation ( \hat{N} ) is conceived as living in an algebraic fiber attached to a point on a smooth reference manifold ( M ). In practical terms for the library, this means every UniversalNumber is associated with an underlying algebra (typically modeled conceptually as a Clifford algebra ( C_x ) at some fixed reference point ( x \in M )). The graded components of this algebraic object hold the number’s digits in various bases, effectively giving a geometric interpretation to the number’s different representations.

While the library operates primarily as a math tool, it is designed to be compatible with these geometric interpretations:

  • Fixed Reference Frame: The implementation can assume a fixed reference point (and thus a fixed algebra ( C_x )) for all numbers, since all operations occur within the same global context. The Prime Framework axioms guarantee that any choice of reference is equivalent up to symmetry transformations. For simplicity, the library does not require users to manage multiple frames; instead, it implicitly works in the “universal coordinate space” which serves as a stand-in for a fiber at a chosen reference.
  • Clifford Algebra Structure: The library does not need to explicitly implement a full Clifford algebra, but it should adhere to the idea of graded components. In practice, the universal number can be thought of as a multi-grade structure (e.g., an object that could conceptually be decomposed into grade-1 for base-2 digits, grade-2 for base-3 digits, etc.). The specifics of Clifford multiplication and addition correspond in our context to the usual operations on those digit sequences. Implementors can treat the internal representation as a formal direct sum of components. However, explicit operations on individual graded components are typically not exposed to the end user; they are handled behind the scenes to ensure consistency.
  • Geometric Transformations (G-action): The Prime Framework allows a symmetry group ( G ) to act on ( M ) and carry representations from one fiber to another. In the library, all numbers are by default in the same fiber, so no transformation is needed for standard operations. However, it is important that the library’s design would in principle allow consistent interpretation under such transformations. This means if in the future numbers were tagged with a location or frame, converting a UniversalNumber from one reference to another (via a group action) should not change its abstract value or factorization – only the coordinates of the graded components might permute according to the algebra isomorphism. Since we operate at a single reference, we naturally satisfy consistency: all users see the same canonical form of a number regardless of context.
  • Smooth Parameterization: The term "smooth transformations" implies that changes in the reference manifold point could vary continuously. While not directly implemented in a discrete library, this concept assures that there is no sudden change or ambiguity in number representations. The library’s representations are coherent (see next section) and smoothly vary (trivially constant in our fixed frame scenario). Therefore, implementors should not introduce discontinuous choices or randomization in the number representation; it should be deterministic and smooth by design.

By aligning with these topological and geometric principles, the library remains faithful to the Prime Framework’s spirit. The result is that even though a typical user will just see a number object, behind the scenes it conforms to an algebraic structure that could be interpreted in geometric terms (for advanced users or theoretical extensions). This ensures future-proofing: any extension of the library to multi-fiber or geometric contexts would require no fundamental change in how numbers are stored or manipulated, only additional metadata about reference frames if needed.

Coherence Inner Product and Norm

At the heart of the Prime Framework is the coherence inner product on each fiber algebra ( C_x ). This positive-definite inner product ( \langle\cdot,\cdot\rangle_c ) has a crucial role: it penalizes any discrepancies in representing the same abstract number in different ways. Concretely, if an abstract number could be encoded by two different algebra elements, the one with any inconsistency across bases (digit expansions not aligning to the same number) would have a larger norm. The unique minimal-norm representation corresponds to the fully consistent encoding of the number. This is how the framework ensures each number has a single canonical embedding.

In the library specification, the coherence inner product influences design rather than requiring explicit computation:

  • Unique Canonical Representation: Thanks to the coherence norm principle, the library will always choose the canonical form of a number for storage. For implementors, this means whenever an operation could yield multiple representations (which is rare in practice except for how one might derive the same number via different paths), the library must reconcile the result to the unique normalized form. In practical terms, this often boils down to simplifying fractions, combining like terms, or re-sorting factors. For example, if some internal operation produced two copies of the same prime factor in different components, the library should combine them into one (which reduces the "norm" by eliminating redundancy).
  • Inner Product Availability: If needed for advanced functionality, the library can expose a method to compute the inner product between two UniversalNumber objects. For instance, UniversalNumber.innerProduct(a, b) could be defined to reflect ( \langle \hat{a}, \hat{b} \rangle_c ). This would involve summing products of corresponding components of the two numbers' graded representations. However, unless there is a specific use-case, this is largely theoretical. The main point is that the library’s data structures would allow such a calculation (because each number’s components in each grade/base are accessible in principle).
  • Minimal Norm Enforcement: When constructing a UniversalNumber or performing operations, the library should internally enforce consistency that correlates with minimal norm. For example, if a number is given only in one base and not others, the library will implicitly fill out or adjust the representation so that it is valid across all bases. This might involve computing the prime factorization to ensure the digit sequences for all other bases are coherent. The end effect is that any UniversalNumber instantiated or returned by the library is guaranteed to be the minimal-norm, fully coherent embedding of that integer. Developers implementing the library should note that the coherence inner product’s role is to guide these design choices: always cohere multiple pieces of information about a number into one consistent whole before presenting it to the user.

In summary, while the average use of the library won’t involve directly calculating norms or inner products, the concept ensures the correctness and uniqueness of representations. Implementors must be mindful that behind every UniversalNumber lies the principle that “if it were represented in any other way, it would only be ‘less optimal’”. Thus, the library consistently returns the simplest, most unified form for each number, matching the theoretical minimal-norm criterion.

Arithmetic and Algebraic Operations

The Prime Math Library supports a full range of arithmetic and algebraic operations on UniversalNumber objects, leveraging their intrinsic representations. All operations are defined to be consistent with the intrinsic algebra (mirroring how operations would work on the embedded elements ( \hat{N} ) in the fiber algebra) and align with the Prime Operator construction principles. The goal is to make calculation with UniversalNumber as seamless as using built-in JavaScript numbers, but with the additional guarantees of the Prime Framework.

Key operations and how they are handled:

  • Addition (a.add(b) or a.plus(b)): The sum of two universal numbers ( \hat{A} + \hat{B} ) should yield the universal representation of the integer ( A+B ). Because the intrinsic representation is unique and factorization-based, addition is not as straightforward as multiplication (which just combines factors). The library will handle addition by converting the operands to a common concrete form (likely a big integer internally) to perform the addition, and then re-embedding the result as a new UniversalNumber. In doing so, it ensures the result is in canonical form (e.g. by immediately factorizing ( A+B ) to get its prime coordinates). This operation should preserve full precision – if ( A ) and ( B ) are huge, their sum is computed exactly with no overflow. The time complexity will depend on the size of the numbers (it may rely on BigInt or a similar mechanism for the raw addition step).
  • Subtraction (a.subtract(b)): Similarly, difference ( A - B ) is computed by converting ( \hat{A} ) and ( \hat{B} ) to a concrete numeric form or using their internal factor data to derive the result. The output is re-embedded as a UniversalNumber. If ( B > A ), the result would conceptually be a negative number; since the Prime Framework primarily addresses natural numbers, the library can either extend UniversalNumber to represent negative integers (with a sign flag) or else throw/indicate an error for invalid (negative) results. This specification focuses on natural numbers, but implementors should document how they handle cases like subtraction resulting in negatives.
  • Multiplication (a.multiply(b) or a.times(b)): The product ( \hat{A} \cdot \hat{B} ) corresponds directly to multiplying the two numbers’ prime factorizations. The library will implement multiplication by merging the prime exponent lists of a and b. For each prime ( p ) that appears in either factor, its exponents are added (as exponents in the factorization, not to be confused with adding the numeric values). The result is thus immediately obtained in universal coordinate form: if ( A = \prod p_i^{e_i} ) and ( B = \prod p_i^{f_i} ) (with the index set merged appropriately), then ( A \cdot B = \prod p_i^{e_i + f_i} ). This operation is extremely precise and leverages the intrinsic representation. The result is again stored as a UniversalNumber with the combined prime factors. No rounding or approximation occurs. This intrinsic operation aligns with the Prime Operator’s multiplicative structure, ensuring that the factorization (divisor structure) of the result is exactly the combination of the operand structures.
  • Division (a.divide(b)): For quotient ( A / B ), if the division is exact (i.e. ( B ) divides ( A ) in the integers), the library can perform it by subtracting prime exponents (the inverse of multiplication). If ( B ) is not a divisor of ( A ), then ( A/B ) is not a natural number; the library might either extend to rational numbers or simply indicate that the result is not an integer (perhaps by returning undefined or throwing an error). In the case of exact division, if ( A = \prod p_i^{e_i} ) and ( B = \prod p_i^{f_i} ), then ( A/B = \prod p_i^{e_i - f_i} ) (verifying that ( e_i - f_i ) are all non-negative, otherwise the division wasn't exact in ( \mathbb{N} )). The result is then embedded as a UniversalNumber of that quotient. Division, when exact, also maintains the intrinsic structure seamlessly.
  • Exponentiation (a.pow(n)): Exponentiating a universal number by a natural exponent ( n ) can be achieved by scaling its exponents. If ( A = \prod p_i^{e_i} ), then ( A^n = \prod p_i^{e_i \cdot n} ). The library will implement power operations by multiplying the exponent values by the exponent ( n ). This again yields an immediate prime factor representation for the result. Care should be taken for large ( n ) as exponents might grow very large; however, the operation remains exact and is limited only by the ability to handle large integers for the exponents.
  • Greatest Common Divisor (gcd(a, b)) and Least Common Multiple (lcm(a, b)): Because of the prime factor representation, computing GCD and LCM is straightforward. The GCD of two UniversalNumber objects can be obtained by taking the minimum exponent of each prime that appears in both numbers. Similarly, the LCM takes the maximum exponent of each prime. The library should include gcd and lcm functions that operate on UniversalNumber inputs and return a new UniversalNumber result. These operations highlight the strength of the intrinsic representation: they can be done by direct inspection of the prime coordinates without needing to perform large integer operations.
  • Comparison Operations: It should be possible to compare UniversalNumber objects for equality, and potentially for ordering (<, <=, etc.). Equality can be checked by comparing the prime factor maps (or by comparing the BigInt values if available). For ordering, since every UniversalNumber can produce a BigInt or numeric value, the library can compute those for comparison when needed. Alternatively, a custom compareTo method can be used which multiplies out factors in a controlled manner or uses logarithmic estimates to decide order if the numbers are extremely large.

All these operations must be carefully implemented to ensure they respect the coherence and canonical representation rules. For example, after any arithmetic operation, the result should be immediately reduced to canonical form (i.e., factorized and simplified) so that it remains a valid UniversalNumber. There should never be a case where a UniversalNumber internally holds an inconsistent state (such as two different representations of the same underlying integer). This consistency mirrors the idea that the Prime Operator (a theoretical linear operator encoding divisor relations) would operate on a space where each number has one representation. In effect, operations in the library correspond to operations in the abstract algebra of embedded numbers, which is designed to reflect normal arithmetic but within the Prime Framework’s unique coordinate system.

Operator Overloading and Syntax (Optional): JavaScript does not natively support operator overloading, but if using a transpiler or in documentation examples, it might be shown that +, -, *, ** etc. work with UniversalNumber. Realistically, the library will provide methods (as described above) or static functions in a PrimeMath namespace to perform these operations. Ensuring the interface feels familiar means, for example, providing UniversalNumber.add() or a static PrimeMath.add(a, b) similar to BigInt usage, and possibly valueOf/toString overrides so that String(univNum) or template strings produce meaningful output.

Compatibility with Standard JavaScript Math Interfaces

To lower the learning curve and integrate well with existing code, the Prime Math Library maintains a familiar interface akin to standard JavaScript number handling (Number, BigInt, Math). While the internal mechanics are far more advanced, from a developer’s perspective, using UniversalNumber should resemble using built-in numeric types:

  • Literals and Initialization: The library may allow a special notation or provide factory methods for easy creation. For example, UniversalNumber(123) or UniversalNumber.from(123) could construct the universal representation of 123. Additionally, converting from a BigInt or string is essential for large values (e.g., UniversalNumber.from("98765432101234567890")). The library should automatically perform the necessary factorization or embedding steps on initialization.
  • Basic Arithmetic Usage: As mentioned, arithmetic operations are exposed in a straightforward manner – either as methods on the UniversalNumber class or as static functions. Developers should not need to manage the intricacies of prime coordinates; they can call a.add(b) or PrimeMath.add(a, b) and get the correct result. Chaining operations (e.g., a.add(b).multiply(c)) should be supported and yield a UniversalNumber result at each step.
  • Interoperability with BigInt/Number: It should be possible to seamlessly convert between UniversalNumber and JavaScript’s native numeric types when needed. For instance:
    • UniversalNumber.toBigInt() returns a BigInt with the same value.
    • UniversalNumber.toNumber() returns a Number (if it’s small enough to fit in JavaScript’s double precision range; otherwise this might throw or produce Infinity/approximation).
    • Possibly provide valueOf() such that using a UniversalNumber in a context that expects a primitive triggers conversion to BigInt or Number. (Care must be taken because automatic conversion could be slow for huge numbers; maybe only convert to BigInt which can handle arbitrary size.)
  • Math Library Functions: While many Math functions (like trigonometry, logarithms, etc.) are beyond the scope of an integer-focused library, some standard functions could be provided:
    • PrimeMath.abs(univNum) to get absolute value (especially if negatives are allowed).
    • PrimeMath.min(...univNums) and PrimeMath.max(...univNums) to pick extremes from a list.
    • PrimeMath.pow(univNum, exponent) as an alternative to univNum.pow(exponent).
    • PrimeMath.randomIntrinsicPrime(nBits) (if randomness is allowed) to generate a random prime of a specified size.
    • Other number-theoretic functions like PrimeMath.isPrime(univNum) (which would call the intrinsic prime test) or PrimeMath.totient(univNum) if such advanced functions are desired.
  • Error Handling: Similar to built-in operations that throw (for example, division by zero), the library should throw exceptions on invalid operations (like dividing by a non-divisor, or providing malformed input to a constructor). Errors should be instances of a PrimeMathError (or a standard Error) with clear messages, so developers know what constraint was violated.

The library’s API documentation should mirror the style of MDN’s documentation for BigInt and Math where possible, to aid familiarity. For example, specifying that UniversalNumber is akin to BigInt in usage (immutable, arbitrary precision integers) but with added structure. Where differences exist (like the need to asynchronously factor very large numbers, perhaps), they should be highlighted clearly. Overall, by keeping the interface conventional, the library ensures that users can adopt it without steep learning curve, focusing on the enhanced precision and capabilities transparently.

Precision and Robustness

One of the primary motivations for the Prime Math Library is to provide extreme precision and robust handling of very large integers, beyond what typical JavaScript engines comfortably support with Number or even BigInt. While BigInt can represent arbitrarily large integers in theory, operations on very large BigInt values can become slow and memory-intensive, and BigInt does not give any insight into the number’s structure. The Prime Math Library addresses these issues by using the intrinsic prime representation and careful algorithms:

  • Arbitrary Size Integers: The library imposes no fixed upper bound on the magnitude of numbers. Whether an integer has 100 digits or millions of digits, it can be represented as a UniversalNumber (assuming enough memory and time to process it). The use of prime factor coordinates means that if a number has a special form (e.g., it is a product of small primes raised to high powers), it can be stored compactly. Even if a number is itself prime and millions of digits long, the library will handle it — though the prime would be stored essentially as that large value itself since it has no smaller factors.
  • Enhanced Precision Arithmetic: All arithmetic in the library is exact. There is no rounding error or loss of precision, since we are dealing with integers via their exact prime decompositions. When converting to floating-point (if that ever happens via toNumber()), standard IEEE754 considerations apply, but within the library’s domain (natural numbers and their factor structures), computations are lossless. This means the library is suitable for cryptography, computational number theory, and any application where large exact integers are needed.
  • Performance Considerations: Implementors should ensure algorithms are optimized for large inputs:
    • Multiplication of two numbers is efficient if their prime factorizations are known (just add exponents). If factorization for one or both operands is not known yet, the library may need to factor them first (for example, if one UniversalNumber was created from a BigInt without factoring immediately, lazy factorization strategies might be used). It's recommended to always factorize upon creation to maintain the canonical form.
    • Addition and subtraction are less straightforward with factorization, so they might rely on BigInt under the hood. If so, care must be taken that the BigInt can handle the size; this might involve using multiple BigInts for extremely huge numbers (though JavaScript BigInt itself can handle very large values, it may be slow for multi-billion-bit numbers). In extreme cases, implementors might consider using FFT-based big integer addition or other high-precision arithmetic libraries that are Prime Framework–compatible (ensuring they don't lose information).
    • Factorization of large numbers is the toughest operation in terms of performance. The library should document the complexity and possibly provide tuning parameters or alternate strategies. For instance, if a number is astronomically large (as might occur when interfacing with a 512-bit digest or larger), the library might not automatically factor it fully if not necessary. It could carry a partially factored state or recognize special forms. However, to avoid ambiguity, the simplest approach is to always factor completely (guaranteeing the uniqueness property).
  • Robustness and Edge Cases: The library must gracefully handle edge cases:
    • The number 1 should be representable as a UniversalNumber (it is the multiplicative identity). Its prime factorization is an empty set or all exponents zero. The library should recognize 1 as a special intrinsic value (not prime, but also not composite).
    • Zero (0) is not a natural number in the typical Prime Framework context (which starts at 1), but if the library chooses to support 0, it must be handled carefully (0 has no valid prime factorization and cannot be embedded via the same scheme since it’s the additive identity, not multiplicative). It may be simplest to exclude 0 or treat it separately.
    • Negative integers, if supported, should carry a sign flag but otherwise use the same absolute value representation for the factorization. The library could represent -N by a sign bit and the universal coordinates of N. Since the Prime Framework discussion centers on natural numbers, negativity is an extension beyond the core theory.
    • Extremely large prime numbers (with hundreds of thousands of digits) should be handled in the sense that they can be stored and used in operations, but factorization will just detect that they are prime (likely via a primality test) and store them as a single prime factor. Users should be aware that factoring such numbers is infeasible – the library’s prime test would identify them as prime rather than attempt factorization.
    • Memory usage: Storing a number as a list of primes and exponents is usually efficient (especially if many primes are small). But a pathological case is a number that is the product of many small primes (e.g., primorials or highly composite numbers). The library must be able to handle a very long list of prime factors. Implementors should use efficient data structures (perhaps a map from prime to exponent, rather than a flat list of repeated primes) to store factorizations.

In essence, the library extends the precision of integer arithmetic to the limits of current computational feasibility and does so in a structured way. By avoiding floating-point representation entirely and improving on plain big integers with a factorized form, it ensures that if a computation is theoretically possible (no matter how large the numbers involved), the library can perform it given enough resources, without any internal overflow or approximation. This makes the Prime Math Library suitable for high-precision tasks and guarantees that numerical robustness is preserved: the only errors that can occur are due to explicit limits (like running out of memory or time), never due to the library representing a number incorrectly.

Conversion and Interoperability Utilities

To facilitate working between conventional representations and the Prime Math Library’s UniversalNumber objects, a suite of conversion utilities is provided. These ensure that users can easily bring data into the Prime Framework format and extract results out of it, without ambiguity or loss.

  • Standard to Universal Conversion: The library offers functions to convert standard numbers into the universal format:
    • UniversalNumber.fromNumber(n: number): Takes a JavaScript Number (floating-point). This will typically floor the number if it’s not an integer, or throw an error if the number is not safe (above 2^53). It produces a UniversalNumber by converting the number to a BigInt and then factorizing.
    • UniversalNumber.fromBigInt(b: BigInt): Takes a BigInt and returns its universal representation. This is a crucial method since BigInt is how JavaScript represents large integers. The implementation will factor the BigInt to determine all prime exponents (the universal coordinates). If b is 0 or negative, the method should handle those appropriately (0 might be disallowed as noted, negative handled via sign).
    • UniversalNumber.fromString(str: string, base?: number): Parses a string representing a number in a given base (default base 10) and returns a UniversalNumber. This is useful to directly accept binary (base=2), hexadecimal (base=16), or other base inputs. The parse can utilize BigInt (e.g., BigInt("0b101010")) for efficiency, then proceed to factorize.
    • In all these conversions, the result is a fully canonical UniversalNumber. The algorithms essentially perform the factorization of the input integer. The conversion functions should be optimized and possibly run in worker threads or asynchronously if factoring large inputs (to avoid blocking the main thread in a JS environment).
  • Universal to Standard Conversion: Conversely, the library allows extracting conventional representations from a UniversalNumber:
    • univNum.toBigInt(): Returns a BigInt equal to the value of the UniversalNumber. This essentially requires multiplying out the prime factors. The library will compute ( \prod p_i^{e_i} ) using big integer multiplication. This operation can be heavy if the number has a huge magnitude (e.g., thousands of digits), but it will produce a correct BigInt result. If the number is extremely large (beyond what the host environment can realistically handle in memory as a single BigInt), the library should document this limitation. (However, JavaScript BigInt is theoretically unbounded, so the main limit is memory.)
    • univNum.toString(base?: number): Returns a string of the number in the specified base. This is similar to BigInt’s toString method with a radix parameter. Internally, to generate this, the library could either use the BigInt from toBigInt() and then convert, or implement its own conversion using the prime factors (which might be more complex but could be done via repeated division algorithm for a given base). Since base conversion is a common requirement, it should be well-supported. Notably, because the universal representation already contains all bases, there might be an opportunity to derive the base representation directly: recall that the universal representation conceptually contains the digit sequence for every base. The library could in theory store or compute the digits for each base on demand. But that is likely impractical for arbitrary bases; instead, computing digits via division is fine.
    • univNum.toJSON(): It might be useful to define a JSON serialization, which could output the prime factor list or a standardized coordinate format. This would allow easy interoperability or debugging (for example, an object like { "type": "UniversalNumber", "primeFactors": {"2":3, "3":2, "5":1} } for 360).
  • Universal Coordinates Access: A method like univNum.getCoordinates() can return a structured representation of the universal number’s coordinates. For instance, it could return an array of prime-exponent pairs sorted by prime, or an object mapping each prime to its exponent. This gives users direct access to the factorization. It’s essentially the result of factorization but without recomputation. Since the library likely stores this internally, this is just exposing it.
  • Base-specific Digit Extraction: Although the universal number encodes all bases, the library might provide a utility to get the digit expansion in a particular base without full conversion to a string:
    • univNum.getDigits(base: number): number[]: Returns an array of digits of the number in the specified base (with the least significant digit first or last as documented). This can be done via division by the base repeatedly. Having this low-level access could be useful for certain algorithms or visualizing the number in various bases. It leverages the universal nature in that any base’s digits are well-defined from the one universal value.
  • Round-trip Consistency: Conversions should be fully reversible. If one converts a standard number to a UniversalNumber and back, the result should be identical to the original input (provided the original was an integer within representable range). Similarly, converting a UniversalNumber to a BigInt or string and re-importing it should yield an equivalent UniversalNumber. This is crucial for trust in the library; tests should cover many such round trips, including edge cases like 1, primes, powers of primes, products of large primes, etc., to ensure no information is lost or altered.

These utilities make the library practical in real-world scenarios. They ensure that while UniversalNumber might be a new type, it can interface with all the usual data formats developers encounter. By encapsulating the conversion logic, the library also centralizes the complex task of factorization and base encoding, so that users and even other library components don’t have to reimplement those – they can rely on these provided methods. All conversion and output routines must be rigorously tested for correctness, given that any mistake in conversion could violate the no-ambiguity principle.