/**
* Configuration system for the math-js library
* Central storage and management of all configurable settings and limits
* @module config
*/
/**
* Default configuration for the library
* Contains all settings and their default values
* @type {Object}
*/
const defaultConfig = {
/**
* Performance profile for the library
* - "balanced": Default profile balancing speed and precision
* - "speed": Optimize for speed with potential precision trade-offs
* - "precision": Maximum precision regardless of performance impact
* @type {string}
*/
performanceProfile: 'balanced',
/**
* Controls caching behavior for computational results
* @type {Object}
*/
cache: {
/**
* Whether to enable caching of computational results
* @type {boolean}
*/
enabled: true,
/**
* Maximum size of the cache in bytes (approximate)
* @type {number}
*/
maxSize: 1024 * 1024 * 10, // 10MB default
/**
* Eviction policy for cache ("lru", "fifo", "random")
* @type {string}
*/
evictionPolicy: 'lru',
/**
* Maximum number of entries in the prime number cache
* @type {number}
*/
maxPrimeCacheSize: 100000,
/**
* Maximum number of entries in the factorization cache
* @type {number}
*/
maxFactorizationCacheSize: 1000,
/**
* Whether to use persistent caching (if available in the environment)
* @type {boolean}
*/
persistentCache: false,
/**
* Time-to-live for cache entries in milliseconds (0 = no expiry)
* @type {number}
*/
ttl: 0
},
/**
* Controls factorization behavior
* @type {Object}
*/
factorization: {
/**
* Whether to compute factorization lazily
* @type {boolean}
*/
lazy: true,
/**
* Maximum size (in digits) for which to attempt complete factorization
* @type {number}
*/
completeSizeLimit: 100,
/**
* Algorithm to use for factorization ("auto", "trial", "pollard", "quadratic", etc.)
* @type {string}
*/
algorithm: 'auto',
/**
* Maximum time (in milliseconds) to spend on a factorization attempt (0 = no limit)
* @type {number}
*/
timeLimit: 0,
/**
* Memory limit (in MB) for factorization operations (0 = no limit)
* @type {number}
*/
memoryLimit: 0,
/**
* Maximum number of iterations for probabilistic factorization algorithms
* @type {number}
*/
maxIterations: 1000000,
/**
* Thresholds for factorization method selection based on number size (digits)
* Controls when to switch between different factorization algorithms
* @type {Object}
*/
thresholds: {
/**
* Maximum digit size for using simple trial division
* Numbers up to this size will use basic trial division
* @type {number}
*/
trialDivision: 6,
/**
* Maximum digit size for using optimized trial division with precomputed primes
* Numbers up to this size will use trial division with cached primes
* @type {number}
*/
optimizedTrialDivision: 12,
/**
* Maximum digit size for using Pollard's Rho algorithm
* Numbers up to this size will use Pollard's Rho
* @type {number}
*/
pollardRho: 25,
/**
* Maximum digit size for using Elliptic Curve Method (ECM)
* Numbers up to this size will use ECM
* @type {number}
*/
ecm: 50,
/**
* Maximum digit size for using Quadratic Sieve
* Numbers up to this size will use Quadratic Sieve for factorization
* @type {number}
*/
quadraticSieve: 100
},
/**
* Elliptic Curve Method (ECM) specific settings
* @type {Object}
*/
ecm: {
/**
* Default maximum number of curves to try in ECM
* Higher values increase chance of finding factors but take longer
* @type {number}
*/
maxCurves: 100,
/**
* Default B1 bound for stage 1 of ECM
* Larger values can find larger factors
* @type {number}
*/
defaultB1: 100000,
/**
* Default B2 bound for stage 2 of ECM (0 = auto-calculate as B1 * 100)
* Larger values can find larger factors
* @type {number}
*/
defaultB2: 0,
/**
* Maximum memory (in MB) to use for ECM stage 2
* Higher values improve performance but increase memory usage
* 0 = no specific limit (use system available memory)
* @type {number}
*/
maxMemory: 0,
/**
* Base factor for scaling B1 parameter based on input size
* B1 is multiplied by this factor raised to a power based on number size
* @type {number}
*/
b1ScaleFactor: 1.1,
/**
* Default memory (in MB) for ECM stage 2 when no limit is specified
* @type {number}
*/
defaultMemory: 100
}
},
/**
* Controls behavior of asynchronous operations
* @type {Object}
*/
async: {
/**
* Whether to use WebWorkers when available
* @type {boolean}
*/
useWorkers: true,
/**
* Default timeout for async operations in milliseconds (0 = no timeout)
* @type {number}
*/
defaultTimeout: 30000,
/**
* Whether to report progress events for long-running operations
* @type {boolean}
*/
reportProgress: true,
/**
* Maximum number of concurrent workers for parallel operations
* @type {number}
*/
maxWorkers: 4
},
/**
* Controls memory usage and optimization
* @type {Object}
*/
memory: {
/**
* Whether to optimize memory usage at the expense of performance
* @type {boolean}
*/
optimizeMemory: false,
/**
* Whether to use compact representations for storage
* @type {boolean}
*/
useCompactRepresentation: false,
/**
* Maximum memory usage limit in MB (0 = no explicit limit)
* @type {number}
*/
maxMemoryUsage: 0,
/**
* Garbage collection strategy ("auto", "aggressive", "conservative")
* @type {string}
*/
gcStrategy: 'auto'
},
/**
* Controls primality testing behavior
* @type {Object}
*/
primalityTesting: {
/**
* Number of Miller-Rabin rounds for primality testing
* Higher values give more confidence for large numbers
* @type {number}
*/
millerRabinRounds: 40,
/**
* Maximum size (in digits) for deterministic primality testing
* Larger numbers will use probabilistic tests
* @type {number}
*/
deterministicTestLimit: 20,
/**
* Whether to use trial division before advanced primality tests
* @type {boolean}
*/
useTrialDivision: true,
/**
* Size of each segment for the segmented sieve of Eratosthenes algorithm
* Controls memory usage vs. performance tradeoff
* @type {number}
*/
segmentedSieveSize: 1000000,
/**
* Whether to dynamically adjust segment size based on range size
* and available system resources
* @type {boolean}
*/
dynamicSegmentSizing: true,
/**
* Size of each chunk for the basic sieve when handling large ranges
* Controls memory usage vs. performance tradeoff
* @type {number}
*/
basicSieveChunkSize: 1000000,
/**
* Maximum number of primes to generate in a single operation
* Acts as a safety limit to prevent memory exhaustion
* @type {number}
*/
maxPrimesGenerated: 10000000,
/**
* Threshold for using different primality testing algorithms
* Numbers below this threshold use simple primality test
* Numbers above this threshold use Miller-Rabin test
* @type {number}
*/
verificationThreshold: 1000000
},
/**
* Controls number conversion behavior
* @type {Object}
*/
conversion: {
/**
* Maximum size (in digits) for direct conversion without chunking
* @type {number}
*/
directConversionLimit: 1000,
/**
* Default base for number conversion operations (when not specified)
* @type {number}
*/
defaultBase: 10,
/**
* Whether to cache conversion results
* @type {boolean}
*/
cacheResults: true,
/**
* Minimum allowable base for conversions
* @type {number}
*/
minBase: 2,
/**
* Maximum allowable base for conversions
* @type {number}
*/
maxBase: 36
},
/**
* Controls error handling and reporting
* @type {Object}
*/
errorHandling: {
/**
* Whether to include stack traces in errors
* @type {boolean}
*/
includeStackTrace: true,
/**
* Level of detail in error messages ("minimal", "standard", "verbose")
* @type {string}
*/
verbosity: 'standard',
/**
* Whether to throw on potentially recoverable errors
* @type {boolean}
*/
strictMode: false
}
}
// Create a mutable current configuration by copying the default
let currentConfig = JSON.parse(JSON.stringify(defaultConfig))
/**
* Update configuration with custom settings
* @param {Object} options - Configuration options to update
* @returns {Object} The updated configuration object
*/
function configure(options) {
if (!options || typeof options !== 'object') {
throw new Error('Configuration options must be an object')
}
// Helper function to recursively merge objects
function deepMerge(target, source) {
for (const key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
if (
source[key] instanceof Object &&
key in target &&
target[key] instanceof Object
) {
deepMerge(target[key], source[key])
} else {
target[key] = source[key]
}
}
}
return target
}
// Merge the provided options into the current configuration
deepMerge(currentConfig, options)
// Update environment-specific optimizations based on the profile
applyProfileSpecificSettings()
return currentConfig
}
/**
* Apply profile-specific optimizations
* @private
*/
function applyProfileSpecificSettings() {
// Adjust settings based on the selected performance profile
switch (currentConfig.performanceProfile) {
case 'speed':
// Optimize for speed
if (!userConfiguredValue('cache.enabled')) {
currentConfig.cache.enabled = true
}
if (!userConfiguredValue('factorization.lazy')) {
currentConfig.factorization.lazy = true
}
if (!userConfiguredValue('memory.optimizeMemory')) {
currentConfig.memory.optimizeMemory = false
}
break
case 'precision':
// Optimize for precision
if (!userConfiguredValue('factorization.lazy')) {
currentConfig.factorization.lazy = false
}
if (!userConfiguredValue('primalityTesting.millerRabinRounds')) {
currentConfig.primalityTesting.millerRabinRounds = 100
}
if (!userConfiguredValue('errorHandling.strictMode')) {
currentConfig.errorHandling.strictMode = true
}
break
case 'balanced':
default:
// Use default settings
break
}
// Apply memory-specific optimizations
if (currentConfig.memory.optimizeMemory) {
if (!userConfiguredValue('cache.maxSize')) {
currentConfig.cache.maxSize = 1024 * 1024 * 2 // 2MB
}
if (!userConfiguredValue('cache.maxPrimeCacheSize')) {
currentConfig.cache.maxPrimeCacheSize = 10000
}
if (!userConfiguredValue('cache.maxFactorizationCacheSize')) {
currentConfig.cache.maxFactorizationCacheSize = 200
}
}
}
/**
* Check if a specific configuration value has been explicitly set by the user
* @private
* @param {string} path - Dot-separated path to the configuration value
* @returns {boolean} True if the value was explicitly configured
*/
function userConfiguredValue(path) {
// Split the path into parts
const parts = path.split('.')
// Track the default and current config values
let defaultValue = defaultConfig
let currentValue = currentConfig
// Traverse the path
for (const part of parts) {
defaultValue = defaultValue[part]
currentValue = currentValue[part]
// If either value is undefined, the path is invalid
if (defaultValue === undefined || currentValue === undefined) {
return false
}
}
// Check if the value differs from the default
return JSON.stringify(defaultValue) !== JSON.stringify(currentValue)
}
/**
* Reset configuration to default values
* @returns {Object} The reset configuration object
*/
function resetConfig() {
currentConfig = JSON.parse(JSON.stringify(defaultConfig))
return currentConfig
}
/**
* Get the current configuration
* @returns {Object} The current configuration (read-only)
*/
function getConfig() {
// Return a deep copy to prevent external modification
return JSON.parse(JSON.stringify(currentConfig))
}
/**
* Check if the current environment has limited resources
* Used to automatically adjust settings based on platform constraints
* @private
* @returns {boolean} True if the environment has memory or processing constraints
*/
function isLimitedEnvironment() {
// Check for Node.js vs Browser
if (typeof window !== 'undefined') {
// Browser environment
// Check if it's a mobile device (approximate)
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
typeof navigator !== 'undefined' ? navigator.userAgent : ''
)
return isMobile
} else {
// Node.js environment
try {
// Try to get system memory
const os = require('os')
const totalMemory = os.totalmem()
// Consider environments with < 1GB RAM to be limited
return totalMemory < 1024 * 1024 * 1024
} catch (e) {
// If we can't detect, assume it's not limited
return false
}
}
}
// Apply environment-specific adjustments on initial load
if (isLimitedEnvironment()) {
// Apply conservative defaults for limited environments
currentConfig.cache.maxSize = 1024 * 1024 * 2 // 2MB
currentConfig.cache.maxPrimeCacheSize = 5000
currentConfig.cache.maxFactorizationCacheSize = 100
currentConfig.factorization.completeSizeLimit = 50
currentConfig.memory.optimizeMemory = true
}
// Export the configuration system
module.exports = {
configure,
resetConfig,
getConfig,
config: currentConfig
}