0

As a spare-time project, I'm trying to implement some cryptographic algorithms using free-standing C - that is, the variant of C without standard library functions (standard library types and constants are still available), and without optional features such as VLA (variable-length array).

One of the things I've done is implement a few functions for big integers (size > 128 bits). However, the function for integer division in this setting requires keeping track of remainder in its current form, and since I'm using free-standing environment, the caller has to supply space for it.

Is it possible to implement division algorithm for calculating quotient without relying on keeping track of remainder, and possibly using bitslicing technique? It's acceptable to use call recursion to keep variables on the stack.

We'll assume the type for big integer is bigint_t:

#define N 8
typedef uint32_t bigint_t[N]; // least-significant word first. 
DannyNiu
  • 1,313
  • 8
  • 27
  • 2
    Why not keep track of remainder? Is it just an arbitrary restriction, or is there a reason? – anatolyg Jun 03 '19 at 07:37
  • Because free-standing, the caller have to supply space for the remainder. – DannyNiu Jun 03 '19 at 07:46
  • Without the binary method suggested in an answer (a similiar way can be used to take an integer square root) how can you divide **without** keeping track of the remainder? Think of the pencil and paper way of doing a division: a carry moves through the calculation and becomes the remainder. It is feasible though, to keep the remainder and not the quotient, when implementing the `%` modulus operator. – Weather Vane Jun 03 '19 at 07:47
  • I don't see how you can afford a recursive call stack with variables but not a remainder. – Weather Vane Jun 03 '19 at 07:53
  • Oh, I get it: you don't have standard functions like `alloca` or `malloc` so you want to implement arbitrary-length intermediate results using the call stack! This might be an interesting obfuscation technique. But in your code, `N` is a constant, so I am not sure I guessed correctly. – anatolyg Jun 03 '19 at 07:59
  • 1
    Why not use Newton Raphson Division? With a proper initial guess, it requires ~5 steps to converge and ~10 multiplications and additions. – Alain Merigot Jun 03 '19 at 08:38
  • @AlainMerigot It can be applied to integers? – DannyNiu Jun 03 '19 at 08:58
  • Integers, no. But you can implement it with fixed point operations for NR iterations (but it may be a bit tricky). A good alternative could also be Goldschmidt division. Its convergence is also quadratic and I think it can be implemented with integers (not sure, I'll try to check...) – Alain Merigot Jun 03 '19 at 09:13

2 Answers2

5

I gather that you're concerned with the extra space that the remainder requires, because you can't call malloc.

Note that, during the normal long-division style implementation, the quotient grows in length as the dividend shrinks to become the remainder. At every stage, the quotient and remainder together require at most a constant amount of space over that required for the original dividend.

If you keep the dividend-becoming-remainder and the growing quotient in the same array, you only need a couple variables for the extra digits.

Matt Timmermans
  • 53,709
  • 3
  • 46
  • 87
3

You can use binary search. Pick a number, multiply it by the divisor. If the result is too big, decrease the number; if the result is too small, increase the number. Control the increment/decrement so it tends to zero exponentially.

anatolyg
  • 26,506
  • 9
  • 60
  • 134
  • You can start with the MSB. Set it, and if the product is too large, unset it. Repeat for each other bit towards the LSB. But in the example that will take 256 long multiplications to calculate the quotient. – Weather Vane Jun 03 '19 at 07:57
  • Seems to be the only viable solution that requires the heavy-wight multiplication operation as building block that's not even constant-time. – DannyNiu Jun 03 '19 at 07:58
  • Doesn't this still require temporary space for the trial product? I thought the goal was to do the work with O(1) temporaries. – rici Jun 03 '19 at 15:03
  • It's not clear what the goal is. Also not clear whether N is O(1). – anatolyg Jun 03 '19 at 15:09
  • My comment is based on the clarification by OP in the second comment to the question: "Because free-standing, the caller have to supply space for the remainder." Of course, that clarification should be edited into the question. – rici Jun 03 '19 at 15:12