public NetBigInteger ModPow(
                NetBigInteger exponent,
                NetBigInteger m)
            {
                if (m.m_sign < 1)
                    throw new ArithmeticException("Modulus must be positive");

                if (m.Equals(One))
                    return Zero;

                if (exponent.m_sign == 0)
                    return One;

                if (m_sign == 0)
                    return Zero;

                int[] zVal = null;
                int[] yAccum = null;
                int[] yVal;

                // Montgomery exponentiation is only possible if the modulus is odd,
                // but AFAIK, this is always the case for crypto algo's
                bool useMonty = ((m.m_magnitude[m.m_magnitude.Length - 1] & 1) == 1);
                long mQ = 0;
                if (useMonty)
                {
                    mQ = m.GetMQuote();

                    // tmp = this * R mod m
                    NetBigInteger tmp = ShiftLeft(32 * m.m_magnitude.Length).Mod(m);
                    zVal = tmp.m_magnitude;

                    useMonty = (zVal.Length <= m.m_magnitude.Length);

                    if (useMonty)
                    {
                        yAccum = new int[m.m_magnitude.Length + 1];
                        if (zVal.Length < m.m_magnitude.Length)
                        {
                            int[] longZ = new int[m.m_magnitude.Length];
                            zVal.CopyTo(longZ, longZ.Length - zVal.Length);
                            zVal = longZ;
                        }
                    }
                }

                if (!useMonty)
                {
                    if (m_magnitude.Length <= m.m_magnitude.Length)
                    {
                        //zAccum = new int[m.magnitude.Length * 2];
                        zVal = new int[m.m_magnitude.Length];
                        m_magnitude.CopyTo(zVal, zVal.Length - m_magnitude.Length);
                    }
                    else
                    {
                        //
                        // in normal practice we'll never see ..
                        //
                        NetBigInteger tmp = Remainder(m);

                        //zAccum = new int[m.magnitude.Length * 2];
                        zVal = new int[m.m_magnitude.Length];
                        tmp.m_magnitude.CopyTo(zVal, zVal.Length - tmp.m_magnitude.Length);
                    }

                    yAccum = new int[m.m_magnitude.Length * 2];
                }

                yVal = new int[m.m_magnitude.Length];

                //
                // from LSW to MSW
                //
                for (int i = 0; i < exponent.m_magnitude.Length; i++)
                {
                    int v = exponent.m_magnitude[i];
                    int bits = 0;

                    if (i == 0)
                    {
                        while (v > 0)
                        {
                            v <<= 1;
                            bits++;
                        }

                        //
                        // first time in initialise y
                        //
                        zVal.CopyTo(yVal, 0);

                        v <<= 1;
                        bits++;
                    }

                    while (v != 0)
                    {
                        if (useMonty)
                        {
                            // Montgomery square algo doesn't exist, and a normal
                            // square followed by a Montgomery reduction proved to
                            // be almost as heavy as a Montgomery mulitply.
                            MultiplyMonty(yAccum, yVal, yVal, m.m_magnitude, mQ);
                        }
                        else
                        {
                            Square(yAccum, yVal);
                            Remainder(yAccum, m.m_magnitude);
                            Array.Copy(yAccum, yAccum.Length - yVal.Length, yVal, 0, yVal.Length);
                            ZeroOut(yAccum);
                        }
                        bits++;

                        if (v < 0)
                        {
                            if (useMonty)
                            {
                                MultiplyMonty(yAccum, yVal, zVal, m.m_magnitude, mQ);
                            }
                            else
                            {
                                Multiply(yAccum, yVal, zVal);
                                Remainder(yAccum, m.m_magnitude);
                                Array.Copy(yAccum, yAccum.Length - yVal.Length, yVal, 0,
                                    yVal.Length);
                                ZeroOut(yAccum);
                            }
                        }

                        v <<= 1;
                    }

                    while (bits < 32)
                    {
                        if (useMonty)
                        {
                            MultiplyMonty(yAccum, yVal, yVal, m.m_magnitude, mQ);
                        }
                        else
                        {
                            Square(yAccum, yVal);
                            Remainder(yAccum, m.m_magnitude);
                            Array.Copy(yAccum, yAccum.Length - yVal.Length, yVal, 0, yVal.Length);
                            ZeroOut(yAccum);
                        }
                        bits++;
                    }
                }

                if (useMonty)
                {
                    // Return y * R^(-1) mod m by doing y * 1 * R^(-1) mod m
                    ZeroOut(zVal);
                    zVal[zVal.Length - 1] = 1;
                    MultiplyMonty(yAccum, yVal, zVal, m.m_magnitude, mQ);
                }

                NetBigInteger result = new NetBigInteger(1, yVal, true);

                return exponent.m_sign > 0
                    ? result
                    : result.ModInverse(m);
            }