summaryrefslogtreecommitdiffstats
path: root/pbn_add.c
diff options
context:
space:
mode:
Diffstat (limited to 'pbn_add.c')
-rw-r--r--pbn_add.c118
1 files changed, 118 insertions, 0 deletions
diff --git a/pbn_add.c b/pbn_add.c
new file mode 100644
index 0000000..25372f0
--- /dev/null
+++ b/pbn_add.c
@@ -0,0 +1,118 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2007 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, Inc.,
+ * 59 Temple Place Ste 330, Boston MA 02111-1307, USA; version 2.1,
+ * incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * pbn_add.c
+ */
+
+#include "pbnint.h"
+
+struct pbn *pbn_addsub(struct pbn *s1, struct pbn *s2, int issub)
+{
+ int bits, len;
+ struct pbn *d;
+ int i;
+ int s1minus = s1->minus;
+ int s2minus = s2->minus ^ issub;
+ int dminus;
+
+ bits = s2->bits > s1->bits ? s2->bits+1 : s1->bits+1;
+ len = (bits+PBN_ATOM_BITS-1)/PBN_ATOM_BITS;
+
+ if (s1 == s2) {
+ s1 = pbn_dupx(s1, len);
+ pbn_free(s2); /* Drop one reference */
+ }
+
+ if (s1minus == s2minus) {
+ pbn_2atom_t c;
+ /* Sign is the same, addition */
+
+ if (s1->ref > s2->ref) {
+ struct pbn *t = s1;
+ s1 = s2;
+ s2 = t;
+ }
+
+ d = pbn_cow(s1, len);
+ d->bits = bits;
+
+ c = 0;
+ for (i = 0; i < len; i++) {
+ c += d->num[i];
+ if (i < s2->len)
+ c += s2->num[i];
+
+ d->num[i] = c;
+ c >>= PBN_ATOM_BITS;
+ }
+ pbn_free(s2);
+
+ /* d->minus already copied from s1->minus */
+ } else {
+ pbn_2satom_t c; /* Relies on 2's complement with
+ proper right arithmetic shift... */
+ /* We know the sign is different */
+ dminus = 0;
+
+ if (s2minus) {
+ struct pbn *t = s1;
+ s1 = s2;
+ s2 = t;
+ dminus = !dminus;
+ }
+
+ /* Now s1 is the addend and s2 is the subtrahend */
+ if (pbn_cmp(s1, s2) < 0) {
+ /* addend is smaller than subtrahend, reverse and
+ invert minus */
+ struct pbn *t = s1;
+ s1 = s2;
+ s2 = t;
+ dminus = !dminus;
+ }
+
+ if (s1->ref > s2->ref) {
+ d = pbn_cow(s2, len);
+
+ c = 0;
+ for (i = 0; i < len; i++) {
+ c -= d->num[i];
+ if (i < s1->len)
+ c += s1->num[i];
+
+ d->num[i] = c;
+ c >>= PBN_ATOM_BITS;
+ }
+ pbn_free(s1);
+ } else {
+ d = pbn_cow(s1, len);
+
+ c = 0;
+ for (i = 0; i < len; i++) {
+ c += d->num[i];
+ if (i < s2->len)
+ c -= s2->num[i];
+
+ d->num[i] = c;
+ c >>= PBN_ATOM_BITS;
+ }
+ pbn_free(s2);
+ }
+
+ d->minus = dminus;
+ }
+
+ _pbn_adjust_bits(d);
+
+ return d;
+}