static int _fp_exptmod_base_2(fp_int * X, int digits, fp_int * P,
                              fp_int * Y)
{
  fp_digit buf, mp;
  int      err, bitbuf, bitcpy, bitcnt, digidx, x, y;
#ifdef WOLFSSL_SMALL_STACK
  fp_int  *res;
  fp_int  *tmp;
#else
  fp_int   res[1];
  fp_int   tmp[1];
#endif

#ifdef WOLFSSL_SMALL_STACK
  res = (fp_int*)XMALLOC(2*sizeof(fp_int), NULL, DYNAMIC_TYPE_TMP_BUFFER);
  if (res == NULL) {
     return FP_MEM;
  }
  tmp = &res[1];
#endif

  /* now setup montgomery  */
  if ((err = fp_montgomery_setup(P, &mp)) != FP_OKAY) {
#ifdef WOLFSSL_SMALL_STACK
     XFREE(res, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
     return err;
  }

  /* setup result */
  fp_init(res);
  fp_init(tmp);

  fp_mul_2d(P, 1 << WINSIZE, tmp);

  /* now we need R mod m */
  fp_montgomery_calc_normalization(res, P);

  /* Get the top bits left over after taking WINSIZE bits starting at the
   * least-significant.
   */
  digidx = digits - 1;
  bitcpy = (digits * DIGIT_BIT) % WINSIZE;
  if (bitcpy > 0) {
      bitcnt = (int)DIGIT_BIT - bitcpy;
      buf    = X->dp[digidx--];
      bitbuf = (int)(buf >> bitcnt);
      /* Multiply montgomery representation of 1 by 2 ^ top */
      fp_mul_2d(res, bitbuf, res);
      fp_add(res, tmp, res);
      err = fp_mod(res, P, res);
      if (err != FP_OKAY) {
      #ifdef WOLFSSL_SMALL_STACK
        XFREE(res, NULL, DYNAMIC_TYPE_TMP_BUFFER);
      #endif
        return err;
      }
      /* Move out bits used */
      buf  <<= bitcpy;
      bitcnt++;
  }
  else {
      bitcnt = 1;
      buf    = 0;
  }

  /* empty window and reset  */
  bitbuf = 0;
  bitcpy = 0;

  for (;;) {
    /* grab next digit as required */
    if (--bitcnt == 0) {
      /* if digidx == -1 we are out of digits so break */
      if (digidx == -1) {
        break;
      }
      /* read next digit and reset bitcnt */
      buf    = X->dp[digidx--];
      bitcnt = (int)DIGIT_BIT;
    }

    /* grab the next msb from the exponent */
    y       = (int)(buf >> (DIGIT_BIT - 1)) & 1;
    buf   <<= (fp_digit)1;
    /* add bit to the window */
    bitbuf |= (y << (WINSIZE - ++bitcpy));

    if (bitcpy == WINSIZE) {
      /* ok window is filled so square as required and multiply  */
      /* square first */
      for (x = 0; x < WINSIZE; x++) {
        err = fp_sqr(res, res);
        if (err != FP_OKAY) {
        #ifdef WOLFSSL_SMALL_STACK
          XFREE(res, NULL, DYNAMIC_TYPE_TMP_BUFFER);
        #endif
          return err;
        }
        err = fp_montgomery_reduce(res, P, mp);
        if (err != FP_OKAY) {
        #ifdef WOLFSSL_SMALL_STACK
          XFREE(res, NULL, DYNAMIC_TYPE_TMP_BUFFER);
        #endif
          return err;
        }
      }

      /* then multiply by 2^bitbuf */
      fp_mul_2d(res, bitbuf, res);
      /* Add in value to make mod operation take same time */
      fp_add(res, tmp, res);
      err = fp_mod(res, P, res);
      if (err != FP_OKAY) {
      #ifdef WOLFSSL_SMALL_STACK
        XFREE(res, NULL, DYNAMIC_TYPE_TMP_BUFFER);
      #endif
        return err;
      }

      /* empty window and reset */
      bitcpy = 0;
      bitbuf = 0;
    }
  }

  /* fixup result if Montgomery reduction is used
   * recall that any value in a Montgomery system is
   * actually multiplied by R mod n.  So we have
   * to reduce one more time to cancel out the factor
   * of R.
   */
  err = fp_montgomery_reduce(res, P, mp);

  /* swap res with Y */
  fp_copy(res, Y);

#ifdef WOLFSSL_SMALL_STACK
  XFREE(res, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
  return err;
}
static int _fp_exptmod_base_2(fp_int * X, int digits, fp_int * P,
                              fp_int * Y)
{
  fp_digit buf, mp;
  int      err, bitbuf, bitcpy, bitcnt, digidx, x, y;
#ifdef WOLFSSL_SMALL_STACK
  fp_int  *res;
  fp_int  *tmp;
#else
  fp_int   res[1];
  fp_int   tmp[1];
#endif

#ifdef WOLFSSL_SMALL_STACK
  res = (fp_int*)XMALLOC(2*sizeof(fp_int), NULL, DYNAMIC_TYPE_TMP_BUFFER);
  if (res == NULL) {
     return FP_MEM;
  }
  tmp = &res[1];
#endif

   
  if ((err = fp_montgomery_setup(P, &mp)) != FP_OKAY) {
#ifdef WOLFSSL_SMALL_STACK
     XFREE(res, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
     return err;
  }

   
  fp_init(res);
  fp_init(tmp);

  fp_mul_2d(P, 1 << WINSIZE, tmp);

   
  fp_montgomery_calc_normalization(res, P);

   
  digidx = digits - 1;
  bitcpy = (digits * DIGIT_BIT) % WINSIZE;
  if (bitcpy > 0) {
      bitcnt = (int)DIGIT_BIT - bitcpy;
      buf    = X->dp[digidx--];
      bitbuf = (int)(buf >> bitcnt);
       
      fp_mul_2d(res, bitbuf, res);
      fp_add(res, tmp, res);
      err = fp_mod(res, P, res);
      if (err != FP_OKAY) {
      #ifdef WOLFSSL_SMALL_STACK
        XFREE(res, NULL, DYNAMIC_TYPE_TMP_BUFFER);
      #endif
        return err;
      }
       
      buf  <<= bitcpy;
      bitcnt++;
  }
  else {
      bitcnt = 1;
      buf    = 0;
  }

   
  bitbuf = 0;
  bitcpy = 0;

  for (;;) {
     
    if (--bitcnt == 0) {
       
      if (digidx == -1) {
        break;
      }
       
      buf    = X->dp[digidx--];
      bitcnt = (int)DIGIT_BIT;
    }

     
    y       = (int)(buf >> (DIGIT_BIT - 1)) & 1;
    buf   <<= (fp_digit)1;
     
    bitbuf |= (y << (WINSIZE - ++bitcpy));

    if (bitcpy == WINSIZE) {
       
       
      for (x = 0; x < WINSIZE; x++) {
        err = fp_sqr(res, res);
        if (err != FP_OKAY) {
        #ifdef WOLFSSL_SMALL_STACK
          XFREE(res, NULL, DYNAMIC_TYPE_TMP_BUFFER);
        #endif
          return err;
        }
        err = fp_montgomery_reduce(res, P, mp);
        if (err != FP_OKAY) {
        #ifdef WOLFSSL_SMALL_STACK
          XFREE(res, NULL, DYNAMIC_TYPE_TMP_BUFFER);
        #endif
          return err;
        }
      }

       
      fp_mul_2d(res, bitbuf, res);
       
      fp_add(res, tmp, res);
      err = fp_mod(res, P, res);
      if (err != FP_OKAY) {
      #ifdef WOLFSSL_SMALL_STACK
        XFREE(res, NULL, DYNAMIC_TYPE_TMP_BUFFER);
      #endif
        return err;
      }

       
      bitcpy = 0;
      bitbuf = 0;
    }
  }

   
  err = fp_montgomery_reduce(res, P, mp);

   
  fp_copy(res, Y);

#ifdef WOLFSSL_SMALL_STACK
  XFREE(res, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
  return err;
}