// "euclid-gcd.hac"

import "testlib/int-arith.hac";
import "testlib/int-sort.hac";
import "testlib/int-split.hac";
import "testlib/int-merge.hac";
import "testlib/int-buf.hac";
import "testlib/int-copy.hac";
import "testlib/bool-buf.hac";
import "testlib/bool-copy.hac";

//=============================================================================
/**
	Sequential CHP: Find GCD of (A,B), return result in C.
	Uses subtraction.  
	Initially, a, b, must be non-zero.
 */
template <pint N>
defproc euclid_gcd_seq(chan?(int<N>) A, B; chan!(int<N>) C) {
int<N> a, b, p, q;
chp {
*[A?(a),B?(b);
	*[ b!=0 ->
		[(a>b) -> p:=a, q:=b [] else -> p:=b, q:=a ];	// sort
		a:=q, b:=p-q			// subtract
	];
	C!(a)
]
}
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	Sequential CHP for euclid GCD using modulo operation.  
 */
template <pint N><preal D>
defproc euclid_mod_gcd_seq(chan?(int<N>) A, B; chan!(int<N>) C) {
int<N> a, b, p, q, r;
chp {
*[A?(a),B?(b);
	[(a>b) -> p:=a, q:=b [] else -> p:=b, q:=a ];	// pre-sort
	*[ q!=0 ->
		$(after=D) q:=p%q, r:=p;
		p:=q; q:=r
	];
	C!(p)
]
}
}

//=============================================================================
/**
	Computes p % q.
	p should be greated than q, both should be positive.  
 */
template <pint W><preal D>
defproc remainder(chan?(int<W>) P, Q; chan!(int<W>) R) {
int<W> p, q;
chp {
*[ P?(p),Q?(q);
	$(after=D) R!(p%q)
]
}
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	Forwards input to O.  
	\return Z true if integer is zero.
 */
template <pint N>
defproc zero_comp_tap(chan?(int<N>) I; chan!(bool) Z; chan!(int<N>) O) {
int<N> i;
chp { *[ I?(i); [i!=0 -> O!(i),Z!(false) [] else -> Z!(true)] ] }
}

//=============================================================================
/**
	Decomposed CHP for Euclid GCD algorithm, using subtraction.
 */
template <pint N>
defproc euclid_gcd_dec(chan?(int<N>) A, B; chan!(int<N>) C) {
chan(bool) I, IA, IB, _I, J, SP;
chan(int<N>) AI, BI, CI, P, Q, PS, QS, QL, QB;
bool_buf_init<true> init(_I, I);	// initial-selection for do-while
bool_copy<2> ic(I, {IA, IB});
int_merge_2<N> am(IA, {AI, A}, P), bm(IB, {BI, B}, Q);
int_sort2<N> pqs({Q, P}, {QS, PS});	// QS < PS
zero_comp_tap<N> qz(QS, J, QL);
bool_copy<2> jc(J, {_I, SP});
int_split_2<N> cs(SP, PS, {CI, C});
int_copy<2,N> qc(QL, {QB, AI});
subtractor<N> sub(CI, QB, BI);
}

//=============================================================================
/**
	Decomposed CHP for Euclid GCD algorithm, using modulo.
 */
template <pint N><preal D>
defproc euclid_mod_gcd_dec(chan?(int<N>) A, B; chan!(int<N>) C) {
chan(bool) I, IA, IB, _I, J, SP;
chan(int<N>) AI, BI, CI, P, Q, PS, QS, QL, QB;
bool_buf_init<true> init(_I, I);	// initial-selection for do-while
bool_copy<2> ic(I, {IA, IB});
int_merge_2<N> am(IA, {AI, A}, P), bm(IB, {BI, B}, Q);
int_sort2<N> pqs({Q, P}, {QS, PS});	// QS < PS
zero_comp_tap<N> qz(QS, J, QL);
bool_copy<2> jc(J, {_I, SP});
int_split_2<N> cs(SP, PS, {CI, C});
int_copy<2,N> qc(QL, {QB, AI});
remainder<N><D> rem(CI, QB, BI);
}

//=============================================================================

