Skip to content

Commit e997250

Browse files
committed
Fix bit shift methods to respect byte order
Closes #41. Bytes.leftShift and Bytes.rightShift now behave as expected based on the set byte order.
1 parent 2208aad commit e997250

File tree

5 files changed

+120
-55
lines changed

5 files changed

+120
-55
lines changed

src/main/java/at/favre/lib/bytes/Bytes.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -967,7 +967,11 @@ public Bytes not() {
967967
* @see <a href="https://en.wikipedia.org/wiki/Bitwise_operation#Bit_shifts">Bit shifts</a>
968968
*/
969969
public Bytes leftShift(int shiftCount) {
970-
return transform(new BytesTransformer.ShiftTransformer(shiftCount, BytesTransformer.ShiftTransformer.Type.LEFT_SHIFT));
970+
return transform(new BytesTransformer.ShiftTransformer(
971+
shiftCount,
972+
BytesTransformer.ShiftTransformer.Type.LEFT_SHIFT,
973+
byteOrder
974+
));
971975
}
972976

973977
/**
@@ -982,7 +986,11 @@ public Bytes leftShift(int shiftCount) {
982986
* @see <a href="https://en.wikipedia.org/wiki/Bitwise_operation#Bit_shifts">Bit shifts</a>
983987
*/
984988
public Bytes rightShift(int shiftCount) {
985-
return transform(new BytesTransformer.ShiftTransformer(shiftCount, BytesTransformer.ShiftTransformer.Type.RIGHT_SHIFT));
989+
return transform(new BytesTransformer.ShiftTransformer(
990+
shiftCount,
991+
BytesTransformer.ShiftTransformer.Type.RIGHT_SHIFT,
992+
byteOrder
993+
));
986994
}
987995

988996
/**

src/main/java/at/favre/lib/bytes/BytesTransformer.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
package at.favre.lib.bytes;
2323

24+
import java.nio.ByteOrder;
2425
import java.security.MessageDigest;
2526
import java.security.NoSuchAlgorithmException;
2627
import java.util.Objects;
@@ -131,10 +132,12 @@ public enum Type {
131132

132133
private final int shiftCount;
133134
private final Type type;
135+
private final ByteOrder byteOrder;
134136

135-
ShiftTransformer(int shiftCount, Type type) {
137+
ShiftTransformer(int shiftCount, Type type, ByteOrder byteOrder) {
136138
this.shiftCount = shiftCount;
137139
this.type = Objects.requireNonNull(type, "passed shift type must not be null");
140+
this.byteOrder = Objects.requireNonNull(byteOrder, "passed byteOrder type must not be null");
138141
}
139142

140143
@Override
@@ -143,10 +146,10 @@ public byte[] transform(byte[] currentArray, boolean inPlace) {
143146

144147
switch (type) {
145148
case RIGHT_SHIFT:
146-
return Util.Byte.shiftRight(out, shiftCount);
149+
return Util.Byte.shiftRight(out, shiftCount, byteOrder);
147150
default:
148151
case LEFT_SHIFT:
149-
return Util.Byte.shiftLeft(out, shiftCount);
152+
return Util.Byte.shiftLeft(out, shiftCount, byteOrder);
150153
}
151154
}
152155

src/main/java/at/favre/lib/bytes/Util.java

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -291,25 +291,42 @@ static void reverse(byte[] array, int fromIndex, int toIndex) {
291291
*
292292
* @param byteArray to shift
293293
* @param shiftBitCount how many bits to shift
294+
* @param byteOrder endianness of given byte array
294295
* @return shifted byte array
295296
*/
296-
static byte[] shiftLeft(byte[] byteArray, int shiftBitCount) {
297+
static byte[] shiftLeft(byte[] byteArray, int shiftBitCount, ByteOrder byteOrder) {
297298
final int shiftMod = shiftBitCount % 8;
298299
final byte carryMask = (byte) ((1 << shiftMod) - 1);
299300
final int offsetBytes = (shiftBitCount / 8);
300301

301302
int sourceIndex;
302-
for (int i = 0; i < byteArray.length; i++) {
303-
sourceIndex = i + offsetBytes;
304-
if (sourceIndex >= byteArray.length) {
305-
byteArray[i] = 0;
306-
} else {
307-
byte src = byteArray[sourceIndex];
308-
byte dst = (byte) (src << shiftMod);
309-
if (sourceIndex + 1 < byteArray.length) {
310-
dst |= byteArray[sourceIndex + 1] >>> (8 - shiftMod) & carryMask;
303+
if (byteOrder == ByteOrder.BIG_ENDIAN) {
304+
for (int i = 0; i < byteArray.length; i++) {
305+
sourceIndex = i + offsetBytes;
306+
if (sourceIndex >= byteArray.length) {
307+
byteArray[i] = 0;
308+
} else {
309+
byte src = byteArray[sourceIndex];
310+
byte dst = (byte) (src << shiftMod);
311+
if (sourceIndex + 1 < byteArray.length) {
312+
dst |= byteArray[sourceIndex + 1] >>> (8 - shiftMod) & carryMask;
313+
}
314+
byteArray[i] = dst;
315+
}
316+
}
317+
} else {
318+
for (int i = byteArray.length - 1; i >= 0; i--) {
319+
sourceIndex = i - offsetBytes;
320+
if (sourceIndex < 0) {
321+
byteArray[i] = 0;
322+
} else {
323+
byte src = byteArray[sourceIndex];
324+
byte dst = (byte) (src << shiftMod);
325+
if (sourceIndex - 1 >= 0) {
326+
dst |= byteArray[sourceIndex - 1] >>> (8 - shiftMod) & carryMask;
327+
}
328+
byteArray[i] = dst;
311329
}
312-
byteArray[i] = dst;
313330
}
314331
}
315332
return byteArray;
@@ -330,25 +347,42 @@ static byte[] shiftLeft(byte[] byteArray, int shiftBitCount) {
330347
*
331348
* @param byteArray to shift
332349
* @param shiftBitCount how many bits to shift
350+
* @param byteOrder endianness of given byte array
333351
* @return shifted byte array
334352
*/
335-
static byte[] shiftRight(byte[] byteArray, int shiftBitCount) {
353+
static byte[] shiftRight(byte[] byteArray, int shiftBitCount, ByteOrder byteOrder) {
336354
final int shiftMod = shiftBitCount % 8;
337355
final byte carryMask = (byte) (0xFF << (8 - shiftMod));
338356
final int offsetBytes = (shiftBitCount / 8);
339357

340358
int sourceIndex;
341-
for (int i = byteArray.length - 1; i >= 0; i--) {
342-
sourceIndex = i - offsetBytes;
343-
if (sourceIndex < 0) {
344-
byteArray[i] = 0;
345-
} else {
346-
byte src = byteArray[sourceIndex];
347-
byte dst = (byte) ((0xff & src) >>> shiftMod);
348-
if (sourceIndex - 1 >= 0) {
349-
dst |= byteArray[sourceIndex - 1] << (8 - shiftMod) & carryMask;
359+
if (byteOrder == ByteOrder.BIG_ENDIAN) {
360+
for (int i = byteArray.length - 1; i >= 0; i--) {
361+
sourceIndex = i - offsetBytes;
362+
if (sourceIndex < 0) {
363+
byteArray[i] = 0;
364+
} else {
365+
byte src = byteArray[sourceIndex];
366+
byte dst = (byte) ((0xff & src) >>> shiftMod);
367+
if (sourceIndex - 1 >= 0) {
368+
dst |= byteArray[sourceIndex - 1] << (8 - shiftMod) & carryMask;
369+
}
370+
byteArray[i] = dst;
371+
}
372+
}
373+
} else {
374+
for (int i = 0; i < byteArray.length; i++) {
375+
sourceIndex = i + offsetBytes;
376+
if (sourceIndex >= byteArray.length) {
377+
byteArray[i] = 0;
378+
} else {
379+
byte src = byteArray[sourceIndex];
380+
byte dst = (byte) ((0xff & src) >>> shiftMod);
381+
if (sourceIndex + 1 < byteArray.length) {
382+
dst |= byteArray[sourceIndex + 1] << (8 - shiftMod) & carryMask;
383+
}
384+
byteArray[i] = dst;
350385
}
351-
byteArray[i] = dst;
352386
}
353387
}
354388
return byteArray;

src/test/java/at/favre/lib/bytes/BytesTransformTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import java.math.BigInteger;
2727
import java.nio.ByteBuffer;
28+
import java.nio.ByteOrder;
2829
import java.nio.charset.StandardCharsets;
2930
import java.security.SecureRandom;
3031
import java.util.Comparator;
@@ -446,7 +447,7 @@ public void transformerInPlaceTest() {
446447
assertTrue(new BytesTransformer.BitSwitchTransformer(0, true).supportInPlaceTransformation());
447448
assertTrue(new BytesTransformer.BitWiseOperatorTransformer(new byte[]{}, BytesTransformer.BitWiseOperatorTransformer.Mode.XOR).supportInPlaceTransformation());
448449
assertTrue(new BytesTransformer.NegateTransformer().supportInPlaceTransformation());
449-
assertTrue(new BytesTransformer.ShiftTransformer(0, BytesTransformer.ShiftTransformer.Type.LEFT_SHIFT).supportInPlaceTransformation());
450+
assertTrue(new BytesTransformer.ShiftTransformer(0, BytesTransformer.ShiftTransformer.Type.LEFT_SHIFT, ByteOrder.BIG_ENDIAN).supportInPlaceTransformation());
450451
assertTrue(new BytesTransformer.ReverseTransformer().supportInPlaceTransformation());
451452

452453
assertFalse(new BytesTransformer.MessageDigestTransformer("SHA1").supportInPlaceTransformation());

src/test/java/at/favre/lib/bytes/UtilByteTest.java

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.junit.Test;
2626

2727
import java.math.BigInteger;
28+
import java.nio.ByteOrder;
2829
import java.util.Arrays;
2930
import java.util.Random;
3031

@@ -162,21 +163,34 @@ private static void testReverse(byte[] input, int fromIndex, int toIndex, byte[]
162163
@Test
163164
public void testLeftShift() {
164165
byte[] test = new byte[]{0, 0, 1, 0};
165-
assertArrayEquals(new byte[]{0, 1, 0, 0}, Util.Byte.shiftLeft(new byte[]{0, 0, -128, 0}, 1));
166-
assertArrayEquals(new byte[]{0, 1, 0, 0}, Util.Byte.shiftLeft(new byte[]{0, 0, 64, 0}, 2));
167-
assertArrayEquals(new byte[]{1, 1, 1, 0}, Util.Byte.shiftLeft(new byte[]{-128, -128, -128, -128}, 1));
168-
assertArrayEquals(new byte[]{0, 0, 2, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 1));
169-
assertArrayEquals(new byte[]{0, 0, 4, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 2));
170-
assertArrayEquals(new byte[]{0, 0, 8, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 3));
171-
assertArrayEquals(new byte[]{0, 1, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 8));
172-
assertArrayEquals(new byte[]{0, 2, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 9));
173-
assertArrayEquals(new byte[]{1, 0, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 16));
174-
assertArrayEquals(new byte[]{2, 0, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 17));
175-
assertArrayEquals(new byte[]{-128, 0, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 23));
176-
assertArrayEquals(new byte[]{0, 0, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 24));
177-
assertArrayEquals(new byte[]{0, 0, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 24));
178-
179-
assertSame(test, Util.Byte.shiftLeft(test, 1));
166+
assertArrayEquals(new byte[]{0, 1, 0, 0}, Util.Byte.shiftLeft(new byte[]{0, 0, -128, 0}, 1, ByteOrder.BIG_ENDIAN));
167+
assertArrayEquals(new byte[]{0, 1, 0, 0}, Util.Byte.shiftLeft(new byte[]{0, 0, 64, 0}, 2, ByteOrder.BIG_ENDIAN));
168+
assertArrayEquals(new byte[]{1, 1, 1, 0}, Util.Byte.shiftLeft(new byte[]{-128, -128, -128, -128}, 1, ByteOrder.BIG_ENDIAN));
169+
assertArrayEquals(new byte[]{0, 0, 2, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 1, ByteOrder.BIG_ENDIAN));
170+
assertArrayEquals(new byte[]{0, 0, 4, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 2, ByteOrder.BIG_ENDIAN));
171+
assertArrayEquals(new byte[]{0, 0, 8, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 3, ByteOrder.BIG_ENDIAN));
172+
assertArrayEquals(new byte[]{0, 1, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 8, ByteOrder.BIG_ENDIAN));
173+
assertArrayEquals(new byte[]{0, 2, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 9, ByteOrder.BIG_ENDIAN));
174+
assertArrayEquals(new byte[]{1, 0, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 16, ByteOrder.BIG_ENDIAN));
175+
assertArrayEquals(new byte[]{2, 0, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 17, ByteOrder.BIG_ENDIAN));
176+
assertArrayEquals(new byte[]{-128, 0, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 23, ByteOrder.BIG_ENDIAN));
177+
assertArrayEquals(new byte[]{0, 0, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 24, ByteOrder.BIG_ENDIAN));
178+
assertArrayEquals(new byte[]{0, 0, 0, 0}, Util.Byte.shiftLeft(Bytes.from(test).array(), 24, ByteOrder.BIG_ENDIAN));
179+
180+
assertSame(test, Util.Byte.shiftLeft(test, 1, ByteOrder.BIG_ENDIAN));
181+
182+
assertArrayEquals(new byte[]{0, 0, 1, 0}, Util.Byte.shiftLeft(new byte[]{0, 1, 0, 0}, 8, ByteOrder.LITTLE_ENDIAN));
183+
assertArrayEquals(new byte[]{(byte) 0, 0, 1, 0}, Util.Byte.shiftLeft(new byte[]{(byte) 0, 1, 0, 1}, 8, ByteOrder.LITTLE_ENDIAN));
184+
assertArrayEquals(new byte[]{(byte) 0x54, 0x1}, Util.Byte.shiftLeft(new byte[]{(byte) 0xAA, 0}, 1, ByteOrder.LITTLE_ENDIAN));
185+
assertArrayEquals(new byte[]{(byte) 0xA8, 0x2}, Util.Byte.shiftLeft(new byte[]{(byte) 0xAA, 0}, 2, ByteOrder.LITTLE_ENDIAN));
186+
assertArrayEquals(new byte[]{(byte) 0x50, 0x5}, Util.Byte.shiftLeft(new byte[]{(byte) 0xAA, 0}, 3, ByteOrder.LITTLE_ENDIAN));
187+
assertArrayEquals(new byte[]{(byte) 0xA0, 0xA}, Util.Byte.shiftLeft(new byte[]{(byte) 0xAA, 0}, 4, ByteOrder.LITTLE_ENDIAN));
188+
assertArrayEquals(new byte[]{(byte) 0x40, 0x15}, Util.Byte.shiftLeft(new byte[]{(byte) 0xAA, 0}, 5, ByteOrder.LITTLE_ENDIAN));
189+
assertArrayEquals(new byte[]{(byte) 0x80, 0x2A}, Util.Byte.shiftLeft(new byte[]{(byte) 0xAA, 0}, 6, ByteOrder.LITTLE_ENDIAN));
190+
assertArrayEquals(new byte[]{(byte) 0x00, 0x55}, Util.Byte.shiftLeft(new byte[]{(byte) 0xAA, 0}, 7, ByteOrder.LITTLE_ENDIAN));
191+
assertArrayEquals(new byte[]{(byte) 0, (byte) 0xAA}, Util.Byte.shiftLeft(new byte[]{(byte) 0xAA, 0}, 8, ByteOrder.LITTLE_ENDIAN));
192+
assertArrayEquals(new byte[]{0, 0, 0, 0x40}, Util.Byte.shiftLeft(new byte[]{1, 0, 0, 0}, 30, ByteOrder.LITTLE_ENDIAN));
193+
180194
}
181195

182196
@Test
@@ -187,7 +201,7 @@ public void testLeftShiftAgainstRefImpl() {
187201
Bytes rnd = Bytes.random(4 + new Random().nextInt(14));
188202

189203
byte[] expected = Bytes.from(new BigInteger(rnd.array()).shiftLeft(shift).toByteArray()).resize(rnd.length(), BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_MAX_LENGTH).array();
190-
byte[] actual = Bytes.from(Util.Byte.shiftLeft(rnd.copy().array(), shift)).resize(rnd.length(), BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_MAX_LENGTH).array();
204+
byte[] actual = Bytes.from(Util.Byte.shiftLeft(rnd.copy().array(), shift, ByteOrder.BIG_ENDIAN)).resize(rnd.length(), BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_MAX_LENGTH).array();
191205

192206
System.out.println("Original \t" + rnd.encodeBinary() + " << " + shift);
193207
System.out.println("Expected \t" + Bytes.wrap(expected).encodeBinary());
@@ -202,18 +216,23 @@ public void testLeftShiftAgainstRefImpl() {
202216
public void testRightShift() {
203217
byte[] test = new byte[]{0, 0, 16, 0};
204218
assertEquals(0b01111110, 0b11111101 >>> 1);
205-
assertArrayEquals(new byte[]{0b00101101, (byte) 0b01111110}, Util.Byte.shiftRight(new byte[]{0b01011010, (byte) 0b11111101}, 1));
206-
assertArrayEquals(new byte[]{0, -128, -128, -128}, Util.Byte.shiftRight(new byte[]{1, 1, 1, 1}, 1));
207-
assertArrayEquals(new byte[]{0, -128, 66, 0}, Util.Byte.shiftRight(new byte[]{2, 1, 8, 2}, 2));
219+
assertArrayEquals(new byte[]{0b00101101, (byte) 0b01111110}, Util.Byte.shiftRight(new byte[]{0b01011010, (byte) 0b11111101}, 1, ByteOrder.BIG_ENDIAN));
220+
assertArrayEquals(new byte[]{0, -128, -128, -128}, Util.Byte.shiftRight(new byte[]{1, 1, 1, 1}, 1, ByteOrder.BIG_ENDIAN));
221+
assertArrayEquals(new byte[]{0, -128, 66, 0}, Util.Byte.shiftRight(new byte[]{2, 1, 8, 2}, 2, ByteOrder.BIG_ENDIAN));
208222
assertArrayEquals(new byte[]{0, -128, 66, 0}, new BigInteger(new byte[]{2, 1, 8, 2}).shiftRight(2).toByteArray());
209-
assertArrayEquals(new byte[]{0, 0, 0, -128}, Util.Byte.shiftRight(Bytes.from(test).array(), 5));
210-
assertArrayEquals(new byte[]{0, 0, 0, -128}, Util.Byte.shiftRight(new byte[]{0, 0, 1, 0}, 1));
211-
assertArrayEquals(new byte[]{0, 0, 8, 0}, Util.Byte.shiftRight(Bytes.from(test).array(), 1));
212-
assertArrayEquals(new byte[]{0, 0, 4, 0}, Util.Byte.shiftRight(Bytes.from(test).array(), 2));
213-
assertArrayEquals(new byte[]{0, 0, 2, 0}, Util.Byte.shiftRight(Bytes.from(test).array(), 3));
214-
assertArrayEquals(new byte[]{0, 0, 1, 0}, Util.Byte.shiftRight(Bytes.from(test).array(), 4));
223+
assertArrayEquals(new byte[]{0, 0, 0, -128}, Util.Byte.shiftRight(Bytes.from(test).array(), 5, ByteOrder.BIG_ENDIAN));
224+
assertArrayEquals(new byte[]{0, 0, 0, -128}, Util.Byte.shiftRight(new byte[]{0, 0, 1, 0}, 1, ByteOrder.BIG_ENDIAN));
225+
assertArrayEquals(new byte[]{0, 0, 8, 0}, Util.Byte.shiftRight(Bytes.from(test).array(), 1, ByteOrder.BIG_ENDIAN));
226+
assertArrayEquals(new byte[]{0, 0, 4, 0}, Util.Byte.shiftRight(Bytes.from(test).array(), 2, ByteOrder.BIG_ENDIAN));
227+
assertArrayEquals(new byte[]{0, 0, 2, 0}, Util.Byte.shiftRight(Bytes.from(test).array(), 3, ByteOrder.BIG_ENDIAN));
228+
assertArrayEquals(new byte[]{0, 0, 1, 0}, Util.Byte.shiftRight(Bytes.from(test).array(), 4, ByteOrder.BIG_ENDIAN));
229+
230+
assertSame(test, Util.Byte.shiftRight(test, 1, ByteOrder.BIG_ENDIAN));
215231

216-
assertSame(test, Util.Byte.shiftRight(test, 1));
232+
assertArrayEquals(new byte[]{0, 0}, Util.Byte.shiftRight(new byte[]{1, 0}, 1, ByteOrder.LITTLE_ENDIAN));
233+
assertArrayEquals(new byte[]{(byte) 0x80, 0}, Util.Byte.shiftRight(new byte[]{0, 0x02}, 2, ByteOrder.LITTLE_ENDIAN));
234+
assertArrayEquals(new byte[]{(byte) 0x80, 0, 0, 0}, Util.Byte.shiftRight(new byte[]{0, 0, 0, 1}, 17, ByteOrder.LITTLE_ENDIAN));
235+
assertArrayEquals(new byte[]{(byte) 1, 0, 0, 0}, Util.Byte.shiftRight(new byte[]{0, 0, 0, (byte) 0x80}, 31, ByteOrder.LITTLE_ENDIAN));
217236
}
218237

219238
@Test
@@ -223,7 +242,7 @@ public void testRightShiftAgainstRefImpl() {
223242
Bytes rnd = Bytes.random(4 + new Random().nextInt(12));
224243
if (!rnd.bitAt(rnd.lengthBit() - 1)) { //only unsigned
225244
byte[] expected = Bytes.from(new BigInteger(rnd.array()).shiftRight(shift).toByteArray()).resize(rnd.length(), BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_MAX_LENGTH).array();
226-
byte[] actual = Bytes.from(Util.Byte.shiftRight(rnd.copy().array(), shift)).resize(rnd.length(), BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_MAX_LENGTH).array();
245+
byte[] actual = Bytes.from(Util.Byte.shiftRight(rnd.copy().array(), shift, ByteOrder.BIG_ENDIAN)).resize(rnd.length(), BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_MAX_LENGTH).array();
227246

228247
// System.out.println("Original \t" + rnd.encodeBinary() + " >> " + shift);
229248
// System.out.println("Expected \t" + Bytes.wrap(expected).encodeBinary());

0 commit comments

Comments
 (0)