note
	description: "Summary description for {NUMBER_GCD}."
	author: "Colin LeMahieu"
	date: "$Date: 2011-11-11 18:13:16 +0100 (ven., 11 nov. 2011) $"
	revision: "$Revision: 87787 $"
	quote: "My reading of history convinces me that most bad government results from too much government. -  Thomas Jefferson."

deferred class
	SPECIAL_GCD

inherit
	SPECIAL_LOGIC
	LIMB_MANIPULATION
	SPECIAL_ARITHMETIC
	SPECIAL_DIVISION
	SPECIAL_UTILITY

feature

	find_a (cp0: NATURAL_32; cp1: NATURAL_32): NATURAL_32
		local
			leading_zero_bits: INTEGER
			n1_l: NATURAL_32
			n1_h: NATURAL_32
			n2_l: NATURAL_32
			n2_h: NATURAL_32
			i: INTEGER
			tmp: NATURAL_32
		do
			n1_l := cp0
			n1_h := cp1
			n2_l := 0 - n1_l
			n2_h := n1_h.bit_not
			from
			until
				n2_h = 0
			loop
				if not n2_h.bit_test (limb_high_bit - leading_zero_bits) then
					i := leading_zeros (n2_h)
					i := i - leading_zero_bits
					leading_zero_bits := leading_zero_bits + i
					n2_h := (n2_h |<< i).bit_or (n2_l |>> (limb_bits - i))
					n2_l := n2_l |<< i
					from
					until
						i = 0
					loop
						if n1_h > n2_h or (n1_h = n2_h and n1_l >= n2_l) then
							n1_h := n1_h - (n2_h + (n1_l < n2_l).to_integer.to_natural_32)
							n1_l := (n1_l - n2_l)
						end
						n2_l := (n2_l |>> 1).bit_or (n2_h |<< (limb_bits - 1))
						n2_h := n2_h |>> 1
						i := i - 1
					end
				end
				if n1_h > n2_h or (n1_h = n2_h and n1_l >= n2_l) then
					n1_h := n1_h - (n2_h + (n1_l < n2_l).to_integer.to_natural_32)
					n1_l := n1_l - n2_l
				end
				tmp := n1_h
				n1_h := n2_h
				n2_h := tmp
				tmp := n1_l
				n1_l := n2_l
				n2_l := tmp
			end
			Result := n2_l
		end

	gcd_2 (vp: SPECIAL [NATURAL_32]; vp_offset: INTEGER; up: SPECIAL [NATURAL_32]; up_offset: INTEGER): INTEGER
		local
			u0: NATURAL_32
			u1: NATURAL_32
			v0: NATURAL_32
			v1: NATURAL_32
			vsize: INTEGER
			r: INTEGER
		do
			u0 := up [0]
			u1 := up [1]
			v0 := vp [0]
			v1 := vp [1]
			from
			until
				u1 = v1 or u0 = v0
			loop
				if u1 > v1 then
					u1 := u1 - (v1 + (u0 < v0).to_integer.to_natural_32)
					u0 := u0 - v0
					r := trailing_zeros (u0)
					u0 := (u1 |<< (limb_bits - r)).bit_or (u0 |>> r)
					u1 := u1 |>> r
				else
					v1 := v1 - (u1 + (v0 < u0).to_integer.to_natural_32)
					v0 := v0 - u0
					r := trailing_zeros (v0)
					v0 := (v1 |<< (limb_bits - r)).bit_or (v0 |>> r)
					v1 := v1 |>> r
				end
			end
			vp [vp_offset] := v0
			vp [vp_offset + 1] := v1
			vsize := 1 + (v1 /= 0).to_integer
			if u1 = v1 and u0 = v0 then
				Result := vsize
			else
				if u0 = v0 then
					if v1 > v1 then
						v0 := u1 - v1
					else
						v0 := v1 - u1
					end
				else
					if u0 > v0 then
						v0 := u0 - v0
					else
						v0 := v0 - u0
					end
				end
				vp [vp_offset] := gcd_1 (vp, vp_offset, vsize, v0)
				Result := 1
			end
		end

	gcd (gp: SPECIAL [NATURAL_32]; gp_offset: INTEGER; ap: SPECIAL [NATURAL_32]; ap_offset: INTEGER; ap_count: INTEGER; bp: SPECIAL [NATURAL_32]; bp_offset: INTEGER; n: INTEGER): INTEGER
		do
			Result := basic_gcd (gp, gp_offset, ap, ap_offset, ap_count, bp, bp_offset, n)
		ensure
			Result > 0
			Result <= ap_count
			Result <= n
		end

	gcd_1 (up: SPECIAL [NATURAL_32]; up_offset: INTEGER; size: INTEGER; vlimb_a: NATURAL_32): NATURAL_32
		require
			size >= 1
			vlimb_a /= 0
		local
			remainder: CELL [NATURAL_32]
			ulimb: NATURAL_32
			zero_bits: INTEGER
			u_low_zero_bits: INTEGER
			tmp: NATURAL_32
			vlimb: NATURAL_32
			maybe: BOOLEAN
			done: BOOLEAN
		do
			create remainder.put (0)
			vlimb := vlimb_a
			ulimb := up [up_offset]
			zero_bits := trailing_zeros (vlimb)
			vlimb := vlimb |>> zero_bits
			if size > 1 then
				if ulimb /= 0 then
					u_low_zero_bits := trailing_zeros (ulimb)
					zero_bits := zero_bits.min (u_low_zero_bits)
				end
				mod_1 (up, up_offset, size, vlimb, remainder)
				ulimb := remainder.item
				if ulimb = 0 then
					done := True
				else
					maybe := True
				end
			else
				u_low_zero_bits := trailing_zeros (ulimb)
				ulimb := ulimb |>> u_low_zero_bits
				zero_bits := zero_bits.min (u_low_zero_bits)
				if vlimb > ulimb then
					tmp := ulimb
					ulimb := vlimb
					vlimb := tmp
				end
				if (ulimb |>> 16) > vlimb then
					ulimb := ulimb \\ vlimb
					if ulimb = 0 then
						done := True
					else
						maybe := True
					end
				end
			end
			if not done then
				from
				until
					ulimb = vlimb and not maybe
				loop
					if ulimb > vlimb or maybe then
						if not maybe then
							ulimb := ulimb - vlimb
						end
						from
							if not maybe then
								ulimb := ulimb |>> 1
							end
						until
							not maybe and ulimb.bit_test (0)
						loop
							if not maybe then
								ulimb := ulimb |>> 1
							else
								maybe := False
							end
						end
					else
						vlimb := vlimb - ulimb
						from
							vlimb := vlimb |>> 1
						until
							vlimb.bit_test (0)
						loop
							vlimb := vlimb |>> 1
						end
					end
				end
			end
			Result := vlimb |<< zero_bits
		end

	basic_gcd (gp: SPECIAL [NATURAL_32]; gp_offset: INTEGER; up_a: SPECIAL [NATURAL_32]; up_offset_a: INTEGER; usize_a: INTEGER; vp_a: SPECIAL [NATURAL_32]; vp_offset_a: INTEGER; vsize_a: INTEGER): INTEGER
		require
			usize_a >= 1
			vsize_a >= 1
			usize_a >= vsize_a
			vp_a.valid_index (vp_offset_a)
			vp_a.valid_index (vp_offset_a + vsize_a - 1)
			up_a.valid_index (up_offset_a)
			up_a.valid_index (up_offset_a + usize_a - 1)
			vp_a [vp_offset_a].bit_test (0)
			up_a [up_offset_a + usize_a - 1] /= 0
			vp_a [vp_offset_a + vsize_a - 1] /= 0
		local
			orig_vp: SPECIAL [NATURAL_32]
			orig_vp_offset: INTEGER
			orig_vsize: INTEGER
			binary_gcd_ctr: INTEGER
			scratch: INTEGER
			tp: SPECIAL [NATURAL_32]
			tp_offset: INTEGER
			vbitsize: INTEGER
			d: INTEGER
			orig_up: SPECIAL [NATURAL_32]
			orig_up_offset: INTEGER
			orig_usize: INTEGER
			anchor_up: SPECIAL [NATURAL_32]
			anchor_up_offset: INTEGER
			up: SPECIAL [NATURAL_32]
			up_offset: INTEGER
			usize: INTEGER
			i: INTEGER
			vp: SPECIAL [NATURAL_32]
			vp_offset: INTEGER
			vsize: INTEGER
			r: INTEGER
			tmp_special: SPECIAL [NATURAL_32]
			tmp_integer: INTEGER
			junk: NATURAL_32
			bp0: NATURAL_32
			bp1: NATURAL_32
			cp0: NATURAL_32
			cp1: NATURAL_32
			u_inv: NATURAL_32
			hi: CELL [NATURAL_32]
			lo: CELL [NATURAL_32]
			v_inv: NATURAL_32
			c: NATURAL_32
			b: NATURAL_32
			rsize: INTEGER
			done: BOOLEAN
			continue: BOOLEAN
			carry: CELL [NATURAL_32]
		do
			create carry.put (0)
			create hi.put (0)
			create lo.put (0)
			up := up_a
			up_offset := up_offset_a
			usize := usize_a
			vp := vp_a
			vp_offset := vp_offset_a
			vsize := vsize_a
			orig_vp := vp
			orig_vp_offset := vp_offset
			orig_vsize := vsize
			if vsize >= 5 then
				orig_up := up
				orig_up_offset := up_offset
				orig_usize := usize
				create anchor_up.make_filled (0, usize + 2)
				anchor_up.copy_data (orig_up, orig_up_offset, anchor_up_offset, usize)
				up := anchor_up
				d := leading_zeros (up [up_offset + usize - 1])
				d := usize * limb_bits - d
				vbitsize := leading_zeros (vp [vp_offset + vsize - 1])
				vbitsize := vsize * limb_bits - vbitsize
				d := d - vbitsize + 1
				up [up_offset + usize] := 0
				usize := usize + 1
				junk := bdivmod (up, up_offset, up, up_offset, usize, vp, vp_offset, vsize, d)
				d := d // limb_bits
				up_offset := up_offset + d
				usize := usize - d
				from
				until
					usize = 0 or up [up_offset] /= 0
				loop
					up_offset := up_offset + 1
					usize := usize - 1
				end
				if usize = 0 then
					done := True
				else
					create vp.make_filled (0, vsize + 2)
					vp_offset := 0
					vp.copy_data (orig_vp, orig_vp_offset, 0, vsize)
					from
					until
						usize = 0
					loop
						if up [up_offset + usize - 1].bit_test (limb_high_bit) then
							anchor_up [anchor_up_offset] := 0 - up [up_offset]
							from
								i := 1
							until
								i >= usize
							loop
								anchor_up [anchor_up_offset + i] := up [up_offset + i].bit_not
								i := i + 1
							end
							up := anchor_up
							up_offset := anchor_up_offset
						end
						usize := normalize (up, up_offset, usize)
						if not up [up_offset].bit_test (0) then
							r := trailing_zeros (up [up_offset])
							rshift (anchor_up, anchor_up_offset, up, up_offset, usize, r, carry)
							junk := carry.item
							usize := usize - (anchor_up [anchor_up_offset + usize - 1] = 0).to_integer
						else
							anchor_up.copy_data (up, up_offset, anchor_up_offset, usize)
						end
						tmp_special := anchor_up
						anchor_up := vp
						vp := tmp_special
						tmp_integer := anchor_up_offset
						anchor_up_offset := vp_offset
						vp_offset := tmp_integer
						tmp_integer := usize
						usize := vsize
						vsize := tmp_integer
						up := anchor_up
						up_offset := anchor_up_offset
						if vsize <= 2 then
							usize := 0
						else
							d := vbitsize
							vbitsize := leading_zeros (vp [vp_offset + vsize - 1])
							vbitsize := vsize * limb_bits - vbitsize
							d := d - vbitsize + 1
							if d > 16 then
								up [up_offset + usize] := 0
								usize := usize + 1
								junk := bdivmod (up, up_offset, up, up_offset, usize, vp, vp_offset, vsize, d)
								d := d // limb_bits
								up_offset := up_offset + d
								usize := usize - d
							else
								u_inv := modlimb_invert (up [up_offset])
								cp0 := vp [vp_offset] * u_inv
								umul_ppmm (hi, lo, cp0, up [up_offset])
								cp1 := (vp [vp_offset + 1] - hi.item - cp0 * up [up_offset + 1]) * u_inv
								mul_1 (up, up_offset, up, up_offset, usize, find_a (cp0, cp1), carry)
								up [up_offset + usize] := carry.item
								usize := usize + 1
								v_inv := modlimb_invert (vp [vp_offset])
								bp0 := (up [up_offset] * v_inv)
								umul_ppmm (hi, lo, bp0, vp [vp_offset])
								bp1 := (up [up_offset + 1] + hi.item + (bp0.bit_and (vp [vp_offset + 1]))).bit_and (0x1)
								up [up_offset + usize] := 0
								usize := usize + 1
								if bp1 /= 0 then
									addmul_1 (up, up_offset, vp, vp_offset, vsize, 0 - bp0, carry)
									c := carry.item
									add_1 (up, up_offset + vsize, up, up_offset + vsize, usize - vsize, c, carry)
									junk := carry.item
								else
									submul_1 (up, up_offset, vp, vp_offset, vsize, bp0, carry)
									b := carry.item
									sub_1 (up, up_offset + vsize, up, up_offset + vsize, usize - vsize, b, carry)
									junk := carry.item
								end
								up_offset := up_offset + 2
								usize := usize - 2
							end
							from
							until
								usize = 0 or up [up_offset] /= 0
							loop
								up_offset := up_offset + 1
								usize := usize - 1
							end
						end
					end
					up := orig_up
					up_offset := orig_up_offset
					usize := orig_usize
					binary_gcd_ctr := 2
				end
			else
				binary_gcd_ctr := 1
			end
			if not done then
				scratch := (2 * vsize).max (vsize + 1)
				if usize + 1 > scratch then
					scratch := usize + 1
				end
				create tp.make_filled (0, scratch)
				tp_offset := 0
				from
				until
					binary_gcd_ctr = 0
				loop
					continue := False
					binary_gcd_ctr := binary_gcd_ctr - 1
					if usize > vsize then
						tdiv_qr (tp, tp_offset + vsize, tp, tp_offset, up, up_offset, usize, vp, vp_offset, vsize)
						rsize := vsize
						rsize := normalize (tp, tp_offset, rsize)
						if rsize = 0 then
							continue := True
						else
							up.copy_data (tp, tp_offset, up_offset, vsize)
						end
					end
					if not continue then
						vsize := ngcd_lehmer (vp, vp_offset, up, up_offset, vp, vp_offset, vsize, tp, tp_offset)
					end
					up := orig_vp
					up_offset := orig_vp_offset
					usize := orig_vsize
				end
			end
			if vp /= gp then
				gp.copy_data (vp, vp_offset, gp_offset, vsize)
			end
			Result := vsize
		ensure
			Result > 0
			Result <= usize_a
			Result <= vsize_a
		end

	ngcd_matrix1_vector (m: SPECIAL [NATURAL_32]; n_a: INTEGER; ap: SPECIAL [NATURAL_32]; ap_offset: INTEGER; bp: SPECIAL [NATURAL_32]; bp_offset: INTEGER; tp: SPECIAL [NATURAL_32]; tp_offset: INTEGER): INTEGER
		local
			h0: NATURAL_32
			h1: NATURAL_32
			n: INTEGER
			carry: CELL [NATURAL_32]
		do
			create carry.put (0)
			n := n_a
			tp.copy_data (ap, ap_offset, tp_offset, n)
			mul_1 (ap, ap_offset, ap, ap_offset, n, m [3], carry)
			h0 := carry.item
			submul_1 (ap, ap_offset, bp, bp_offset, n, m [1], carry)
			h1 := carry.item
			mul_1 (bp, bp_offset, bp, bp_offset, n, m [0], carry)
			h0 := carry.item
			submul_1 (bp, bp_offset, tp, tp_offset, n, m [2], carry)
			h1 := carry.item
			n := n - (ap [ap_offset + n - 1].bit_or (bp [bp_offset + n - 1]) = 0).to_integer
			Result := n
		end

	ngcd_subdiv_step (gp: SPECIAL [NATURAL_32]; gp_offset: INTEGER; gn: TUPLE [gn: INTEGER]; ap_a: SPECIAL [NATURAL_32]; ap_offset_a: INTEGER; bp_a: SPECIAL [NATURAL_32]; bp_offset_a: INTEGER; n: INTEGER; tp: SPECIAL [NATURAL_32]; tp_offset: INTEGER): INTEGER
		local
			an: INTEGER
			bn: INTEGER
			ap: SPECIAL [NATURAL_32]
			ap_offset: INTEGER
			bp: SPECIAL [NATURAL_32]
			bp_offset: INTEGER
			tmp_special: SPECIAL [NATURAL_32]
			tmp_integer: INTEGER
			junk: NATURAL_32
			c: INTEGER
			carry: CELL [NATURAL_32]
		do
			create carry.put (0)
			ap := ap_a
			ap_offset := ap_offset_a
			bp := bp_a
			bp_offset := bp_offset_a
			from
				an := n
			until
				an <= 0 or else ap [ap_offset + an - 1] /= bp [bp_offset + an - 1]
			loop
				an := an - 1
			end
			if an = 0 then
				gp.copy_data (ap, ap_offset, gp_offset, n)
				gn.gn := n
				Result := 0
			else
				if ap [ap_offset + an - 1] < bp [bp_offset + an - 1] then
					tmp_special := ap
					ap := bp
					bp := tmp_special
					tmp_integer := ap_offset
					ap_offset := bp_offset
					bp_offset := tmp_integer
					bn := n
					bn := normalize (bp, bp_offset, bn)
					if bn = 0 then
						gp.copy_data (ap, ap_offset, gp_offset, n)
						gn.gn := n
						Result := 0
					else
						sub_n (ap, ap_offset, ap, ap_offset, bp, bp_offset, an, carry)
						junk := carry.item
						an := normalize (ap, ap_offset, an)
						if an < bn then
							tmp_special := ap
							ap := bp
							bp := tmp_special
							tmp_integer := ap_offset
							ap_offset := bp_offset
							bp_offset := tmp_integer
							tmp_integer := an
							an := bn
							bn := tmp_integer
						elseif an = bn then
							c := cmp (ap, ap_offset, bp, bp_offset, an)
							if c < 0 then
								tmp_special := ap
								ap := bp
								bp := tmp_special
								tmp_integer := ap_offset
								ap_offset := bp_offset
								bp_offset := tmp_integer
							end
						end
						tdiv_qr (tp, tp_offset + bn, tp, tp_offset, ap, ap_offset, an, bp, bp_offset, bn)
						an := bn
						an := normalize (tp, tp_offset, an)
						if an = 0 then
							gp.copy_data (bp, bp_offset, gp_offset, bn)
							gn.gn := bn
							Result := 0
						else
							ap.copy_data (tp, tp_offset, ap_offset, bn)
							Result := bn
						end
					end
				end
			end
		end

	nhgcd2 (ah_a: NATURAL_32; al_a: NATURAL_32; bh_a: NATURAL_32; bl_a: NATURAL_32; m: SPECIAL [NATURAL_32]): BOOLEAN
		local
			u00: NATURAL_32
			u01: NATURAL_32
			u10: NATURAL_32
			u11: NATURAL_32
			al: CELL [NATURAL_32]
			ah: CELL [NATURAL_32]
			bl: CELL [NATURAL_32]
			bh: CELL [NATURAL_32]
			done: BOOLEAN
			subtract_a: BOOLEAN
			r0: CELL [NATURAL_32]
			r1: CELL [NATURAL_32]
			q: NATURAL_32
		do
			create r0.put (0)
			create r1.put (0)
			create al.put (al_a)
			create ah.put (ah_a)
			create bl.put (bl_a)
			create bh.put (bh_a)
			if ah.item < 2 or bh.item < 2 then
				Result := False
			else
				if ah.item > bh.item or (ah.item = bh.item and al.item > bl.item) then
					sub_ddmmss (ah, al, ah.item, al.item, bh.item, bl.item)
					if ah.item < 2 then
						Result := False
						done := True
					else
						u11 := 1
						u01 := u11
						u00 := u01
						u10 := 0
					end
				else
					sub_ddmmss (bh, bl, bh.item, bl.item, ah.item, al.item)
					if bh.item < 2 then
						Result := False
						done := True
					else
						u11 := 1
						u10 := u11
						u00 := u10
						u01 := 0
					end
				end
				if not done then
					if ah.item < bh.item then
						subtract_a := True
					end
					from
					until
						not subtract_a and done
					loop
						if not subtract_a then
							if ah.item = bh.item then
								done := True
							else
								sub_ddmmss (ah, al, ah.item, al.item, bh.item, bl.item)
								if ah.item < 2 then
									done := True
								else
									if ah.item <= bh.item then
										u01 := u01 + u00
										u11 := u11 + u10
									else
										q := div2 (r0, r1, ah.item, al.item, bh.item, bl.item)
										al.put (r0.item)
										ah.put (r1.item)
										if ah.item < 2 then
											u01 := u01 + q * u00
											u11 := u11 + q * u10
											done := True
										else
											q := q + 1
											u01 := u01 + q * u00
											u11 := u11 + q * u10
										end
									end
								end
							end
						end
						if not done then
							subtract_a := False
							if ah.item = bh.item then
								done := True
							else
								sub_ddmmss (bh, bl, bh.item, bl.item, ah.item, al.item)
								if bh.item < 2 then
									done := True
								else
									if bh.item <= ah.item then
										u00 := u00 + u01
										u10 := u10 + u11
									else
										q := div2 (r0, r1, bh.item, bl.item, ah.item, al.item)
										bl.put (r0.item)
										bh.put (r1.item)
										if bh.item < 2 then
											u00 := u00 + q * u01
											u10 := u10 + q * u11
											done := True
										else
											q := q + 1
											u00 := u00 + q * u01
											u10 := u10 + q * u11
										end
									end
								end
							end
						end
					end
					m [0] := u00
					m [1] := u01
					m [2] := u10
					m [3] := u11
					Result := True
				end
			end
		end

	div2 (r0: CELL [NATURAL_32]; r1: CELL [NATURAL_32]; nh_a: NATURAL_32; nl_a: NATURAL_32; dh_a: NATURAL_32; dl_a: NATURAL_32): NATURAL_32
		require
			dh_a /= 0 or dl_a /= 0
		local
			q: NATURAL_32
			count: INTEGER_32
			dh: NATURAL_32
			dl: NATURAL_32
			nh: CELL [NATURAL_32]
			nl: CELL [NATURAL_32]
		do
			create nh.put (0)
			create nl.put (0)
			nh.put (nh_a)
			nl.put (nl_a)
			dh := dh_a
			dl := dl_a
			if nh.item.bit_test (limb_high_bit) then
				from
					count := 1
				invariant
					dh /= 0 or dl /= 0
				until
					dh.bit_test (limb_high_bit)
				loop
					dh := (dh |<< 1).bit_or (dl |>> (limb_bits - 1))
					dl := (dl |<< 1)
					count := count + 1
				end
				from
				until
					count = 0
				loop
					q := q |<< 1
					if nh.item > dh or (nh.item = dh and nl.item >= dl) then
						sub_ddmmss (nh, nl, nh.item, nl.item, dh, dl)
						q := q.bit_or (1)
					end
					dl := (dh |<< (limb_bits - 1)).bit_or (dl |>> 1)
					dh := dh |>> 1
					count := count - 1
				end
			else
				from
				until
					not (nh.item > dh or (nh.item = dh and nl.item >= dl))
				loop
					dh := (dh |<< 1).bit_or (dl |>> (limb_bits - 1))
					dl := dl |<< 1
					count := count + 1
				end
				from
				until
					count = 0
				loop
					dl := (dh |<< (limb_bits - 1)).bit_or (dl |>> 1)
					dh := dh |>> 1
					q := q |<< 1
					if nh.item > dh or (nh.item = dh and nl.item >= dl) then
						sub_ddmmss (nh, nl, nh.item, nl.item, dh, dl)
						q := q.bit_or (1)
					end
					count := count - 1
				end
			end
			r0.put (nl.item)
			r1.put (nh.item)
			Result := q
		end

	mul_2 (rp: SPECIAL [NATURAL_32]; rp_offset: INTEGER; ap: SPECIAL [NATURAL_32]; ap_offset: INTEGER; n: INTEGER; bp: SPECIAL [NATURAL_32]; bp_offset: INTEGER)
		local
			bh: NATURAL_32
			bl: NATURAL_32
			cy: NATURAL_32
			hi: CELL [NATURAL_32]
			lo: CELL [NATURAL_32]
			sh: CELL [NATURAL_32]
			sl: CELL [NATURAL_32]
			th: CELL [NATURAL_32]
			tl: CELL [NATURAL_32]
			i: INTEGER
		do
			create hi.put (0)
			create lo.put (0)
			create sh.put (0)
			create sl.put (0)
			create tl.put (0)
			create th.put (0)
			bl := bp [bp_offset]
			umul_ppmm (hi, lo, bl, ap [ap_offset])
			rp [rp_offset] := lo.item
			bh := bp [bp_offset + 1]
			from
				i := 1
				cy := 0
			until
				i >= n
			loop
				umul_ppmm (sh, sl, bh, ap [ap_offset + i - 1])
				umul_ppmm (th, tl, bl, ap [ap_offset + i])
				add_ssaaaa (sh, sl, sh.item, sl.item, cy, hi.item)
				add_ssaaaa (hi, lo, th.item, tl.item, sh.item, sl.item)
				rp [rp_offset + i] := lo.item
				cy := (hi.item < th.item).to_integer.to_natural_32
				i := i + 1
			end
			umul_ppmm (sh, sl, bh, ap [ap_offset + n - 1])
			add_ssaaaa (hi, lo, sh.item, sl.item, cy, hi.item)
			rp [rp_offset + n + 1] := hi.item
			rp [rp_offset + n] := lo.item
		end

	ngcd_matrix1_adjust (m: SPECIAL [NATURAL_32]; n_a: INTEGER; ap: SPECIAL [NATURAL_32]; ap_offset: INTEGER; bp: SPECIAL [NATURAL_32]; bp_offset: INTEGER; p: INTEGER; tp: SPECIAL [NATURAL_32]; tp_offset: INTEGER): INTEGER
		local
			ah: NATURAL_32
			bh: NATURAL_32
			cy: NATURAL_32
			n: INTEGER
			carry: CELL [NATURAL_32]
		do
			create carry.put (0)
			n := n_a
			tp.copy_data (ap, ap_offset, tp_offset, p)
			mul_1 (ap, ap_offset, ap, ap_offset, p, m [3], carry)
			ah := carry.item
			submul_1 (ap, ap_offset, bp, bp_offset, p, m [1], carry)
			cy := carry.item
			if cy > ah then
				decr_u (ap, ap_offset + p, cy - ah)
				ah := 0
			else
				ah := ah - cy
				if ah > 0 then
					add_1 (ap, ap_offset + p, ap, ap_offset + p, n, ah, carry)
					ah := carry.item
				end
			end
			mul_1 (bp, bp_offset, bp, bp_offset, p, m [0], carry)
			bh := carry.item
			submul_1 (bp, bp_offset, tp, tp_offset, p, m [2], carry)
			cy := carry.item
			if cy > bh then
				decr_u (bp, bp_offset + p, cy - bh)
				bh := 0
			else
				bh := bh - cy
				if bh > 0 then
					add_1 (bp, bp_offset + p, bp, bp_offset + p, n, bh, carry)
					bh := carry.item
				end
			end
			n := n + p
			if ah > 0 or bh > 0 then
				ap [ap_offset + n] := ah
				bp [bp_offset + n] := bh
				n := n + 1
			else
				if ap [ap_offset + n - 1] = 0 and bp [bp_offset + n - 1] = 0 then
					n := n - 1
				end
			end
			Result := n
		end

	ngcd_matrix2_adjust (m: SPECIAL [NATURAL_32]; n_a: INTEGER; ap: SPECIAL [NATURAL_32]; ap_offset: INTEGER; bp: SPECIAL [NATURAL_32]; bp_offset: INTEGER; p: INTEGER; tp: SPECIAL [NATURAL_32]; tp_offset: INTEGER): INTEGER
		local
			t0: SPECIAL [NATURAL_32]
			t0_offset: INTEGER
			t1: SPECIAL [NATURAL_32]
			t1_offset: INTEGER
			ah: NATURAL_32
			bh: NATURAL_32
			cy: NATURAL_32
			n: INTEGER
			junk: NATURAL_32
			carry: CELL [NATURAL_32]
		do
			create carry.put (0)
			n := n_a
			t0 := tp
			t0_offset := tp_offset
			t1 := tp
			t1_offset := tp_offset + p + 2
			mul_2 (t0, t0_offset, ap, ap_offset, p, m, 3)
			mul_2 (t1, t1_offset, ap, ap_offset, p, m, 2)
			ap.copy_data (t0, t0_offset, ap_offset, p)
			add (ap, ap_offset + p, ap, ap_offset + p, n, t0, t0_offset + p, 2, carry)
			ah := carry.item
			mul (t0, t0_offset, bp, bp_offset, p, m, 1, 2, carry)
			junk := carry.item
			sub (ap, ap_offset, ap, ap_offset, n + p, t0, t0_offset, p + 2, carry)
			cy := carry.item
			ah := ah - cy
			mul (t0, t0_offset, bp, bp_offset, p, m, 0, 2, carry)
			junk := carry.item
			bp.copy_data (t0, t0_offset, bp_offset, p)
			add (bp, bp_offset + p, bp, bp_offset + p, n, t0, t0_offset + p, 2, carry)
			bh := carry.item
			sub (bp, bp_offset, bp, bp_offset, n + p, t1, t1_offset, p + 2, carry)
			cy := carry.item
			bh := bh - cy
			n := n + p
			if ah > 0 or bh > 0 then
				ap [ap_offset + n] := ah
				bp [bp_offset + n] := bh
				n := n + 1
			else
				if ap [ap_offset + n - 1] = 0 and bp [bp_offset + n - 1] = 0 then
					n := n - 1
				end
			end
			Result := n
		end

	ngcd_lehmer (gp: SPECIAL [NATURAL_32]; gp_offset: INTEGER; ap_a: SPECIAL [NATURAL_32]; ap_offset_a: INTEGER; bp_a: SPECIAL [NATURAL_32]; bp_offset_a: INTEGER; n_a: INTEGER; tp: SPECIAL [NATURAL_32]; tp_offset: INTEGER): INTEGER
		local
			gn: TUPLE [gn: INTEGER]
			n: INTEGER
			m1: SPECIAL [NATURAL_32]
			m2: SPECIAL [NATURAL_32]
			p: INTEGER
			res: TUPLE [res: INTEGER]
			nn: INTEGER
			m: SPECIAL [NATURAL_32]
			ah: NATURAL_32
			al: NATURAL_32
			bh: NATURAL_32
			bl: NATURAL_32
			mask: NATURAL_32
			shift: INTEGER
			tmp_special: SPECIAL [NATURAL_32]
			tmp_integer: INTEGER
			r: INTEGER
			ap: SPECIAL [NATURAL_32]
			ap_offset: INTEGER
			bp: SPECIAL [NATURAL_32]
			bp_offset: INTEGER
			done: BOOLEAN
		do
			ap := ap_a
			ap_offset := ap_offset_a
			bp := bp_a
			bp_offset := bp_offset_a
			create gn
			create res
			n := n_a
			create m1.make_filled (0, 4)
			create m2.make_filled (0, 4)
			create m.make_filled (0, 4)
			from
			until
				n < 7
			loop
				p := n - 5
				nn := nhgcd5 (ap, ap_offset + p, bp, bp_offset + p, res, m1, m2)
				inspect res.res
				when 0 then
					n := ngcd_subdiv_step (gp, gp_offset, gn, ap, ap_offset, bp, bp_offset, n, tp, tp_offset)
				when 1 then
					n := ngcd_matrix1_adjust (m1, nn, ap, ap_offset, bp, bp_offset, p, tp, tp_offset)
				when 2 then
					n := ngcd_matrix2_adjust (m2, nn, ap, ap_offset, bp, bp_offset, p, tp, tp_offset)
				end
			end
			from
			until
				n <= 2 or done
			loop
				mask := ap [ap_offset + n - 1].bit_or (bp [bp_offset + n - 1])
				if mask.bit_test (limb_high_bit) then
					ah := ap [ap_offset + n - 1]
					al := ap [ap_offset + n - 2]
					bh := bp [bp_offset + n - 1]
					bl := bp [bp_offset + n - 2]
				else
					shift := leading_zeros (mask)
					ah := extract_limb (shift, ap [ap_offset + n - 1], ap [ap_offset + n - 2])
					al := extract_limb (shift, ap [ap_offset + n - 2], ap [ap_offset + n - 3])
					bh := extract_limb (shift, bp [bp_offset + n - 1], bp [bp_offset + n - 2])
					bl := extract_limb (shift, bp [bp_offset + n - 2], bp [bp_offset + n - 3])
				end
				if nhgcd2 (ah, al, bh, bl, m) then
					n := ngcd_matrix1_vector (m, n, ap, ap_offset, bp, bp_offset, tp, tp_offset)
				else
					n := ngcd_subdiv_step (gp, gp_offset, gn, ap, ap_offset, bp, bp_offset, n, tp, tp_offset)
					if n = 0 then
						Result := gn.gn
						done := True
					end
				end
			end
			if not done then
				if n = 1 then
					gp [gp_offset] := gcd_1 (ap, ap_offset, 1, bp [bp_offset])
					Result := 1
				else
					if not ap [ap_offset].bit_test (0) then
						tmp_special := ap
						ap := bp
						bp := tmp_special
						tmp_integer := ap_offset
						ap_offset := bp_offset
						bp_offset := tmp_integer
					end
					if bp [bp_offset] = 0 then
						gp [gp_offset] := gcd_1 (ap, ap_offset, 2, bp [bp_offset + 1])
						Result := 1
					else
						if not bp [bp_offset].bit_test (0) then
							r := trailing_zeros (bp [bp_offset])
							bp [bp_offset] := (bp [bp_offset + 1] |<< (limb_bits - r)).bit_or (bp [bp_offset] |>> r)
							bp [bp_offset + 1] := bp [bp_offset + 1] |>> r
						end
						n := gcd_2 (ap, ap_offset, bp, bp_offset)
						gp.copy_data (ap, ap_offset, gp_offset, n)
						Result := n
					end
				end
			end
		ensure
			Result > 0
			Result <= n_a
		end

	nhgcd5 (ap: SPECIAL [NATURAL_32]; ap_offset: INTEGER; bp: SPECIAL [NATURAL_32]; bp_offset: INTEGER; res: TUPLE [res: INTEGER]; m1: SPECIAL [NATURAL_32]; m: SPECIAL [NATURAL_32]): INTEGER_32
		local
			m2: SPECIAL [NATURAL_32]
			t: SPECIAL [NATURAL_32]
			n: INTEGER
			ah: NATURAL_32
			al: NATURAL_32
			bh: NATURAL_32
			bl: NATURAL_32
			mask: NATURAL_32
			shift: INTEGER
			ph: CELL [NATURAL_32]
			pl: CELL [NATURAL_32]
		do
			create ph.put (0)
			create pl.put (0)
			create t.make_filled (0, 5)
			create m2.make_filled (0, 4)
			mask := ap [ap_offset + 4].bit_or (bp [bp_offset + 4])
			if mask.bit_test (31) then
				ah := ap [ap_offset + 4]
				al := ap [ap_offset + 3]
				bh := bp [bp_offset + 4]
				bl := bp [bp_offset + 3]
			else
				shift := leading_zeros (mask)
				ah := extract_limb (shift, ap [ap_offset + 4], ap [ap_offset + 3])
				al := extract_limb (shift, ap [ap_offset + 3], ap [ap_offset + 2])
				bh := extract_limb (shift, bp [bp_offset + 4], bp [bp_offset + 3])
				bl := extract_limb (shift, bp [bp_offset + 3], bp [bp_offset + 2])
			end
			if nhgcd2 (ah, al, bh, bl, m1) then
				res.res := 0
				Result := 0
			else
				n := ngcd_matrix1_vector (m1, 5, ap, ap_offset, bp, bp_offset, t, 0)
				mask := ap [ap_offset + n - 1].bit_or (bp [bp_offset + n - 1])
				if mask.bit_test (31) then
					ah := ap [ap_offset + n - 1]
					al := ap [ap_offset + n - 2]
					bh := bp [bp_offset + n - 1]
					bl := bp [bp_offset + n - 2]
				else
					shift := leading_zeros (mask)
					ah := extract_limb (shift, ap [ap_offset + n - 1], ap [ap_offset + n - 2])
					al := extract_limb (shift, ap [ap_offset + n - 2], ap [ap_offset + n - 3])
					bh := extract_limb (shift, bp [bp_offset + n - 1], bp [bp_offset + n - 2])
					bl := extract_limb (shift, bp [bp_offset + n - 2], bp [bp_offset + n - 3])
				end
				if not nhgcd2 (ah, al, bh, bl, m2) then
					res.res := 1
					Result := n
				else
					n := ngcd_matrix1_vector (m2, n, ap, ap_offset, bp, bp_offset, t, 0)
					dotmul_ppxxyy (ph, pl, m1 [0], m1 [1], m2 [0], m2 [2])
					m [1] := ph.item
					m [0] := pl.item
					dotmul_ppxxyy (ph, pl, m1 [0], m1 [1], m2 [1], m2 [3])
					m [3] := ph.item
					m [2] := pl.item
					dotmul_ppxxyy (ph, pl, m1 [2], m1 [3], m2 [0], m2 [2])
					m [5] := ph.item
					m [4] := pl.item
					dotmul_ppxxyy (ph, pl, m1 [2], m1 [3], m2 [1], m2 [3])
					m [7] := ph.item
					m [6] := pl.item
					res.res := 2
					Result := n
				end
			end
		end

	dotmul_ppxxyy (ph: CELL [NATURAL_32]; pl: CELL [NATURAL_32]; x1: NATURAL_32; x2: NATURAL_32; y1: NATURAL_32; y2: NATURAL_32)
		local
			dotmul_sh: CELL [NATURAL_32]
			dotmul_sl: CELL [NATURAL_32]
			dotmul_th: CELL [NATURAL_32]
			dotmul_tl: CELL [NATURAL_32]
			sh: CELL [NATURAL_32]
			sl: CELL [NATURAL_32]
		do
			create dotmul_sh.put (0)
			create dotmul_sl.put (0)
			create dotmul_th.put (0)
			create dotmul_tl.put (0)
			create sh.put (0)
			create sl.put (0)

			umul_ppmm (dotmul_sh, dotmul_sl, x1, y1)
			umul_ppmm (dotmul_th, dotmul_tl, x2, y2)
			add_ssaaaa (sh, sl, dotmul_sh.item, dotmul_sl.item, dotmul_th.item, dotmul_tl.item)
			ph.put (sh.item)
			pl.put (sl.item)
		end
end
