Java tutorial
/* * @(#)Ternary.java 1.0 Mar 7, 2008 * * The MIT License * * Copyright (c) 2008 Malachi de AElfweald <malachid@gmail.com> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ //package org.eoti.math.ternary; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Comparator; import java.util.concurrent.CopyOnWriteArrayList; public class Ternary extends Number implements Comparable<Ternary> { protected BigInteger biThree = BigInteger.valueOf(3); protected BigDecimal bdThree = new BigDecimal(biThree); protected CopyOnWriteArrayList<Trit> trits; public static void main(String[] args) { if (args.length == 0) { System.out.format("java org.eoti.lang.Ternary number [[number]...]\n"); System.exit(0); } for (String arg : args) { BigInteger toConvert = new BigInteger(arg); Ternary ternary = new Ternary(toConvert); System.out.format("\nDecimal:\t%s\nTernary:\t%s\n", toConvert, ternary); } } public Ternary(int toConvert) { this(BigInteger.valueOf(toConvert)); } public Ternary(BigInteger toConvert) { this(); int position = 0; BigInteger remaining = toConvert; BigInteger rounded, left; while (!remaining.equals(BigInteger.ZERO)) { rounded = ((new BigDecimal(remaining)).divide(bdThree, 0, BigDecimal.ROUND_HALF_UP)).toBigInteger(); left = remaining.subtract(rounded.multiply(biThree)); if (left.equals(BigInteger.ONE)) setTrit(position++, Trit.POSITIVE); else if (left.equals(BigInteger.ZERO)) setTrit(position++, Trit.NEUTRAL); else setTrit(position++, Trit.NEGATIVE); remaining = rounded; } } public Ternary() { super(); trits = new CopyOnWriteArrayList<Trit>(); } public Ternary(Ternary toClone) { this(); trits.addAll(toClone.trits); } public Ternary abs() { if (signum() >= 0) return new Ternary(this); return invert(this); } public int tritLength() { for (int position = trits.size() - 1; position >= 0; position--) { if (!trits.get(position).equals(Trit.NEUTRAL)) return position + 1; } return 0; //return trits.size(); } public void clearTrit(int position) { setTrit(position, Trit.NEUTRAL); } public void setTrit(int position, Trit trit) { if (trits.size() <= position) ensureCapacity(position + 1); trits.set(position, trit); } public Trit getTrit(int position) { if (position < 0) return Trit.NEUTRAL; if (trits.size() <= position) ensureCapacity(position + 1); return trits.get(position); } public int signum() { return getTrit(tritLength() - 1).toInt(); } public void ensureCapacity(int nTerts) { while (trits.size() < nTerts) trits.add(Trit.NEUTRAL); } public void trim() { while ((trits.size() > 0) && (trits.get(trits.size() - 1).isNeutral())) trits.remove(trits.size() - 1); } public void increment() { increment(0); } public void increment(int position) { Trit t = getTrit(position).rotateUp(); setTrit(position, t); if (t.isNegative()) // carry increment(position + 1); } public void decrement() { decrement(0); } public void decrement(int position) { Trit t = getTrit(position).rotateDown(); setTrit(position, t); if (t.isPositive()) // borrow decrement(position + 1); } public String toString() { if (trits.size() == 0) return Trit.NEUTRAL.toString(); StringBuilder sb = new StringBuilder(); for (Trit trit : trits) sb.append(trit); return sb.reverse().toString(); } public int compareTo(Ternary t) { // shortcut if one is positive and the other negative int ourSig = signum(); int theirSig = t.signum(); if (ourSig > theirSig) return 1; if (theirSig > ourSig) return -1; // we're the same sign... who has more trits? int ourLength = tritLength(); int theirLength = t.tritLength(); if (ourLength > theirLength) return (ourSig > 0 ? 1 : -1); if (theirLength > ourLength) return (ourSig > 0 ? -1 : 1); // same sign, same length... guess we need to find first mismatch for (int position = ourLength - 1; position >= 0; position--) { int comparison = getTrit(position).compareTo(t.getTrit(position)); if (comparison != 0) return comparison; } // guess we are identical return 0; } public boolean equals(Object obj) { if (obj == null) return false; return toString().equals(obj.toString()); } public int hashCode() { return toString().hashCode(); } public BigInteger toBigInteger() { BigInteger toRet = BigInteger.ZERO; BigInteger curr; for (int pos = 0; pos < trits.size(); pos++) { curr = (biThree.pow(pos)).multiply(BigInteger.valueOf(getTrit(pos).toInt())); toRet = toRet.add(curr); } return toRet; } public void shiftLeft() { shiftLeft(1); } public void shiftLeft(int num) { CopyOnWriteArrayList<Trit> newTrits = new CopyOnWriteArrayList<Trit>(); for (int i = 0; i < num; i++) newTrits.add(Trit.NEUTRAL); newTrits.addAll(trits); trits = newTrits; } public void shiftRight() { shiftRight(1); } public void shiftRight(int num) { ensureCapacity(trits.size() + num); for (int i = 0; i < num; i++) trits.remove(0); } public byte byteValue() { return toBigInteger().byteValue(); } public double doubleValue() { return toBigInteger().doubleValue(); } public float floatValue() { return toBigInteger().floatValue(); } public int intValue() { return toBigInteger().intValue(); } public long longValue() { return toBigInteger().longValue(); } public short shortValue() { return toBigInteger().shortValue(); } /** * Add the specified addend to this augend * * @param addend to be added * @return ternary summation */ public Ternary add(Ternary addend) { /** * Try to find a better way of doing this * using gate logic * and DON'T just use toBigInteger to do it */ // make sure the LONGER one is on top if (tritLength() < addend.tritLength()) return addend.add(this); Ternary summation = new Ternary(this); Ternary toCarry = new Ternary(); for (int pos = 0; pos < trits.size(); pos++) { Trit a = summation.getTrit(pos); Trit b = addend.getTrit(pos); switch (a.state) { case Negative: summation.setTrit(pos, b.rotateDown()); break; case Positive: summation.setTrit(pos, b.rotateUp()); break; default: summation.setTrit(pos, b); } if (a.equals(b)) toCarry.setTrit(pos + 1, a); } if (toCarry.tritLength() == 0) return summation; return summation.add(toCarry); } /** * Subtract the specified subtrahend from this minuend * * @param subtrahend to be subtracted * @return ternary difference */ public Ternary subtract(Ternary subtrahend) { return add(invert(subtrahend)); } public static Ternary invert(Ternary toInvert) { Ternary inverted = new Ternary(); for (int i = 0; i < toInvert.trits.size(); i++) inverted.setTrit(i, toInvert.getTrit(i).invert()); return inverted; } /** * Multiply this multiplicand by the specified multiplier * * @param multiplier to multiply by * @return ternary product */ public Ternary multiply(Ternary multiplier) { /** * Try to find a better way of doing this * using gate logic * and DON'T just use toBigInteger to do it */ // make sure the LONGER one is on top if (tritLength() < multiplier.tritLength()) return multiplier.multiply(this); Ternary product = new Ternary(); for (int posB = 0; posB < multiplier.trits.size(); posB++) { Ternary row = new Ternary(); for (int posA = 0; posA < trits.size(); posA++) { Trit a = getTrit(posA); Trit b = multiplier.getTrit(posB); Trit c = a.equality(b); if (a.isNeutral() && b.isNeutral()) c = Trit.NEUTRAL; row.setTrit(posA + posB, c); } product = product.add(row); } return product; } /** * Divide this divided by the specified divisor * * @param divisor to divide by * @return ternary[] containing {quotient,remainder} */ public Ternary[] divide(Ternary divisor) { /** * 6/3=2r0 * 6=dividend * 3=divisor * 2=quotient * 0=remainder */ Ternary dividend = new Ternary(this); Ternary quotient = new Ternary(0); Ternary remainder = new Ternary(0); int dividendSign = dividend.signum(); if (dividendSign == 0) return new Ternary[] { quotient, remainder }; int divisorSign = divisor.signum(); if (divisorSign == 0) throw new ArithmeticException("Divide by Zero not currently supported."); if (dividendSign != divisorSign) { // if one or the other (not both) are negative, then the result will have a negative quotient Ternary tmp = null; Ternary[] results = null; if (dividendSign < 0) { tmp = new Ternary(dividend); tmp = invert(tmp); //results = tmp.divide(divisor); results = tmp.divide(divisor); } else { tmp = new Ternary(divisor); tmp = invert(tmp); //results = dividend.divide(tmp); results = dividend.divide(tmp); } quotient = invert(results[0]); remainder = dividend.subtract(quotient.multiply(divisor)); return new Ternary[] { quotient, remainder }; } // two positives or two negatives will be positive results if (dividendSign < 0) { dividend = invert(dividend); divisor = invert(divisor); } int position = dividend.tritLength() - 1; while (position >= 0) { remainder = (new Ternary(dividend)).subtract(quotient.multiply(divisor)); remainder.shiftRight(position); int compare = remainder.compareTo(divisor); if (compare > 0) { quotient.increment(); } else if (compare < 0) { if (position > 0) quotient.shiftLeft(1); position--; } else { quotient.increment(); position--; } } remainder = (new Ternary(dividend)).subtract(quotient.multiply(divisor)); return new Ternary[] { quotient, remainder }; } public Ternary sqrt() { return sqrt(this); } public static Ternary sqrt(Ternary number) { return sqrt(number, new Ternary(1)); } protected static Ternary sqrt(Ternary number, Ternary guess) { // ((n/g) + g)/2: until same result twice in a row Ternary result = new Ternary(0); Ternary flipA = new Ternary(result); Ternary flipB = new Ternary(result); boolean first = true; while (result.compareTo(guess) != 0) { if (!first) guess = result; else first = false; result = (number.divide(guess))[0]; result = result.add(guess); result = (result.divide(new Ternary(2)))[0]; result.trim(); // handle flip flops if (result.equals(flipB)) return flipA; flipB = flipA; flipB.trim(); flipA = result; flipA.trim(); } return result; } } class Trit implements Comparable<Trit>, Comparator<Trit> { protected enum State { Negative(-1, '\u012B'), Neutral(0, '0'), Positive(1, '1'); State(int intValue, char charValue) { this.intValue = intValue; this.charValue = charValue; } protected int intValue; protected char charValue; public int toInt() { return intValue; } public char toChar() { return charValue; } } // convenience access public static final Trit NEGATIVE = new Trit(State.Negative); public static final Trit NEUTRAL = new Trit(State.Neutral); public static final Trit POSITIVE = new Trit(State.Positive); protected State state; public Trit(State state) { this.state = state; } public Trit(Trit trit) { this.state = trit.state; } public boolean isNegative() { return state == State.Negative; } public boolean isNeutral() { return state == State.Neutral; } public boolean isPositive() { return state == State.Positive; } public Trit max(Trit other) { return toInt() >= other.toInt() ? this : other; } public Trit min(Trit other) { return toInt() <= other.toInt() ? this : other; } public Trit or(Trit other) { return max(other).clone(); } public Trit and(Trit other) { return min(other).clone(); } public Trit xor(Trit other) { if (isNegative() && other.isNegative()) return NEGATIVE.clone(); if (isNegative() && other.isPositive()) return POSITIVE.clone(); if (isPositive() && other.isNegative()) return POSITIVE.clone(); return NEUTRAL.clone(); } public Trit nand(Trit other) { return not(and(other)); } public Trit nor(Trit other) { return not(or(other)); } public Trit nxor(Trit other) { return not(xor(other)); } /** * Invert unary operation * ? becomes 1 * 0 becomes 0 * 1 becomes ? */ public Trit invert() { return not(); } public Trit not() { switch (state) { case Negative: return Trit.POSITIVE.clone(); case Positive: return Trit.NEGATIVE.clone(); default: return Trit.NEUTRAL.clone(); } } public Trit not(Trit other) { return inequality(other); } public Trit inequality(Trit other) { Trit eq = equality(other); if (eq.isPositive()) return NEGATIVE.clone(); if (eq.isNegative()) return POSITIVE.clone(); return NEUTRAL.clone(); } public Trit disjunction(Trit other) { return or(other); } public Trit conjunction(Trit other) { return and(other); } public Trit equality(Trit other) { if (state == other.state) return POSITIVE.clone(); if (isNeutral()) return NEUTRAL.clone(); if (other.isNeutral()) return NEUTRAL.clone(); return NEGATIVE.clone(); } public Trit implication(Trit other) { if (other.isNegative() && isNeutral()) return NEUTRAL.clone(); if (other.isNegative() && isPositive()) return NEGATIVE.clone(); if (other.isNeutral() && isPositive()) return NEUTRAL.clone(); return POSITIVE.clone(); } public Trit alternation(Trit other) { return xor(other); } /** * ShiftUp unary operation * ? becomes 0 * 0 becomes 1 * 1 becomes 1 */ public Trit shiftUp() { return NEUTRAL.implication(this); } /** * ShiftDown unary operation * ? becomes ? * 0 becomes ? * 1 becomes 0 */ public Trit shiftDown() { return not(implication(NEUTRAL)); } /** * RotateUp unary operation * ? becomes 0 * 0 becomes 1 * 1 becomes ? */ public Trit rotateUp() { /** * Change to use gate logic? */ switch (state) { case Negative: return NEUTRAL.clone(); case Neutral: return POSITIVE.clone(); default: return NEGATIVE.clone(); } } /** * RotateDown unary operation * ? becomes 1 * 0 becomes ? * 1 becomes 0 */ public Trit rotateDown() { /** * Change to use gate logic? */ switch (state) { case Negative: return POSITIVE.clone(); case Neutral: return NEGATIVE.clone(); default: return NEUTRAL.clone(); } } public String toString() { return "" + toChar(); } public char toChar() { return state.toChar(); } public int toInt() { return state.toInt(); } public Trit clone() { return new Trit(state); } public boolean equals(Object obj) { if (obj == null) return false; if (!Trit.class.isAssignableFrom(obj.getClass())) return false; return (compareTo((Trit) obj) == State.Neutral.toInt()); } public int compareTo(Trit obj) { return compare(this, obj); } public int compare(Trit t1, Trit t2) { if (t1.state.toInt() < t2.state.toInt()) return State.Negative.toInt(); if (t1.state.toInt() > t2.state.toInt()) return State.Positive.toInt(); return State.Neutral.toInt(); } /** * Returns a hash code value for the object. This method is supported for the benefit of hashtables such as those * provided by <code>java.util.Hashtable</code>. * <p/> * The general contract of <code>hashCode</code> is: <ul> <li>Whenever it is invoked on the same object more than once * during an execution of a Java application, the <tt>hashCode</tt> method must consistently return the same integer, * provided no information used in <tt>equals</tt> comparisons on the object is modified. This integer need not remain * consistent from one execution of an application to another execution of the same application. <li>If two objects are * equal according to the <tt>equals(Object)</tt> method, then calling the <code>hashCode</code> method on each of the * two objects must produce the same integer result. <li>It is <em>not</em> required that if two objects are unequal * according to the {@link Object#equals(Object)} method, then calling the <tt>hashCode</tt> method on each of the two * objects must produce distinct integer results. However, the programmer should be aware that producing distinct * integer results for unequal objects may improve the performance of hashtables. </ul> * <p/> * As much as is reasonably practical, the hashCode method defined by class <tt>Object</tt> does return distinct * integers for distinct objects. (This is typically implemented by converting the internal address of the object into * an integer, but this implementation technique is not required by the Java<font size="-2"><sup>TM</sup></font> * programming language.) * * @return a hash code value for this object. * * @see Object#equals(Object) * @see java.util.Hashtable */ @Override public int hashCode() { return toString().hashCode(); } } /* package org.eoti.math.ternary; import junit.framework.TestCase; import junit.framework.Test; import junit.framework.TestSuite; public class TernaryTest extends TestCase { public TernaryTest(String name){super(name);} public static Test suite(){return new TestSuite(TernaryTest.class);} public void testInt() { int toTest = 13; assertEquals("Int", toTest, (new Ternary(toTest)).intValue()); } public void testSignum() { assertEquals("Positive Signum", 1, (new Ternary(10)).signum()); assertEquals("Neutral Signum", 0, (new Ternary(0)).signum()); assertEquals("Negative Signum", -1, (new Ternary(-10)).signum()); } public void testTritLength() { assertTrue("TritLength(13)", (new Ternary(13)).tritLength() == 3); assertTrue("TritLength(14)", (new Ternary(14)).tritLength() == 4); } public void testClearTrit() { Ternary tst = new Ternary(13); tst.clearTrit(1); Ternary expected = new Ternary(10); assertEquals("ClearTrit", expected, tst); } public void testSetTrit() { Ternary tst = new Ternary(10); tst.setTrit(1, Trit.POSITIVE); Ternary expected = new Ternary(13); assertEquals("SetTrit", expected, tst); } public void testIncrement() { Ternary tst = new Ternary(-1); tst.increment(); assertEquals("Increment -1->0", 0, tst.intValue()); tst.increment(); assertEquals("Increment 0->1", 1, tst.intValue()); tst.increment(); assertEquals("Increment 1->2", 2, tst.intValue()); tst.increment(); assertEquals("Increment 2->3", 3, tst.intValue()); } public void testDecrement() { Ternary tst = new Ternary(3); tst.decrement(); assertEquals("Decrement 3->2", 2, tst.intValue()); tst.decrement(); assertEquals("Decrement 2->1", 1, tst.intValue()); tst.decrement(); assertEquals("Decrement 1->0", 0, tst.intValue()); tst.decrement(); assertEquals("Decrement 0->-1", -1, tst.intValue()); } public void testShiftLeft() { Ternary tst = new Ternary(8); // +0- tst.shiftLeft(); tst.trim(); Ternary expected = new Ternary(24);// +0-0 expected.trim(); assertEquals("ShiftLeft", expected, tst); } public void testShiftRight() { Ternary tst = new Ternary(24);// +0-0 tst.shiftRight(); tst.trim(); Ternary expected = new Ternary(8); // +0- expected.trim(); assertEquals("ShiftRight", expected, tst); } public void testAdd() { assertEquals("Add PosPos", new Ternary(13), (new Ternary(10)).add(new Ternary(3))); assertEquals("Add PosNeg", new Ternary(7), (new Ternary(10)).add(new Ternary(-3))); assertEquals("Add NegNeg", new Ternary(-13), (new Ternary(-10)).add(new Ternary(-3))); assertEquals("Add NegPos", new Ternary(-7), (new Ternary(-10)).add(new Ternary(3))); } public void testSubtract() { assertEquals("Subtract PosPos", new Ternary(7), (new Ternary(10)).subtract(new Ternary(3))); assertEquals("Subtract PosNeg", new Ternary(13), (new Ternary(10)).subtract(new Ternary(-3))); assertEquals("Subtract NegNeg", new Ternary(-7), (new Ternary(-10)).subtract(new Ternary(-3))); assertEquals("Subtract NegPos", new Ternary(-13), (new Ternary(-10)).subtract(new Ternary(3))); } public void testMultiply() { assertEquals("Multiply PosPos", new Ternary(15), (new Ternary(5)).multiply(new Ternary(3))); assertEquals("Multiply PosNeg", new Ternary(-15), (new Ternary(5)).multiply(new Ternary(-3))); assertEquals("Multiply NegNeg", new Ternary(15), (new Ternary(-5)).multiply(new Ternary(-3))); assertEquals("Multiply NegPos", new Ternary(-15), (new Ternary(-5)).multiply(new Ternary(3))); } public void testDivide() { // 6/3=2r0; 6=dividend; 3=divisor; 2=quotient; 0=remainder Ternary dividend = new Ternary(7); Ternary divisor = new Ternary(2); Ternary expectedQuotient = new Ternary(3); Ternary expectedRemainder = new Ternary(1); Ternary[] results = dividend.divide(divisor); results[0].trim(); results[1].trim(); assertEquals("Divide(7/2) Quotient", expectedQuotient, results[0]); assertEquals("Divide(7/2) Remainder", expectedRemainder, results[1]); dividend = new Ternary(7); divisor = new Ternary(-2); expectedQuotient = new Ternary(-3); expectedRemainder = new Ternary(1); results = dividend.divide(divisor); results[0].trim(); results[1].trim(); assertEquals("Divide(7/-2) Quotient", expectedQuotient, results[0]); assertEquals("Divide(7/-2) Remainder", expectedRemainder, results[1]); dividend = new Ternary(-7); divisor = new Ternary(2); expectedQuotient = new Ternary(-3); expectedRemainder = new Ternary(-1); results = dividend.divide(divisor); results[0].trim(); results[1].trim(); assertEquals("Divide(-7/2) Quotient", expectedQuotient, results[0]); assertEquals("Divide(-7/2) Remainder", expectedRemainder, results[1]); dividend = new Ternary(-7); divisor = new Ternary(-2); expectedQuotient = new Ternary(3); expectedRemainder = new Ternary(1); results = dividend.divide(divisor); results[0].trim(); results[1].trim(); assertEquals("Divide(-7/-2) Quotient", expectedQuotient, results[0]); assertEquals("Divide(-7/-2) Remainder", expectedRemainder, results[1]); dividend = new Ternary(0); divisor = new Ternary(2); expectedQuotient = new Ternary(0); expectedRemainder = new Ternary(0); results = dividend.divide(divisor); results[0].trim(); results[1].trim(); assertEquals("Divide(0/2) Quotient", expectedQuotient, results[0]); assertEquals("Divide(0/2) Remainder", expectedRemainder, results[1]); try{ dividend = new Ternary(2); divisor = new Ternary(0); results = dividend.divide(divisor); fail("Divide(2/0): Should have caused ArithmeticException"); }catch(ArithmeticException ae){ // good } } public void testSqrt() { if(true) { assertTrue("SKIPPING testSqrt", true); return; } assertEquals("SQRT", new Ternary(3), (new Ternary(15)).sqrt()); assertEquals("SQRT", new Ternary(4), (new Ternary(16)).sqrt()); assertEquals("SQRT", new Ternary(4), (new Ternary(17)).sqrt()); assertEquals("SQRT", new Ternary(6), (new Ternary(48)).sqrt()); assertEquals("SQRT", new Ternary(7), (new Ternary(49)).sqrt()); assertEquals("SQRT", new Ternary(7), (new Ternary(50)).sqrt()); assertEquals("SQRT", new Ternary(41), (new Ternary(1702)).sqrt()); assertEquals("SQRT", new Ternary(41), (new Ternary(1703)).sqrt()); assertEquals("SQRT", new Ternary(41), (new Ternary(1704)).sqrt()); assertEquals("SQRT", new Ternary(10), (new Ternary(100)).sqrt()); } public void testAbs() { assertEquals("SQRT", new Ternary(5), (new Ternary(5)).abs()); assertEquals("SQRT", new Ternary(5), (new Ternary(-5)).abs()); assertEquals("SQRT", new Ternary(0), (new Ternary(0)).abs()); } } */