#require "algebra/euclidian-ring"
require "algebra/array-supplement"

module Algebra
module ChineseRemainderTheorem
  # return coeffs s.t. coeffs.inner_product(ary) == c
  # entries of c and ary must be euclidian ring
  def decompose_on_factors(c, ary)
    f1, *fs = ary
    k, *coeffs = f1.gcd_ext_all(*fs)
    q, r = c.divmod(k)
    unless r.zero?
      print "c = "
      p c
      print "k = "
      p k
      print "ary = "
      p ary
      raise "#{c} is not expressed by parameters"
    end
    d1, *ds = coeffs.collect{|x| x * q}
    [d1]+ds #this is ok
  end

  def decompose_on_cofactors_modp(c, ary, cofacts)
    # c: Polynomial/Zp
    # ary: Array of Polynomial/Zp
    # cofacts: cofacts of ary
    # OutPut: coeff.inner_product(cofacts of ary) == c

    pf, prime = c.ground, c.ground.char
    coeffs = ary.collect{|f| f.zero}
    d1, *ds = decompose_on_factors(c, cofacts)
    #normalize
    ds_new = []
    ds.each_with_index do |d, i|
      fq, fr = d.divmod(ary[i+1])
      d1 += fq * ary[0]
      ds_new << fr
    end
    coeffs = [d1] + ds_new

#    p c == coeffs.inner_product(cofacts)
    coeffs
  end

  def decomp_on_cofactors_over_p_adic_int_first(c, ary, prime, cofacts)
    pf = Polynomial.reduction(Integer, prime, "x")
    ary_Zp = ary.collect{|f| pf.reduce(f)}
    cofacts_Zp = cofacts.collect{|f| pf.reduce(f)}
    c_zp = pf.reduce(c)

    d1, *ds = decompose_on_factors(c_zp, cofacts_Zp)

    #normalize
    ds_new = []
    ds.each_with_index do |d, i|
      fq, fr = d.divmod(ary_Zp[i+1])
      d1 += fq * ary_Zp[0]
      ds_new << fr
    end
    coeffs = [d1] + ds_new
    ring = c.class
    coeffs.collect{|f| lifting(f, ring)}
  end
  def lifting(f, ring)
    f.project(ring){|c, i| c.lift}
  end

  private :decomp_on_cofactors_over_p_adic_int_first, :lifting

  def mk_cofacts(ary)
    tot = ary.first.unity
    ary.each do |f|; tot *= f; end
    cofacts = []
    ary.each do |f|; cofacts.push tot / f; end
    cofacts
  end
  
  def decompose_on_cofactors_p_adic(c, ary, cofacts, prime, k)
    # c: Polynomial/Intger
    # ary: Array of Polynomial/Integer
    # OutPut: coeff.inner_product(cofacts of ary) === c mod prime**k

    raise if ary.find{|f| (f.lc % prime).zero?}
#    cofacts = mk_cofacts(ary)
    coeffs = ary.collect{|f| f.zero}
    0.upto k-1 do |j|
      e = prime ** j
      c1 = (c - coeffs.inner_product(cofacts)) / e
      coeffs1 = decomp_on_cofactors_over_p_adic_int_first(c1,
							ary, prime, cofacts)
      coeffs.each_index do |i|
	coeffs[i] += coeffs1[i]*e
      end
    end
#    p c - coeffs.inner_product(cofacts)
    coeffs
  end
end
end

if $0 == __FILE__
  CRT = ChineseRemainderTheorem
  include CRT
  require "algebra/polynomial"
  require "algebra/residue-class-ring"
  require "algebra/polynomial-factor"
  Zp = ResidueClassRing(Integer, 5)
  P = Polynomial(Zp, "x")
  x = P.var
  a = x + 1
  b = x + 2
  c = x + 3
#  ary = [b*c, c*a, a*b]
  ary = [a, b, c]
  f = a*b*c
#  coeffs =  decompose_on_factors(P.unity, ary)
  coeffs =  decompose_on_cofactors_modp(P.unity, ary, mk_cofacts(ary))
  p cofacts = mk_cofacts(ary)
  p coeffs
  p coeffs.inner_product(cofacts)

exit
  PZ = Polynomial(Integer, "x")
  x = PZ.var
  a = (x + 1)**2
  b = (x + 2)**3
  c = (x + 3)**1
  ary = [a, b, c]
  p decompose_on_cofactors_over_p_adic_int(PZ.unity, ary, mk_cofacts(ary),
					   11, 2)
end
