#
#--> QuotientField(D)
#
# Creates a Quotient Field over the domain D (must be a GcdDomain)
# E.g. if Z := Integer(); then QuotientField(Z); creates the rationals
# The representation is a pair [n,d] meaning n/d where d is unit normal.
# For a polynomial domain D, this means expanded normal form.
# 
# Author: MBM 1989
#
QuotientField := proc() local G,Q,env;

	G := args[1];
	if not hasCategory(G,GcdDomain) then
		ERROR(`1st argument must be an GcdDomain`) fi;

	if hasCategory(G,OrderedDomain)
		then Q := OrderedField() else Q := Field() fi;

	defOperations( {Numer,Denom}, Q &-> G, Q);
	defOperation( Slash, [G,G] &-> Q, Q );
	if hasOperation(G,ModularHomomorphism) then
	    defOperation( ModularHomomorphism,
		[] &-> [Q &-> Union(Integer,FAIL),Integer], Q );
	fi;

	addCategory(Q,QuotientField);
	Q[DomainName] := QuotientField;
	Q[Characteristic] := G[Characteristic];
	Q[AbsoluteDegree] := G[AbsoluteDegree];

	# Rep := [G,G]
	Q[Numer] := proc(x) x[1] end;
	Q[Denom] := proc(x) x[2] end;
	env := ['C' = G, 'D' = Q];

	Q[Type] := subs(env, proc(x) C[Type](x[1]) and C[Type](x[2]) end);
	Q[Input] := subs(env, proc(x) QuotientFieldInput(x,C,D) end);
	Q[Output] := subs(env, proc(x) C[Output](x[1])/C[Output](x[2]) end);

	Q[`=`] := <evalb(x=y)>;
	Q[0] := [G[0],G[1]];
	Q[1] := [G[1],G[1]];
	Q[`+`] := subs(env, proc() QuotientFieldAdd(C,D,args) end);
	Q[`*`] := subs(env, proc() QuotientFieldMul(C,D,args) end);
	Q[Inv] := subs(env, proc(x) QuotientFieldInverse(C,D,x) end);
	Q[Random] := subs(env, proc() QuotientFieldRandom(C,D) end);
	Q[Slash] := subs(env, proc(x,y) QuotientFieldSlash(C,D,x,y) end);

	if hasCategory(G,OrderedDomain) then
	    Q[Sign] := subs(env, proc(a)
		C[Sign](D[Numer](a))*C[Sign](D[Denom](a)) end);
	fi;
	if hasOperation(G,ModularHomomorphism) then
	    Q[ModularHomomorphism] := subs( env, proc() option remember;
		QuotientFieldHomoMorphism(C,D) end)
	fi;

	op(Q)
end:

QuotientFieldHomoMorphism := proc(C,D) local f,h,p;
	f := C[ModularHomomorphism](); p := f[2]; f := f[1];
	h := subs( {'I'=D,'g'=f,'m'=p}, proc(x) local n,d;
	        n := g(I[Numer](x)); d := g(I[Denom](x));
	        if n=FAIL or d=FAIL or d=0 then FAIL
		else	n := traperror(modp(n/d,m));
			if n = lasterror then FAIL else n fi
		fi
	    end);
	op(h),p
end:

macro(Numer=1,Denom=2);
QuotientFieldRandom := proc(C,D) local d,n;
	n := C[Random]();
	d := C[Random]();
	while d = C[0] do d := C[Random]() od;
	D[Slash](n,d)
end:

QuotientFieldSlash := proc(C,D,x,y) local g,n,d;
	if y = C[0] then ERROR(`division by zero`) fi;
	if y = C[1] then RETURN( [ x, C[1] ] ) fi;
	g := C[Gcd](x,y); n := C[Div](x,g); d := C[Div](y,g);
	d := C[UnitNormal](d);
	[ C[`*`](d[3],n), d[2] ]
end:

QuotientFieldMul := proc(C,D,x,y) local a,b,c,d,g;
	if nargs = 2 then RETURN(D[1]) fi;
	if nargs = 3 then RETURN(x) fi;
	if nargs > 4 then 
		a := x;
		for b in args[4..nargs] do a := D[`*`](a,b) od;
		RETURN(a)
	fi;
	if type(x,integer) then RETURN( D[`*`]( [C[Coerce](x),C[1]],y ) ) fi;
	a := x[Numer]; b := x[Denom];
	c := y[Numer]; d := y[Denom];
	if x = D[1] then y
	elif y = D[1] then x
	elif a = d and b = c then D[1]
	else
		g := C[Gcd](b,c); b := C[Div](b,g); c := C[Div](c,g);
		g := C[Gcd](a,d); a := C[Div](a,g); d := C[Div](d,g);
		[ C[`*`](a,c), C[`*`](b,d) ]
	fi
end:

QuotientFieldInverse := proc(C,D,x) local n,d;
	if type(x,integer) then RETURN( D[Inv](D[Coerce](x)) ) fi;
	n := x[Numer]; d := x[Denom];
	if n = C[0] then ERROR(`division by zero`) fi;
	n := C[UnitNormal](n);
	[ C[`*`](n[3],d), n[2] ]
end:

QuotientFieldAdd := proc(C,D,x,y) local a,b,c,d,b1,d1,g,e,g1,n,n1,t;
	if nargs = 2 then RETURN( D[0] ) fi;
	if nargs = 3 then RETURN( x ) fi;
	if nargs > 4 then
	    t := QuotientFieldAdd(C,D,x,y);
	    RETURN( QuotientFieldAdd(C,D,t,args[5..nargs]) )
	fi;
	if x = D[0] then RETURN(y) elif y = D[0] then RETURN(x) fi;
	a := x[Numer]; b := x[Denom];
	c := y[Numer]; d := y[Denom];
	g := C[Gcd](b,d);
	if g = C[1] then
		n := C[`+`](C[`*`](a,d),C[`*`](b,c));
		RETURN( [ n, C[`*`](b,d) ] )
	fi;
	if g = d then t := a; a := c; c := t; t := b; b := d; d := t fi;
	if g = b then
		d1 := C[Div](d,g);
		n := C[`+`](c,C[`*`](a,d1));
		e := C[Gcd](n,b); n1 := C[Div](n,e); b1 := C[Div](b,e);
		RETURN( [ n1, C[`*`](b1,d1) ] )
	fi;
	b1 := C[Div](b,g); d1 := C[Div](d,g);
	n := C[`+`](C[`*`](d1,a),C[`*`](b1,c));
	e := C[Gcd](n,g);
	if e = C[1] then RETURN( [ n, C[`*`](b,d1) ] ) fi;
	n1 := C[Div](n,e); g1 := C[Div](g,e);
	[ n1, C[`*`](g1,C[`*`](b1,d1)) ]
end: 

QuotientFieldInput := proc(x,C,D) local s,t;
	t := C[Input](x);
	if t <> FAIL then [ t, C[1] ]
	elif type(x,`+`) then
	    t := map( QuotientFieldInput, [op(x)], C, D );
	    if has(t,FAIL) then FAIL else D[`+`](op(t)) fi
	elif type(x,`*`) then
	    t := map( QuotientFieldInput, [op(x)], C, D );
	    if has(t,FAIL) then FAIL else D[`*`](op(t)) fi
	elif type(x,`^`) then
	    t := QuotientFieldInput(op(1,x), C, D);
	    if t = FAIL then FAIL else D[`^`](t,op(2,x)) fi
	elif type(x,fraction) then
	    t := map( QuotientFieldInput, [op(x)], C, D );
	    if has(t,FAIL) then FAIL else D[`/`](op(t)) fi
	else FAIL
	fi
end:
		
save `ENF.m`;
quit
