note
	description: "Summary description for {NUMBER_NUMBER_THEORETIC}."
	author: "Colin LeMahieu"
	date: "$Date: 2011-11-11 18:13:16 +0100 (ven., 11 nov. 2011) $"
	revision: "$Revision: 87787 $"
	quote: "The best argument against democracy is a five-minute conversation with the average voter. -  Winston Churchill"

deferred class
	SPECIAL_NUMBER_THEORETIC

inherit
	SPECIAL_DIVISION
	SPECIAL_UTILITY
	SPECIAL_LOGIC
	LIMB_MANIPULATION

feature

	gcdext_div2 (n1_a: NATURAL_32; n0_a: NATURAL_32; d1_a: NATURAL_32; d0_a: NATURAL_32): NATURAL_32
		require
			d1_a /= 0 or d0_a /= 0
		local
			q: NATURAL_32
			count: INTEGER
			d1: NATURAL_32
			d0: NATURAL_32
			n1: CELL [NATURAL_32]
			n0: CELL [NATURAL_32]
		do
			create n1.put (n1_a)
			create n0.put (n0_a)
			d1 := d1_a
			d0 := d0_a
			if n1.item.bit_test (limb_high_bit) then
				from
					count := 1
				invariant
					d1 /= 0 or d0 /= 0
				until
					d1.bit_test (limb_high_bit)
				loop
					d1 := (d1 |<< 1).bit_or (d0 |>> (limb_bits - 1))
					d0 := d0 |<< 1
					count := count + 1
				end
				q := 0
				from
				until
					count = 0
				loop
					q := q |<< 1
					if n1.item > d1 or (n1.item = d1 and n0.item >= d0) then
						sub_ddmmss (n1, n0, n1.item, n0.item, d1, d0)
						q := q.bit_or (1)
					end
					d0 := (d1 |<< (limb_bits - 1)).bit_or (d0 |>> 1)
					d1 := d1 |>> 1
					count := count - 1
				end
				Result := q
			else
				from
					count := 0
				until
					n1.item <= d1 and (n1.item /= d1 or n0.item < d0)
				loop
					d1 := (d1 |<< 1).bit_or (d0 |>> (limb_bits - 1))
					d0 := d0 |<< 1
					count := count + 1
				end
				q := 0
				from
				until
					count = 0
				loop
					d0 := (d1 |<< (limb_bits - 1)).bit_or (d0 |>> 1)
					d1 := d1 |>> 1
					q := q |<< 1
					if n1.item > d1 or (n1.item = d1 and n0.item >= d0) then
						sub_ddmmss (n1, n0, n1.item, n0.item, d1, d0)
						q := q.bit_or (1)
					end
					count := count - 1
				end
				Result := q
			end
		end

	basic_gcdext (target: SPECIAL [NATURAL_32]; target_offset: INTEGER_32; cofactor: SPECIAL [NATURAL_32]; cofactor_offset: INTEGER_32; cofactor_count: TUPLE [cofactor_count: INTEGER_32]; op1: SPECIAL [NATURAL_32]; op1_offset: INTEGER_32; op1_count: INTEGER_32; op2: SPECIAL [NATURAL_32]; op2_offset: INTEGER_32; op2_count: INTEGER_32): INTEGER
		require
			op1_count >= op2_count
			target.valid_index (target_offset)
			target.valid_index (target_offset + op1_count - 1)
			cofactor.valid_index (cofactor_offset)
			cofactor.valid_index (cofactor_offset + op1_count - 1)
			op1.valid_index (op1_offset)
			op1.valid_index (op1_offset + op1_count - 0)
			op1 [op1_offset + op1_count - 1] /= 0
			op2.valid_index (op2_offset)
			op2.valid_index (op2_offset + op2_count - 0)
			op2 [op2_offset + op2_count - 1] /= 0
			op2_count >= 1
		local
			tp: SPECIAL [NATURAL_32]
			tp_offset: INTEGER_32
			s1p: SPECIAL [NATURAL_32]
			s1p_offset: INTEGER
		do
			create tp.make_filled (0, op1_count + 1)
			tp_offset := 0
			create s1p.make_filled (0, op1_count + 1)
			s1p_offset := 0
			cofactor.fill_with (0, cofactor_offset, cofactor_offset + op1_count - 1)
			s1p.fill_with (0, s1p_offset, s1p_offset + op1_count - 1)
			cofactor [cofactor_offset] := 1
			s1p [s1p_offset] := 0
			if op1_count > op2_count then
				tdiv_qr (tp, tp_offset, op1, op1_offset, op1, op1_offset, op1_count, op2, op2_offset, op2_count)
				cofactor [cofactor_offset] := 0
				s1p [s1p_offset] := 1
				Result := basic_gcdext_arranged (target, target_offset, cofactor, cofactor_offset, cofactor_count, op2, op2_offset, op2_count, op1, op1_offset, op1_count, -1, s1p, s1p_offset, tp, tp_offset)
			else
				Result := basic_gcdext_arranged (target, target_offset, cofactor, cofactor_offset, cofactor_count, op1, op1_offset, op1_count, op2, op2_offset, op2_count, 1, s1p, s1p_offset, tp, tp_offset)
			end
		end

	basic_gcdext_arranged (target: SPECIAL [NATURAL_32]; target_offset: INTEGER_32; cofactor_a: SPECIAL [NATURAL_32]; cofactor_offset_a: INTEGER_32; cofactor_count: TUPLE [cofactor_count: INTEGER_32]; op1_a: SPECIAL [NATURAL_32]; op1_offset_a: INTEGER_32; op1_count_a: INTEGER_32; op2_a: SPECIAL [NATURAL_32]; op2_offset_a: INTEGER_32; op2_count_a: INTEGER_32; sign_a: INTEGER; s1p_a: SPECIAL [NATURAL_32]; s1p_offset_a: INTEGER; tp_a: SPECIAL [NATURAL_32]; tp_offset_a: INTEGER): INTEGER
		local
			a: NATURAL_32
			b: NATURAL_32
			c: NATURAL_32
			d: NATURAL_32
			wp: SPECIAL [NATURAL_32]
			wp_offset: INTEGER_32
			orig_s0p: SPECIAL [NATURAL_32]
			orig_s0p_offset: INTEGER
			use_double_flag: BOOLEAN
			cnt: INTEGER_32
			assign_l: NATURAL_32
			op2_count: INTEGER
			done: BOOLEAN
			done_2: BOOLEAN
			uh: NATURAL_32
			vh: NATURAL_32
			ul: NATURAL_32
			vl: NATURAL_32
			tac: NATURAL_32
			tbd: NATURAL_32
			q1: NATURAL_32
			q2: NATURAL_32
			nh: CELL [NATURAL_32]
			nl: CELL [NATURAL_32]
			dh: CELL [NATURAL_32]
			dl: CELL [NATURAL_32]
			t1: CELL [NATURAL_32]
			t0: CELL [NATURAL_32]
			thigh: CELL [NATURAL_32]
			tlow: CELL [NATURAL_32]
			sign: INTEGER
			q: NATURAL_32
			t: NATURAL_32
			ssize: INTEGER_32
			qsize: INTEGER_32
			i: INTEGER_32
			cy: NATURAL_32
			cofactor: SPECIAL [NATURAL_32]
			cofactor_offset: INTEGER_32
			op1: SPECIAL [NATURAL_32]
			op1_offset: INTEGER_32
			op2: SPECIAL [NATURAL_32]
			op2_offset: INTEGER_32
			tp: SPECIAL [NATURAL_32]
			tp_offset: INTEGER_32
			s1p: SPECIAL [NATURAL_32]
			s1p_offset: INTEGER_32
			temp_special: SPECIAL [NATURAL_32]
			temp_integer: INTEGER_32
			op1_count: INTEGER
			tsize: INTEGER
			wsize: INTEGER
			junk: NATURAL_32
			cy1: NATURAL_32
			cy2: NATURAL_32
			carry: CELL [NATURAL_32]
		do
			create carry.put (0)
			cofactor := cofactor_a
			cofactor_offset := cofactor_offset_a
			op1 := op1_a
			op1_offset := op1_offset_a
			op1_count := op1_count_a
			op2 := op2_a
			op2_offset := op2_offset_a
			tp := tp_a
			tp_offset := tp_offset_a
			s1p := s1p_a
			s1p_offset := s1p_offset_a
			ssize := 1
			sign := sign_a
			op2_count := op2_count_a
			orig_s0p := cofactor
			orig_s0p_offset := cofactor_offset
			create wp.make_filled (0, op1_count + 1)
			wp_offset := 0
			use_double_flag := op1_count > 17
			from
			until
				done
			loop
				op2_count := op1_count
				op2_count := normalize (op2, op2_offset, op2_count)
				if op2_count <= 1 then
					done := True
				else
					if use_double_flag then
						uh := op1 [op1_count - 1]
						vh := op2 [op1_count - 1]
						ul := op1 [op1_count - 2]
						vl := op2 [op1_count - 2]
						cnt := leading_zeros (uh)
						if cnt /= 0 then
							uh := (uh |<< cnt).bit_or (ul |>> (32 - cnt))
							vh := (vh |<< cnt).bit_or (vl |>> (32 - cnt))
							vl := vl |<< cnt
							ul := ul |<< cnt
							if op1_count >= 3 then
								ul := ul.bit_or (op1 [op1_offset + op1_count - 3] |>> (32 - cnt))
								vl := vl.bit_or (op2 [op2_offset + op1_count - 3] |>> (32 - cnt))
							end
						end
						a := 1
						b := 0
						c := 0
						d := 1
						assign_l := 0
						from
							done_2 := False
							create nh.put (0)
							create nl.put (0)
							create dh.put (0)
							create dl.put (0)
							create thigh.put (0)
							create tlow.put (0)
							create t1.put (0)
							create t0.put (0)
						until
							done_2
						loop
							sub_ddmmss (dh, dl, vh, vl, 0, c)
							if dh.item = 0 then
								done_2 := True
							else
								add_ssaaaa (nh, nl, uh, ul, 0, a)
								q1 := gcdext_div2 (nh.item, nl.item, dh.item, dl.item)
								add_ssaaaa (dh, dl, vh, vl, 0, d)
								if dh.item = 0 then
									done_2 := True
								else
									sub_ddmmss (nh, nl, uh, ul, 0, b)
									q2 := gcdext_div2 (nh.item, nl.item, dh.item, dl.item)
									if q1 /= q2 then
										done_2 := True
									else
										tac := a + q1 * c
										tbd := b + q1 * d
										a := c
										c := tac
										b := d
										d := tbd
										umul_ppmm (t1, t0, q1, vl)
										t1.put (t1.item + q1 * vh)
										sub_ddmmss (thigh, tlow, uh, ul, t1.item, t0.item)
										uh := vh
										ul := vl
										vh := thigh.item
										vl := tlow.item
										assign_l := assign_l.bit_not
										add_ssaaaa (dh, dl, vh, vl, 0, c)
										sub_ddmmss (nh, nl, uh, ul, 0, a)
										q1 := gcdext_div2 (nh.item, nl.item, dh.item, dl.item)
										sub_ddmmss (dh, dl, vh, vl, 0, d)
										if dh.item = 0 then
											done_2 := True
										else
											add_ssaaaa (nh, nl, uh, ul, 0, b)
											q2 := gcdext_div2 (nh.item, nl.item, dh.item, dl.item)
											if q1 /= q2 then
												done_2 := True
											else
												tac := a + q1 * c
												tbd := b + q1 * d
												a := c
												c := tac
												b := d
												d := tbd
												umul_ppmm (t1, t0, q1, vl)
												t1.put (t1.item + (q1 * vh))
												sub_ddmmss (thigh, tlow, uh, ul, t1.item, t0.item)
												uh := vh
												ul := vl
												vh := thigh.item
												vl := tlow.item
												assign_l := assign_l.bit_not
											end
										end
									end
								end
							end
						end
						if assign_l /= 0 then
							sign := -sign
						end
					else
						uh := op1 [op1_offset + op1_count - 1]
						vh := op2 [op2_offset + op1_count - 1]
						cnt := leading_zeros (uh)
						if cnt /= 0 then
							uh := (uh |<< cnt).bit_or (op1 [op1_offset + op1_count - 2] |>> (limb_bits - cnt))
							vh := (vh |<< cnt).bit_or (op2 [op2_offset + op1_count - 2] |>> (limb_bits - cnt))
						end
						a := 1
						b := 0
						c := 0
						d := 1
						assign_l := 0
						from
							done_2 := False
						until
							done_2
						loop
							if vh - c = 0 or vh + d = 0 then
								done_2 := True
							else
								q := (uh + a) // (vh - c)
								if q /= (uh - b) // (vh + d) then
									done_2 := True
								else
									t := a + q * c
									a := c
									c := t
									t := b + q * d
									b := d
									d := t
									t := uh - q * vh
									uh := vh
									vh := t
									assign_l := assign_l.bit_not
									if vh - d = 0 then
										done_2 := True
									else
										q := (uh - a) // (vh + c)
										if q /= (uh + b) // (vh - d) then
											done_2 := True
										else
											t := a + q * c
											a := c
											c := t
											t := b + q * d
											b := d
											d := t
											t := uh - q * vh
											uh := vh
											vh := t
											assign_l := assign_l.bit_not
										end
									end
								end
							end
						end
						if assign_l /= 0 then
							sign := -sign
						end
					end
					if b = 0 then
						tdiv_qr (wp, wp_offset, op1, op1_offset, op1, op1_offset, op1_count, op2, op2_offset, op2_count)
						tp.copy_data (cofactor, cofactor_offset, tp_offset, ssize)
						qsize := op1_count - op2_count + 1
						s1p.fill_with (0, s1p_offset + ssize, s1p_offset + ssize + qsize - 1)
						from
							i := 0
						until
							i >= qsize
						loop
							addmul_1 (tp, tp_offset + i, s1p, s1p_offset, ssize, wp [wp_offset + i], carry)
							cy := carry.item
							tp [tp_offset + ssize + i] := cy
							i := i + 1
						end
						ssize := ssize + qsize
						ssize := ssize - (tp [tp_offset + ssize - 1] = 0).to_integer
						sign := -sign
						temp_special := cofactor
						temp_integer := cofactor_offset
						cofactor := s1p
						cofactor_offset := s1p_offset
						s1p := temp_special
						s1p_offset := temp_integer
						temp_special := s1p
						temp_integer := s1p_offset
						s1p := tp
						s1p_offset := tp_offset
						tp := temp_special
						tp_offset := temp_integer
						op1_count := op2_count
						temp_special := op1
						temp_integer := op1_offset
						op1 := op2
						op1_offset := op2_offset
						op2 := temp_special
						op2_offset := temp_integer
					else
						if a = 0 then
							tp.copy_data (op2, op2_offset, tp_offset, op1_count)
							wp.copy_data (op1, op1_offset, wp_offset, op1_count)
							submul_1 (wp, wp_offset, op2, op2_offset, op1_count, d, carry)
							junk := carry.item
							temp_special := tp
							temp_integer := tp_offset
							tp := op1
							tp_offset := op1_offset
							op1 := temp_special
							op1_offset := temp_integer
							temp_special := wp
							temp_integer := wp_offset
							wp := op2
							wp_offset := op2_offset
							op2 := temp_special
							op2_offset := temp_integer
							tp.copy_data (s1p, s1p_offset, tp_offset, ssize)
							tsize := ssize
							tp [tp_offset + ssize] := 0
							wp.copy_data (cofactor, cofactor_offset, wp_offset, ssize)
							addmul_1 (wp, wp_offset, s1p, s1p_offset, ssize, d, carry)
							cy := carry.item
							wp [wp_offset + ssize] := cy
							wsize := ssize + (cy /= 0).to_integer
							temp_special := tp
							temp_integer := tp_offset
							tp := cofactor
							tp_offset := cofactor_offset
							cofactor := temp_special
							cofactor_offset := temp_integer
							temp_special := wp
							temp_integer := wp_offset
							wp := s1p
							wp_offset := s1p_offset
							s1p := temp_special
							s1p_offset := temp_integer
							ssize := wsize.max (tsize)
						else
							if assign_l /= 0 then
								mul_1 (tp, tp_offset, op2, op2_offset, op1_count, b, carry)
								junk := carry.item
								submul_1 (tp, tp_offset, op1, op1_offset, op1_count, a, carry)
								junk := carry.item
								mul_1 (wp, wp_offset, op1, op1_offset, op1_count, c, carry)
								junk := carry.item
								submul_1 (wp, wp_offset, op2, op2_offset, op1_count, d, carry)
								junk := carry.item
							else
								mul_1 (tp, tp_offset, op1, op1_offset, op1_count, a, carry)
								junk := carry.item
								submul_1 (tp, tp_offset, op2, op2_offset, op1_count, b, carry)
								junk := carry.item
								mul_1 (wp, wp_offset, op2, op2_offset, op1_count, d, carry)
								junk := carry.item
								submul_1 (wp, wp_offset, op1, op1_offset, op1_count, c, carry)
								junk := carry.item
							end
							temp_special := tp
							temp_integer := tp_offset
							tp := op1
							tp_offset := op1_offset
							op1 := temp_special
							op1_offset := temp_integer
							temp_special := wp
							temp_integer := wp_offset
							wp := op2
							wp_offset := op2_offset
							op2 := temp_special
							op2_offset := temp_integer
							mul_1 (tp, tp_offset, cofactor, cofactor_offset, ssize, a, carry)
							cy1 := carry.item
							addmul_1 (tp, tp_offset, s1p, s1p_offset, ssize, b, carry)
							cy2 := carry.item
							cy := cy1 + cy2
							tp [tp_offset + ssize] := cy
							tsize := ssize + (cy /= 0).to_integer
							if cy < cy1 then
								tp [tp_offset + tsize] := 1
								wp [wp_offset + tsize] := 0
								tsize := tsize + 1
							end
							mul_1 (wp, wp_offset, s1p, s1p_offset, ssize, d, carry)
							cy1 := carry.item
							addmul_1 (wp, wp_offset, cofactor, cofactor_offset, ssize, c, carry)
							cy2 := carry.item
							cy := cy1 + cy2
							wp [wp_offset + ssize] := cy
							wsize := ssize + (cy /= 0).to_integer
							if cy < cy1 then
								wp [wp_offset + wsize] := 1
								if wsize >= tsize then
									tp [tp_offset + wsize] := 0
								end
								wsize := wsize + 1
							end
							temp_special := tp
							temp_integer := tp_offset
							tp := cofactor
							tp_offset := cofactor_offset
							cofactor := temp_special
							cofactor_offset := temp_integer
							temp_special := wp
							temp_integer := wp_offset
							wp := s1p
							wp_offset := s1p_offset
							s1p := temp_special
							s1p_offset := temp_integer
							ssize := wsize.max (tsize)
						end
						op1_count := op1_count - (op1 [op1_offset + op1_count - 1] = 0).to_integer
					end
				end
			end
			if op2_count = 0 then
				if target /= op1 then
					target.copy_data (op1, op1_offset, target_offset, op1_count)
				end
				ssize := normalize (cofactor, cofactor_offset, ssize)
				if orig_s0p /= cofactor then
					orig_s0p.copy_data (cofactor, cofactor_offset, orig_s0p_offset, ssize)
				end
				if sign >= 0 then
					cofactor_count.cofactor_count := ssize
				else
					cofactor_count.cofactor_count := -ssize
				end
				Result := op1_count
			else
				vl := op2 [op2_offset]
				t := divrem_1 (wp, wp_offset, op1, op1_offset, op1_count, vl)
				tp.copy_data (cofactor, cofactor_offset, tp_offset, ssize)
				qsize := op1_count - (wp [wp_offset + op1_count - 1] = 0).to_integer
				if ssize < qsize then
					tp.fill_with (0, tp_offset + ssize, qsize - ssize)
					s1p.fill_with (0, s1p_offset + ssize, qsize)
					from
						i := 0
					until
						i >= ssize
					loop
						addmul_1 (tp, tp_offset + i, wp, wp_offset, qsize, s1p [s1p_offset + i], carry)
						cy := carry.item
						tp [tp_offset + qsize + i] := cy
						i := i + 1
					end
				else
					s1p.fill_with (0, s1p_offset + ssize, s1p_offset + ssize + qsize - 1)
					from
						i := 0
					until
						i >= qsize
					loop
						addmul_1 (tp, tp_offset + i, s1p, s1p_offset, ssize, wp [wp_offset + i], carry)
						cy := carry.item
						tp [tp_offset + ssize + i] := cy
						i := i + 1
					end
				end
				ssize := ssize + qsize
				ssize := ssize - (tp [tp_offset + ssize - 1] = 0).to_integer
				sign := -sign
				temp_special := cofactor
				temp_integer := cofactor_offset
				cofactor := s1p
				cofactor_offset := s1p_offset
				s1p := temp_special
				s1p_offset := temp_integer
				temp_special := s1p
				temp_integer := s1p_offset
				s1p := tp
				s1p_offset := tp_offset
				tp := temp_special
				tp_offset := temp_integer
				ul := vl
				vl := t
				from
				until
					vl = 0
				loop
					q := ul // vl
					t := ul - q * vl
					tp.copy_data (cofactor, cofactor_offset, tp_offset, ssize)
					s1p.fill_with (0, s1p_offset + ssize, s1p_offset + ssize)
					addmul_1 (tp, tp_offset, s1p, s1p_offset, ssize, q, carry)
					cy := carry.item
					tp [tp_offset + ssize] := cy
					ssize := ssize + 1
					ssize := ssize - (tp [tp_offset + ssize - 1] = 0).to_integer
					sign := -sign
					temp_special := cofactor
					temp_integer := cofactor_offset
					cofactor := s1p
					cofactor_offset := s1p_offset
					s1p := temp_special
					s1p_offset := temp_integer
					temp_special := s1p
					temp_integer := s1p_offset
					s1p := tp
					s1p_offset := tp_offset
					tp := temp_special
					tp_offset := temp_integer
					ul := vl
					vl := t
				end
				target [target_offset] := ul
				ssize := normalize (cofactor, cofactor_offset, ssize)
				if orig_s0p /= cofactor then
					orig_s0p.copy_data (cofactor, cofactor_offset, orig_s0p_offset, ssize)
				end
				if sign >= 0 then
					cofactor_count.cofactor_count := ssize
				else
					cofactor_count.cofactor_count := -ssize
				end
				Result := 1
			end
		end

	gcdext (target: SPECIAL [NATURAL_32]; target_offset: INTEGER; cofactor: SPECIAL [NATURAL_32]; cofactor_offset: INTEGER; cofactor_count: TUPLE [cofactor_count: INTEGER]; op1: SPECIAL [NATURAL_32]; op1_offset: INTEGER; op1_count: INTEGER; op2: SPECIAL [NATURAL_32]; op2_offset: INTEGER; op2_count: INTEGER): INTEGER
		require
			op1_count >= op2_count
			op2_count >= 0
			op1.valid_index (op1_offset)
			op1.valid_index (op1_offset + op1_count - 0)
			op1 [op1_offset + op1_count - 1] /= 0
			op2.valid_index (op2_offset)
			op2.valid_index (op2_offset + op2_count - 0)
			op1 [op1_offset + op1_count - 1] /= 0
			target.valid_index (target_offset)
			target.valid_index (target_offset + op1_count - 1)
			cofactor.valid_index (cofactor_offset)
			cofactor.valid_index (cofactor_offset + op1_count - 1)
		local
			orig_n: INTEGER
		do
			orig_n := op2_count
			Result := basic_gcdext (target, target_offset, cofactor, cofactor_offset, cofactor_count, op1, op1_offset, op1_count, op2, op2_offset, op2_count)
		end

	modexact_1c_odd (op1: SPECIAL [NATURAL_32]; op1_offset: INTEGER; op1_count: INTEGER; d: NATURAL_32; orig_c: NATURAL_32): NATURAL_32
		require
			op1_count >= 1
			d.bit_test (0)
		local
			s: NATURAL_32
			h: CELL [NATURAL_32]
			l: CELL [NATURAL_32]
			inverse: NATURAL_32
			dummy: CELL [NATURAL_32]
			dmul: NATURAL_32
			c: CELL [NATURAL_32]
			i: INTEGER
		do
			create l.put (0)
			create c.put (orig_c)
			create h.put (0)
			create dummy.put (0)
			if op1_count = 1 then
				s := op1 [op1_offset]
				if s > c.item then
					l.put (s - c.item)
					h.put (l.item \\ d)
					if h.item /= 0 then
						h.put (d - h.item)
					end
				else
					l.put (c.item - s)
					h.put (l.item \\ d)
				end
				Result := h.item
			else
				inverse := modlimb_invert (d)
				dmul := d
				from
					i := 0
				until
					i >= op1_count - 1
				loop
					s := op1 [op1_offset + i]
					subc_limb (c, l, s, c.item)
					l.put (l.item * inverse)
					umul_ppmm (h, dummy, l.item, dmul)
					c.put (c.item + h.item)
					i := i + 1
				end
				s := op1 [op1_offset + i]
				if s <= d then
					l.put (c.item - s)
					if c.item < s then
						l.put (l.item + d)
					end
					Result := l.item
				else
					subc_limb (c, l, s, c.item)
					l.put (l.item * inverse)
					umul_ppmm (h, dummy, l.item, dmul)
					c.put (c.item + h.item)
					Result := c.item
				end
			end
		ensure
			orig_c < d implies Result < d
			orig_c >= d implies Result <= d
		end

	preinv_mod_1 (up: SPECIAL [NATURAL_32]; up_offset: INTEGER; un: INTEGER; d: NATURAL_32; dinv: NATURAL_32): NATURAL_32
		require
			un >= 1
			d.bit_test (31)
		local
			i: INTEGER
			n0: NATURAL_32
			r: CELL [NATURAL_32]
			dummy: CELL [NATURAL_32]
		do
			create r.put (up [up_offset + un - 1])
			create dummy.put (0)
			if r.item >= d then
				r.put (r.item - d)
			end
			from
				i := un - 2
			until
				i < 0
			loop
				n0 := up [up_offset + i]
				udiv_qrnnd_preinv (dummy, r, r.item, n0, d, dinv)
				i := i - 1
			end
			Result := r.item
		end

	redc_basecase (cp: SPECIAL [NATURAL_32]; cp_offset: INTEGER; mp: SPECIAL [NATURAL_32]; mp_offset: INTEGER; n: INTEGER; n_prim: NATURAL_32; tp: SPECIAL [NATURAL_32]; tp_offset_a: INTEGER)
		local
			cy: NATURAL_32
			q: NATURAL_32
			j: INTEGER
			tp_offset: INTEGER
			junk: NATURAL_32
			carry: CELL [NATURAL_32]
		do
			create carry.put (0)
			tp_offset := tp_offset_a
			from
				j := 0
			until
				j >= n
			loop
				q := tp [tp_offset] * n_prim
				addmul_1 (tp, tp_offset, mp, mp_offset, n, q, carry)
				tp [tp_offset] := carry.item
				tp_offset := tp_offset + 1
				j := j + 1
			end
			add_n (cp, cp_offset, tp, tp_offset, tp, tp_offset - n, n, carry)
			cy := carry.item
			if cy /= 0 then
				sub_n (cp, cp_offset, cp, cp_offset, mp, mp_offset, n, carry)
				junk := carry.item
			end
		end

	subc_limb (cout: CELL [NATURAL_32]; w: CELL [NATURAL_32]; x: NATURAL_32; y: NATURAL_32)
		do
			w.put (x - y)
			cout.put ((w.item > x).to_integer.to_natural_32)
		end

	invert_gf (target: SPECIAL [NATURAL_32] target_offset: INTEGER op1_a: SPECIAL [NATURAL_32] op1_offset: INTEGER op1_count_a: INTEGER op2_a: SPECIAL [NATURAL_32] op2_offset: INTEGER op2_count_a: INTEGER)
			-- Invert `op1' over `op2' using the extended euclidian algorithm in F2M
		require
			op2_count_a > 0
			op1_count_a >= 0
			op1_count_a <= op2_count_a;
			(op1_count_a = op2_count_a) implies cmp (op1_a, op1_offset, op2_a, op2_offset, op2_count_a) <= 0
		local
			op1: SPECIAL [NATURAL_32]
			op1_count: INTEGER
			op1_leading_zeros: INTEGER
			op2: SPECIAL [NATURAL_32]
			op2_count: INTEGER
			op2_leading_zeros: INTEGER
			tmp_special: SPECIAL [NATURAL_32]
			tmp_integer: INTEGER
			g1: SPECIAL [NATURAL_32]
			g1_count: INTEGER
			g2: SPECIAL [NATURAL_32]
			g2_count: INTEGER
			operand_sizes: INTEGER
			left_shift_amount: INTEGER
		do
			operand_sizes := op2_count_a
			op1_count := op1_count_a
			op2_count := op2_count_a
			create op1.make_filled (0, operand_sizes + operand_sizes)
			create op2.make_filled (0, operand_sizes + operand_sizes)
			op1.copy_data (op1_a, op1_offset, 0, op1_count.min (operand_sizes))
			op2.copy_data (op2_a, op2_offset, 0, op2_count.min (operand_sizes))
			create g1.make_filled (0, operand_sizes + operand_sizes)
			create g2.make_filled (0, operand_sizes + operand_sizes)
			g1 [0] := 1
			g1_count := 1
			g2_count := 0
			from
			until
				op1_count = 0
			loop
				op1_leading_zeros := leading_zeros (op1 [op1_count - 1])
				op2_leading_zeros := leading_zeros (op2 [op2_count - 1])
				if op1_count < op2_count or (op1_count = op2_count and op1_leading_zeros > op2_leading_zeros) then
					tmp_special := op1
					op1 := op2
					op2 := tmp_special
					tmp_special := g1
					g1 := g2
					g2 := tmp_special
					tmp_integer := op1_count
					op1_count := op2_count
					op2_count := tmp_integer
					tmp_integer := op1_leading_zeros
					op1_leading_zeros := op2_leading_zeros
					op2_leading_zeros := tmp_integer
				end
				if op1_count /= op2_count or (op1_count = op2_count and op1_leading_zeros /= op2_leading_zeros) then
					left_shift_amount := (op1_count - op2_count) * limb_bits + op2_leading_zeros - op1_leading_zeros
					bit_xor_lshift (op1, 0, op1, 0, op1_count, op2, 0, op2_count, left_shift_amount)
					bit_xor_lshift (g1, 0, g1, 0, operand_sizes, g2, 0, operand_sizes, left_shift_amount)
				else
					bit_xor (op1, 0, op1, 0, op1_count, op2, 0, op2_count)
					bit_xor (g1, 0, g1, 0, operand_sizes, g2, 0, operand_sizes)
				end
				op1_count := normalize (op1, 0, op1_count)
				op2_count := normalize (op2, 0, op2_count)
			end
			target.copy_data (g2, 0, target_offset, operand_sizes)
		end
end
