/*
 * uint16-t.c : Routines for simulating multiply, divide and mod operations on
 *              two 16 bits unsigned integers, using shift, add and subtract
 *              operations. A main function is also provided to test these.
 *
 * Copyright (C) 2006, Sandeep Kumar <shimple0@yahoo.com>
 *                     Homepage : http://sandeepkumar.fortunecity.com/
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 */


#include <stdint.h>
#include <stdio.h>
#include "signal.h"


/* Signal to be raised when 'division by 0' situation is encountered. */
#define  SIGNAL_DIV_BY_0  (SIGFPE)


/*
 * Simulates (num1 * num2).
 */
uint32_t umul16 (uint16_t num1, uint16_t num2)
{
    uint32_t bigger, result = 0;
    uint16_t smaller;
    static const int jumptab[4] = { (&&case_0 - &&case_3),
        (&&case_1 - &&case_3), (&&case_2 - &&case_3), (&&case_3 - &&case_3) };

    /* If either of num1 and num2 is zero, result will be zero. */
    if (!(num1 && num2)) return 0;

    if (num1 < num2) {
        smaller = num1;
        bigger = num2;
    } else {
        smaller = num2;
        bigger = num1;
    }

    while (smaller != 0)
    {

#if  0
        /* Slower While */

        int i = (smaller & 3);
        while (i-- > 0) result += bigger;

#else

        /* Faster - maintain your own jump table. */

        goto *(&&case_3 + jumptab[(smaller & 3)]);

        case_3 : result += bigger;
        case_2 : result += bigger;
        case_1 : result += bigger;
        case_0 : /* Just continue. */
#endif

        smaller >>= 2;
        bigger <<= 2;
    }

    return result;
}


/*
 * Simulates (num1 % num2).
 * Raises signal indicated by SIGNAL_DIV_BY_0 on encountering
 * zero-denominator.
 */
uint16_t umod16 (uint16_t num1, uint16_t num2)
{
    uint32_t numb, divisor;

    if (0 == num2)
        raise(SIGNAL_DIV_BY_0);

    /* If numerator is zero, or denominator is 1, result will be zero */
    if ((!num1) || (1 == num2)) return 0;

    numb = num1;
    divisor = num2;

    while (numb > divisor)
        divisor <<= 2;

    if (numb == divisor) return 0;

    divisor >>= 2;

    while ((numb >= num2) && (divisor >= num2))
    {
        while (numb >= divisor)
            numb -= divisor;
        divisor >>= 2;
    }

    return (uint16_t) numb;
}


/*
 * Simulates (num1 / num2).
 * Raises signal indicated by SIGNAL_DIV_BY_0 on encountering
 * zero-denominator.
 */
uint16_t udiv16 (uint16_t num1, uint16_t num2)
{
    uint32_t numb, divisor;
    uint16_t result = 1;

    if (0 == num2)
        raise(SIGNAL_DIV_BY_0);

    /* If numerator is zero, result will be zero. */
    if (!num1) return 0;

    /* If denominator is one, result will be numerator itself. */
    if (1 == num2) return num1;

    numb = num1;
    divisor = num2;

    while (numb > divisor) {
        divisor <<= 2;
        result <<= 2;
    }

    if (numb == divisor) return result;

    divisor >>= 2;
    result = 0;

    while (divisor >= num2)
    {
        result <<= 2;

        while (numb >= divisor) {
            numb -= divisor;
            result ++;
        }

        divisor >>= 2;
    }

    return result;
}

int main ()
{
    uint16_t N=0, M=0;

    scanf ("%hu", &N);
    scanf ("%hu", &M);

    printf ("N=%hu, M=%hu\n", N, M);

    printf ("N*M = %u , ", (N*M));
    printf ("umul16(N,M) = %u\n", umul16(N,M));

    printf ("N%%M = %hu , ", (N%M));
    printf ("umod16(N,M) = %hu\n", umod16(N,M));

    printf ("N/M = %hu , ", (N/M));
    printf ("udiv16(N,M) = %hu\n", udiv16(N,M));

    return 0;
}
