// curvemod.cc: implementation of class curvemodq for elliptic curve mod q
//////////////////////////////////////////////////////////////////////////
//
// Copyright 1990-2026 John Cremona
//
// This file is part of the eclib package.
//
// eclib is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version.
//
// eclib is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License
// along with eclib; if not, write to the Free Software Foundation,
// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
//
//////////////////////////////////////////////////////////////////////////


#include "eclib/pari_interface.h"
#include "eclib/pointsmod.h"

// constructors
curvemodq::curvemodq(void)
{
  Fq=0;
  //  cout<<"In curvemodq default constructor, Fq = "<<Fq<<endl;
}

curvemodq::curvemodq(const Curve& E, const ZZ& qq)
{
  static const ZZ zero(0);
  q=qq; Fq=new galois_field(q);
  //  cout<<"In curvemodq constructor, Fq = "<<Fq<<endl;
  ZZ A1,A2,A3,A4,A6;
  E.getai(A1,A2,A3,A4,A6);
  a1 = to_ZZ_p(A1);
  a2 = to_ZZ_p(A2);
  a3 = to_ZZ_p(A3);
  a4 = to_ZZ_p(A4);
  a6 = to_ZZ_p(A6);
  order=zero;
}

curvemodq::~curvemodq()
{
  //  cout<<"In curvemodq destructor, Fq = "<<Fq<<endl;
  if(Fq) delete Fq;
}

curvemodq::curvemodq(const curvemodq& C) // copy constructor
  : q(C.q),
    a1(C.a1), a2(C.a2), a3(C.a3), a4(C.a4), a6(C.a6),
    order(C.order)
{
  Fq = new galois_field(q); // Not enough to copy the pointer
  //  cout<<"In curvemodq copy constructor, Fq = "<<Fq<<endl;
}

void curvemodq::operator=(const curvemodq& C) // assignment
{
  q=C.q;
  delete Fq;
  Fq = new galois_field(q); // Not enough to copy the pointer
  //  cout<<"In curvemodq operator=, Fq = "<<Fq<<endl;
  a1=C.a1; a2=C.a2; a3=C.a3; a4=C.a4; a6=C.a6;
  order=C.order;
}

void curvemodq::set_group_order_via_legendre()
{
  static const ZZ zero(0), one(1);
  // Do NOT make these static as the modulus might change!
  ZZ_p two=to_ZZ_p(2);
  ZZ_p four=two+two;
  if(!is_zero(order)) return; // order already set!
  order=one;  // point at infinity
  ZZ_p b2 = a1*a1 + four*a2;
  ZZ_p b4 = two*a4 + a1*a3;
  ZZ_p b6 = a3*a3 + four*a6;
  ZZ ix; ZZ_p x, d;
  for(ix=zero; ix<q; ix++)
    {
      x = to_ZZ_p(ix);
      d = ((four*x+b2)*x+(two*b4))*x+b6;
      order+=(1+legendre(rep(d),q));
    }
}

void curvemodq::set_group_order()
{
  ZZ a1z = NTL::conv<ZZ>(a1),
    a2z = NTL::conv<ZZ>(a2),
    a3z = NTL::conv<ZZ>(a3),
    a4z = NTL::conv<ZZ>(a4),
    a6z = NTL::conv<ZZ>(a6);
  order = 1+q-ellap(a1z,a2z,a3z,a4z,a6z,q);
}

// Division poly functions:

ZZ_pX makepdivpol(const curvemodq& C, int p)
{
  if(p==2)
    {
      ZZ_p a1,a2,a3,a4,a6;
      C.get_ai(a1,a2,a3,a4,a6);
      ZZ_pX f;
      SetCoeff(f,0,a3*a3 + 4*a6);
      SetCoeff(f,1,2*(2*a4 + a1*a3));
      SetCoeff(f,2,a1*a1 + 4*a2);
      SetCoeff(f,3, to_ZZ_p(4));
      return f;
    }

  // default for odd p: use recursive method

  return div_pol_odd(C,p);
}

// div_pol_odd(C,n) returns the polynomial in x whose zeros are the
// (x-coordinates of the) non-zero points P on C satisfying nP=0 (for
// odd n)

// The poly itself is found recursively

ZZ_pX div_pol_odd_rec(const curvemodq& C, int n);

ZZ_pX div_pol_odd(const curvemodq& C, int n)
{
  return div_pol_odd_rec(C,n);
}

ZZ_pX div_pol_odd_rec(const curvemodq& C, int n)
{
  const galois_field Fq=get_field(C);
  ZZ_pX X; SetX(X);
  ZZ_p a1,a2,a3,a4,a6;
  C.get_ai(a1,a2,a3,a4,a6);
  ZZ_pX f1 = X*(X*(X+a2)+a4)+a6;
  ZZ_pX f2 = a1*X+a3;
  ZZ_pX psi24=(to_ZZ_p(4)*f1+f2*f2); psi24*=psi24;
  ZZ_pX ans;
  switch(n) {
  case 0:
    ans = to_ZZ_p(0); return ans;
  case 1: case 2:
    ans = to_ZZ_p(1); return ans;
  case 3:
    SetCoeff(ans,4,3);
    SetCoeff(ans,3,a1*a1+4*a2);
    SetCoeff(ans,2,3*a1*a3+6*a4);
    SetCoeff(ans,1,3*a3*a3+12*a6);
    SetCoeff(ans,0,a1*a1*a6-a1*a3*a4+a2*a3*a3+4*a2*a6-a4*a4);
    return ans;
  case 4:
    SetCoeff(ans,6,2);
    SetCoeff(ans,5,a1*a1+4*a2);
    SetCoeff(ans,4,5*a1*a3+10*a4);
    SetCoeff(ans,3,10*a3*a3+40*a6);
    SetCoeff(ans,2,10*a1*a1*a6-10*a1*a3*a4+10*a2*a3*a3+40*a2*a6-10*a4*a4);
    SetCoeff(ans,1,a1*a1*a1*a1*a6-a1*a1*a1*a3*a4+a1*a1*a2*a3*a3+8*a1*a1*a2*a6-a1*a1*a4*a4-4*a1*a2*a3*a4-a1*a3*a3*a3-4*a1*a3*a6+4*a2*a2*a3*a3+16*a2*a2*a6-4*a2*a4*a4-2*a3*a3*a4-8*a4*a6);
    SetCoeff(ans,0,a1*a1*a1*a3*a6-a1*a1*a3*a3*a4+2*a1*a1*a4*a6+a1*a2*a3*a3*a3+4*a1*a2*a3*a6-3*a1*a3*a4*a4+2*a2*a3*a3*a4+8*a2*a4*a6-a3*a3*a3*a3-8*a3*a3*a6-2*a4*a4*a4-16*a6*a6);
    return ans;
  default: // general case, use recursion
    // If n is odd, n=2m+1:
    if(n%2==1)
      {
	int m=(n-1)/2;
	ZZ_pX t1=div_pol_odd_rec(C,m);
	t1=div_pol_odd_rec(C,m+2)*t1*t1*t1;
	ZZ_pX t2=div_pol_odd_rec(C,m+1);
	t2=div_pol_odd_rec(C,m-1)*t2*t2*t2;
	if(m%2==1) return t1-psi24*t2;
	return psi24*t1-t2;
      }
    else // n is even, n=2m:
      {
	int m=n/2;
	ZZ_pX t1=div_pol_odd_rec(C,m-1);
	t1=div_pol_odd_rec(C,m+2)*t1*t1;
	ZZ_pX t2=div_pol_odd_rec(C,m+1);
	t2=div_pol_odd_rec(C,m-2)*t2*t2;
	return div_pol_odd_rec(C,m)*(t1-t2);
      }
  }
}
