Advertisement
Guest User

Untitled

a guest
Jun 20th, 2019
77
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.65 KB | None | 0 0
  1. package net.coderodde.util;
  2.  
  3. import java.util.Random;
  4.  
  5. /**
  6. * This class contains a method for converting {@code long} values to unsigned
  7. * strings.
  8. *
  9. * @author Rodion "rodde" Efremov
  10. * @version 1.61 (May 16, 2019)
  11. */
  12. public final class Long {
  13.  
  14. /**
  15. * Caching a string builder in order to save some computation.
  16. */
  17. private static final StringBuilder STRING_BUILDER =
  18. new StringBuilder(java.lang.Long.SIZE);
  19.  
  20. /**
  21. * Maps individual radices and bits to the numbers they represent in the
  22. * given radix.
  23. */
  24. private static final Digit[][] bitIndexToDigitChainMaps = new Digit[37][];
  25.  
  26. /**
  27. * Maps a given internal representation of a digit character to its visual
  28. * glyph.
  29. */
  30. private static char[] digitsToCharsMap;
  31.  
  32. /**
  33. * This static inner class represents a single decimal digit.
  34. */
  35. static final class Digit {
  36.  
  37. /**
  38. * The actual decimal digit.
  39. */
  40. int value;
  41.  
  42. /**
  43. * The higher-order decimal digit.
  44. */
  45. Digit next;
  46.  
  47. Digit(int digit) {
  48. this.value = digit;
  49. }
  50. }
  51.  
  52. static {
  53. initializeBitDigitLists();
  54. initalizeDigitsToCharMap();
  55. }
  56.  
  57. private static final void initializeBitDigitLists() {
  58. for (int radix = 2; radix != 37; radix++) {
  59. bitIndexToDigitChainMaps[radix] = new Digit[java.lang.Long.SIZE];
  60.  
  61. for (int bitIndex = 0; bitIndex < 63; bitIndex++) {
  62. long value = 1L << bitIndex;
  63. bitIndexToDigitChainMaps[radix][bitIndex] =
  64. getDigitList(value, radix);
  65. }
  66.  
  67. bitIndexToDigitChainMaps[radix][java.lang.Long.SIZE - 1] =
  68. getLastDigitList(radix);
  69. }
  70. }
  71.  
  72. private static final void initalizeDigitsToCharMap() {
  73. digitsToCharsMap = new char[] {
  74. '0' , '1' , '2' , '3' , '4' , '5' ,
  75. '6' , '7' , '8' , '9' , 'a' , 'b' ,
  76. 'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
  77. 'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,
  78. 'o' , 'p' , 'q' , 'r' , 's' , 't' ,
  79. 'u' , 'v' , 'w' , 'x' , 'y' , 'z'
  80. };
  81. }
  82.  
  83. /**
  84. * Converts the given {@code long} value as unsigned to a
  85. * {@link java.lang.String} using the input radix.
  86. *
  87. * @param value the value to convert.
  88. * @param radix the requested radix.
  89. * @return the string representation of the input value as unsigned.
  90. */
  91. public static String toUnsignedString(long value, int radix) {
  92. checkRadix(radix);
  93. final Digit leastSignificantDigit = new Digit(0);
  94.  
  95. for (int bitIndex = 0; bitIndex != java.lang.Long.SIZE; bitIndex++) {
  96. if ((value & (1L << bitIndex)) != 0) {
  97. digitsPlus(bitIndexToDigitChainMaps[radix][bitIndex],
  98. leastSignificantDigit,
  99. radix);
  100. }
  101. }
  102.  
  103. return inferString(leastSignificantDigit);
  104. }
  105.  
  106. public static String toUnsignedBinaryString(long value) {
  107. return toUnsignedString(value, 2);
  108. }
  109.  
  110. public static String toUnsignedOctalString(long value) {
  111. return toUnsignedString(value, 8);
  112. }
  113.  
  114. public static String toUnsignedString(long value) {
  115. return toUnsignedString(value, 10);
  116. }
  117.  
  118. public static String toUnsignedHexString(long value) {
  119. return toUnsignedString(value, 16);
  120. }
  121.  
  122. public static final class ThreadSafe {
  123.  
  124. /**
  125. * Converts the given {@code long} value as unsigned to a
  126. * {@link java.lang.String}. Unlike
  127. * {@link net.coderodde.util.Long#toUnsignedString(long)}, this version
  128. * is thread-safe.
  129. *
  130. * @param value the value to convert.
  131. * @return the string representation of the input value as unsigned.
  132. */
  133. public static String toUnsignedString(long value, int radix) {
  134. final Digit leastSignificantDigit = new Digit(0);
  135.  
  136. for (int bitIndex = 0; bitIndex != java.lang.Long.SIZE; bitIndex++) {
  137. if ((value & (1L << bitIndex)) != 0) {
  138. digitsPlus(bitIndexToDigitChainMaps[radix][bitIndex],
  139. leastSignificantDigit,
  140. radix);
  141. }
  142. }
  143.  
  144. return inferStringThreadSafe(leastSignificantDigit);
  145. }
  146.  
  147. public static String toUnsignedBinaryString(long value) {
  148. return toUnsignedString(value, 2);
  149. }
  150.  
  151. public static String toUnsignedOctalString(long value) {
  152. return toUnsignedString(value, 8);
  153. }
  154.  
  155. public static String toUnsignedString(long value) {
  156. return toUnsignedString(value, 10);
  157. }
  158.  
  159. public static String toUnsignedHexString(long value) {
  160. return toUnsignedString(value, 16);
  161. }
  162. }
  163.  
  164. /**
  165. * Infers the {@code long} string from the digit-wise representation.
  166. *
  167. * @param leastSignificantDigit the least-significant digit of the value.
  168. * @return the string representing the digit-wise number.
  169. */
  170. private static final String inferString(Digit leastSignificantDigit) {
  171. STRING_BUILDER.setLength(0);
  172. return inferString(leastSignificantDigit, STRING_BUILDER);
  173. }
  174.  
  175. /**
  176. * Infers the {@code long} string from the digit-wise representation. Unlike
  177. * {@link net.coderodde.util.Long#inferString(net.coderodde.util.Long.Digit)},
  178. * this implementation is thread-safe.
  179. *
  180. * @param leastSignificantDigit the least-significant digit of the number to
  181. * infer.
  182. * @return the string representation of the given number.
  183. */
  184. private static final String inferStringThreadSafe(
  185. Digit leastSignificantDigit) {
  186. return inferString(leastSignificantDigit,
  187. new StringBuilder(java.lang.Long.SIZE));
  188. }
  189.  
  190. /**
  191. * Infers the resulting string from the input digit list.
  192. *
  193. * @param leastSignificantDigit the digit list.
  194. * @param stringBuilder the string builder.
  195. * @return the resulting string.
  196. */
  197. private static final String inferString(Digit leastSignificantDigit,
  198. StringBuilder stringBuilder) {
  199. for (Digit digit = leastSignificantDigit;
  200. digit != null;
  201. digit = digit.next) {
  202. stringBuilder.append(digitsToCharsMap[digit.value]);
  203. }
  204.  
  205. return stringBuilder.reverse().toString();
  206. }
  207.  
  208. /**
  209. * Performs the addition operation upon two input digit lists.
  210. *
  211. * @param sourceDigits the digits to add.
  212. * @param targetDigits the digits to which to add.
  213. */
  214. static final void digitsPlus(Digit sourceDigits,
  215. Digit targetDigits,
  216. int radix) {
  217. Digit sourceDigit = sourceDigits;
  218. Digit targetDigit = targetDigits;
  219. Digit targetNumberHead = targetDigit;
  220. boolean carryFlag = false;
  221.  
  222. //! Try to remove sourceDigit != null
  223. while (sourceDigit != null && targetDigit != null) {
  224. int digitValue = sourceDigit.value + targetDigit.value +
  225. (carryFlag ? 1 : 0);
  226.  
  227. if (digitValue >= radix) {
  228. digitValue -= radix;
  229. carryFlag = true;
  230. } else {
  231. carryFlag = false;
  232. }
  233.  
  234. targetNumberHead = targetDigit;
  235. targetDigit.value = digitValue;
  236. sourceDigit = sourceDigit.next;
  237. targetDigit = targetDigit.next;
  238. }
  239.  
  240. // Deal with the leftovers:
  241. while (sourceDigit != null) {
  242. int value = sourceDigit.value + (carryFlag ? 1 : 0);
  243.  
  244. if (value >= radix) {
  245. value -= radix;
  246. carryFlag = true;
  247. } else {
  248. carryFlag = false;
  249. }
  250.  
  251. targetNumberHead.next = new Digit(value);
  252. targetNumberHead = targetNumberHead.next;
  253. sourceDigit = sourceDigit.next;
  254. }
  255.  
  256. if (carryFlag) {
  257. targetNumberHead.next = new Digit(1);
  258. }
  259. }
  260.  
  261. /**
  262. * Computes the digit list representing {@code value}.
  263. *
  264. * @param value the target value.
  265. * @return the digit list representing the input value.
  266. */
  267. private static final Digit getDigitList(long value, int radix) {
  268. Digit previousDigit = null;
  269. Digit leastSignificantDigit = null;
  270.  
  271. while (value != 0L) {
  272. int digit = (int)(value % radix);
  273.  
  274. if (previousDigit == null) {
  275. previousDigit = new Digit(digit);
  276. leastSignificantDigit = previousDigit;
  277. } else {
  278. Digit tmp = new Digit(digit);
  279. previousDigit.next = tmp;
  280. previousDigit = tmp;
  281. }
  282.  
  283. // Drop the last digit of 'value':
  284. value /= radix;
  285. }
  286.  
  287. return leastSignificantDigit;
  288. }
  289.  
  290. /**
  291. * Copies the digit list starting from {@code leastSignificantDigit}.
  292. *
  293. * @param leastSignificantDigit the least-significant digit of the digit
  294. * list to be copied.
  295. * @return the copy of the input digit list.
  296. */
  297. static final Digit copyDigitList(Digit leastSignificantDigit) {
  298. Digit currentSourceDigit = leastSignificantDigit;
  299. Digit returnDigit = new Digit(leastSignificantDigit.value);
  300. Digit headTargetDigit = returnDigit;
  301. currentSourceDigit = currentSourceDigit.next;
  302.  
  303. while (currentSourceDigit != null) {
  304. Digit targetDigit = new Digit(currentSourceDigit.value);
  305. headTargetDigit.next = targetDigit;
  306. headTargetDigit = targetDigit;
  307. currentSourceDigit = currentSourceDigit.next;
  308. }
  309.  
  310. return returnDigit;
  311. }
  312.  
  313. /**
  314. * Returns the decimal number corresponding to {@code 2^64 - 1}.
  315. *
  316. * @return the decimal number corresponding to {@code 2^64 - 1}.
  317. */
  318. private static final Digit getLastDigitList(int radix) {
  319. Digit source = bitIndexToDigitChainMaps[radix][62];
  320. Digit target = copyDigitList(source);
  321. digitsPlus(source, target, radix);
  322. return target;
  323. }
  324.  
  325. private static final int BENCHMARK_ITERATIONS = 100_000;
  326.  
  327. /**
  328. * @param args the command line arguments
  329. */
  330. public static void main(String[] args) {
  331. long seed = System.currentTimeMillis();
  332. Random random1 = new Random(seed);
  333. Random random2 = new Random(seed);
  334. System.out.println("main(): seed = " + seed);
  335. run(BENCHMARK_ITERATIONS, random1, random2, false); // Warm up.
  336. run(BENCHMARK_ITERATIONS, random1, random2, true); // Benchmark.
  337. }
  338.  
  339. private static final void run(int numberOfValuesToGenerate,
  340. Random random1,
  341. Random random2,
  342. boolean printElapsedTime) {
  343. long startTime = System.currentTimeMillis();
  344.  
  345. for (int iteration = 0;
  346. iteration < numberOfValuesToGenerate;
  347. iteration++) {
  348. long value = random1.nextLong();
  349. net.coderodde.util.Long.toUnsignedString(value);
  350. }
  351.  
  352. long endTime = System.currentTimeMillis();
  353.  
  354. if (printElapsedTime) {
  355. System.out.print("net.coderodde.util.Long.toString() in ");
  356. System.out.print(endTime - startTime);
  357. System.out.println(" milliseconds.");
  358. }
  359.  
  360. startTime = System.currentTimeMillis();
  361.  
  362. for (int iteration = 0;
  363. iteration < numberOfValuesToGenerate;
  364. iteration++) {
  365. long value = random2.nextLong();
  366. java.lang.Long.toUnsignedString(value);
  367. }
  368.  
  369. endTime = System.currentTimeMillis();
  370.  
  371. if (printElapsedTime) {
  372. System.out.print("java.lang.Long.toString() in ");
  373. System.out.print(endTime - startTime);
  374. System.out.println(" milliseconds.");
  375. }
  376. }
  377.  
  378. private static final void checkRadix(int radix) {
  379. if (radix < 2 || radix > digitsToCharsMap.length) {
  380. throw new IllegalArgumentException("Bad radix: " + radix);
  381. }
  382. }
  383. }
  384.  
  385. package net.coderodde.util;
  386.  
  387. import java.util.Random;
  388. import net.coderodde.util.Long.Digit;
  389. import static net.coderodde.util.Long.digitsPlus;
  390. import org.junit.Test;
  391. import static org.junit.Assert.*;
  392.  
  393. /**
  394. * This unit test class tests the {@link net.coderodde.util.Long}.
  395. *
  396. * @author Rodion "rodde" Efremov
  397. * @version 1.6 (May 14, 2019)
  398. */
  399. public class LongTest {
  400.  
  401. /**
  402. * The number of brute force iteration when comparing the {@code toString}
  403. * static methods.
  404. */
  405. private static final int BRUTE_FORCE_ITERATIONS = 1_000;
  406.  
  407. @Test
  408. public void testDigitsPlusOnEqualLengthSourceTargetNumbers() {
  409. // Number 123:
  410. Digit source3 = new Digit(1);
  411. Digit source2 = new Digit(2);
  412. Digit source1 = new Digit(3);
  413.  
  414. source1.next = source2;
  415. source2.next = source3;
  416.  
  417. // Number 456:
  418. Digit target3 = new Digit(4);
  419. Digit target2 = new Digit(5);
  420. Digit target1 = new Digit(6);
  421.  
  422. target1.next = target2;
  423. target2.next = target3;
  424.  
  425. // Number 579:
  426. digitsPlus(source1, target1, 10);
  427.  
  428. assertEquals(9, target1.value);
  429. assertEquals(7, target2.value);
  430. assertEquals(5, target3.value);
  431. }
  432.  
  433. @Test
  434. public void testDigitsPlusOnLongerTargetNumber() {
  435. Digit source = new Digit(7);
  436. Digit target = new Digit(8);
  437. digitsPlus(source, target, 10);
  438.  
  439. assertEquals(5, target.value);
  440. assertEquals(1, target.next.value);
  441. }
  442.  
  443. @Test
  444. public void testDigitsPlusWhenSourceIsLonger() {
  445. // source = 591
  446. Digit source1 = new Digit(1);
  447. Digit source2 = new Digit(9);
  448. Digit source3 = new Digit(5);
  449.  
  450. source1.next = source2;
  451. source2.next = source3;
  452.  
  453. // target = 79
  454. Digit target1 = new Digit(9);
  455. Digit target2 = new Digit(7);
  456.  
  457. target1.next = target2;
  458. // 591 + 79
  459. digitsPlus(source1, target1, 10);
  460.  
  461. // 591 + 79 = 670
  462. assertEquals(6, target1.next.next.value);
  463. assertEquals(7, target1.next.value);
  464. assertEquals(0, target1.value);
  465. }
  466.  
  467. @Test
  468. public void testDigitsPlusWhenSourceNumberContainsLongCarryChain() {
  469. // 99500
  470. Digit source1 = new Digit(0);
  471. Digit source2 = new Digit(0);
  472. Digit source3 = new Digit(5);
  473. Digit source4 = new Digit(9);
  474. Digit source5 = new Digit(9);
  475.  
  476. source1.next = source2;
  477. source2.next = source3;
  478. source3.next = source4;
  479. source4.next = source5;
  480.  
  481. // 601
  482. Digit target1 = new Digit(1);
  483. Digit target2 = new Digit(0);
  484. Digit target3 = new Digit(6);
  485.  
  486. target1.next = target2;
  487. target2.next = target3;
  488.  
  489. // 100101
  490. digitsPlus(source1, target1, 10);
  491.  
  492. assertEquals(1, target1.value);
  493. assertEquals(0, target1.next.value);
  494. assertEquals(1, target1.next.next.value);
  495. assertEquals(0, target1.next.next.next.value);
  496. assertEquals(0, target1.next.next.next.next.value);
  497. assertEquals(1, target1.next.next.next.next.next.value);
  498. }
  499.  
  500. @Test
  501. public void testLongToStringWithBruteForce() {
  502. long seed = System.currentTimeMillis();
  503. Random random = new Random(seed);
  504. System.out.println("testLongToStringWithBruteForce, seed = " + seed);
  505.  
  506. for (int i = 0; i < BRUTE_FORCE_ITERATIONS; i++) {
  507. long value = random.nextLong();
  508. int radix = 2 + random.nextInt(35);
  509. String expected = java.lang.Long.toUnsignedString(value, radix);
  510. String actual = net.coderodde.util.Long.toUnsignedString(value,
  511. radix);
  512. assertEquals(expected, actual);
  513. }
  514. }
  515.  
  516. @Test
  517. public void testWhenValueIsNegative() {
  518. String expected = java.lang.Long.toUnsignedString(-1000);
  519. String actual = net.coderodde.util.Long.toUnsignedString(-1000);
  520. assertEquals(expected, actual);
  521. }
  522.  
  523. @Test(expected = IllegalArgumentException.class)
  524. public void testThrowsWhenTooSmallRadix() {
  525. net.coderodde.util.Long.toUnsignedString(1, 1);
  526. }
  527.  
  528. @Test(expected = IllegalArgumentException.class)
  529. public void testThrowsWhenTooLargeRadix() {
  530. net.coderodde.util.Long.toUnsignedString(1, 37);
  531. }
  532.  
  533. @Test
  534. public void testWhenLargestRadix() {
  535. assertEquals(java.lang.Long.toUnsignedString(1000L, 36),
  536. net.coderodde.util.Long.toUnsignedString(1000L, 36));
  537. }
  538. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement