Skip to content

Latest commit

 

History

History
323 lines (304 loc) · 9.14 KB

File metadata and controls

323 lines (304 loc) · 9.14 KB

Context

System wants you to give repo map for this file, File path: /Volumes/Indexing/Calculator.ts

Full Content

import { EventEmitter } from 'events'

/**
 * Calculator class for performing basic arithmetic operations with history tracking.
 * @description A comprehensive calculator that supports addition, subtraction, multiplication, 
 * division, and maintains a history of all calculations. Includes error handling for 
 * division by zero and invalid operations.
 */
export class Calculator extends EventEmitter {
  /** 
   * Array to store calculation history
   * @private
   * @type {Array<{operation: string, operands: number[], result: number, timestamp: Date}>}
   */
  private history: Array<{
    operation: string
    operands: number[]
    result: number
    timestamp: Date
  }> = []

  /**
   * Maximum number of calculations to keep in history
   * @private
   * @readonly
   * @default 100
   */
  private readonly maxHistorySize: number = 100

  /**
   * Current calculation result
   * @private
   * @type {number}
   * @default 0
   */
  private currentResult: number = 0

  /**
   * Creates a new Calculator instance
   * @constructor
   * @param {number} [maxHistory=100] - Maximum number of calculations to store in history
   * @example
   * ```typescript
   * const calc = new Calculator(50)
   * const calc2 = new Calculator() // Uses default 100
   * ```
   */
  constructor(maxHistory: number = 100) {
    super()
    this.maxHistorySize = maxHistory
    this.emit('initialized', { maxHistory })
  }

  /**
   * Adds two numbers and stores the result in history
   * @param {number} a - First number to add
   * @param {number} b - Second number to add
   * @returns {number} The sum of a and b
   * @throws {Error} When operands are not valid numbers
   * @example
   * ```typescript
   * const result = calc.add(5, 3) // Returns 8
   * ```
   */
  add(a: number, b: number): number {
    this.validateOperands(a, b)
    const result = a + b
    this.addToHistory('add', [a, b], result)
    this.currentResult = result
    this.emit('calculation', { operation: 'add', operands: [a, b], result })
    return result
  }

  /**
   * Subtracts the second number from the first number
   * @param {number} a - Number to subtract from
   * @param {number} b - Number to subtract
   * @returns {number} The difference of a and b
   * @throws {Error} When operands are not valid numbers
   * @example
   * ```typescript
   * const result = calc.subtract(10, 3) // Returns 7
   * ```
   */
  subtract(a: number, b: number): number {
    this.validateOperands(a, b)
    const result = a - b
    this.addToHistory('subtract', [a, b], result)
    this.currentResult = result
    this.emit('calculation', { operation: 'subtract', operands: [a, b], result })
    return result
  }

  /**
   * Multiplies two numbers
   * @param {number} a - First number to multiply
   * @param {number} b - Second number to multiply
   * @returns {number} The product of a and b
   * @throws {Error} When operands are not valid numbers
   * @example
   * ```typescript
   * const result = calc.multiply(4, 5) // Returns 20
   * ```
   */
  multiply(a: number, b: number): number {
    this.validateOperands(a, b)
    const result = a * b
    this.addToHistory('multiply', [a, b], result)
    this.currentResult = result
    this.emit('calculation', { operation: 'multiply', operands: [a, b], result })
    return result
  }

  /**
   * Divides the first number by the second number
   * @param {number} a - Dividend (number to divide)
   * @param {number} b - Divisor (number to divide by)
   * @returns {number} The quotient of a and b
   * @throws {Error} When divisor is zero or operands are not valid numbers
   * @example
   * ```typescript
   * const result = calc.divide(15, 3) // Returns 5
   * const result2 = calc.divide(10, 0) // Throws Error: Division by zero
   * ```
   */
  divide(a: number, b: number): number {
    this.validateOperands(a, b)
    if (b === 0) {
      const error = new Error('Division by zero is not allowed')
      this.emit('error', { operation: 'divide', operands: [a, b], error: error.message })
      throw error
    }
    const result = a / b
    this.addToHistory('divide', [a, b], result)
    this.currentResult = result
    this.emit('calculation', { operation: 'divide', operands: [a, b], result })
    return result
  }

  /**
   * Calculates the power of a number
   * @param {number} base - The base number
   * @param {number} exponent - The exponent
   * @returns {number} The result of base raised to the power of exponent
   * @throws {Error} When operands are not valid numbers
   * @example
   * ```typescript
   * const result = calc.power(2, 3) // Returns 8
   * ```
   */
  power(base: number, exponent: number): number {
    this.validateOperands(base, exponent)
    const result = Math.pow(base, exponent)
    this.addToHistory('power', [base, exponent], result)
    this.currentResult = result
    this.emit('calculation', { operation: 'power', operands: [base, exponent], result })
    return result
  }

  /**
   * Calculates the square root of a number
   * @param {number} value - The number to calculate square root of
   * @returns {number} The square root of the value
   * @throws {Error} When value is negative or not a valid number
   * @example
   * ```typescript
   * const result = calc.sqrt(16) // Returns 4
   * ```
   */
  sqrt(value: number): number {
    this.validateOperands(value)
    if (value < 0) {
      const error = new Error('Cannot calculate square root of negative number')
      this.emit('error', { operation: 'sqrt', operands: [value], error: error.message })
      throw error
    }
    const result = Math.sqrt(value)
    this.addToHistory('sqrt', [value], result)
    this.currentResult = result
    this.emit('calculation', { operation: 'sqrt', operands: [value], result })
    return result
  }

  /**
   * Gets the current calculation result
   * @returns {number} The current result
   * @example
   * ```typescript
   * calc.add(5, 3)
   * const current = calc.getCurrentResult() // Returns 8
   * ```
   */
  getCurrentResult(): number {
    return this.currentResult
  }

  /**
   * Gets the calculation history
   * @returns {Array} A copy of the calculation history
   * @example
   * ```typescript
   * const history = calc.getHistory()
   * console.log(history.length) // Number of calculations performed
   * ```
   */
  getHistory(): Array<{
    operation: string
    operands: number[]
    result: number
    timestamp: Date
  }> {
    return [...this.history]
  }

  /**
   * Clears all calculation history
   * @emits clearHistory
   * @example
   * ```typescript
   * calc.clearHistory()
   * console.log(calc.getHistory().length) // Returns 0
   * ```
   */
  clearHistory(): void {
    const previousCount = this.history.length
    this.history = []
    this.currentResult = 0
    this.emit('clearHistory', { previousCount })
  }

  /**
   * Gets the number of calculations in history
   * @returns {number} The number of calculations stored
   * @example
   * ```typescript
   * const count = calc.getHistoryCount() // Returns number of calculations
   * ```
   */
  getHistoryCount(): number {
    return this.history.length
  }

  /**
   * Validates that operands are valid numbers
   * @private
   * @param {...number} operands - The operands to validate
   * @throws {Error} When any operand is not a valid number
   */
  private validateOperands(...operands: number[]): void {
    for (const operand of operands) {
      if (typeof operand !== 'number' || isNaN(operand) || !isFinite(operand)) {
        throw new Error(`Invalid operand: ${operand}. Must be a valid number.`)
      }
    }
  }

  /**
   * Adds a calculation to the history
   * @private
   * @param {string} operation - The operation performed
   * @param {number[]} operands - The operands used
   * @param {number} result - The result of the calculation
   */
  private addToHistory(operation: string, operands: number[], result: number): void {
    this.history.push({
      operation,
      operands,
      result,
      timestamp: new Date()
    })
    if (this.history.length > this.maxHistorySize) {
      this.history.shift()
    }
  }

  /**
   * Resets the calculator to initial state
   * @emits reset
   * @example
   * ```typescript
   * calc.reset()
   * console.log(calc.getCurrentResult()) // Returns 0
   * ```
   */
  reset(): void {
    this.currentResult = 0
    this.emit('reset')
  }

  /**
   * Gets a summary of calculator statistics
   * @returns {Object} Summary object with statistics
   * @example
   * ```typescript
   * const stats = calc.getStats()
   * console.log(stats.totalCalculations) // Total number of calculations
   * ```
   */
  getStats(): {
    totalCalculations: number
    operationsCount: Record<string, number>
    lastCalculation?: Date
    currentResult: number
  } {
    const operationsCount: Record<string, number> = {}
    for (const entry of this.history) {
      operationsCount[entry.operation] = (operationsCount[entry.operation] || 0) + 1
    }
    return {
      totalCalculations: this.history.length,
      operationsCount,
      lastCalculation: this.history.length > 0 ? this.history[this.history.length - 1].timestamp : undefined,
      currentResult: this.currentResult
    }
  }
}