package org.bouncycastle.math.ec;

import java.math.BigInteger;

public abstract class ECFieldElement
    implements ECConstants
{
    BigInteger x;
    BigInteger p;

    protected ECFieldElement(BigInteger q, BigInteger x)
    {
        if (x.compareTo(q) >= 0)
        {
            throw new IllegalArgumentException("x value too large in field element");
        }

        this.x = x;
        this.p = q; // curve.getQ();
    }

    public BigInteger toBigInteger()
    {
        return x;
    }

    public boolean equals(Object other)
    {
        if ( other == this )
            return true;

        if ( !(other instanceof ECFieldElement) )
            return false;

        ECFieldElement o = (ECFieldElement)other;
        return p.equals(o.p) && x.equals(o.x);
    }

    public abstract String         getFieldName();
    public abstract ECFieldElement add(ECFieldElement b);
    public abstract ECFieldElement subtract(ECFieldElement b);
    public abstract ECFieldElement multiply(ECFieldElement b);
    public abstract ECFieldElement divide(ECFieldElement b);
    public abstract ECFieldElement negate();
    public abstract ECFieldElement square();
    public abstract ECFieldElement invert();
    public abstract ECFieldElement sqrt();

    public static class Fp extends ECFieldElement
    {
        /**
         * return the field name for this field.
         *
         * @return the string "Fp".
         */
        public String getFieldName()
        {
            return "Fp";
        }

        public Fp(BigInteger q, BigInteger x)
        {
            super(q, x);
        }

        public ECFieldElement add(ECFieldElement b)
        {
            return new Fp(p, x.add(b.x).mod(p));
        }

        public ECFieldElement subtract(ECFieldElement b)
        {
            return new Fp(p, x.subtract(b.x).mod(p));
        }

        public ECFieldElement multiply(ECFieldElement b)
        {
            return new Fp(p, x.multiply(b.x).mod(p));
        }

        public ECFieldElement divide(ECFieldElement b)
        {
            return new Fp(p, x.multiply(b.x.modInverse(p)).mod(p));
        }

        public ECFieldElement negate()
        {
            return new Fp(p, x.negate().mod(p));
        }

        public ECFieldElement square()
        {
            return new Fp(p, x.multiply(x).mod(p));
        }

        public ECFieldElement invert()
        {
            return new Fp(p, x.modInverse(p));
        }

        // D.1.4 91
        /**
         * return a sqrt root - the routine verifies that the calculation
         * returns the right value - if none exists it returns null.
         */
        public ECFieldElement sqrt()
        {
            // p mod 4 == 3
            if ( p.testBit(1) )
            {
                // z = g^(u+1) + p, p = 4u + 3
                ECFieldElement z = new Fp(p, x.modPow(p.shiftRight(2).add(ONE), p));

                return z.square().equals(this) ? z : null;
            }

            throw new RuntimeException("not done yet");
        }
    }
}
