diff --git a/src/main/java/ua/kpi/comsys/test2/implementation/NumberListImpl.java b/src/main/java/ua/kpi/comsys/test2/implementation/NumberListImpl.java index 2cbeb37..532ff64 100644 --- a/src/main/java/ua/kpi/comsys/test2/implementation/NumberListImpl.java +++ b/src/main/java/ua/kpi/comsys/test2/implementation/NumberListImpl.java @@ -7,28 +7,58 @@ package ua.kpi.comsys.test2.implementation; -import java.io.File; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; +import java.io.*; +import java.nio.file.Files; +import java.util.*; import ua.kpi.comsys.test2.NumberList; /** * Custom implementation of INumberList interface. * Has to be implemented by each student independently. + * + * Linear doubly linked list, hexadecimal number system + * Additional operation: multiplication + * Secondary system: binary * - * @author Alexander Podrubailo - * + * @author Mykyta Kalachuk, variant 9 */ public class NumberListImpl implements NumberList { + + private Node head; + private Node tail; + private int size; + private int radix; // 16 for hex, 2 for binary + + // Node for linear doubly linked list + private static class Node { + byte data; + Node next; + Node prev; + + Node(byte data) { + this.data = data; + } + } /** * Default constructor. Returns empty NumberListImpl */ public NumberListImpl() { - // TODO Auto-generated method stub + this.head = null; + this.tail = null; + this.size = 0; + this.radix = 16; // hex by default + } + + /** + * Constructor with specified radix + */ + private NumberListImpl(int radix) { + this.head = null; + this.tail = null; + this.size = 0; + this.radix = radix; } @@ -39,7 +69,15 @@ public NumberListImpl() { * @param file - file where number is stored. */ public NumberListImpl(File file) { - // TODO Auto-generated method stub + this(); + try { + if (file != null && file.exists()) { + String content = new String(Files.readAllBytes(file.toPath())).trim(); + initFromDecimalString(content); + } + } catch (IOException e) { + // file not found - leave empty + } } @@ -50,7 +88,27 @@ public NumberListImpl(File file) { * @param value - number in string notation. */ public NumberListImpl(String value) { - // TODO Auto-generated method stub + this(); + initFromDecimalString(value); + } + + private void initFromDecimalString(String decimal) { + if (decimal == null || decimal.isEmpty()) { + return; + } + + // validate input: check that string contains only digits + if (!decimal.matches("\\d+")) { + return; // invalid input - leave list empty + } + + String hex = decimalToHex(decimal); + + for (int i = 0; i < hex.length(); i++) { + char c = hex.charAt(i); + byte digit = (byte) Character.digit(c, 16); + add(digit); + } } @@ -61,7 +119,11 @@ public NumberListImpl(String value) { * @param file - file where number has to be stored. */ public void saveList(File file) { - // TODO Auto-generated method stub + try (PrintWriter writer = new PrintWriter(file)) { + writer.print(toDecimalString()); + } catch (IOException e) { + throw new RuntimeException("Error writing file", e); + } } @@ -71,8 +133,7 @@ public void saveList(File file) { * @return student's record book number. */ public static int getRecordBookNumber() { - // TODO Auto-generated method stub - return 0; + return 9; } @@ -85,8 +146,24 @@ public static int getRecordBookNumber() { * @return NumberListImpl in other scale of notation. */ public NumberListImpl changeScale() { - // TODO Auto-generated method stub - return null; + String decimal = toDecimalString(); + String binary = decimalToBinary(decimal); + + NumberListImpl result = new NumberListImpl(2); // binary radix + for (int i = 0; i < binary.length(); i++) { + byte digit = (byte) (binary.charAt(i) - '0'); + // add digits directly with correct radix + Node newNode = new Node(digit); + if (result.head == null) { + result.head = result.tail = newNode; + } else { + result.tail.next = newNode; + newNode.prev = result.tail; + result.tail = newNode; + } + result.size++; + } + return result; } @@ -101,8 +178,12 @@ public NumberListImpl changeScale() { * @return result of additional operation. */ public NumberListImpl additionalOperation(NumberList arg) { - // TODO Auto-generated method stub - return null; + // multiplication operation + String thisDecimal = toDecimalString(); + String argDecimal = ((NumberListImpl) arg).toDecimalString(); + + String result = multiplyDecimal(thisDecimal, argDecimal); + return new NumberListImpl(result); } @@ -113,215 +194,726 @@ public NumberListImpl additionalOperation(NumberList arg) { * @return string representation in decimal scale. */ public String toDecimalString() { - // TODO Auto-generated method stub - return null; + if (isEmpty()) { + return "0"; + } + + String str = toString(); + + // convert to decimal depending on number system + if (radix == 2) { + return binaryToDecimal(str); + } else { + return hexToDecimal(str); + } } @Override public String toString() { - // TODO Auto-generated method stub - return null; + if (isEmpty()) { + return "0"; + } + + StringBuilder sb = new StringBuilder(); + Node current = head; + while (current != null) { + if (radix == 2) { + // for binary just append the digit + sb.append(current.data); + } else { + // for hex format as hex + sb.append(Integer.toHexString(current.data).toUpperCase()); + } + current = current.next; + } + + return sb.toString(); } @Override public boolean equals(Object o) { - // TODO Auto-generated method stub - return false; + if (this == o) return true; + if (!(o instanceof NumberList)) return false; + + NumberList other = (NumberList) o; + if (this.size() != other.size()) return false; + + Iterator it1 = this.iterator(); + Iterator it2 = other.iterator(); + + while (it1.hasNext() && it2.hasNext()) { + if (!it1.next().equals(it2.next())) { + return false; + } + } + + return true; } @Override public int size() { - // TODO Auto-generated method stub - return 0; + return size; } @Override public boolean isEmpty() { - // TODO Auto-generated method stub - return false; + return size == 0; } @Override public boolean contains(Object o) { - // TODO Auto-generated method stub + if (!(o instanceof Byte)) return false; + + Node current = head; + while (current != null) { + if (current.data == (Byte) o) { + return true; + } + current = current.next; + } + return false; } @Override public Iterator iterator() { - // TODO Auto-generated method stub - return null; + return new Iterator() { + private Node current = head; + + @Override + public boolean hasNext() { + return current != null; + } + + @Override + public Byte next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + byte data = current.data; + current = current.next; + return data; + } + }; } @Override public Object[] toArray() { - // TODO Auto-generated method stub - return null; + Object[] result = new Object[size]; + int i = 0; + for (Byte b : this) { + result[i++] = b; + } + return result; } @Override public T[] toArray(T[] a) { - // TODO Auto-generated method stub return null; } @Override public boolean add(Byte e) { - // TODO Auto-generated method stub - return false; + if (e == null || e < 0) { + throw new IllegalArgumentException("Element must be non-negative"); + } + + int maxDigit = radix - 1; + if (e > maxDigit) { + throw new IllegalArgumentException("Element must be 0-" + maxDigit + " for radix " + radix); + } + + Node newNode = new Node(e); + + if (head == null) { + head = tail = newNode; + } else { + tail.next = newNode; + newNode.prev = tail; + tail = newNode; + } + + size++; + return true; } @Override public boolean remove(Object o) { - // TODO Auto-generated method stub + if (!(o instanceof Byte) || isEmpty()) { + return false; + } + + byte value = (Byte) o; + Node current = head; + + while (current != null) { + if (current.data == value) { + if (current.prev != null) { + current.prev.next = current.next; + } else { + head = current.next; + } + + if (current.next != null) { + current.next.prev = current.prev; + } else { + tail = current.prev; + } + + size--; + return true; + } + current = current.next; + } + return false; } @Override public boolean containsAll(Collection c) { - // TODO Auto-generated method stub - return false; + for (Object o : c) { + if (!contains(o)) { + return false; + } + } + return true; } @Override public boolean addAll(Collection c) { - // TODO Auto-generated method stub - return false; + boolean modified = false; + for (Byte e : c) { + if (add(e)) { + modified = true; + } + } + return modified; } @Override public boolean addAll(int index, Collection c) { - // TODO Auto-generated method stub - return false; + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(); + } + + int i = index; + for (Byte e : c) { + add(i++, e); + } + return !c.isEmpty(); } @Override public boolean removeAll(Collection c) { - // TODO Auto-generated method stub - return false; + boolean modified = false; + for (Object o : c) { + while (remove(o)) { + modified = true; + } + } + return modified; } @Override public boolean retainAll(Collection c) { - // TODO Auto-generated method stub - return false; + boolean modified = false; + Node current = head; + + while (current != null) { + Node next = current.next; + if (!c.contains(current.data)) { + remove((Byte) current.data); + modified = true; + } + current = next; + } + + return modified; } @Override public void clear() { - // TODO Auto-generated method stub - + head = tail = null; + size = 0; } @Override public Byte get(int index) { - // TODO Auto-generated method stub - return null; + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(); + } + + Node current = head; + for (int i = 0; i < index; i++) { + current = current.next; + } + return current.data; } @Override public Byte set(int index, Byte element) { - // TODO Auto-generated method stub - return null; + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(); + } + if (element == null || element < 0) { + throw new IllegalArgumentException("Element must be non-negative"); + } + + int maxDigit = radix - 1; + if (element > maxDigit) { + throw new IllegalArgumentException("Element must be 0-" + maxDigit + " for radix " + radix); + } + + Node current = head; + for (int i = 0; i < index; i++) { + current = current.next; + } + byte oldValue = current.data; + current.data = element; + return oldValue; } @Override public void add(int index, Byte element) { - // TODO Auto-generated method stub - + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(); + } + if (element == null || element < 0) { + throw new IllegalArgumentException("Element must be non-negative"); + } + + int maxDigit = radix - 1; + if (element > maxDigit) { + throw new IllegalArgumentException("Element must be 0-" + maxDigit + " for radix " + radix); + } + + if (index == size) { + add(element); + return; + } + + Node newNode = new Node(element); + + if (index == 0) { + newNode.next = head; + if (head != null) { + head.prev = newNode; + } + head = newNode; + if (tail == null) { + tail = newNode; + } + } else { + Node current = head; + for (int i = 0; i < index; i++) { + current = current.next; + } + + newNode.next = current; + newNode.prev = current.prev; + if (current.prev != null) { + current.prev.next = newNode; + } + current.prev = newNode; + } + + size++; } @Override public Byte remove(int index) { - // TODO Auto-generated method stub - return null; + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(); + } + + Node current = head; + for (int i = 0; i < index; i++) { + current = current.next; + } + + byte removed = current.data; + + if (current.prev != null) { + current.prev.next = current.next; + } else { + head = current.next; + } + + if (current.next != null) { + current.next.prev = current.prev; + } else { + tail = current.prev; + } + + size--; + return removed; } @Override public int indexOf(Object o) { - // TODO Auto-generated method stub - return 0; + if (!(o instanceof Byte) || isEmpty()) { + return -1; + } + + Node current = head; + for (int i = 0; i < size; i++) { + if (current.data == (Byte) o) { + return i; + } + current = current.next; + } + return -1; } @Override public int lastIndexOf(Object o) { - // TODO Auto-generated method stub - return 0; + if (!(o instanceof Byte) || isEmpty()) { + return -1; + } + + int lastIndex = -1; + Node current = head; + for (int i = 0; i < size; i++) { + if (current.data == (Byte) o) { + lastIndex = i; + } + current = current.next; + } + return lastIndex; } @Override public ListIterator listIterator() { - // TODO Auto-generated method stub - return null; + return listIterator(0); } @Override public ListIterator listIterator(int index) { - // TODO Auto-generated method stub - return null; + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(); + } + + return new ListIterator() { + private int currentIndex = index; + + @Override + public boolean hasNext() { + return currentIndex < size; + } + + @Override + public Byte next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return get(currentIndex++); + } + + @Override + public boolean hasPrevious() { + return currentIndex > 0; + } + + @Override + public Byte previous() { + if (!hasPrevious()) { + throw new NoSuchElementException(); + } + return get(--currentIndex); + } + + @Override + public int nextIndex() { + return currentIndex; + } + + @Override + public int previousIndex() { + return currentIndex - 1; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + @Override + public void set(Byte e) { + NumberListImpl.this.set(currentIndex - 1, e); + } + + @Override + public void add(Byte e) { + NumberListImpl.this.add(currentIndex++, e); + } + }; } @Override public List subList(int fromIndex, int toIndex) { - // TODO Auto-generated method stub - return null; + if (fromIndex < 0 || toIndex > size || fromIndex > toIndex) { + throw new IndexOutOfBoundsException(); + } + + List result = new ArrayList<>(); + for (int i = fromIndex; i < toIndex; i++) { + result.add(get(i)); + } + return result; } @Override public boolean swap(int index1, int index2) { - // TODO Auto-generated method stub - return false; + if (index1 < 0 || index1 >= size || index2 < 0 || index2 >= size) { + return false; + } + + if (index1 == index2) { + return true; + } + + Byte temp = get(index1); + set(index1, get(index2)); + set(index2, temp); + return true; } @Override public void sortAscending() { - // TODO Auto-generated method stub + if (size <= 1) return; + + for (int i = 0; i < size - 1; i++) { + for (int j = 0; j < size - i - 1; j++) { + if (get(j) > get(j + 1)) { + swap(j, j + 1); + } + } + } } @Override public void sortDescending() { - // TODO Auto-generated method stub + if (size <= 1) return; + + for (int i = 0; i < size - 1; i++) { + for (int j = 0; j < size - i - 1; j++) { + if (get(j) < get(j + 1)) { + swap(j, j + 1); + } + } + } } @Override public void shiftLeft() { - // TODO Auto-generated method stub - + if (size <= 1) return; + + byte first = head.data; + Node current = head; + + while (current.next != null) { + current.data = current.next.data; + current = current.next; + } + + current.data = first; } @Override public void shiftRight() { - // TODO Auto-generated method stub - + if (size <= 1) return; + + byte last = tail.data; + Node current = tail; + + while (current.prev != null) { + current.data = current.prev.data; + current = current.prev; + } + + current.data = last; + } + + // Helper methods for number conversion + + private String decimalToHex(String decimal) { + if (decimal.equals("0")) return "0"; + + StringBuilder result = new StringBuilder(); + List digits = new ArrayList<>(); + + for (char c : decimal.toCharArray()) { + digits.add(c - '0'); + } + + while (!isZero(digits)) { + int remainder = divideBy16(digits); + result.insert(0, Integer.toHexString(remainder).toUpperCase()); + } + + return result.length() == 0 ? "0" : result.toString(); + } + + private String hexToDecimal(String hex) { + if (hex.equals("0")) return "0"; + + String result = "0"; + String power = "1"; + + for (int i = hex.length() - 1; i >= 0; i--) { + int digit = Character.digit(hex.charAt(i), 16); + String term = multiplyDecimal(power, String.valueOf(digit)); + result = addDecimal(result, term); + power = multiplyDecimal(power, "16"); + } + + return result; + } + + private String decimalToBinary(String decimal) { + if (decimal.equals("0")) return "0"; + + StringBuilder result = new StringBuilder(); + List digits = new ArrayList<>(); + + for (char c : decimal.toCharArray()) { + digits.add(c - '0'); + } + + while (!isZero(digits)) { + int remainder = divideBy2(digits); + result.insert(0, remainder); + } + + return result.length() == 0 ? "0" : result.toString(); + } + + private String binaryToDecimal(String binary) { + if (binary.equals("0")) return "0"; + + String result = "0"; + String power = "1"; + + for (int i = binary.length() - 1; i >= 0; i--) { + int digit = binary.charAt(i) - '0'; + if (digit == 1) { + result = addDecimal(result, power); + } + power = multiplyDecimal(power, "2"); + } + + return result; + } + + private boolean isZero(List digits) { + for (int d : digits) { + if (d != 0) return false; + } + return true; + } + + private int divideBy16(List digits) { + int remainder = 0; + for (int i = 0; i < digits.size(); i++) { + int current = remainder * 10 + digits.get(i); + digits.set(i, current / 16); + remainder = current % 16; + } + + while (digits.size() > 1 && digits.get(0) == 0) { + digits.remove(0); + } + + return remainder; + } + + private int divideBy2(List digits) { + int remainder = 0; + for (int i = 0; i < digits.size(); i++) { + int current = remainder * 10 + digits.get(i); + digits.set(i, current / 2); + remainder = current % 2; + } + + while (digits.size() > 1 && digits.get(0) == 0) { + digits.remove(0); + } + + return remainder; + } + + private String addDecimal(String a, String b) { + StringBuilder result = new StringBuilder(); + int carry = 0; + int i = a.length() - 1; + int j = b.length() - 1; + + while (i >= 0 || j >= 0 || carry > 0) { + int sum = carry; + if (i >= 0) sum += a.charAt(i--) - '0'; + if (j >= 0) sum += b.charAt(j--) - '0'; + + result.insert(0, sum % 10); + carry = sum / 10; + } + + return result.toString(); + } + + private String multiplyDecimal(String a, String b) { + if (a.equals("0") || b.equals("0")) return "0"; + + int[] result = new int[a.length() + b.length()]; + + for (int i = a.length() - 1; i >= 0; i--) { + for (int j = b.length() - 1; j >= 0; j--) { + int mul = (a.charAt(i) - '0') * (b.charAt(j) - '0'); + int p1 = i + j; + int p2 = i + j + 1; + int sum = mul + result[p2]; + + result[p2] = sum % 10; + result[p1] += sum / 10; + } + } + + StringBuilder sb = new StringBuilder(); + for (int num : result) { + if (!(sb.length() == 0 && num == 0)) { + sb.append(num); + } + } + + return sb.length() == 0 ? "0" : sb.toString(); } }