###
 Simplify factorials

The following script

  F(n,k) = k binomial(n,k)
  (F(n,k) + F(n,k-1)) / F(n+1,k)

generates

       k! n!             n! (1 - k + n)!              k! n!
 -------------------- + -------------------- - ----------------------
 (-1 + k)! (1 + n)!     (1 + n)! (-k + n)!     k (-1 + k)! (1 + n)!

Simplify each term to get

    k       1 - k + n       1
 ------- + ----------- - -------
  1 + n       1 + n       1 + n

Then simplify the sum to get

    n
 -------
  1 + n

###



# simplify factorials term-by-term

Eval_simfac = ->
  push(cadr(p1))
  Eval()
  simfac()

#if 1

simfac = ->
  h = 0
  save()
  p1 = pop()
  if (car(p1) == symbol(ADD))
    h = tos
    p1 = cdr(p1)
    while (p1 != symbol(NIL))
      push(car(p1))
      simfac_term()
      p1 = cdr(p1)
    add_all(tos - h)
  else
    push(p1)
    simfac_term()
  restore()


#else
###
void
simfac(void)
{
  int h
  save()
  p1 = pop()
  if (car(p1) == symbol(ADD)) {
    h = tos
    p1 = cdr(p1)
    while (p1 != symbol(NIL)) {
      push(car(p1))
      simfac_term()
      p1 = cdr(p1)
    }
    addk(tos - h)
    p1 = pop()
    if (find(p1, symbol(FACTORIAL))) {
      push(p1)
      if (car(p1) == symbol(ADD)) {
        Condense()
        simfac_term()
      }
    }
  } else {
    push(p1)
    simfac_term()
  }
  restore()
}

#endif
###

simfac_term = ->
  h = 0

  save()

  p1 = pop()

  # if not a product of factors then done

  if (car(p1) != symbol(MULTIPLY))
    push(p1)
    restore()
    return

  # push all factors

  h = tos
  p1 = cdr(p1)
  while (p1 != symbol(NIL))
    push(car(p1))
    p1 = cdr(p1)

  # keep trying until no more to do

  while (yysimfac(h))
    doNothing = 1

  multiply_all_noexpand(tos - h)

  restore()

# try all pairs of factors

yysimfac = (h) ->
  i = 0
  j = 0

  for i in [h...tos]
    p1 = stack[i]
    for j in [h...tos]
      if (i == j)
        continue
      p2 = stack[j]

      #  n! / n    ->  (n - 1)!

      if (car(p1) == symbol(FACTORIAL)\
      && car(p2) == symbol(POWER)\
      && isminusone(caddr(p2))\
      && equal(cadr(p1), cadr(p2)))
        push(cadr(p1))
        push(one)
        subtract()
        factorial()
        stack[i] = pop()
        stack[j] = one
        return 1

      #  n / n!    ->  1 / (n - 1)!

      if (car(p2) == symbol(POWER)\
      && isminusone(caddr(p2))\
      && caadr(p2) == symbol(FACTORIAL)\
      && equal(p1, cadadr(p2)))
        push(p1)
        push_integer(-1)
        add()
        factorial()
        reciprocate()
        stack[i] = pop()
        stack[j] = one
        return 1

      #  (n + 1) n!  ->  (n + 1)!

      if (car(p2) == symbol(FACTORIAL))
        push(p1)
        push(cadr(p2))
        subtract()
        p3 = pop()
        if (isplusone(p3))
          push(p1)
          factorial()
          stack[i] = pop()
          stack[j] = one
          return 1

      #  1 / ((n + 1) n!)  ->  1 / (n + 1)!

      if (car(p1) == symbol(POWER)\
      && isminusone(caddr(p1))\
      && car(p2) == symbol(POWER)\
      && isminusone(caddr(p2))\
      && caadr(p2) == symbol(FACTORIAL))
        push(cadr(p1))
        push(cadr(cadr(p2)))
        subtract()
        p3 = pop()
        if (isplusone(p3))
          push(cadr(p1))
          factorial()
          reciprocate()
          stack[i] = pop()
          stack[j] = one
          return 1

      #  (n + 1)! / n!  ->  n + 1

      #  n! / (n + 1)!  ->  1 / (n + 1)

      if (car(p1) == symbol(FACTORIAL)\
      && car(p2) == symbol(POWER)\
      && isminusone(caddr(p2))\
      && caadr(p2) == symbol(FACTORIAL))
        push(cadr(p1))
        push(cadr(cadr(p2)))
        subtract()
        p3 = pop()
        if (isplusone(p3))
          stack[i] = cadr(p1)
          stack[j] = one
          return 1
        if (isminusone(p3))
          push(cadr(cadr(p2)))
          reciprocate()
          stack[i] = pop()
          stack[j] = one
          return 1
        if (equaln(p3, 2))
          stack[i] = cadr(p1)
          push(cadr(p1))
          push_integer(-1)
          add()
          stack[j] = pop()
          return 1
        if (equaln(p3, -2))
          push(cadr(cadr(p2)))
          reciprocate()
          stack[i] = pop()
          push(cadr(cadr(p2)))
          push_integer(-1)
          add()
          reciprocate()
          stack[j] = pop()
          return 1
  return 0
