xref: /netbsd-src/external/lgpl3/gmp/dist/mpz/and.c (revision 72c7faa4dbb41dbb0238d6b4a109da0d4b236dd4)
1 /* mpz_and -- Logical and.
2 
3 Copyright 1991, 1993, 1994, 1996, 1997, 2000, 2001, 2003, 2005, 2012,
4 2015-2018 Free Software Foundation, Inc.
5 
6 This file is part of the GNU MP Library.
7 
8 The GNU MP Library is free software; you can redistribute it and/or modify
9 it under the terms of either:
10 
11   * the GNU Lesser General Public License as published by the Free
12     Software Foundation; either version 3 of the License, or (at your
13     option) any later version.
14 
15 or
16 
17   * the GNU General Public License as published by the Free Software
18     Foundation; either version 2 of the License, or (at your option) any
19     later version.
20 
21 or both in parallel, as here.
22 
23 The GNU MP Library is distributed in the hope that it will be useful, but
24 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
25 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
26 for more details.
27 
28 You should have received copies of the GNU General Public License and the
29 GNU Lesser General Public License along with the GNU MP Library.  If not,
30 see https://www.gnu.org/licenses/.  */
31 
32 #include "gmp-impl.h"
33 
34 void
mpz_and(mpz_ptr res,mpz_srcptr op1,mpz_srcptr op2)35 mpz_and (mpz_ptr res, mpz_srcptr op1, mpz_srcptr op2)
36 {
37   mp_srcptr op1_ptr, op2_ptr;
38   mp_size_t op1_size, op2_size;
39   mp_ptr res_ptr;
40   mp_size_t res_size;
41   mp_size_t i;
42 
43   op1_size = SIZ(op1);
44   op2_size = SIZ(op2);
45 
46   if (op1_size < op2_size)
47     {
48       MPZ_SRCPTR_SWAP (op1, op2);
49       MP_SIZE_T_SWAP (op1_size, op2_size);
50     }
51 
52   op1_ptr = PTR(op1);
53   op2_ptr = PTR(op2);
54 
55   if (op2_size >= 0)
56     {
57       /* First loop finds the size of the result.  */
58       for (i = op2_size; --i >= 0;)
59 	if ((op1_ptr[i] & op2_ptr[i]) != 0)
60 	  {
61 	    res_size = i + 1;
62 	    /* Handle allocation, now then we know exactly how much space is
63 	       needed for the result.  */
64 	    /* Don't re-read op1_ptr and op2_ptr.  Since res_size <=
65 	       MIN(op1_size, op2_size), res is not changed when op1
66 	       is identical to res or op2 is identical to res.  */
67 	    SIZ (res) = res_size;
68 	    mpn_and_n (MPZ_NEWALLOC (res, res_size), op1_ptr, op2_ptr, res_size);
69 	    return;
70 	  }
71 
72       SIZ (res) = 0;
73     }
74   else
75     {
76       TMP_DECL;
77 
78       op2_size = -op2_size;
79       TMP_MARK;
80       if (op1_size < 0)
81 	{
82 	  mp_ptr opx, opy;
83 
84 	  /* Both operands are negative, so will be the result.
85 	     -((-OP1) & (-OP2)) = -(~(OP1 - 1) & ~(OP2 - 1)) =
86 	     = ~(~(OP1 - 1) & ~(OP2 - 1)) + 1 =
87 	     = ((OP1 - 1) | (OP2 - 1)) + 1      */
88 
89 	  /* It might seem as we could end up with an (invalid) result with
90 	     a leading zero-limb here when one of the operands is of the
91 	     type 1,,0,,..,,.0.  But some analysis shows that we surely
92 	     would get carry into the zero-limb in this situation...  */
93 
94 	  op1_size = -op1_size;
95 
96 	  TMP_ALLOC_LIMBS_2 (opx, op1_size, opy, op2_size);
97 	  mpn_sub_1 (opx, op1_ptr, op1_size, (mp_limb_t) 1);
98 	  op1_ptr = opx;
99 
100 	  mpn_sub_1 (opy, op2_ptr, op2_size, (mp_limb_t) 1);
101 	  op2_ptr = opy;
102 
103 	  res_ptr = MPZ_NEWALLOC (res, 1 + op2_size);
104 	  /* Don't re-read OP1_PTR and OP2_PTR.  They point to temporary
105 	     space--never to the space PTR(res) used to point to before
106 	     reallocation.  */
107 
108 	  MPN_COPY (res_ptr + op1_size, op2_ptr + op1_size,
109 		    op2_size - op1_size);
110 	  mpn_ior_n (res_ptr, op1_ptr, op2_ptr, op1_size);
111 	  TMP_FREE;
112 	  res_size = op2_size;
113 
114 	  res_ptr[res_size] = 0;
115 	  MPN_INCR_U (res_ptr, res_size + 1, (mp_limb_t) 1);
116 	  res_size += res_ptr[res_size];
117 
118 	  SIZ(res) = -res_size;
119 	}
120       else
121 	{
122 #if ANDNEW
123 	  mp_size_t op2_lim;
124 	  mp_size_t count;
125 
126 	  /* OP2 must be negated as with infinite precision.
127 
128 	     Scan from the low end for a non-zero limb.  The first non-zero
129 	     limb is simply negated (two's complement).  Any subsequent
130 	     limbs are one's complemented.  Of course, we don't need to
131 	     handle more limbs than there are limbs in the other, positive
132 	     operand as the result for those limbs is going to become zero
133 	     anyway.  */
134 
135 	  /* Scan for the least significant non-zero OP2 limb, and zero the
136 	     result meanwhile for those limb positions.  (We will surely
137 	     find a non-zero limb, so we can write the loop with one
138 	     termination condition only.)  */
139 	  for (i = 0; op2_ptr[i] == 0; i++)
140 	    res_ptr[i] = 0;
141 	  op2_lim = i;
142 
143 	  if (op1_size <= op2_size)
144 	    {
145 	      /* The ones-extended OP2 is >= than the zero-extended OP1.
146 		 RES_SIZE <= OP1_SIZE.  Find the exact size.  */
147 	      for (i = op1_size - 1; i > op2_lim; i--)
148 		if ((op1_ptr[i] & ~op2_ptr[i]) != 0)
149 		  break;
150 	      res_size = i + 1;
151 	      for (i = res_size - 1; i > op2_lim; i--)
152 		res_ptr[i] = op1_ptr[i] & ~op2_ptr[i];
153 	      res_ptr[op2_lim] = op1_ptr[op2_lim] & -op2_ptr[op2_lim];
154 	      /* Yes, this *can* happen!  */
155 	      MPN_NORMALIZE (res_ptr, res_size);
156 	    }
157 	  else
158 	    {
159 	      /* The ones-extended OP2 is < than the zero-extended OP1.
160 		 RES_SIZE == OP1_SIZE, since OP1 is normalized.  */
161 	      res_size = op1_size;
162 	      MPN_COPY (res_ptr + op2_size, op1_ptr + op2_size, op1_size - op2_size);
163 	      for (i = op2_size - 1; i > op2_lim; i--)
164 		res_ptr[i] = op1_ptr[i] & ~op2_ptr[i];
165 	      res_ptr[op2_lim] = op1_ptr[op2_lim] & -op2_ptr[op2_lim];
166 	    }
167 #else
168 
169 	  /* OP1 is positive and zero-extended,
170 	     OP2 is negative and ones-extended.
171 	     The result will be positive.
172 	     OP1 & -OP2 = OP1 & ~(OP2 - 1).  */
173 
174 	  mp_ptr opx;
175 
176 	  opx = TMP_ALLOC_LIMBS (op2_size);
177 	  mpn_sub_1 (opx, op2_ptr, op2_size, (mp_limb_t) 1);
178 	  op2_ptr = opx;
179 
180 	  if (op1_size > op2_size)
181 	    {
182 	      /* The result has the same size as OP1, since OP1 is normalized
183 		 and longer than the ones-extended OP2.  */
184 	      res_size = op1_size;
185 
186 	      /* Handle allocation, now then we know exactly how much space is
187 		 needed for the result.  */
188 	      res_ptr = MPZ_NEWALLOC (res, res_size);
189 	      /* Don't re-read OP1_PTR or OP2_PTR.  Since res_size = op1_size,
190 		 op1 is not changed if it is identical to res.
191 		 OP2_PTR points to temporary space.  */
192 
193 	      mpn_andn_n (res_ptr, op1_ptr, op2_ptr, op2_size);
194 	      MPN_COPY (res_ptr + op2_size, op1_ptr + op2_size, res_size - op2_size);
195 	    }
196 	  else
197 	    {
198 	      /* Find out the exact result size.  Ignore the high limbs of OP2,
199 		 OP1 is zero-extended and would make the result zero.  */
200 	      res_size = 0;
201 	      for (i = op1_size; --i >= 0;)
202 		if ((op1_ptr[i] & ~op2_ptr[i]) != 0)
203 		  {
204 		    res_size = i + 1;
205 		    /* Handle allocation, now then we know exactly how much
206 		       space is needed for the result.  */
207 		    /* Don't re-read OP1_PTR.  Since res_size <= op1_size,
208 		       op1 is not changed if it is identical to res.  Don't
209 		       re-read OP2_PTR.  It points to temporary space--never
210 		       to the space PTR(res) used to point to before
211 		       reallocation.  */
212 		    mpn_andn_n (MPZ_NEWALLOC (res, res_size), op1_ptr, op2_ptr, res_size);
213 
214 		    break;
215 		  }
216 	    }
217 #endif
218 	  SIZ(res) = res_size;
219 	  TMP_FREE;
220 	}
221     }
222 }
223