note
	description: "Summary description for {NUMBER_ARITHMETIC}."
	author: "Colin LeMahieu"
	date: "$Date: 2011-11-11 18:13:16 +0100 (ven., 11 nov. 2011) $"
	revision: "$Revision: 87787 $"
	quote: "Dependence leads to subservience. -  Thomas Jefferson"

deferred class
	SPECIAL_ARITHMETIC

inherit
	LIMB_BIT_SCANNING
	LIMB_MANIPULATION
	SPECIAL_LOGIC

feature

	add (target: SPECIAL [NATURAL_32]; target_offset: 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; carry: CELL [NATURAL_32])
		require
			op2_count >= 0
			op1_count >= op2_count
			op2_count = 0 or target.valid_index (target_offset)
			op2_count = 0 or target.valid_index (target_offset + op1_count - 1)
			op2_count = 0 or op1.valid_index (op1_offset)
			op2_count = 0 or op1.valid_index (op1_offset + op1_count - 1)
			op2_count = 0 or op2.valid_index (op2_offset)
			op2_count = 0 or op2.valid_index (op2_offset + op2_count - 1)
		local
			i: INTEGER_32
			x: NATURAL_32
			add_carry: NATURAL_32
			j: INTEGER_32
			done: BOOLEAN
		do
			i := op2_count
			add_n (target, target_offset, op1, op1_offset, op2, op2_offset, i, carry)
			add_carry := carry.item
			if add_carry /= 0 then
				from
					x := 0
				until
					done or x /= 0
				loop
					if i >= op1_count then
						carry.put (1)
						done := True
					else
						x := op1 [op1_offset + i]
						x := x + 1
						target [target_offset + i] := x
						i := i + 1
					end
				end
			end
			if not done then
				if target /= op1 then
					from
						j := i
					until
						j >= op1_count
					loop
						target [target_offset + j] := op1 [op1_offset + j]
						j := j + 1
					end
				end
				carry.put (0)
			end
		end

	add_1 (target: SPECIAL [NATURAL_32]; target_offset: INTEGER_32; op1: SPECIAL [NATURAL_32]; op1_offset: INTEGER_32; size: INTEGER_32; op2: NATURAL_32; carry: CELL [NATURAL_32])
			-- Adds number in SPECIAL `op1' at `op1_offset' of length `size' to the single limb `op2' storing the result in `target' at `target_offset' and carry in `carry'
		local
			i: INTEGER_32
			r: NATURAL_32
		do
			r := op1 [op1_offset] + op2
			target [target_offset] := r
			if r < op2 then
				from
					i := 1
					r := 0
				until
					i >= size or r /= 0
				loop
					r := op1 [op1_offset + i] + 1
					target [target_offset + i] := r
					i := i + 1
				end
				target.copy_data (op1, op1_offset + i, target_offset + i, size - i)
				carry.put ((i = size and r = 0).to_integer.to_natural_32)
			else
				if target /= op1 then
					target.copy_data (op1, op1_offset + 1, target_offset + 1, size - 1)
				end
				carry.put (0)
			end
		ensure
			carry.item = 0 or carry.item = 1
		end

	add_n (target: SPECIAL [NATURAL_32]; target_offset: INTEGER_32; op1: SPECIAL [NATURAL_32]; op1_offset: INTEGER_32; op2: SPECIAL [NATURAL_32]; op2_offset: INTEGER_32; n: INTEGER_32; carry: CELL [NATURAL_32])
			-- Add two numbers in SPECIAL `op1' at `op1_offset' and `op2' at `op2_offset' both of length `n' putting result in `target' at `target_offset' and carry in `carry'
		require
			n >= 0
			n = 0 or op1.valid_index (op1_offset)
			n = 0 or op1.valid_index (op1_offset + n - 1)
			n = 0 or op2.valid_index (op2_offset)
			n = 0 or op2.valid_index (op2_offset + n - 1)
			n = 0 or target.valid_index (target_offset)
			n = 0 or target.valid_index (target_offset + n - 1)
		local
			ul: NATURAL_32
			vl: NATURAL_32
			sl: NATURAL_32
			rl: NATURAL_32
			cy1: NATURAL_32
			cy2: NATURAL_32
			cursor: INTEGER_32
			carry_l: NATURAL_32
		do
			from
				cursor := 0
			until
				cursor = n
			loop
				ul := op1 [cursor + op1_offset]
				vl := op2 [cursor + op2_offset]
				sl := ul + vl
				cy1 := (sl < ul).to_integer.to_natural_32
				rl := sl + carry_l
				cy2 := (rl < sl).to_integer.to_natural_32
				carry_l := cy1.bit_or (cy2)
				target [cursor + target_offset] := rl
				cursor := cursor + 1
			variant
				n - cursor + 1
			end
			carry.put (carry_l)
		end

	addmul_1 (target: SPECIAL [NATURAL_32]; target_offset: INTEGER_32; op1: SPECIAL [NATURAL_32]; op1_offset: INTEGER_32; n: INTEGER_32; vl: NATURAL_32; carry: CELL [NATURAL_32])
		require
			n >= 0
		local
			ul: NATURAL_32
			hpl: CELL [NATURAL_32]
			lpl: CELL [NATURAL_32]
			rl: NATURAL_32
			cursor: INTEGER_32
			carry_l: NATURAL_32
		do
			create hpl.put (0)
			create lpl.put (0)
			from
				cursor := 0
			until
				n - cursor = 0
			loop
				ul := op1 [op1_offset + cursor]
				umul_ppmm (hpl, lpl, ul, vl)
				lpl.put (lpl.item + carry_l)
				carry_l := (lpl.item < carry_l).to_integer.to_natural_32 + hpl.item
				rl := target [target_offset + cursor]
				lpl.put (rl + lpl.item)
				carry_l := carry_l + (lpl.item < rl).to_integer.to_natural_32
				target [target_offset + cursor] := lpl.item
				cursor := cursor + 1
			variant
				n - cursor + 1
			end
			carry.put (carry_l)
		end

	decr_u (target: SPECIAL [NATURAL_32]; target_offset: INTEGER_32; decr: NATURAL_32)
		local
			x: NATURAL_32
			cursor: INTEGER_32
		do
			cursor := target_offset
			x := target [target_offset]
			target [target_offset] := x - decr
			if x < decr then
				from
					cursor := target_offset + 1
					x := 0
				until
					x /= 0
				loop
					x := target [cursor]
					x := x - 1
					target [cursor] := x
					cursor := cursor + 1
				end
			end
		end

	incr_u (target: SPECIAL [NATURAL_32]; target_offset: INTEGER_32; incr: NATURAL_32)
		local
			x: NATURAL_32
			cursor: INTEGER_32
		do
			x := target [target_offset] + incr
			target [target_offset] := x
			if x < incr then
				from
					cursor := target_offset + 1
					x := 0
				until
					x /= 0
				loop
					x := target [cursor]
					x := x + 1
					target [cursor] := x
					cursor := cursor + 1
				end
			end
		end

	mul (target: SPECIAL [NATURAL_32]; target_offset_a: 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; carry: CELL [NATURAL_32])
		require
			target.valid_index (target_offset_a)
			target.valid_index (target_offset_a + op2_count_a - 1)
			op1_a.valid_index (op1_offset_a)
			op1_a.valid_index (op1_offset_a + op2_count_a - 1)
			op1_count_a >= op2_count_a
			op2_count_a >= 1
		local
			l: INTEGER_32
			t: NATURAL_32
			ws: SPECIAL [NATURAL_32]
			op1: SPECIAL [NATURAL_32]
			op2: SPECIAL [NATURAL_32]
			int_temp: INTEGER_32
			special_temp: SPECIAL [NATURAL_32]
			op1_count: INTEGER_32
			op2_count: INTEGER_32
			op1_offset: INTEGER_32
			op2_offset: INTEGER_32
			target_offset: INTEGER_32
			tp: SPECIAL [NATURAL_32]
			tp_offset: INTEGER
			cy: NATURAL_32
			c: NATURAL_32
		do
			op1 := op1_a
			op2 := op2_a
			op1_count := op1_count_a
			op2_count := op2_count_a
			op1_offset := op1_offset_a
			op2_offset := op2_offset_a
			target_offset := target_offset_a
			if op1_count = op2_count then
				if op1 = op2 and op1_offset = op2_offset then
					sqr_n (target, target_offset, op1, op1_offset, op1_count)
					carry.put (target [target_offset + 2 * op1_count - 1])
				else
					mul_n (target, target_offset, op1, op1_offset, op2, op2_offset, op1_count)
					carry.put (target [target_offset + 2 * op1_count - 1])
				end
			else
				if op2_count < 32 then
					if op1_count <= 500 then
						mul_basecase (target, target_offset, op1, op1_offset, op1_count, op2, op2_offset, op2_count)
					else
						create tp.make_filled (0, 32)
						mul_basecase (target, target_offset, op1, op1_offset, 500, op2, op2_offset, op2_count)
						target_offset := target_offset + 500
						tp.copy_data (target, target_offset, tp_offset, op2_count)
						op1_offset := op1_offset + 500
						op1_count := op1_count - 500
						from
						until
							op1_count <= 500
						loop
							mul_basecase (target, target_offset, op1, op1_offset, 500, op2, op2_offset, op2_count)
							add_n (target, target_offset, target, target_offset, tp, tp_offset, op2_count, carry)
							cy := carry.item
							incr_u (target, target_offset + op2_count, cy)
							target_offset := target_offset + 500
							tp.copy_data (target, target_offset, tp_offset, op2_count)
							op1_offset := op1_offset + 500
							op1_count := op1_count - 500
						end
						if op1_count > op2_count then
							mul_basecase (target, target_offset, op1, op1_offset, op1_count, op2, op2_offset, op2_count)
						else
							mul_basecase (target, target_offset, op2, op2_offset, op2_count, op1, op1_offset, op1_count)
						end
						add_n (target, target_offset, target, target_offset, tp, tp_offset, op2_count, carry)
						cy := carry.item
						incr_u (target, target_offset + op2_count, cy)
					end
					carry.put (target [target_offset + op1_count + op2_count - 1])
				else
					mul_n (target, target_offset, op1, op1_offset, op2, op2_offset, op2_count)
					if op1_count /= op2_count then
						target_offset := target_offset + op2_count
						l := op2_count
						op1_offset := op1_offset + op2_count
						op1_count := op1_count - op2_count
						if op1_count < op2_count then
							special_temp := op1
							op1 := op2
							op2 := special_temp
							int_temp := op1_offset
							op1_offset := op2_offset
							op2_offset := int_temp
							int_temp := op1_count
							op1_count := op2_count
							op2_count := int_temp
						end
						if op2_count >= 32 then
							create ws.make_filled (0, op2_count + op2_count)
						else
							create ws.make_filled (0, op1_count + op2_count)
						end
						from
							t := 0
						until
							op2_count < 32
						loop
							mul_n (ws, 0, op1, op1_offset, op2, op2_offset, op2_count)
							if l <= 2 * op2_count then
								add_n (target, target_offset, target, target_offset, ws, 0, 1, carry)
								t := t + carry.item
								if l /= 2 * op2_count then
									add_1 (target, target_offset + 1, ws, 1, 2 * op2_count - 1, t, carry)
									t := carry.item
									l := 2 * op2_count
								end
							else
								add_n (target, target_offset, target, target_offset, ws, 0, 2 * op2_count, carry)
								c := carry.item
								add_1 (target, target_offset + 2 * op2_count, target, target_offset + 2 * op2_count, 1 - 2 * op2_count, c, carry)
								t := t + carry.item
							end
							target_offset := target_offset + op2_count
							l := l - op2_count
							op1_offset := op1_offset + op2_count
							op1_count := op1_count - op2_count
							if op1_count < op2_count then
								special_temp := op1
								op1 := op2
								op2 := special_temp
								int_temp := op1_count
								op1_count := op2_count
								op2_count := int_temp
								int_temp := op1_offset
								op1_offset := op2_offset
								op2_offset := int_temp
							end
						end
						if op2_count /= 0 then
							mul_basecase (ws, 0, op1, op1_offset, op1_count, op2, op2_offset, op2_count)
							if l <= op1_count + op2_count then
								add_n (target, target_offset, target, target_offset, ws, 0, l, carry)
								t := t + carry.item
								if l /= op1_count + op2_count then
									add_1 (target, target_offset + l, ws, l, op1_count + op2_count - l, t, carry)
									t := carry.item
								else
									add_n (target, target_offset, target, target_offset, ws, 0, op1_count + op2_count, carry)
									c := carry.item
									add_1 (target, target_offset + op1_count + op2_count, target, target_offset + op1_count + op2_count, l - op1_count - op2_count, c, carry)
									t := t + carry.item
								end
							end
						end
					end
					carry.put (target [op1_count + op2_count - 1])
				end
			end
		end

	mul_1 (target: SPECIAL [NATURAL_32]; target_offset: INTEGER_32; op1: SPECIAL [NATURAL_32]; op1_offset: INTEGER_32; n: INTEGER_32; vl: NATURAL_32; carry: CELL [NATURAL_32])
		require
			op1.valid_index (op1_offset)
			op1.valid_index (op1_offset + n - 1)
			target.valid_index (target_offset)
			target.valid_index (target_offset + n - 1)
		local
			ul: NATURAL_32
			hpl: CELL [NATURAL_32]
			lpl: CELL [NATURAL_32]
			cursor: INTEGER_32
			carry_l: NATURAL_32
		do
			create hpl.put (0)
			create lpl.put (0)
			from
			until
				n = cursor
			loop
				ul := op1 [op1_offset + cursor]
				umul_ppmm (hpl, lpl, ul, vl)
				lpl.put (lpl.item + carry_l)
				carry_l := (lpl.item < carry_l).to_integer.to_natural_32
				carry_l := carry_l + hpl.item
				target [target_offset + cursor] := lpl.item
				cursor := cursor + 1
			end
			carry.put (carry_l)
		end

	mul_basecase (target: SPECIAL [NATURAL_32]; target_offset: 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)
		require
			op2_count >= 1
			op1_count >= op2_count
			target.valid_index (target_offset)
			target.valid_index (target_offset + op1_count - 1)
			op1.valid_index (op1_offset)
			op1.valid_index (op1_offset + op1_count - 1)
		local
			cursor: INTEGER_32
			carry: CELL [NATURAL_32]
		do
			create carry.put (0)
			mul_1 (target, target_offset, op1, op1_offset, op1_count, op2 [op2_offset], carry)
			target [target_offset + op1_count] := carry.item
			cursor := 1
			from
			until
				op2_count = cursor
			loop
				addmul_1 (target, target_offset + cursor, op1, op1_offset, op1_count, op2 [op2_offset + cursor], carry)
				target [target_offset + op1_count + cursor] := carry.item
				cursor := cursor + 1
			end
		end

	mul_n (target: SPECIAL [NATURAL_32]; target_offset: INTEGER_32; op1: SPECIAL [NATURAL_32]; op1_offset: INTEGER_32; op2: SPECIAL [NATURAL_32]; op2_offset: INTEGER_32; n: INTEGER_32)
		require
			n >= 1
			target.valid_index (target_offset)
			target.valid_index (target_offset + n - 1)
			op1.valid_index (op1_offset)
			op1.valid_index (op1_offset + n - 1)
		local
			ws: SPECIAL [NATURAL_32]
		do
			if n < 32 then
				mul_basecase (target, target_offset, op1, op1_offset, n, op2, op2_offset, n)
			else
				create ws.make_filled (0, 2 * n + 2 * 32)
				kara_mul_n (target, target_offset, op1, op1_offset, op2, op2_offset, n, ws, 0)
			end
		end

	kara_mul_n (target: SPECIAL [NATURAL_32]; target_offset: INTEGER_32; a: SPECIAL [NATURAL_32]; a_offset: INTEGER_32; b: SPECIAL [NATURAL_32]; b_offset: INTEGER_32; n: INTEGER_32; ws: SPECIAL [NATURAL_32]; w_offset: INTEGER_32)
		require
			n >= 2
		local
			w: NATURAL_32
			w0: NATURAL_32
			w1: NATURAL_32
			c: NATURAL_32
			n2: INTEGER_32
			x: INTEGER_32
			y: INTEGER_32
			i: INTEGER_32
			sign: INTEGER_32
			n1: INTEGER_32
			n3: INTEGER_32
			nm1: INTEGER_32
			junk: NATURAL_32
			xlimb: NATURAL_32
			cursor: INTEGER_32
			carry: CELL [NATURAL_32]
		do
			create carry.put (0)
			n2 := n |>> 1
			if n.bit_and (0x1) /= 0 then
				n3 := n - n2
				sign := 0
				w := a [a_offset + n2]
				if w /= 0 then
					sub_n (target, target_offset, a, a_offset, a, a_offset + n3, n2, carry)
					w := w - carry.item
				else
					i := n2
					from
						w0 := w1
					until
						w0 /= w1 or else i = 0
					loop
						i := i - 1
						w0 := a [a_offset + i]
						w1 := a [a_offset + n3 + i]
					end
					if w0 < w1 then
						x := a_offset + n3
						y := a_offset
						sign := -1
					else
						x := a_offset
						y := a_offset + n3
					end
					sub_n (target, target_offset, a, x, a, y, n2, carry)
					junk := carry.item
				end
				target [target_offset + n2] := w
				w := b [b_offset + n2]
				if w /= 0 then
					sub_n (target, target_offset + n3, b, b_offset, b, b_offset + n3, n2, carry)
					w := w - carry.item
				else
					i := n2
					from
						w0 := w1
					until
						w0 /= w1 or else i = 0
					loop
						i := i - 1
						w0 := b [b_offset + i]
						w1 := b [b_offset + n3 + i]
					end
					if w0 < w1 then
						x := b_offset + n3
						y := b_offset
						sign := sign.bit_not
					else
						x := b_offset
						y := b_offset + n3
					end
					sub_n (target, target_offset + n3, b, x, b, y, n2, carry)
					junk := carry.item
				end
				target [target_offset + n] := w
				n1 := n + 1
				if n2 < 32 then
					if n3 < 32 then
						mul_basecase (ws, w_offset, target, target_offset, n3, target, target_offset + n3, n3)
						mul_basecase (target, target_offset, a, a_offset, n3, b, b_offset, n3)
					else
						kara_mul_n (ws, w_offset, target, target_offset, target, target_offset + n3, n3, ws, w_offset + n1)
						kara_mul_n (target, target_offset, a, a_offset, b, b_offset, n3, ws, w_offset + n1)
					end
					mul_basecase (target, target_offset + n1, a, a_offset + n3, n2, b, b_offset + n3, n2)
				else
					kara_mul_n (ws, w_offset, target, target_offset, target, target_offset + n3, n3, ws, w_offset + n1)
					kara_mul_n (target, target_offset, a, a_offset, b, b_offset, n3, ws, w_offset + n1)
					kara_mul_n (target, target_offset + n1, a, a_offset + n3, b, b_offset + n3, n2, ws, w_offset + n1)
				end
				nm1 := n - 1
				if sign /= 0 then
					add_n (ws, w_offset, target, target_offset, ws, w_offset, n1, carry)
					junk := carry.item
					add_n (ws, w_offset, target, target_offset + n1, ws, w_offset, nm1, carry)
					c := carry.item
					if c /= 0 then
						xlimb := ws [w_offset + nm1] + 1
						ws [w_offset + nm1] := xlimb
						if xlimb = 0 then
							ws [w_offset + n] := ws [w_offset + n] + 1
						end
					end
				else
					sub_n (ws, w_offset, target, target_offset, ws, w_offset, n1, carry)
					junk := carry.item
					add_n (ws, w_offset, target, target_offset + n1, ws, w_offset, nm1, carry)
					c := carry.item
					if c /= 0 then
						xlimb := ws [w_offset + nm1] + 1
						ws [w_offset + nm1] := xlimb
						if xlimb = 0 then
							ws [w_offset + n] := ws [w_offset + n] + 1
						end
					end
				end
				add_n (target, target_offset + n3, target, target_offset + n3, ws, w_offset, n1, carry)
				xlimb := carry.item
				if xlimb /= 0 then
					xlimb := target [target_offset + n1 + n3] + 1
					target [target_offset + n1 + n3] := xlimb
					from
						cursor := 1
					until
						xlimb /= 0
					loop
						xlimb := target [target_offset + n1 + n3 + cursor] + 1
						target [target_offset + n1 + n3 + cursor] := xlimb
						cursor := cursor + 1
					end
				end
			else
				i := n2
				from
					w0 := w1
				until
					w0 /= w1 or i = 0
				loop
					i := i - 1
					w0 := a [a_offset + i]
					w1 := a [a_offset + n2 + i]
				end
				sign := 0
				if w0 < w1 then
					x := a_offset + n2
					y := a_offset
					sign := -1
				else
					x := a_offset
					y := a_offset + n2
				end
				sub_n (target, target_offset, a, x, a, y, n2, carry)
				junk := carry.item
				i := n2
				from
					w0 := w1
				until
					w0 /= w1 or i = 0
				loop
					i := i - 1
					w0 := b [b_offset + i]
					w1 := b [b_offset + n2 + i]
				end
				if w0 < w1 then
					x := b_offset + n2
					y := b_offset
					sign := sign.bit_not
				else
					x := b_offset
					y := b_offset + n2
				end
				sub_n (target, target_offset + n2, b, x, b, y, n2, carry)
				junk := carry.item
				if n2 < 32 then
					mul_basecase (ws, w_offset, target, target_offset, n2, target, target_offset + n2, n2)
					mul_basecase (target, target_offset, a, a_offset, n2, b, b_offset, n2)
					mul_basecase (target, target_offset + n, a, a_offset + n2, n2, b, b_offset + n2, n2)
				else
					kara_mul_n (ws, w_offset, target, target_offset, target, target_offset + n2, n2, ws, w_offset + n)
					kara_mul_n (target, target_offset, a, a_offset, b, b_offset, n2, ws, w_offset + n)
					kara_mul_n (target, target_offset + n, a, a_offset + n2, b, b_offset + n2, n2, ws, w_offset + n)
				end
				if sign /= 0 then
					add_n (ws, w_offset, target, target_offset, ws, w_offset, n, carry)
					w := carry.item
					add_n (ws, w_offset, target, target_offset + n, ws, w_offset, n, carry)
					w := w + carry.item
				else
					sub_n (ws, w_offset, target, target_offset, ws, w_offset, n, carry)
					w := (0).to_natural_32 - carry.item
					add_n (ws, w_offset, target, target_offset + n, ws, w_offset, n, carry)
					w := w + carry.item
				end
				add_n (target, target_offset + n2, target, target_offset + n2, ws, w_offset, n, carry)
				w := w + carry.item
				xlimb := target [target_offset + n2 + n] + w
				target [target_offset + n2 + n] := xlimb
				if xlimb < w then
					from
						xlimb := 0
						cursor := 1
					until
						xlimb /= 0
					loop
						xlimb := target [target_offset + n2 + n + cursor] + 1
						target [target_offset + n2 + n + cursor] := xlimb
						cursor := cursor + 1
					end
				end
			end
		end

	kara_sqr_n (target: SPECIAL [NATURAL_32]; target_offset: INTEGER; op1: SPECIAL [NATURAL_32]; op1_offset: INTEGER; op1_count: INTEGER; ws: SPECIAL [NATURAL_32]; ws_offset: INTEGER)
		require
			op1_count >= 2
		local
			w: NATURAL_32
			w0: NATURAL_32
			w1: NATURAL_32
			c: NATURAL_32
			n2: INTEGER
			x: SPECIAL [NATURAL_32]
			x_offset: INTEGER
			y: SPECIAL [NATURAL_32]
			y_offset: INTEGER
			i: INTEGER
			n1: INTEGER
			n3: INTEGER
			nm1: INTEGER
			junk: NATURAL_32
			xlimb: NATURAL
			carry: CELL [NATURAL_32]
		do
			create carry.put (0)
			n2 := op1_count |>> 1
			if op1_count.bit_test (0) then
				n3 := op1_count - n2
				w := op1 [op1_offset + n2]
				if w /= 0 then
					sub_n (target, target_offset, op1, op1_offset, op1, op1_offset + n3, n2, carry)
					w := w - carry.item
				else
					i := n2
					from
						w0 := w1
						check i /= 0 end
					until
						w0 /= w1 or i = 0
					loop
						i := i - 1
						w0 := op1 [op1_offset + i]
						w1 := op1 [op1_offset + n3 + i]
					end
					if w0 < w1 then
						x := op1
						x_offset := op1_offset + n3
						y := op1
						y_offset := op1_offset
					else
						x := op1
						x_offset := op1_offset
						y := op1
						y_offset := op1_offset + n3
					end
					sub_n (target, target_offset, x, x_offset, y, y_offset, n2, carry)
					junk := carry.item
				end
				target [target_offset + n2] := w
				n1 := op1_count + 1
				if n3 < 64 then
					sqr_basecase (ws, ws_offset, target, target_offset, n3)
					sqr_basecase (target, target_offset, op1, op1_offset, n3)
				else
					kara_sqr_n (ws, ws_offset, target, target_offset, n3, ws, ws_offset + n1)
					kara_sqr_n (target, target_offset, op1, op1_offset, n3, ws, ws_offset + n1)
				end
				if n2 < 64 then
					sqr_basecase (target, target_offset + n1, op1, op1_offset + n3, n2)
				else
					kara_sqr_n (target, target_offset + n1, op1, op1_offset + n3, n3, ws, ws_offset + n1)
				end
				nm1 := op1_count - 1
				sub_n (ws, ws_offset, target, target_offset, ws, ws_offset, n1, carry)
				junk := carry.item
				add_n (ws, ws_offset, target, target_offset + n1, ws, ws_offset, nm1, carry)
				c := carry.item
				if c /= 0 then
					xlimb := ws [ws_offset + nm1]
					ws [ws_offset + nm1] := xlimb
					if xlimb = 0 then
						ws [ws_offset + op1_count] := ws [ws_offset + op1_count] + 1
					end
				end
				add_n (target, target_offset + n3, target, target_offset + n3, ws, ws_offset, n1, carry)
				if carry.item /= 0 then
					incr_u (target, target_offset + n1 + n3, 1)
				end
			else
				i := n2
				from
					w0 := w1
					check i /= 0 end
				until
					w0 /= w1 or i = 0
				loop
					i := i - 1
					w0 := op1 [op1_offset + i]
					w1 := op1 [op1_offset + n2 + i]
				end
				if w0 < w1 then
					x := op1
					x_offset := op1_offset + n2
					y := op1
					y_offset := op1_offset
				else
					x := op1
					x_offset := op1_offset
					y := op1
					y_offset := op1_offset + n2
				end
				sub_n (target, target_offset, x, x_offset, y, y_offset, n2, carry)
				junk := carry.item
				if n2 < 64 then
					sqr_basecase (ws, ws_offset, target, target_offset, n2)
					sqr_basecase (target, target_offset, op1, op1_offset, n2)
					sqr_basecase (target, target_offset + op1_count, op1, op1_offset + n2, n2)
				else
					kara_sqr_n (ws, ws_offset, target, target_offset, n2, ws, ws_offset + op1_count)
					kara_sqr_n (target, target_offset, op1, op1_offset, n2, ws, ws_offset + op1_count)
					kara_sqr_n (target, target_offset + op1_count, op1, op1_offset + n2, n2, ws, ws_offset + op1_count)
				end
				sub_n (ws, ws_offset, target, target_offset, ws, ws_offset, op1_count, carry)
				w := 0 - carry.item
				add_n (ws, ws_offset, target, target_offset + op1_count, ws, ws_offset, op1_count, carry)
				w := w + carry.item
				add_n (target, target_offset + n2, target, target_offset + n2, ws, ws_offset, op1_count, carry)
				w := w + carry.item
				incr_u (target, target_offset + n2 + op1_count, w)
			end
		end

	sqr_basecase (target: SPECIAL [NATURAL_32]; target_offset: INTEGER; op1: SPECIAL [NATURAL_32]; op1_offset: INTEGER; op1_count: INTEGER)
		require
			op1_count >= 1
		local
			i: INTEGER
			ul: NATURAL_32
			lpl: CELL [NATURAL_32]
			temp: CELL [NATURAL_32]
			tarr: SPECIAL [NATURAL_32]
			tp: SPECIAL [NATURAL_32]
			tp_offset: INTEGER
			cy: NATURAL_32
			carry: CELL [NATURAL_32]
		do
			create carry.put (0)
			create lpl.put (0)
			create temp.put (0)
			ul := op1 [op1_offset]
			umul_ppmm (temp, lpl, ul, ul)
			target [target_offset + 1] := temp.item
			target [target_offset] := lpl.item
			if op1_count > 1 then
				create tarr.make_filled (0, 128)
				tp := tarr
				mul_1 (tp, tp_offset, op1, op1_offset + 1, op1_count - 1, op1 [op1_offset], carry)
				cy := carry.item
				tp [tp_offset + op1_count - 1] := cy
				from
					i := 2
				until
					i >= op1_count
				loop
					addmul_1 (tp, tp_offset + 2 * i - 2, op1, op1_offset + i, op1_count - i, op1 [op1_offset + i - 1], carry)
					cy := carry.item
					tp [tp_offset + op1_count + i - 2] := cy
					i := i + 1
				end
				sqr_diagonal (target, target_offset + 2, op1, op1_offset + 1, op1_count - 1)
				lshift (tp, tp_offset, tp, tp_offset, 2 * op1_count - 2, 1, carry)
				cy := carry.item
				add_n (target, target_offset + 1, target, target_offset + 1, tp, tp_offset, 2 * op1_count - 2, carry)
				cy := cy + carry.item
				target [target_offset + 2 * op1_count - 1] := target [target_offset + 2 * op1_count - 1] + cy
			end
		end

	sqr_diagonal (target: SPECIAL [NATURAL_32]; target_offset: INTEGER; op1: SPECIAL [NATURAL_32]; op1_offset: INTEGER; op1_count: INTEGER)
		local
			i: INTEGER
			ul: NATURAL_32
			lpl: CELL [NATURAL_32]
			temp: CELL [NATURAL_32]
		do
			create lpl.put (0)
			create temp.put (0)
			from
				i := 0
			until
				i >= op1_count
			loop
				ul := op1 [op1_offset + i]
				umul_ppmm (temp, lpl, ul, ul)
				target [target_offset + 2 * i + 1] := temp.item
				target [target_offset + 2 * i] := lpl.item
				i := i + 1
			end
		end

	sqr_n (target: SPECIAL [NATURAL_32]; target_offset: INTEGER; op1: SPECIAL [NATURAL_32]; op1_offset: INTEGER; op1_count: INTEGER)
		require
			op1_count >= 1
		local
			ws: SPECIAL [NATURAL_32]
			ws_offset: INTEGER
		do
			if op1_count < 64 then
				sqr_basecase (target, target_offset, op1, op1_offset, op1_count)
			else
				create ws.make_filled (0, 2 * op1_count + 2 * 32)
				kara_sqr_n (target, target_offset, op1, op1_offset, op1_count, ws, ws_offset)
			end
		end

	sub (target: SPECIAL [NATURAL_32]; target_offset: 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; carry: CELL [NATURAL_32])
		local
			i: INTEGER_32
			j: INTEGER_32
			x: NATURAL_32
			subtract_borrow: NATURAL_32
			done: BOOLEAN
		do
			i := op2_count
			sub_n (target, target_offset, op1, op1_offset, op2, op2_offset, i, carry)
			subtract_borrow := carry.item
			if subtract_borrow /= 0 then
				from
					x := 0
				until
					done or x /= 0
				loop
					if i >= op1_count then
						carry.put (1)
						done := True
					else
						x := op1 [op1_offset + i]
						target [target_offset + i] := x - 1
						i := i + 1
					end
				end
			end
			if not done then
				if target /= op1 then
					from
						j := i
					until
						j >= op1_count
					loop
						target [target_offset + j] := op1 [op1_offset + j]
						j := j + 1
					end
				end
				carry.put (0)
			end
		end

	sub_1 (target: SPECIAL [NATURAL_32]; target_offset: INTEGER_32; op1: SPECIAL [NATURAL_32]; op1_offset: INTEGER_32; op1_count: INTEGER_32; op2: NATURAL_32; carry: CELL [NATURAL_32])
		require
			op1.valid_index (op1_offset)
			op1.valid_index (op1_offset + op1_count - 1)
			target.valid_index (target_offset)
			target.valid_index (target_offset + op1_count - 1)
		local
			i: INTEGER_32
			x: NATURAL_32
			r: NATURAL_32
		do
			x := op1 [op1_offset]
			r := x - op2
			target [target_offset] := r
			if x < op2 then
				carry.put (1)
				from
					i := 1
				until
					i >= op1_count
				loop
					x := op1 [op1_offset + i]
					r := x - 1
					target [target_offset + i] := r
					i := i + 1
					if not (x < 1) then
						if op1 /= target then
							target.copy_data (op1, op1_offset + i, target_offset + i, op1_count - i)
						end
						carry.put (0)
						i := op1_count
					end
				end
			else
				if op1 /= target then
					target.copy_data (op1, op1_offset + 1, target_offset + 1, op1_count - 1)
				end
				carry.put (0)
			end
		end

	sub_n (target: SPECIAL [NATURAL_32]; target_offset: INTEGER_32; op1: SPECIAL [NATURAL_32]; op1_offset: INTEGER_32; op2: SPECIAL [NATURAL_32]; op2_offset: INTEGER_32; n: INTEGER_32; carry: CELL [NATURAL_32])
		local
			ul: NATURAL_32
			vl: NATURAL_32
			sl: NATURAL_32
			rl: NATURAL_32
			cy1: NATURAL_32
			cy2: NATURAL_32
			cursor: INTEGER_32
			carry_l: NATURAL_32
		do
			from
				cursor := 0
			until
				cursor = n
			loop
				ul := op1 [op1_offset + cursor]
				vl := op2 [op2_offset + cursor]
				sl := ul - vl
				cy1 := (sl > ul).to_integer.to_natural_32
				rl := sl - carry_l
				cy2 := (rl > sl).to_integer.to_natural_32
				carry_l := cy1.bit_or (cy2)
				target [target_offset + cursor] := rl
				cursor := cursor + 1
			end
			carry.put (carry_l)
		end

	submul_1 (target: SPECIAL [NATURAL_32]; target_offset: INTEGER_32; op1: SPECIAL [NATURAL_32]; op1_offset: INTEGER_32; n: INTEGER_32; op2: NATURAL_32; carry: CELL [NATURAL_32])
		require
			target.valid_index (target_offset)
			target.valid_index (target_offset + n - 1)
			n >= 1
		local
			ul: NATURAL_32
			hpl: CELL [NATURAL_32]
			lpl: CELL [NATURAL_32]
			rl: NATURAL_32
			cursor: INTEGER_32
			carry_l: NATURAL_32
		do
			create hpl.put (0)
			create lpl.put (0)
			from
				cursor := 0
			until
				cursor >= n
			loop
				ul := op1 [op1_offset + cursor]
				umul_ppmm (hpl, lpl, ul, op2)
				lpl.put (lpl.item + carry_l)
				carry_l := (lpl.item < carry_l).to_integer.to_natural_32 + hpl.item
				rl := target [target_offset + cursor]
				lpl.put (rl - lpl.item)
				carry_l := carry_l + (lpl.item > rl).to_integer.to_natural_32
				target [target_offset + cursor] := lpl.item
				cursor := cursor + 1
			end
			carry.put (carry_l)
		end
end
