
package provide xtcl 0.1


# $Id: calcul.tcl,v 1.2 2001/03/27 07:12:41 diam Exp $
# 13/07/00 (diam) : intégration à pist/xtcl



## new_expr -- Support 'expr' statements for large numbers
# 
# Origin: 
#   Bill Schongar <bschonga@cisco.com>, 12 Jul 2000, comp.lang.tcl
# 
# Exemple:
#    expr  999999999999 - 1
#    => integer value too large to represent
#    new_expr  999999999999 - 1
#    => 999999999998
#   
# Revisions
#   1.0 - June 2000 (bschonga@cisco.com)
#
#  Support 'expr' statements for large numbers,
#  with return values expressed as double-precision
#  strings, not scientific notation. Note that if
#  data is passed to this routine with decimal places,
#  the return value will contain decimal places
#
# Arguments -
#
#  A mathematical expression, using integers
#
# Notes:
#
#  To ensure proper functionality, always use spaces
#  to separate values and operators in expressions.
#
# Example:      new_expr 10 - 9
#  Improper:    new_expr 10-9
# Example:      new_expr (10 - 9) * 100
#  Improper:    new_expr (10 - 9)*100   
#
#  The improper syntax _may work_ in some situations,
# but it is better consistently use spaces.
#
# Questions, comments, suggestions - bschonga@cisco.com

proc expr_doubleInt {args} {

  # Assume no decimal places in return
  set decimal_flag 0


  # Process each part of the expression
  foreach part [split $args] {
    # Check for decimal point
    if {[regexp {\.} $part]} {
        set decimal_flag 1
        set prec ""
    } else {
        set prec ".0"
    }
    # Is there a number in this part?
    # - If so, separate out the numeric part
    if {[regexp {([^0-9]*)([0-9]+)(.*)} \
            $part match prefix number tail]} {
       # Build the string, converting int to double precision if
       # necessary
       lappend final_expr "$prefix$number$prec$tail"
    } else {
       # Just a math symbol or operator, append to the list
       lappend final_expr $part
    }
  }
  
  # Process the expression
  set result [expr $final_expr]

  # Convert from scientific notation, if necessary  
  if {[regexp {(.+)e\+(.+)} $result match new_num sci]} {
    set precision [string length [lindex [split $new_num .] 1] ]
    set fmt "%0[expr $sci -$precision]u"
    set value "${new_num}[format $fmt 0]"
    regsub {\.} $value {} result
  }   

  # Chop off trailing decimal places, if not flagged
  if {!$decimal_flag} {
    regexp {([0-9]+)\..+} $result match result  
  }

  # Return modified (or original) result
  set result
}


## fact -- calcul de factoriel (bête, sans vérification)
# 
# 
proc fact { n } {
    set fact 1
    for {set i $n} {$i > 1} {incr i -1} {
        set fact [expr {$fact * $i}]
    }  ;# endfor
    return $fact
}

## gcd -- calul le pgcd de deux entiers
# 
# exemple :
#   set i [gcd 60 100]
#   => 20
# 
proc gcd {a b} {
    if {$b==0} {
        return $a
    }
    gcd $b [expr {$a % $b}]
}

## lowfrac -- returne la valeur simplifiée d'une fraction {60 100} => 3/5
# 
#   set frac [lowfrac 60/100]
#   => 3/5
#   expr $frac
#   => 0
#   expr "$frac*1.0"
#   => 0
#   expr "1.0*$frac"
#   => 0.6
# 
# proc lowfrac {a b} {
#    set gcd [gcd $a $b]
#    return "[expr {$a/$gcd}]/[expr {$b/$gcd}]"
# }
proc lowfrac {frac} {
    if {![regexp {^(\d+)/(\d+)$} $frac -> a b]} {
        return -code error "Not a fraction: $frac"
    }
    set gcd [gcd $a $b]
    return "[expr {$a/$gcd}]/[expr {$b/$gcd}]"
}


# ./
# 