/*
 * Copyright (C) 1989-2013 Free Software Foundation, Inc.
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include "libgcc2.h"

DWtype
__ashldi3(DWtype u, shift_count_type b)
{
	if (b == 0)
		return u;

	const DWunion uu = {.ll = u};
	const shift_count_type bm = W_TYPE_SIZE - b;
	DWunion w;

	if (bm <= 0) {
		w.s.low = 0;
		w.s.high = (UWtype)uu.s.low << -bm;
	} else {
		const UWtype carries = (UWtype) uu.s.low >> bm;

		w.s.low = (UWtype)uu.s.low << b;
		w.s.high = ((UWtype)uu.s.high << b) | carries;
	}

	return w.ll;
}

DWtype
__ashrdi3(DWtype u, shift_count_type b)
{
	if (b == 0)
		return u;

	const DWunion uu = {.ll = u};
	const shift_count_type bm = W_TYPE_SIZE - b;
	DWunion w;

	if (bm <= 0) {
		/* w.s.high = 1..1 or 0..0 */
		w.s.high = uu.s.high >> (W_TYPE_SIZE - 1);
		w.s.low = uu.s.high >> -bm;
	} else {
		const UWtype carries = (UWtype) uu.s.high << bm;

		w.s.high = uu.s.high >> b;
		w.s.low = ((UWtype)uu.s.low >> b) | carries;
	}

	return w.ll;
}

DWtype
__lshrdi3(DWtype u, shift_count_type b)
{
	if (b == 0)
		return u;

	const DWunion uu = {.ll = u};
	const shift_count_type bm = W_TYPE_SIZE - b;
	DWunion w;

	if (bm <= 0) {
		w.s.high = 0;
		w.s.low = (UWtype)uu.s.high >> -bm;
	} else {
		const UWtype carries = (UWtype)uu.s.high << bm;

		w.s.high = (UWtype)uu.s.high >> b;
		w.s.low = ((UWtype)uu.s.low >> b) | carries;
	}

	return w.ll;
}

unsigned long
udivmodsi4(unsigned long num, unsigned long den, int modwanted)
{
	unsigned long bit = 1;
	unsigned long res = 0;

	while (den < num && bit && !(den & (1L<<31))) {
		den <<= 1;
		bit <<= 1;
	}

	while (bit) {
		if (num >= den) {
			num -= den;
			res |= bit;
		}
		bit >>= 1;
		den >>= 1;
	}

	if (modwanted)
		return num;

	return res;
}

long
__divsi3(long a, long b)
{
	int neg = 0;
	long res;

	if (a < 0) {
		a = -a;
		neg = !neg;
	}

	if (b < 0) {
		b = -b;
		neg = !neg;
	}

	res = udivmodsi4(a, b, 0);

	if (neg)
		res = -res;

	return res;
}

long
__modsi3(long a, long b)
{
	int neg = 0;
	long res;

	if (a < 0) {
		a = -a;
		neg = 1;
	}

	if (b < 0)
		b = -b;

	res = udivmodsi4(a, b, 1);

	if (neg)
		res = -res;

	return res;
}

long
__udivsi3(long a, long b)
{
	return udivmodsi4(a, b, 0);
}

long
__umodsi3(long a, long b)
{
	return udivmodsi4(a, b, 1);
}
