diff --git a/.classpath b/.classpath index 553420ad..f71e03c2 100644 --- a/.classpath +++ b/.classpath @@ -2,12 +2,30 @@ - - - + - + + + + + + + + + + + + + + + + + + + + + diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs index 17e0d76f..95b348ae 100644 --- a/.settings/org.eclipse.core.resources.prefs +++ b/.settings/org.eclipse.core.resources.prefs @@ -1,8 +1,12 @@ eclipse.preferences.version=1 encoding//src/org/nevec/rjm/BigSurd.java=UTF-8 encoding//src/org/nevec/rjm/BigSurdVec.java=UTF-8 -encoding//src/org/warpgate/pi/calculator/Main.java=UTF-8 -encoding//src/org/warpgate/pi/calculator/Parentesi.java=UTF-8 -encoding//src/org/warpgate/pi/calculator/Radice.java=UTF-8 -encoding//src/org/warpgate/pi/calculator/RadiceQuadrata.java=UTF-8 -encoding//src/org/warpgate/pi/calculator/Simboli.java=UTF-8 +encoding//src/org/warp/engine=UTF-8 +encoding//src/org/warp/engine/Display.java=UTF-8 +encoding//src/org/warp/picalculator/Espressione.java=UTF-8 +encoding//src/org/warp/picalculator/Main.java=UTF-8 +encoding//src/org/warp/picalculator/Radice.java=UTF-8 +encoding//src/org/warp/picalculator/RadiceQuadrata.java=UTF-8 +encoding//src/org/warp/picalculator/Simboli.java=UTF-8 +encoding//src/org/warp/picalculator/screens/EquationScreen.java=UTF-8 +encoding/=UTF-8 diff --git a/res/font_big.rft b/res/font_big.rft new file mode 100644 index 00000000..7caf8c7b Binary files /dev/null and b/res/font_big.rft differ diff --git a/res/font_big.ttf b/res/font_big.ttf deleted file mode 100644 index 3121e7f1..00000000 Binary files a/res/font_big.ttf and /dev/null differ diff --git a/res/font_small.rft b/res/font_small.rft new file mode 100644 index 00000000..75bc9b46 Binary files /dev/null and b/res/font_small.rft differ diff --git a/res/font_small.ttf b/res/font_small.ttf deleted file mode 100644 index 07eb5543..00000000 Binary files a/res/font_small.ttf and /dev/null differ diff --git a/res/marioskin.png b/res/marioskin.png new file mode 100644 index 00000000..dd77c3b8 Binary files /dev/null and b/res/marioskin.png differ diff --git a/res/marioskin.xcf b/res/marioskin.xcf new file mode 100644 index 00000000..794b1eaa Binary files /dev/null and b/res/marioskin.xcf differ diff --git a/res/skin.png b/res/skin.png index ce06a188..d10f4733 100644 Binary files a/res/skin.png and b/res/skin.png differ diff --git a/res/skin.xcf b/res/skin.xcf index a9fd0c80..ddc7871f 100644 Binary files a/res/skin.xcf and b/res/skin.xcf differ diff --git a/src/com/rits/cloning/Cloner.java b/src/com/rits/cloning/Cloner.java index e9736098..b938f989 100644 --- a/src/com/rits/cloning/Cloner.java +++ b/src/com/rits/cloning/Cloner.java @@ -8,14 +8,26 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.net.URI; import java.net.URL; -import java.util.*; +import java.util.ArrayList; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; /** * Cloner: deep clone objects. * - * This class is thread safe. One instance can be used by multiple threads on the same time. + * This class is thread safe. One instance can be used by multiple threads on + * the same time. * * @author kostantinos.kougios * 18 Sep 2008 @@ -34,11 +46,13 @@ public class Cloner { } /** - * provide a cloned classes dumper (so i.e. they can be logged or stored in a file + * provide a cloned classes dumper (so i.e. they can be logged or stored in + * a file * instead of the default behaviour which is to println(cloned) ) * - * @param dumpCloned an implementation of the interface which can dump the - * cloned classes. + * @param dumpCloned + * an implementation of the interface which can dump the + * cloned classes. */ public void setDumpCloned(IDumpCloned dumpCloned) { this.dumpCloned = dumpCloned; @@ -66,9 +80,11 @@ public class Cloner { /** * this makes the cloner to set a transient field to null upon cloning. * - * NOTE: primitive types can't be nulled. Their value will be set to default, i.e. 0 for int + * NOTE: primitive types can't be nulled. Their value will be set to + * default, i.e. 0 for int * - * @param nullTransient true for transient fields to be nulled + * @param nullTransient + * true for transient fields to be nulled */ public void setNullTransient(final boolean nullTransient) { this.nullTransient = nullTransient; @@ -98,6 +114,7 @@ public class Cloner { } private IDeepCloner deepCloner = new IDeepCloner() { + @Override public T deepClone(T o, Map clones) { try { return cloneInternal(o, clones); @@ -111,7 +128,8 @@ public class Cloner { protected Object fastClone(final Object o, final Map clones) throws IllegalAccessException { final Class c = o.getClass(); final IFastCloner fastCloner = fastCloners.get(c); - if (fastCloner != null) return fastCloner.clone(o, deepCloner, clones); + if (fastCloner != null) + return fastCloner.clone(o, deepCloner, clones); return null; } @@ -137,7 +155,8 @@ public class Cloner { } /** - * registers some known JDK immutable classes. Override this to register your + * registers some known JDK immutable classes. Override this to register + * your * own list of jdk's immutable classes */ protected void registerKnownJdkImmutableClasses() { @@ -162,18 +181,21 @@ public class Cloner { } protected void registerKnownConstants() { - // registering known constants of the jdk. + // registering known constants of the jdk. registerStaticFields(TreeSet.class, HashSet.class, HashMap.class, TreeMap.class); } /** - * registers all static fields of these classes. Those static fields won't be cloned when an instance + * registers all static fields of these classes. Those static fields won't + * be cloned when an instance * of the class is cloned. * - * This is useful i.e. when a static field object is added into maps or sets. At that point, there is no + * This is useful i.e. when a static field object is added into maps or + * sets. At that point, there is no * way for the cloner to know that it was static except if it is registered. * - * @param classes array of classes + * @param classes + * array of classes */ public void registerStaticFields(final Class... classes) { for (final Class c : classes) { @@ -190,18 +212,23 @@ public class Cloner { /** * spring framework friendly version of registerStaticFields * - * @param set a set of classes which will be scanned for static fields + * @param set + * a set of classes which will be scanned for static fields */ public void setExtraStaticFields(final Set> set) { registerStaticFields((Class[]) set.toArray()); } /** - * instances of classes that shouldn't be cloned can be registered using this method. + * instances of classes that shouldn't be cloned can be registered using + * this method. * - * @param c The class that shouldn't be cloned. That is, whenever a deep clone for - * an object is created and c is encountered, the object instance of c will - * be added to the clone. + * @param c + * The class that shouldn't be cloned. That is, whenever a deep + * clone for + * an object is created and c is encountered, the object instance + * of c will + * be added to the clone. */ public void dontClone(final Class... c) { for (final Class cl : c) { @@ -222,7 +249,8 @@ public class Cloner { /** * instead of cloning these classes will set the field to null * - * @param c the classes to nullify during cloning + * @param c + * the classes to nullify during cloning */ public void nullInsteadOfClone(final Class... c) { for (final Class cl : c) { @@ -238,7 +266,8 @@ public class Cloner { /** * registers an immutable class. Immutable classes are not cloned. * - * @param c the immutable class + * @param c + * the immutable class */ public void registerImmutable(final Class... c) { for (final Class cl : c) { @@ -252,7 +281,8 @@ public class Cloner { } public void registerFastCloner(final Class c, final IFastCloner fastCloner) { - if (fastCloners.containsKey(c)) throw new IllegalArgumentException(c + " already fast-cloned!"); + if (fastCloners.containsKey(c)) + throw new IllegalArgumentException(c + " already fast-cloned!"); fastCloners.put(c, fastCloner); } @@ -263,8 +293,10 @@ public class Cloner { /** * creates a new instance of c. Override to provide your own implementation * - * @param the type of c - * @param c the class + * @param + * the type of c + * @param c + * the class * @return a new instance of c */ protected T newInstance(final Class c) { @@ -275,7 +307,8 @@ public class Cloner { public T fastCloneOrNewInstance(final Class c) { try { final T fastClone = (T) fastClone(c, null); - if (fastClone != null) return fastClone; + if (fastClone != null) + return fastClone; } catch (final IllegalAccessException e) { throw new RuntimeException(e); } @@ -286,13 +319,17 @@ public class Cloner { /** * deep clones "o". * - * @param the type of "o" - * @param o the object to be deep-cloned + * @param + * the type of "o" + * @param o + * the object to be deep-cloned * @return a deep-clone of "o". */ public T deepClone(final T o) { - if (o == null) return null; - if (!cloningEnabled) return o; + if (o == null) + return null; + if (!cloningEnabled) + return o; if (dumpCloned != null) { dumpCloned.startCloning(o.getClass()); } @@ -305,8 +342,10 @@ public class Cloner { } public T deepCloneDontCloneInstances(final T o, final Object... dontCloneThese) { - if (o == null) return null; - if (!cloningEnabled) return o; + if (o == null) + return null; + if (!cloningEnabled) + return o; if (dumpCloned != null) { dumpCloned.startCloning(o.getClass()); } @@ -325,13 +364,17 @@ public class Cloner { * shallow clones "o". This means that if c=shallowClone(o) then * c!=o. Any change to c won't affect o. * - * @param the type of o - * @param o the object to be shallow-cloned + * @param + * the type of o + * @param o + * the object to be shallow-cloned * @return a shallow clone of "o" */ public T shallowClone(final T o) { - if (o == null) return null; - if (!cloningEnabled) return o; + if (o == null) + return null; + if (!cloningEnabled) + return o; try { return cloneInternal(o, null); } catch (final IllegalAccessException e) { @@ -344,9 +387,11 @@ public class Cloner { private boolean cloneAnonymousParent = true; /** - * override this to decide if a class is immutable. Immutable classes are not cloned. + * override this to decide if a class is immutable. Immutable classes are + * not cloned. * - * @param clz the class under check + * @param clz + * the class under check * @return true to mark clz as immutable and skip cloning it */ protected boolean considerImmutable(final Class clz) { @@ -360,13 +405,16 @@ public class Cloner { /** * decides if a class is to be considered immutable or not * - * @param clz the class under check + * @param clz + * the class under check * @return true if the clz is considered immutable */ private boolean isImmutable(final Class clz) { final Boolean isIm = immutables.get(clz); - if (isIm != null) return isIm; - if (considerImmutable(clz)) return true; + if (isIm != null) + return isIm; + if (considerImmutable(clz)) + return true; final Class immutableAnnotation = getImmutableAnnotation(); for (final Annotation annotation : clz.getDeclaredAnnotations()) { @@ -394,24 +442,34 @@ public class Cloner { @SuppressWarnings("unchecked") protected T cloneInternal(final T o, final Map clones) throws IllegalAccessException { - if (o == null) return null; - if (o == this) return null; // don't clone the cloner! - if (ignoredInstances.containsKey(o)) return o; - if (o instanceof Enum) return o; + if (o == null) + return null; + if (o == this) + return null; // don't clone the cloner! + if (ignoredInstances.containsKey(o)) + return o; + if (o instanceof Enum) + return o; final Class clz = (Class) o.getClass(); // skip cloning ignored classes - if (nullInstead.contains(clz)) return null; - if (ignored.contains(clz)) return o; + if (nullInstead.contains(clz)) + return null; + if (ignored.contains(clz)) + return o; for (final Class iClz : ignoredInstanceOf) { - if (iClz.isAssignableFrom(clz)) return o; + if (iClz.isAssignableFrom(clz)) + return o; } - if (isImmutable(clz)) return o; + if (isImmutable(clz)) + return o; if (o instanceof IFreezable) { final IFreezable f = (IFreezable) o; - if (f.isFrozen()) return o; + if (f.isFrozen()) + return o; } final Object clonedPreviously = clones != null ? clones.get(o) : null; - if (clonedPreviously != null) return (T) clonedPreviously; + if (clonedPreviously != null) + return (T) clonedPreviously; final Object fastClone = fastClone(o, clones); if (fastClone != null) { @@ -441,7 +499,7 @@ public class Cloner { for (final Field field : fields) { final int modifiers = field.getModifiers(); if (!Modifier.isStatic(modifiers)) { - if ( ! (nullTransient && Modifier.isTransient(modifiers)) ) { + if (!(nullTransient && Modifier.isTransient(modifiers))) { // request by Jonathan : transient fields can be null-ed final Object fieldObject = field.get(o); final boolean shouldClone = (cloneSynthetics || !field.isSynthetic()) && (cloneAnonymousParent || !isAnonymousParent(field)); @@ -464,7 +522,7 @@ public class Cloner { if (clones != null) { clones.put(o, newInstance); } - if(clz.getComponentType().isPrimitive() || isImmutable(clz.getComponentType())) { + if (clz.getComponentType().isPrimitive() || isImmutable(clz.getComponentType())) { System.arraycopy(o, 0, newInstance, 0, length); } else { for (int i = 0; i < length; i++) { @@ -481,14 +539,20 @@ public class Cloner { } /** - * copies all properties from src to dest. Src and dest can be of different class, provided they contain same field names/types + * copies all properties from src to dest. Src and dest can be of different + * class, provided they contain same field names/types * - * @param src the source object - * @param dest the destination object which must contain as minimum all the fields of src + * @param src + * the source object + * @param dest + * the destination object which must contain as minimum all the + * fields of src */ public void copyPropertiesOfInheritedClass(final T src, final E dest) { - if (src == null) throw new IllegalArgumentException("src can't be null"); - if (dest == null) throw new IllegalArgumentException("dest can't be null"); + if (src == null) + throw new IllegalArgumentException("src can't be null"); + if (dest == null) + throw new IllegalArgumentException("dest can't be null"); final Class srcClz = src.getClass(); final Class destClz = dest.getClass(); if (srcClz.isArray()) { @@ -559,20 +623,24 @@ public class Cloner { * setDumpCloned() if you want to control where to print the cloned * classes. * - * @param dumpClonedClasses true to enable printing all cloned classes + * @param dumpClonedClasses + * true to enable printing all cloned classes */ public void setDumpClonedClasses(final boolean dumpClonedClasses) { if (dumpClonedClasses) { dumpCloned = new IDumpCloned() { + @Override public void startCloning(Class clz) { System.out.println("clone>" + clz); } + @Override public void cloning(Field field, Class clz) { System.out.println("cloned field>" + field + " -- of class " + clz); } }; - } else dumpCloned = null; + } else + dumpCloned = null; } public boolean isCloningEnabled() { @@ -602,9 +670,10 @@ public class Cloner { } /** - * @return if Cloner lib is in a shared jar folder for a container (i.e. tomcat/shared), then - * this method is preferable in order to instantiate cloner. Please - * see https://code.google.com/p/cloning/issues/detail?id=23 + * @return if Cloner lib is in a shared jar folder for a container (i.e. + * tomcat/shared), then + * this method is preferable in order to instantiate cloner. Please + * see https://code.google.com/p/cloning/issues/detail?id=23 */ public static Cloner shared() { return new Cloner(new ObjenesisInstantiationStrategy()); diff --git a/src/com/rits/cloning/CloningException.java b/src/com/rits/cloning/CloningException.java index 09b26786..31873f6a 100644 --- a/src/com/rits/cloning/CloningException.java +++ b/src/com/rits/cloning/CloningException.java @@ -5,14 +5,12 @@ package com.rits.cloning; * * @author kostantinos.kougios * - * 18 Jan 2009 + * 18 Jan 2009 */ -public class CloningException extends RuntimeException -{ - private static final long serialVersionUID = 3815175312001146867L; +public class CloningException extends RuntimeException { + private static final long serialVersionUID = 3815175312001146867L; - public CloningException(final String message, final Throwable cause) - { + public CloningException(final String message, final Throwable cause) { super(message, cause); } diff --git a/src/com/rits/cloning/FastClonerArrayList.java b/src/com/rits/cloning/FastClonerArrayList.java index 51a67783..33d98e5b 100644 --- a/src/com/rits/cloning/FastClonerArrayList.java +++ b/src/com/rits/cloning/FastClonerArrayList.java @@ -6,18 +6,17 @@ import java.util.Map; /** * @author kostantinos.kougios * - * 21 May 2009 + * 21 May 2009 */ -public class FastClonerArrayList implements IFastCloner -{ +public class FastClonerArrayList implements IFastCloner { + @Override @SuppressWarnings({ "unchecked", "rawtypes" }) - public Object clone(final Object t, final IDeepCloner cloner, final Map clones) { + public Object clone(final Object t, final IDeepCloner cloner, final Map clones) { final ArrayList al = (ArrayList) t; final ArrayList l = new ArrayList(al.size()); - for (final Object o : al) - { - final Object cloneInternal = cloner.deepClone(o, clones); - l.add(cloneInternal); + for (final Object o : al) { + final Object cloneInternal = cloner.deepClone(o, clones); + l.add(cloneInternal); } return l; } diff --git a/src/com/rits/cloning/FastClonerCalendar.java b/src/com/rits/cloning/FastClonerCalendar.java index 80c6b45a..2f77aac8 100644 --- a/src/com/rits/cloning/FastClonerCalendar.java +++ b/src/com/rits/cloning/FastClonerCalendar.java @@ -8,11 +8,11 @@ import java.util.TimeZone; /** * @author kostantinos.kougios * - * 21 May 2009 + * 21 May 2009 */ -public class FastClonerCalendar implements IFastCloner -{ - public Object clone(final Object t, final IDeepCloner cloner, final Map clones) { +public class FastClonerCalendar implements IFastCloner { + @Override + public Object clone(final Object t, final IDeepCloner cloner, final Map clones) { final GregorianCalendar gc = new GregorianCalendar(); Calendar c = (Calendar) t; gc.setTimeInMillis(c.getTimeInMillis()); diff --git a/src/com/rits/cloning/FastClonerConcurrentHashMap.java b/src/com/rits/cloning/FastClonerConcurrentHashMap.java index 55fa7691..41771cde 100644 --- a/src/com/rits/cloning/FastClonerConcurrentHashMap.java +++ b/src/com/rits/cloning/FastClonerConcurrentHashMap.java @@ -6,18 +6,17 @@ import java.util.concurrent.ConcurrentHashMap; /** * @author kostantinos.kougios * - * 18 Oct 2011 + * 18 Oct 2011 */ -public class FastClonerConcurrentHashMap implements IFastCloner -{ +public class FastClonerConcurrentHashMap implements IFastCloner { + @Override @SuppressWarnings({ "unchecked", "rawtypes" }) - public Object clone(final Object t, final IDeepCloner cloner, final Map clones) { + public Object clone(final Object t, final IDeepCloner cloner, final Map clones) { final ConcurrentHashMap m = (ConcurrentHashMap) t; final ConcurrentHashMap result = new ConcurrentHashMap(); - for (final Map.Entry e : m.entrySet()) - { - final Object key = cloner.deepClone(e.getKey(), clones); - final Object value = cloner.deepClone(e.getValue(), clones); + for (final Map.Entry e : m.entrySet()) { + final Object key = cloner.deepClone(e.getKey(), clones); + final Object value = cloner.deepClone(e.getValue(), clones); result.put(key, value); } diff --git a/src/com/rits/cloning/FastClonerCustomCollection.java b/src/com/rits/cloning/FastClonerCustomCollection.java index 48a6db55..d7470775 100644 --- a/src/com/rits/cloning/FastClonerCustomCollection.java +++ b/src/com/rits/cloning/FastClonerCustomCollection.java @@ -6,20 +6,19 @@ import java.util.Map; /** * @author kostantinos.kougios * - * 21 May 2009 + * 21 May 2009 */ @SuppressWarnings({ "unchecked", "rawtypes" }) -public abstract class FastClonerCustomCollection implements IFastCloner -{ +public abstract class FastClonerCustomCollection implements IFastCloner { public abstract T getInstance(T o); - public Object clone(final Object t, final IDeepCloner cloner, final Map clones) { + @Override + public Object clone(final Object t, final IDeepCloner cloner, final Map clones) { final T c = getInstance((T) t); final T l = (T) t; - for (final Object o : l) - { - final Object clone = cloner.deepClone(o, clones); - c.add(clone); + for (final Object o : l) { + final Object clone = cloner.deepClone(o, clones); + c.add(clone); } return c; } diff --git a/src/com/rits/cloning/FastClonerCustomMap.java b/src/com/rits/cloning/FastClonerCustomMap.java index 8454b3fc..bbf54b5a 100644 --- a/src/com/rits/cloning/FastClonerCustomMap.java +++ b/src/com/rits/cloning/FastClonerCustomMap.java @@ -6,20 +6,19 @@ import java.util.Set; /** * @author kostantinos.kougios * - * 21 May 2009 + * 21 May 2009 */ @SuppressWarnings({ "unchecked", "rawtypes" }) -public abstract class FastClonerCustomMap implements IFastCloner -{ - public Object clone(final Object t, final IDeepCloner cloner, final Map clones) { +public abstract class FastClonerCustomMap implements IFastCloner { + @Override + public Object clone(final Object t, final IDeepCloner cloner, final Map clones) { final T m = (T) t; final T result = getInstance((T) t); final Set> entrySet = m.entrySet(); - for (final Map.Entry e : entrySet) - { - final Object key = cloner.deepClone(e.getKey(), clones); - final Object value = cloner.deepClone(e.getValue(), clones); - result.put(key, value); + for (final Map.Entry e : entrySet) { + final Object key = cloner.deepClone(e.getKey(), clones); + final Object value = cloner.deepClone(e.getValue(), clones); + result.put(key, value); } return result; } diff --git a/src/com/rits/cloning/FastClonerHashMap.java b/src/com/rits/cloning/FastClonerHashMap.java index 09d0a4b3..7cc8b695 100644 --- a/src/com/rits/cloning/FastClonerHashMap.java +++ b/src/com/rits/cloning/FastClonerHashMap.java @@ -6,18 +6,17 @@ import java.util.Map; /** * @author kostantinos.kougios * - * 21 May 2009 + * 21 May 2009 */ -public class FastClonerHashMap implements IFastCloner -{ +public class FastClonerHashMap implements IFastCloner { + @Override @SuppressWarnings({ "unchecked", "rawtypes" }) - public Object clone(final Object t, final IDeepCloner cloner, final Map clones) { + public Object clone(final Object t, final IDeepCloner cloner, final Map clones) { final HashMap m = (HashMap) t; final HashMap result = new HashMap(); - for (final Map.Entry e : m.entrySet()) - { - final Object key = cloner.deepClone(e.getKey(), clones); - final Object value = cloner.deepClone(e.getValue(), clones); + for (final Map.Entry e : m.entrySet()) { + final Object key = cloner.deepClone(e.getKey(), clones); + final Object value = cloner.deepClone(e.getValue(), clones); result.put(key, value); } diff --git a/src/com/rits/cloning/FastClonerHashSet.java b/src/com/rits/cloning/FastClonerHashSet.java index 94aa127e..f8e145b4 100644 --- a/src/com/rits/cloning/FastClonerHashSet.java +++ b/src/com/rits/cloning/FastClonerHashSet.java @@ -6,18 +6,17 @@ import java.util.Map; /** * @author kostantinos.kougios * - * 21 May 2009 + * 21 May 2009 */ -public class FastClonerHashSet implements IFastCloner -{ +public class FastClonerHashSet implements IFastCloner { + @Override @SuppressWarnings({ "unchecked", "rawtypes" }) - public Object clone(final Object t, final IDeepCloner cloner, final Map clones) { + public Object clone(final Object t, final IDeepCloner cloner, final Map clones) { final HashSet al = (HashSet) t; final HashSet l = new HashSet(); - for (final Object o : al) - { - final Object cloneInternal = cloner.deepClone(o, clones); - l.add(cloneInternal); + for (final Object o : al) { + final Object cloneInternal = cloner.deepClone(o, clones); + l.add(cloneInternal); } return l; } diff --git a/src/com/rits/cloning/FastClonerLinkedList.java b/src/com/rits/cloning/FastClonerLinkedList.java index 488289e7..99c3ad9d 100644 --- a/src/com/rits/cloning/FastClonerLinkedList.java +++ b/src/com/rits/cloning/FastClonerLinkedList.java @@ -6,18 +6,17 @@ import java.util.Map; /** * @author kostantinos.kougios * - * 21 May 2009 + * 21 May 2009 */ -public class FastClonerLinkedList implements IFastCloner -{ +public class FastClonerLinkedList implements IFastCloner { + @Override @SuppressWarnings({ "unchecked", "rawtypes" }) - public Object clone(final Object t, final IDeepCloner cloner, final Map clones) { + public Object clone(final Object t, final IDeepCloner cloner, final Map clones) { final LinkedList al = (LinkedList) t; final LinkedList l = new LinkedList(); - for (final Object o : al) - { - final Object cloneInternal = cloner.deepClone(o, clones); - l.add(cloneInternal); + for (final Object o : al) { + final Object cloneInternal = cloner.deepClone(o, clones); + l.add(cloneInternal); } return l; } diff --git a/src/com/rits/cloning/FastClonerTreeMap.java b/src/com/rits/cloning/FastClonerTreeMap.java index c545a627..21946560 100644 --- a/src/com/rits/cloning/FastClonerTreeMap.java +++ b/src/com/rits/cloning/FastClonerTreeMap.java @@ -6,19 +6,18 @@ import java.util.TreeMap; /** * @author kostantinos.kougios * - * 21 May 2009 + * 21 May 2009 */ -public class FastClonerTreeMap implements IFastCloner -{ +public class FastClonerTreeMap implements IFastCloner { + @Override @SuppressWarnings({ "unchecked", "rawtypes" }) - public Object clone(final Object t, final IDeepCloner cloner, final Map clones) { + public Object clone(final Object t, final IDeepCloner cloner, final Map clones) { final TreeMap m = (TreeMap) t; final TreeMap result = new TreeMap(m.comparator()); - for (final Map.Entry e : m.entrySet()) - { - final Object key = cloner.deepClone(e.getKey(), clones); - final Object value = cloner.deepClone(e.getValue(), clones); - result.put(key, value); + for (final Map.Entry e : m.entrySet()) { + final Object key = cloner.deepClone(e.getKey(), clones); + final Object value = cloner.deepClone(e.getValue(), clones); + result.put(key, value); } return result; } diff --git a/src/com/rits/cloning/IDeepCloner.java b/src/com/rits/cloning/IDeepCloner.java index bb1867b2..27ae31fc 100644 --- a/src/com/rits/cloning/IDeepCloner.java +++ b/src/com/rits/cloning/IDeepCloner.java @@ -8,13 +8,16 @@ import java.util.Map; * @author kostas.kougios Date 24/06/14 */ public interface IDeepCloner { - /** - * deep clones o - * - * @param o the object to be deep cloned - * @param clones pass on the same map from IFastCloner - * @param the type of o - * @return a clone of o - */ - T deepClone(final T o, final Map clones); + /** + * deep clones o + * + * @param o + * the object to be deep cloned + * @param clones + * pass on the same map from IFastCloner + * @param + * the type of o + * @return a clone of o + */ + T deepClone(final T o, final Map clones); } diff --git a/src/com/rits/cloning/IDumpCloned.java b/src/com/rits/cloning/IDumpCloned.java index 92358917..7b8ede3c 100644 --- a/src/com/rits/cloning/IDumpCloned.java +++ b/src/com/rits/cloning/IDumpCloned.java @@ -4,10 +4,9 @@ import java.lang.reflect.Field; /** * @author: kostas.kougios - * Date: 06/08/13 + * Date: 06/08/13 */ -public interface IDumpCloned -{ +public interface IDumpCloned { void startCloning(Class clz); void cloning(Field field, Class clz); diff --git a/src/com/rits/cloning/IFastCloner.java b/src/com/rits/cloning/IFastCloner.java index 1c0ddd6c..f0f9a240 100644 --- a/src/com/rits/cloning/IFastCloner.java +++ b/src/com/rits/cloning/IFastCloner.java @@ -7,8 +7,8 @@ import java.util.Map; * (it has to be registered with Cloner) * * @author kostantinos.kougios - * 21 May 2009 + * 21 May 2009 */ public interface IFastCloner { - public Object clone(Object t, IDeepCloner cloner, Map clones); + public Object clone(Object t, IDeepCloner cloner, Map clones); } diff --git a/src/com/rits/cloning/IFreezable.java b/src/com/rits/cloning/IFreezable.java index 73085831..b05beddc 100644 --- a/src/com/rits/cloning/IFreezable.java +++ b/src/com/rits/cloning/IFreezable.java @@ -3,10 +3,9 @@ package com.rits.cloning; /** * @author kostantinos.kougios * - * 15 Nov 2010 + * 15 Nov 2010 */ -public interface IFreezable -{ +public interface IFreezable { public boolean isFrozen(); } diff --git a/src/com/rits/cloning/IInstantiationStrategy.java b/src/com/rits/cloning/IInstantiationStrategy.java index acafb732..8b451411 100644 --- a/src/com/rits/cloning/IInstantiationStrategy.java +++ b/src/com/rits/cloning/IInstantiationStrategy.java @@ -3,9 +3,8 @@ package com.rits.cloning; /** * @author kostantinos.kougios * - * 17 Jul 2012 + * 17 Jul 2012 */ -public interface IInstantiationStrategy -{ +public interface IInstantiationStrategy { T newInstance(final Class c); } diff --git a/src/com/rits/cloning/Immutable.java b/src/com/rits/cloning/Immutable.java index 16347f0e..1ade2459 100644 --- a/src/com/rits/cloning/Immutable.java +++ b/src/com/rits/cloning/Immutable.java @@ -11,15 +11,17 @@ import java.lang.annotation.Target; * * @author kostantinos.kougios * - * 24 Mar 2011 + * 24 Mar 2011 */ @Target(TYPE) @Retention(RUNTIME) -public @interface Immutable -{ +public @interface Immutable { /** - * by default all subclasses of the @Immutable class are not immutable. This can override it. - * @return true for subclasses of @Immutable class to be regarded as immutable from the cloner + * by default all subclasses of the @Immutable class are not immutable. This + * can override it. + * + * @return true for subclasses of @Immutable class to be regarded as + * immutable from the cloner */ boolean subClass() default false; } diff --git a/src/com/rits/cloning/ObjenesisInstantiationStrategy.java b/src/com/rits/cloning/ObjenesisInstantiationStrategy.java index d1457fed..baf7a386 100644 --- a/src/com/rits/cloning/ObjenesisInstantiationStrategy.java +++ b/src/com/rits/cloning/ObjenesisInstantiationStrategy.java @@ -6,21 +6,19 @@ import org.objenesis.ObjenesisStd; /** * @author kostantinos.kougios * - * 17 Jul 2012 + * 17 Jul 2012 */ -public class ObjenesisInstantiationStrategy implements IInstantiationStrategy -{ - private final Objenesis objenesis = new ObjenesisStd(); +public class ObjenesisInstantiationStrategy implements IInstantiationStrategy { + private final Objenesis objenesis = new ObjenesisStd(); - public T newInstance(Class c) - { + @Override + public T newInstance(Class c) { return objenesis.newInstance(c); } - private static ObjenesisInstantiationStrategy instance = new ObjenesisInstantiationStrategy(); + private static ObjenesisInstantiationStrategy instance = new ObjenesisInstantiationStrategy(); - public static ObjenesisInstantiationStrategy getInstance() - { + public static ObjenesisInstantiationStrategy getInstance() { return instance; } } diff --git a/src/com/rits/perspectives/Perspectives.java b/src/com/rits/perspectives/Perspectives.java index 03383412..681e1de9 100644 --- a/src/com/rits/perspectives/Perspectives.java +++ b/src/com/rits/perspectives/Perspectives.java @@ -5,66 +5,85 @@ import java.util.Collection; import com.rits.cloning.Cloner; /** - * Perspectives: an object instance of a class behaving differently according to the "view angle". - * + * Perspectives: an object instance of a class behaving differently according to + * the "view angle". + * * @author kostantinos.kougios * - * 30 Nov 2009 + * 30 Nov 2009 */ -public class Perspectives -{ - private final Cloner cloner; +public class Perspectives { + private final Cloner cloner; - public Perspectives(final Cloner cloner) - { + public Perspectives(final Cloner cloner) { this.cloner = cloner; } /** - * Sample: if o is an instance of Product and c is OrderedProduct.class then this returns - * and instance of OrderedProduct.class which has equal field values to those of the instance of Product. - * In other words, the returned instance of OrderedProduct.class is the Product instance from the perspective + * Sample: if o is an instance of Product and c is OrderedProduct.class then + * this returns + * and instance of OrderedProduct.class which has equal field values to + * those of the instance of Product. + * In other words, the returned instance of OrderedProduct.class is the + * Product instance from the perspective * of an OrderedProduct * - * View an object o from the perspective of class c. (view o as an instance of c). c must be instanceof o.getClass() + * View an object o from the perspective of class c. (view o as an instance + * of c). c must be instanceof o.getClass() * - * @param the object - * @param this will be the returned type and it must be instanceof T. All properties of o will be copied to this instance. - * @param c the class of E. This is used to generate new instances of c - * @param o the object that must be viewed from a different perspective - * @return the E perspective of o + * @param + * the object + * @param + * this will be the returned type and it must be instanceof T. + * All properties of o will be copied to this instance. + * @param c + * the class of E. This is used to generate new instances of c + * @param o + * the object that must be viewed from a different perspective + * @return the E perspective of o */ - public E viewAs(final Class c, final T o) - { - if (o == null) return null; - if (o instanceof Collection) throw new IllegalArgumentException("for collections please use viewCollectionAs() method. Invalid object " + o); + public E viewAs(final Class c, final T o) { + if (o == null) + return null; + if (o instanceof Collection) + throw new IllegalArgumentException("for collections please use viewCollectionAs() method. Invalid object " + o); final E newInstance = cloner.fastCloneOrNewInstance(c); cloner.copyPropertiesOfInheritedClass(o, newInstance); return newInstance; } /** - * Sample: if o is a [ Products extends LinkedList ] then the returned instance + * Sample: if o is a [ Products extends LinkedList ] then the + * returned instance * is a [ OrderedProducts extends LinkedList ]. * * View a collection o from the perspective of collection E. * - * NOTE: order of the items might not be preserved, depending on the collection type + * NOTE: order of the items might not be preserved, depending on the + * collection type * - * @param the type of the collection o - * @param the type of the elements of the collection o - * @param the type of the perspective collection - * @param the type of the perspective's elements - * @param newCollection the collection to which the adapted instances should be added - * @param currentCollection the collection with the instances to be adapted - * @param perspectiveCollectionItemClass the class of the NI - * @return E, the collection from a different perspective or null if currentCollection is null + * @param + * the type of the collection o + * @param + * the type of the elements of the collection o + * @param + * the type of the perspective collection + * @param + * the type of the perspective's elements + * @param newCollection + * the collection to which the adapted instances should be added + * @param currentCollection + * the collection with the instances to be adapted + * @param perspectiveCollectionItemClass + * the class of the NI + * @return E, the collection from a different perspective or null if + * currentCollection is null */ - public , E extends Collection> E viewCollectionAs(final E newCollection, final Class perspectiveCollectionItemClass, final T currentCollection) - { - if (currentCollection == null) return null; - for (final I item : currentCollection) - { + public , E extends Collection> E viewCollectionAs( + final E newCollection, final Class perspectiveCollectionItemClass, final T currentCollection) { + if (currentCollection == null) + return null; + for (final I item : currentCollection) { final NI newItem = viewAs(perspectiveCollectionItemClass, item); newCollection.add(newItem); } diff --git a/src/org/nevec/rjm/Bernoulli.java b/src/org/nevec/rjm/Bernoulli.java index 1563c6cf..a2a61666 100644 --- a/src/org/nevec/rjm/Bernoulli.java +++ b/src/org/nevec/rjm/Bernoulli.java @@ -1,99 +1,99 @@ -package org.nevec.rjm ; +package org.nevec.rjm; import java.math.BigInteger; import java.util.Vector; -import org.warpgate.pi.calculator.Errore; +import org.warp.picalculator.Errore; +/** + * Bernoulli numbers. + * + * @since 2006-06-25 + * @author Richard J. Mathar + */ +public class Bernoulli { + /* + * The list of all Bernoulli numbers as a vector, n=0,2,4,.... + */ + static Vector a = new Vector(); -/** Bernoulli numbers. -* @since 2006-06-25 -* @author Richard J. Mathar -*/ -public class Bernoulli -{ - /* - * The list of all Bernoulli numbers as a vector, n=0,2,4,.... - */ - static Vector a = new Vector() ; + public Bernoulli() { + if (a.size() == 0) { + a.add(Rational.ONE); + a.add(new Rational(1, 6)); + } + } - public Bernoulli() - { - if ( a.size() == 0 ) - { - a.add(Rational.ONE) ; - a.add(new Rational(1,6)) ; - } - } + /** + * Set a coefficient in the internal table. + * + * @param n + * the zero-based index of the coefficient. n=0 for the constant + * term. + * @param value + * the new value of the coefficient. + */ + protected void set(final int n, final Rational value) { + final int nindx = n / 2; + if (nindx < a.size()) + a.set(nindx, value); + else { + while (a.size() < nindx) + a.add(Rational.ZERO); + a.add(value); + } + } - /** Set a coefficient in the internal table. - * @param n the zero-based index of the coefficient. n=0 for the constant term. - * @param value the new value of the coefficient. - */ - protected void set(final int n, final Rational value) - { - final int nindx = n /2 ; - if ( nindx < a.size()) - a.set(nindx,value) ; - else - { - while ( a.size() < nindx ) - a.add( Rational.ZERO ) ; - a.add(value) ; - } - } - - /** The Bernoulli number at the index provided. - * @param n the index, non-negative. - * @return the B_0=1 for n=0, B_1=-1/2 for n=1, B_2=1/6 for n=2 etc - * @throws Errore - */ - public Rational at(int n) throws Errore - { - if ( n == 1) - return(new Rational(-1,2)) ; - else if ( n % 2 != 0 ) - return Rational.ZERO ; - else - { - final int nindx = n /2 ; - if( a.size() <= nindx ) - { - for(int i= 2*a.size() ; i <= n; i+= 2) - set(i, doubleSum(i) ) ; - } - return a.elementAt(nindx) ; - } - } - - /* Generate a new B_n by a standard double sum. - * @param n The index of the Bernoulli number. - * @return The Bernoulli number at n. - */ - private Rational doubleSum(int n) throws Errore - { - Rational resul = Rational.ZERO ; - for(int k=0 ; k <= n ; k++) - { - Rational jsum = Rational.ZERO ; - BigInteger bin = BigInteger.ONE ; - for(int j=0 ; j <= k ; j++) - { - BigInteger jpown = (new BigInteger(""+j)).pow(n); - if ( j % 2 == 0) - jsum = jsum.add(bin.multiply(jpown)) ; - else - jsum = jsum.subtract(bin.multiply(jpown)) ; - - /* update binomial(k,j) recursively - */ - bin = bin.multiply( new BigInteger(""+(k-j))). divide( new BigInteger(""+(j+1)) ) ; - } - resul = resul.add(jsum.divide(new BigInteger(""+(k+1)))) ; - } - return resul ; - } + /** + * The Bernoulli number at the index provided. + * + * @param n + * the index, non-negative. + * @return the B_0=1 for n=0, B_1=-1/2 for n=1, B_2=1/6 for n=2 etc + * @throws Errore + */ + public Rational at(int n) throws Errore { + if (n == 1) + return (new Rational(-1, 2)); + else if (n % 2 != 0) + return Rational.ZERO; + else { + final int nindx = n / 2; + if (a.size() <= nindx) { + for (int i = 2 * a.size(); i <= n; i += 2) + set(i, doubleSum(i)); + } + return a.elementAt(nindx); + } + } + /* + * Generate a new B_n by a standard double sum. + * + * @param n The index of the Bernoulli number. + * + * @return The Bernoulli number at n. + */ + private Rational doubleSum(int n) throws Errore { + Rational resul = Rational.ZERO; + for (int k = 0; k <= n; k++) { + Rational jsum = Rational.ZERO; + BigInteger bin = BigInteger.ONE; + for (int j = 0; j <= k; j++) { + BigInteger jpown = (new BigInteger("" + j)).pow(n); + if (j % 2 == 0) + jsum = jsum.add(bin.multiply(jpown)); + else + jsum = jsum.subtract(bin.multiply(jpown)); + /* + * update binomial(k,j) recursively + */ + bin = bin.multiply(new BigInteger("" + (k - j))).divide(new BigInteger("" + (j + 1))); + } + resul = resul.add(jsum.divide(new BigInteger("" + (k + 1)))); + } + return resul; + } } /* Bernoulli */ diff --git a/src/org/nevec/rjm/BigComplex.java b/src/org/nevec/rjm/BigComplex.java index 706df1c8..c1a19b16 100644 --- a/src/org/nevec/rjm/BigComplex.java +++ b/src/org/nevec/rjm/BigComplex.java @@ -1,188 +1,218 @@ -package org.nevec.rjm ; +package org.nevec.rjm; import java.math.BigDecimal; import java.math.MathContext; +/** + * Complex numbers with BigDecimal real and imaginary components + * + * @since 2008-10-26 + * @author Richard J. Mathar + */ +public class BigComplex { + /** + * real part + */ + BigDecimal re; -/** Complex numbers with BigDecimal real and imaginary components -* @since 2008-10-26 -* @author Richard J. Mathar -*/ -public class BigComplex -{ - /** real part - */ - BigDecimal re ; + /** + * imaginary part + */ + BigDecimal im; - /** imaginary part - */ - BigDecimal im ; + /** + * The constant that equals zero + */ + final static BigComplex ZERO = new BigComplex(BigDecimal.ZERO, BigDecimal.ZERO); - /** The constant that equals zero - */ - final static BigComplex ZERO = new BigComplex(BigDecimal.ZERO, BigDecimal.ZERO) ; + /** + * Default ctor equivalent to zero. + */ + public BigComplex() { + re = BigDecimal.ZERO; + im = BigDecimal.ZERO; + } - /** Default ctor equivalent to zero. - */ - public BigComplex() - { - re= BigDecimal.ZERO ; - im= BigDecimal.ZERO ; - } + /** + * ctor with real and imaginary parts + * + * @param x + * real part + * @param y + * imaginary part + */ + public BigComplex(BigDecimal x, BigDecimal y) { + re = x; + im = y; + } - /** ctor with real and imaginary parts - * @param x real part - * @param y imaginary part - */ - public BigComplex( BigDecimal x, BigDecimal y) - { - re=x ; - im=y ; - } + /** + * ctor with real part. + * + * @param x + * real part. + * The imaginary part is set to zero. + */ + public BigComplex(BigDecimal x) { + re = x; + im = BigDecimal.ZERO; + } - /** ctor with real part. - * @param x real part. - * The imaginary part is set to zero. - */ - public BigComplex( BigDecimal x ) - { - re=x ; - im= BigDecimal.ZERO ; - } + /** + * ctor with real and imaginary parts + * + * @param x + * real part + * @param y + * imaginary part + */ + public BigComplex(double x, double y) { + re = new BigDecimal(x); + im = new BigDecimal(y); + } - /** ctor with real and imaginary parts - * @param x real part - * @param y imaginary part - */ - public BigComplex( double x, double y) - { - re= new BigDecimal(x) ; - im= new BigDecimal(y) ; - } + /** + * Multiply with another BigComplex + * + * @param oth + * The BigComplex which is a factor in the product + * @param mc + * Defining precision and rounding mode + * @return This multiplied by oth + * @since 2010-07-19 implemented with 3 multiplications and 5 + * additions/subtractions + */ + BigComplex multiply(final BigComplex oth, MathContext mc) { + final BigDecimal a = re.add(im).multiply(oth.re); + final BigDecimal b = oth.re.add(oth.im).multiply(im); + final BigDecimal c = oth.im.subtract(oth.re).multiply(re); + final BigDecimal x = a.subtract(b, mc); + final BigDecimal y = a.add(c, mc); + return new BigComplex(x, y); + } - /** Multiply with another BigComplex - * @param oth The BigComplex which is a factor in the product - * @param mc Defining precision and rounding mode - * @return This multiplied by oth - * @since 2010-07-19 implemented with 3 multiplications and 5 additions/subtractions - */ - BigComplex multiply(final BigComplex oth, MathContext mc) - { - final BigDecimal a = re.add(im).multiply(oth.re) ; - final BigDecimal b = oth.re.add(oth.im).multiply(im) ; - final BigDecimal c = oth.im.subtract(oth.re).multiply(re) ; - final BigDecimal x = a.subtract(b,mc) ; - final BigDecimal y = a.add(c,mc) ; - return new BigComplex(x,y) ; - } + /** + * Add a BigDecimal + * + * @param oth + * the value to be added to the real part. + * @return this added to oth + */ + BigComplex add(final BigDecimal oth) { + final BigDecimal x = re.add(oth); + return new BigComplex(x, im); + } - /** Add a BigDecimal - * @param oth the value to be added to the real part. - * @return this added to oth - */ - BigComplex add(final BigDecimal oth) - { - final BigDecimal x = re.add(oth) ; - return new BigComplex(x,im) ; - } + /** + * Subtract another BigComplex + * + * @param oth + * the value to be subtracted from this. + * @return this minus oth + */ + BigComplex subtract(final BigComplex oth) { + final BigDecimal x = re.subtract(oth.re); + final BigDecimal y = im.subtract(oth.im); + return new BigComplex(x, y); + } - /** Subtract another BigComplex - * @param oth the value to be subtracted from this. - * @return this minus oth - */ - BigComplex subtract(final BigComplex oth) - { - final BigDecimal x = re.subtract(oth.re) ; - final BigDecimal y = im.subtract(oth.im) ; - return new BigComplex(x,y) ; - } + /** + * Complex-conjugation + * + * @return the complex conjugate of this. + */ + BigComplex conj() { + return new BigComplex(re, im.negate()); + } - /** Complex-conjugation - * @return the complex conjugate of this. - */ - BigComplex conj() - { - return new BigComplex(re,im.negate()) ; - } + /** + * The absolute value squared. + * + * @return The sum of the squares of real and imaginary parts. + * This is the square of BigComplex.abs() . + */ + BigDecimal norm() { + return re.multiply(re).add(im.multiply(im)); + } - /** The absolute value squared. - * @return The sum of the squares of real and imaginary parts. - * This is the square of BigComplex.abs() . - */ - BigDecimal norm() - { - return re.multiply(re).add(im.multiply(im)) ; - } + /** + * The absolute value. + * + * @return the square root of the sum of the squares of real and imaginary + * parts. + * @since 2008-10-27 + */ + BigDecimal abs(MathContext mc) { + return BigDecimalMath.sqrt(norm(), mc); + } - /** The absolute value. - * @return the square root of the sum of the squares of real and imaginary parts. - * @since 2008-10-27 - */ - BigDecimal abs(MathContext mc) - { - return BigDecimalMath.sqrt(norm(),mc) ; - } + /** + * The square root. + * + * @return the square root of the this. + * The branch is chosen such that the imaginary part of the result + * has the + * same sign as the imaginary part of this. + * @see Tim Ahrendt, Fast + * High-precision computation of complex square roots, + * ISSAC 1996 p142-149. + * @since 2008-10-27 + */ + BigComplex sqrt(MathContext mc) { + final BigDecimal half = new BigDecimal("2"); + /* + * compute l=sqrt(re^2+im^2), then u=sqrt((l+re)/2) + * and v= +- sqrt((l-re)/2 as the new real and imaginary parts. + */ + final BigDecimal l = abs(mc); + if (l.compareTo(BigDecimal.ZERO) == 0) + return new BigComplex(BigDecimalMath.scalePrec(BigDecimal.ZERO, mc), BigDecimalMath.scalePrec(BigDecimal.ZERO, mc)); + final BigDecimal u = BigDecimalMath.sqrt(l.add(re).divide(half, mc), mc); + final BigDecimal v = BigDecimalMath.sqrt(l.subtract(re).divide(half, mc), mc); + if (im.compareTo(BigDecimal.ZERO) >= 0) + return new BigComplex(u, v); + else + return new BigComplex(u, v.negate()); + } - /** The square root. - * @return the square root of the this. - * The branch is chosen such that the imaginary part of the result has the - * same sign as the imaginary part of this. - * @see Tim Ahrendt, Fast High-precision computation of complex square roots, - * ISSAC 1996 p142-149. - * @since 2008-10-27 - */ - BigComplex sqrt(MathContext mc) - { - final BigDecimal half = new BigDecimal("2") ; - /* compute l=sqrt(re^2+im^2), then u=sqrt((l+re)/2) - * and v= +- sqrt((l-re)/2 as the new real and imaginary parts. - */ - final BigDecimal l = abs(mc) ; - if ( l.compareTo(BigDecimal.ZERO) == 0 ) - return new BigComplex( BigDecimalMath.scalePrec(BigDecimal.ZERO,mc), - BigDecimalMath.scalePrec(BigDecimal.ZERO,mc) ) ; - final BigDecimal u = BigDecimalMath.sqrt( l.add(re).divide(half,mc), mc ); - final BigDecimal v = BigDecimalMath.sqrt( l.subtract(re).divide(half,mc), mc ); - if ( im.compareTo(BigDecimal.ZERO)>= 0 ) - return new BigComplex(u,v) ; - else - return new BigComplex(u,v.negate()) ; - } + /** + * The inverse of this. + * + * @return 1/this + */ + BigComplex inverse(MathContext mc) { + final BigDecimal hyp = norm(); + /* 1/(x+iy)= (x-iy)/(x^2+y^2 */ + return new BigComplex(re.divide(hyp, mc), im.divide(hyp, mc).negate()); + } - /** The inverse of this. - * @return 1/this - */ - BigComplex inverse(MathContext mc) - { - final BigDecimal hyp = norm() ; - /* 1/(x+iy)= (x-iy)/(x^2+y^2 */ - return new BigComplex( re.divide(hyp,mc), im.divide(hyp,mc).negate() ) ; - } + /** + * Divide through another BigComplex number. + * + * @return this/oth + */ + BigComplex divide(BigComplex oth, MathContext mc) { + /* lazy implementation: (x+iy)/(a+ib)= (x+iy)* 1/(a+ib) */ + return multiply(oth.inverse(mc), mc); + } - /** Divide through another BigComplex number. - * @return this/oth - */ - BigComplex divide(BigComplex oth, MathContext mc) - { - /* lazy implementation: (x+iy)/(a+ib)= (x+iy)* 1/(a+ib) */ - return multiply(oth.inverse(mc),mc) ; - } - - /** Human-readable Fortran-type display - * @return real and imaginary part in parenthesis, divided by a comma. - */ - public String toString() - { - return "("+re.toString()+","+im.toString()+")" ; - } - - /** Human-readable Fortran-type display - * @return real and imaginary part in parenthesis, divided by a comma. - */ - public String toString(MathContext mc) - { - return "("+re.round(mc).toString()+","+im.round(mc).toString()+")" ; - } + /** + * Human-readable Fortran-type display + * + * @return real and imaginary part in parenthesis, divided by a comma. + */ + @Override + public String toString() { + return "(" + re.toString() + "," + im.toString() + ")"; + } + /** + * Human-readable Fortran-type display + * + * @return real and imaginary part in parenthesis, divided by a comma. + */ + public String toString(MathContext mc) { + return "(" + re.round(mc).toString() + "," + im.round(mc).toString() + ")"; + } } /* BigComplex */ diff --git a/src/org/nevec/rjm/BigDecimalMath.java b/src/org/nevec/rjm/BigDecimalMath.java index 7ff85d5f..0c6407e5 100644 --- a/src/org/nevec/rjm/BigDecimalMath.java +++ b/src/org/nevec/rjm/BigDecimalMath.java @@ -1,2779 +1,3003 @@ -package org.nevec.rjm ; +package org.nevec.rjm; import java.math.BigDecimal; import java.math.BigInteger; import java.math.MathContext; import java.security.ProviderException; -import org.warpgate.pi.calculator.Errore; - - -/** BigDecimal special functions. -* A Java Math.BigDecimal Implementation of Core Mathematical Functions -* @since 2009-05-22 -* @author Richard J. Mathar -* @see apfloat -* @see dfp -* @see JScience -*/ -public class BigDecimalMath -{ - - /** The base of the natural logarithm in a predefined accuracy. - * http://www.cs.arizona.edu/icon/oddsends/e.htm - * The precision of the predefined constant is one less than - * the string's length, taking into account the decimal dot. - * static int E_PRECISION = E.length()-1 ; - */ - static BigDecimal E = new BigDecimal("2.71828182845904523536028747135266249775724709369995957496696762772407663035354"+ -"759457138217852516642742746639193200305992181741359662904357290033429526059563"+ -"073813232862794349076323382988075319525101901157383418793070215408914993488416"+ -"750924476146066808226480016847741185374234544243710753907774499206955170276183"+ -"860626133138458300075204493382656029760673711320070932870912744374704723069697"+ -"720931014169283681902551510865746377211125238978442505695369677078544996996794"+ -"686445490598793163688923009879312773617821542499922957635148220826989519366803"+ -"318252886939849646510582093923982948879332036250944311730123819706841614039701"+ -"983767932068328237646480429531180232878250981945581530175671736133206981125099"+ -"618188159304169035159888851934580727386673858942287922849989208680582574927961"+ -"048419844436346324496848756023362482704197862320900216099023530436994184914631"+ -"409343173814364054625315209618369088870701676839642437814059271456354906130310"+ -"720851038375051011574770417189861068739696552126715468895703503540212340784981"+ -"933432106817012100562788023519303322474501585390473041995777709350366041699732"+ -"972508868769664035557071622684471625607988265178713419512466520103059212366771"+ -"943252786753985589448969709640975459185695638023637016211204774272283648961342"+ -"251644507818244235294863637214174023889344124796357437026375529444833799801612"+ -"549227850925778256209262264832627793338656648162772516401910590049164499828931") ; - - /** Euler's constant Pi. - * http://www.cs.arizona.edu/icon/oddsends/pi.htm - */ - static BigDecimal PI = new BigDecimal("3.14159265358979323846264338327950288419716939937510582097494459230781640628620"+ -"899862803482534211706798214808651328230664709384460955058223172535940812848111"+ -"745028410270193852110555964462294895493038196442881097566593344612847564823378"+ -"678316527120190914564856692346034861045432664821339360726024914127372458700660"+ -"631558817488152092096282925409171536436789259036001133053054882046652138414695"+ -"194151160943305727036575959195309218611738193261179310511854807446237996274956"+ -"735188575272489122793818301194912983367336244065664308602139494639522473719070"+ -"217986094370277053921717629317675238467481846766940513200056812714526356082778"+ -"577134275778960917363717872146844090122495343014654958537105079227968925892354"+ -"201995611212902196086403441815981362977477130996051870721134999999837297804995"+ -"105973173281609631859502445945534690830264252230825334468503526193118817101000"+ -"313783875288658753320838142061717766914730359825349042875546873115956286388235"+ -"378759375195778185778053217122680661300192787661119590921642019893809525720106"+ -"548586327886593615338182796823030195203530185296899577362259941389124972177528"+ -"347913151557485724245415069595082953311686172785588907509838175463746493931925"+ -"506040092770167113900984882401285836160356370766010471018194295559619894676783"+ -"744944825537977472684710404753464620804668425906949129331367702898915210475216"+ -"205696602405803815019351125338243003558764024749647326391419927260426992279678"+ -"235478163600934172164121992458631503028618297455570674983850549458858692699569"+ -"092721079750930295532116534498720275596023648066549911988183479775356636980742"+ -"654252786255181841757467289097777279380008164706001614524919217321721477235014") ; - - /** Euler-Mascheroni constant lower-case gamma. - * http://www.worldwideschool.org/library/books/sci/math/MiscellaneousMathematicalConstants/chap35.html - */ - static BigDecimal GAMMA = new BigDecimal("0.577215664901532860606512090082402431"+ -"0421593359399235988057672348848677267776646709369470632917467495146314472498070"+ -"8248096050401448654283622417399764492353625350033374293733773767394279259525824"+ -"7094916008735203948165670853233151776611528621199501507984793745085705740029921"+ -"3547861466940296043254215190587755352673313992540129674205137541395491116851028"+ -"0798423487758720503843109399736137255306088933126760017247953783675927135157722"+ -"6102734929139407984301034177717780881549570661075010161916633401522789358679654"+ -"9725203621287922655595366962817638879272680132431010476505963703947394957638906"+ -"5729679296010090151251959509222435014093498712282479497471956469763185066761290"+ -"6381105182419744486783638086174945516989279230187739107294578155431600500218284"+ -"4096053772434203285478367015177394398700302370339518328690001558193988042707411"+ -"5422278197165230110735658339673487176504919418123000406546931429992977795693031"+ -"0050308630341856980323108369164002589297089098548682577736428825395492587362959"+ -"6133298574739302373438847070370284412920166417850248733379080562754998434590761"+ -"6431671031467107223700218107450444186647591348036690255324586254422253451813879"+ -"1243457350136129778227828814894590986384600629316947188714958752549236649352047"+ -"3243641097268276160877595088095126208404544477992299157248292516251278427659657"+ -"0832146102982146179519579590959227042089896279712553632179488737642106606070659"+ -"8256199010288075612519913751167821764361905705844078357350158005607745793421314"+ -"49885007864151716151945"); - - /** Natural logarithm of 2. - * http://www.worldwideschool.org/library/books/sci/math/MiscellaneousMathematicalConstants/chap58.html - */ - static BigDecimal LOG2 = new BigDecimal("0.693147180559945309417232121458176568075"+ -"50013436025525412068000949339362196969471560586332699641868754200148102057068573"+ -"368552023575813055703267075163507596193072757082837143519030703862389167347112335"+ -"011536449795523912047517268157493206515552473413952588295045300709532636664265410"+ -"423915781495204374043038550080194417064167151864471283996817178454695702627163106"+ -"454615025720740248163777338963855069526066834113727387372292895649354702576265209"+ -"885969320196505855476470330679365443254763274495125040606943814710468994650622016"+ -"772042452452961268794654619316517468139267250410380254625965686914419287160829380"+ -"317271436778265487756648508567407764845146443994046142260319309673540257444607030"+ -"809608504748663852313818167675143866747664789088143714198549423151997354880375165"+ -"861275352916610007105355824987941472950929311389715599820565439287170007218085761"+ -"025236889213244971389320378439353088774825970171559107088236836275898425891853530"+ -"243634214367061189236789192372314672321720534016492568727477823445353476481149418"+ -"642386776774406069562657379600867076257199184734022651462837904883062033061144630"+ -"073719489002743643965002580936519443041191150608094879306786515887090060520346842"+ -"973619384128965255653968602219412292420757432175748909770675268711581705113700915"+ -"894266547859596489065305846025866838294002283300538207400567705304678700184162404"+ -"418833232798386349001563121889560650553151272199398332030751408426091479001265168"+ -"243443893572472788205486271552741877243002489794540196187233980860831664811490930"+ -"667519339312890431641370681397776498176974868903887789991296503619270710889264105"+ -"230924783917373501229842420499568935992206602204654941510613"); - - - /** Euler's constant. - * @param mc The required precision of the result. - * @return 3.14159... - * @throws Errore - * @since 2009-05-29 - */ - static public BigDecimal pi(final MathContext mc) throws Errore - { - /* look it up if possible */ - if ( mc.getPrecision() < PI.precision() ) - return PI.round(mc) ; - else - { - /* Broadhurst arXiv:math/9803067 - */ - int[] a = {1,0,0,-1,-1,-1,0,0} ; - BigDecimal S = broadhurstBBP(1,1,a,mc) ; - return multiplyRound(S,8) ; - } - } /* BigDecimalMath.pi */ - - /** Euler-Mascheroni constant. - * @param mc The required precision of the result. - * @return 0.577... - * @throws Errore - * @since 2009-08-13 - */ - static public BigDecimal gamma(MathContext mc) throws Errore - { - /* look it up if possible */ - if ( mc.getPrecision() < GAMMA.precision() ) - return GAMMA.round(mc) ; - else - { - double eps = prec2err(0.577, mc.getPrecision() ) ; - - - /* Euler-Stieltjes as shown in Dilcher, Aequat Math 48 (1) (1994) 55-85 - */ - MathContext mcloc = new MathContext(2+mc.getPrecision()) ; - BigDecimal resul = BigDecimal.ONE ; - resul = resul.add( log(2,mcloc) ) ; - resul = resul.subtract( log(3,mcloc) ) ; - - /* how many terms: zeta-1 falls as 1/2^(2n+1), so the - * terms drop faster than 1/2^(4n+2). Set 1/2^(4kmax+2) < eps. - * Leading term zeta(3)/(4^1*3) is 0.017. Leading zeta(3) is 1.2. Log(2) is 0.7 - */ - int kmax = (int)((Math.log(eps/0.7)-2.)/4.) ; - mcloc = new MathContext( 1+err2prec(1.2,eps/kmax) ) ; - for(int n=1; ; n++) - { - /* zeta is close to 1. Division of zeta-1 through - * 4^n*(2n+1) means divion through roughly 2^(2n+1) - */ - BigDecimal c = zeta(2*n+1,mcloc).subtract(BigDecimal.ONE) ; - BigInteger fourn = new BigInteger(""+(2*n+1)) ; - fourn = fourn.shiftLeft(2*n) ; - c = divideRound(c, fourn) ; - resul = resul.subtract(c) ; - if ( c.doubleValue() < 0.1*eps) - break; - } - return resul.round(mc) ; - } - - } /* BigDecimalMath.gamma */ - - - /** The square root. - * @param x the non-negative argument. - * @param mc - * @return the square root of the BigDecimal. - * @since 2008-10-27 - */ - static public BigDecimal sqrt(final BigDecimal x, final MathContext mc) - { - if ( x.compareTo(BigDecimal.ZERO) < 0 ) - throw new ArithmeticException("negative argument "+x.toString()+ " of square root") ; - if ( x.abs().subtract( new BigDecimal(Math.pow(10.,-mc.getPrecision())) ).compareTo(BigDecimal.ZERO) < 0 ) - return BigDecimalMath.scalePrec(BigDecimal.ZERO,mc) ; - /* start the computation from a double precision estimate */ - BigDecimal s = new BigDecimal( Math.sqrt(x.doubleValue()) ,mc) ; - final BigDecimal half = new BigDecimal("2") ; - - /* increase the local accuracy by 2 digits */ - MathContext locmc = new MathContext(mc.getPrecision()+2,mc.getRoundingMode()) ; - - /* relative accuracy requested is 10^(-precision) - */ - final double eps = Math.pow(10.0,-mc.getPrecision()) ; - for (;;) - { - /* s = s -(s/2-x/2s); test correction s-x/s for being - * smaller than the precision requested. The relative correction is 1-x/s^2, - * (actually half of this, which we use for a little bit of additional protection). - */ - if ( Math.abs(BigDecimal.ONE.subtract(x.divide(s.pow(2,locmc),locmc)).doubleValue()) <= eps) - break ; - s = s.add(x.divide(s,locmc)).divide(half,locmc) ; - /* debugging*/ - //System.out.println("itr "+x.round(locmc).toString() + " " + s.round(locmc).toString()) ; - } - return s ; - } /* BigDecimalMath.sqrt */ - - /** The square root. - * @param x the non-negative argument. - * @return the square root of the BigDecimal rounded to the precision implied by x. - * @since 2009-06-25 - */ - static public BigDecimal sqrt(final BigDecimal x) - { - if ( x.compareTo(BigDecimal.ZERO) < 0 ) - throw new ArithmeticException("negative argument "+x.toString()+ " of square root") ; - - return root(2,x) ; - } /* BigDecimalMath.sqrt */ - - /** The cube root. - * @param x The argument. - * @return The cubic root of the BigDecimal rounded to the precision implied by x. - * The sign of the result is the sign of the argument. - * @since 2009-08-16 - */ - static public BigDecimal cbrt(final BigDecimal x) - { - if ( x.compareTo(BigDecimal.ZERO) < 0 ) - return root(3,x.negate()).negate() ; - else - return root(3,x) ; - } /* BigDecimalMath.cbrt */ - - /** The integer root. - * @param n the positive argument. - * @param x the non-negative argument. - * @return The n-th root of the BigDecimal rounded to the precision implied by x, x^(1/n). - * @since 2009-07-30 - */ - static public BigDecimal root(final int n, final BigDecimal x) - { - if ( x.compareTo(BigDecimal.ZERO) < 0 ) - throw new ArithmeticException("negative argument "+x.toString()+ " of root") ; - if ( n<= 0 ) - throw new ArithmeticException("negative power "+ n + " of root") ; - - if ( n == 1 ) - return x ; - - /* start the computation from a double precision estimate */ - BigDecimal s = new BigDecimal( Math.pow(x.doubleValue(),1.0/n) ) ; - - /* this creates nth with nominal precision of 1 digit - */ - final BigDecimal nth = new BigDecimal(n) ; - - /* Specify an internal accuracy within the loop which is - * slightly larger than what is demanded by 'eps' below. - */ - final BigDecimal xhighpr = scalePrec(x,2) ; - MathContext mc = new MathContext( 2+x.precision() ) ; - - /* Relative accuracy of the result is eps. - */ - final double eps = x.ulp().doubleValue()/(2*n*x.doubleValue()) ; - for (;;) - { - /* s = s -(s/n-x/n/s^(n-1)) = s-(s-x/s^(n-1))/n; test correction s/n-x/s for being - * smaller than the precision requested. The relative correction is (1-x/s^n)/n, - */ - BigDecimal c = xhighpr.divide( s.pow(n-1),mc ) ; - c = s.subtract(c) ; - MathContext locmc = new MathContext( c.precision() ) ; - c = c.divide(nth,locmc) ; - s = s. subtract(c) ; - if ( Math.abs( c.doubleValue()/s.doubleValue()) < eps) - break ; - } - return s.round(new MathContext( err2prec(eps)) ) ; - } /* BigDecimalMath.root */ - - /** The hypotenuse. - * @param x the first argument. - * @param y the second argument. - * @return the square root of the sum of the squares of the two arguments, sqrt(x^2+y^2). - * @since 2009-06-25 - */ - static public BigDecimal hypot(final BigDecimal x, final BigDecimal y) - { - /* compute x^2+y^2 - */ - BigDecimal z = x.pow(2).add(y.pow(2)) ; - - /* truncate to the precision set by x and y. Absolute error = 2*x*xerr+2*y*yerr, - * where the two errors are 1/2 of the ulp's. Two intermediate protectio digits. - */ - BigDecimal zerr = x.abs().multiply(x.ulp()).add(y.abs().multiply(y.ulp())) ; - MathContext mc = new MathContext( 2+err2prec(z,zerr) ) ; - - /* Pull square root */ - z = sqrt(z.round(mc)) ; - - /* Final rounding. Absolute error in the square root is (y*yerr+x*xerr)/z, where zerr holds 2*(x*xerr+y*yerr). - */ - mc = new MathContext( err2prec(z.doubleValue() ,0.5*zerr.doubleValue() /z.doubleValue() ) ) ; - return z.round(mc) ; - } /* BigDecimalMath.hypot */ - - /** The hypotenuse. - * @param n the first argument. - * @param x the second argument. - * @return the square root of the sum of the squares of the two arguments, sqrt(n^2+x^2). - * @since 2009-08-05 - */ - static public BigDecimal hypot(final int n, final BigDecimal x) - { - /* compute n^2+x^2 in infinite precision - */ - BigDecimal z = (new BigDecimal(n)).pow(2).add(x.pow(2)) ; - - /* Truncate to the precision set by x. Absolute error = in z (square of the result) is |2*x*xerr|, - * where the error is 1/2 of the ulp. Two intermediate protection digits. - * zerr is a signed value, but used only in conjunction with err2prec(), so this feature does not harm. - */ - double zerr = x.doubleValue()*x.ulp().doubleValue() ; - MathContext mc = new MathContext( 2+err2prec(z.doubleValue(),zerr) ) ; - - /* Pull square root */ - z = sqrt(z.round(mc)) ; - - /* Final rounding. Absolute error in the square root is x*xerr/z, where zerr holds 2*x*xerr. - */ - mc = new MathContext( err2prec(z.doubleValue(),0.5*zerr/z.doubleValue() ) ) ; - return z.round(mc) ; - } /* BigDecimalMath.hypot */ - - - /** A suggestion for the maximum numter of terms in the Taylor expansion of the exponential. - */ - static private int TAYLOR_NTERM = 8 ; - - /** The exponential function. - * @param x the argument. - * @return exp(x). - * The precision of the result is implicitly defined by the precision in the argument. - * In particular this means that "Invalid Operation" errors are thrown if catastrophic - * cancellation of digits causes the result to have no valid digits left. - * @since 2009-05-29 - * @author Richard J. Mathar - */ - static public BigDecimal exp(BigDecimal x) - { - /* To calculate the value if x is negative, use exp(-x) = 1/exp(x) - */ - if ( x.compareTo(BigDecimal.ZERO) < 0 ) - { - final BigDecimal invx = exp(x.negate() ) ; - /* Relative error in inverse of invx is the same as the relative errror in invx. - * This is used to define the precision of the result. - */ - MathContext mc = new MathContext( invx.precision() ) ; - return BigDecimal.ONE.divide( invx, mc ) ; - } - else if ( x.compareTo(BigDecimal.ZERO) == 0 ) - { - /* recover the valid number of digits from x.ulp(), if x hits the - * zero. The x.precision() is 1 then, and does not provide this information. - */ - return scalePrec(BigDecimal.ONE, -(int)(Math.log10( x.ulp().doubleValue() )) ) ; - } - else - { - /* Push the number in the Taylor expansion down to a small - * value where TAYLOR_NTERM terms will do. If x<1, the n-th term is of the order - * x^n/n!, and equal to both the absolute and relative error of the result - * since the result is close to 1. The x.ulp() sets the relative and absolute error - * of the result, as estimated from the first Taylor term. - * We want x^TAYLOR_NTERM/TAYLOR_NTERM! < x.ulp, which is guaranteed if - * x^TAYLOR_NTERM < TAYLOR_NTERM*(TAYLOR_NTERM-1)*...*x.ulp. - */ - final double xDbl = x.doubleValue() ; - final double xUlpDbl = x.ulp().doubleValue() ; - if ( Math.pow(xDbl,TAYLOR_NTERM) < TAYLOR_NTERM*(TAYLOR_NTERM-1.0)*(TAYLOR_NTERM-2.0)*xUlpDbl ) - { - /* Add TAYLOR_NTERM terms of the Taylor expansion (Euler's sum formula) - */ - BigDecimal resul = BigDecimal.ONE ; - - /* x^i */ - BigDecimal xpowi = BigDecimal.ONE ; - - /* i factorial */ - BigInteger ifac = BigInteger.ONE ; - - /* TAYLOR_NTERM terms to be added means we move x.ulp() to the right - * for each power of 10 in TAYLOR_NTERM, so the addition won't add noise beyond - * what's already in x. - */ - MathContext mcTay = new MathContext( err2prec(1.,xUlpDbl/TAYLOR_NTERM) ) ; - for(int i=1 ; i <= TAYLOR_NTERM ; i++) - { - ifac = ifac.multiply(new BigInteger(""+i) ) ; - xpowi = xpowi.multiply(x) ; - final BigDecimal c= xpowi.divide(new BigDecimal(ifac),mcTay) ; - resul = resul.add(c) ; - if ( Math.abs(xpowi.doubleValue()) < i && Math.abs(c.doubleValue()) < 0.5* xUlpDbl ) - break; - } - /* exp(x+deltax) = exp(x)(1+deltax) if deltax is <<1. So the relative error - * in the result equals the absolute error in the argument. - */ - MathContext mc = new MathContext( err2prec(xUlpDbl/2.) ) ; - return resul.round(mc) ; - } - else - { - /* Compute exp(x) = (exp(0.1*x))^10. Division by 10 does not lead - * to loss of accuracy. - */ - int exSc = (int) ( 1.0-Math.log10( TAYLOR_NTERM*(TAYLOR_NTERM-1.0)*(TAYLOR_NTERM-2.0)*xUlpDbl - /Math.pow(xDbl,TAYLOR_NTERM) ) / ( TAYLOR_NTERM-1.0) ) ; - BigDecimal xby10 = x.scaleByPowerOfTen(-exSc) ; - BigDecimal expxby10 = exp(xby10) ; - - /* Final powering by 10 means that the relative error of the result - * is 10 times the relative error of the base (First order binomial expansion). - * This looses one digit. - */ - MathContext mc = new MathContext( expxby10.precision()-exSc ) ; - /* Rescaling the powers of 10 is done in chunks of a maximum of 8 to avoid an invalid operation - * response by the BigDecimal.pow library or integer overflow. - */ - while ( exSc > 0 ) - { - int exsub = Math.min(8,exSc) ; - exSc -= exsub ; - MathContext mctmp = new MathContext( expxby10.precision()-exsub+2 ) ; - int pex = 1 ; - while ( exsub-- > 0 ) - pex *= 10 ; - expxby10 = expxby10.pow(pex,mctmp) ; - } - return expxby10.round(mc) ; - } - } - } /* BigDecimalMath.exp */ - - /** The base of the natural logarithm. - * @param mc the required precision of the result - * @return exp(1) = 2.71828.... - * @since 2009-05-29 - */ - static public BigDecimal exp(final MathContext mc) - { - /* look it up if possible */ - if ( mc.getPrecision() < E.precision() ) - return E.round(mc) ; - else - { - /* Instantiate a 1.0 with the requested pseudo-accuracy - * and delegate the computation to the public method above. - */ - BigDecimal uni = scalePrec(BigDecimal.ONE, mc.getPrecision() ) ; - return exp(uni) ; - } - } /* BigDecimalMath.exp */ - - /** The natural logarithm. - * @param x the argument. - * @return ln(x). - * The precision of the result is implicitly defined by the precision in the argument. - * @since 2009-05-29 - * @author Richard J. Mathar - */ - static public BigDecimal log(BigDecimal x) - { - /* the value is undefined if x is negative. - */ - if ( x.compareTo(BigDecimal.ZERO) < 0 ) - throw new ArithmeticException("Cannot take log of negative "+ x.toString() ) ; - else if ( x.compareTo(BigDecimal.ONE) == 0 ) - { - /* log 1. = 0. */ - return scalePrec(BigDecimal.ZERO, x.precision()-1) ; - } - else if ( Math.abs(x.doubleValue()-1.0) <= 0.3 ) - { - /* The standard Taylor series around x=1, z=0, z=x-1. Abramowitz-Stegun 4.124. - * The absolute error is err(z)/(1+z) = err(x)/x. - */ - BigDecimal z = scalePrec(x.subtract(BigDecimal.ONE),2) ; - BigDecimal zpown = z ; - double eps = 0.5*x.ulp().doubleValue()/Math.abs(x.doubleValue()) ; - BigDecimal resul = z ; - for(int k= 2;; k++) - { - zpown = multiplyRound(zpown,z) ; - BigDecimal c = divideRound(zpown,k) ; - if ( k % 2 == 0) - resul = resul.subtract(c) ; - else - resul = resul.add(c) ; - if ( Math.abs(c.doubleValue()) < eps) - break; - } - MathContext mc = new MathContext( err2prec(resul.doubleValue(),eps) ) ; - return resul.round(mc) ; - } - else - { - final double xDbl = x.doubleValue() ; - final double xUlpDbl = x.ulp().doubleValue() ; - - /* Map log(x) = log root[r](x)^r = r*log( root[r](x)) with the aim - * to move roor[r](x) near to 1.2 (that is, below the 0.3 appearing above), where log(1.2) is roughly 0.2. - */ - int r = (int) (Math.log(xDbl)/0.2) ; - - /* Since the actual requirement is a function of the value 0.3 appearing above, - * we avoid the hypothetical case of endless recurrence by ensuring that r >= 2. - */ - r = Math.max(2,r) ; - - /* Compute r-th root with 2 additional digits of precision - */ - BigDecimal xhighpr = scalePrec(x,2) ; - BigDecimal resul = root(r,xhighpr) ; - resul = log(resul).multiply(new BigDecimal(r)) ; - - /* error propagation: log(x+errx) = log(x)+errx/x, so the absolute error - * in the result equals the relative error in the input, xUlpDbl/xDbl . - */ - MathContext mc = new MathContext( err2prec(resul.doubleValue(),xUlpDbl/xDbl) ) ; - return resul.round(mc) ; - } - } /* BigDecimalMath.log */ - - /** The natural logarithm. - * @param n The main argument, a strictly positive integer. - * @param mc The requirements on the precision. - * @return ln(n). - * @since 2009-08-08 - * @author Richard J. Mathar - * @throws Errore - */ - static public BigDecimal log(int n, final MathContext mc) throws Errore - { - /* the value is undefined if x is negative. - */ - if ( n <= 0 ) - throw new ArithmeticException("Cannot take log of negative "+ n ) ; - else if ( n == 1) - return BigDecimal.ZERO ; - else if ( n == 2) - { - if ( mc.getPrecision() < LOG2.precision() ) - return LOG2.round(mc) ; - else - { - /* Broadhurst arXiv:math/9803067 - * Error propagation: the error in log(2) is twice the error in S(2,-5,...). - */ - int[] a = {2,-5,-2,-7,-2,-5,2,-3} ; - BigDecimal S = broadhurstBBP(2,1,a, new MathContext(1+mc.getPrecision()) ) ; - S = S.multiply(new BigDecimal(8)) ; - S = sqrt(divideRound(S,3)) ; - return S.round(mc) ; - } - } - else if ( n == 3) - { - /* summation of a series roughly proportional to (7/500)^k. Estimate count - * of terms to estimate the precision (drop the favorable additional - * 1/k here): 0.013^k <= 10^(-precision), so k*log10(0.013) <= -precision - * so k>= precision/1.87. - */ - int kmax = (int)(mc.getPrecision()/1.87) ; - MathContext mcloc = new MathContext( mc.getPrecision()+ 1+(int)(Math.log10(kmax*0.693/1.098)) ) ; - BigDecimal log3 = multiplyRound( log(2,mcloc),19 ) ; - - /* log3 is roughly 1, so absolute and relative error are the same. The - * result will be divided by 12, so a conservative error is the one - * already found in mc - */ - double eps = prec2err(1.098,mc.getPrecision() )/kmax ; - Rational r = new Rational(7153,524288) ; - Rational pk = new Rational(7153,524288) ; - for(int k=1; ; k++) - { - Rational tmp = pk.divide(k) ; - if ( tmp.doubleValue() < eps) - break ; - - /* how many digits of tmp do we need in the sum? - */ - mcloc = new MathContext( err2prec(tmp.doubleValue(),eps) ) ; - BigDecimal c = pk.divide(k).BigDecimalValue(mcloc) ; - if ( k % 2 != 0) - log3 = log3.add(c) ; - else - log3 = log3.subtract(c) ; - pk = pk.multiply(r) ; - } - log3 = divideRound( log3,12 ) ; - return log3.round(mc) ; - } - else if ( n == 5) - { - /* summation of a series roughly proportional to (7/160)^k. Estimate count - * of terms to estimate the precision (drop the favorable additional - * 1/k here): 0.046^k <= 10^(-precision), so k*log10(0.046) <= -precision - * so k>= precision/1.33. - */ - int kmax = (int)(mc.getPrecision()/1.33) ; - MathContext mcloc = new MathContext( mc.getPrecision()+ 1+(int)(Math.log10(kmax*0.693/1.609)) ) ; - BigDecimal log5 = multiplyRound( log(2,mcloc),14 ) ; - - /* log5 is roughly 1.6, so absolute and relative error are the same. The - * result will be divided by 6, so a conservative error is the one - * already found in mc - */ - double eps = prec2err(1.6,mc.getPrecision() )/kmax ; - Rational r = new Rational(759,16384) ; - Rational pk = new Rational(759,16384) ; - for(int k=1; ; k++) - { - Rational tmp = pk.divide(k) ; - if ( tmp.doubleValue() < eps) - break ; - - /* how many digits of tmp do we need in the sum? - */ - mcloc = new MathContext( err2prec(tmp.doubleValue(),eps) ) ; - BigDecimal c = pk.divide(k).BigDecimalValue(mcloc) ; - log5 = log5.subtract(c) ; - pk = pk.multiply(r) ; - } - log5 = divideRound( log5,6 ) ; - return log5.round(mc) ; - } - else if ( n == 7) - { - /* summation of a series roughly proportional to (1/8)^k. Estimate count - * of terms to estimate the precision (drop the favorable additional - * 1/k here): 0.125^k <= 10^(-precision), so k*log10(0.125) <= -precision - * so k>= precision/0.903. - */ - int kmax = (int)(mc.getPrecision()/0.903) ; - MathContext mcloc = new MathContext( mc.getPrecision()+ 1+(int)(Math.log10(kmax*3*0.693/1.098)) ) ; - BigDecimal log7 = multiplyRound( log(2,mcloc),3 ) ; - - /* log7 is roughly 1.9, so absolute and relative error are the same. - */ - double eps = prec2err(1.9,mc.getPrecision() )/kmax ; - Rational r = new Rational(1,8) ; - Rational pk = new Rational(1,8) ; - for(int k=1; ; k++) - { - Rational tmp = pk.divide(k) ; - if ( tmp.doubleValue() < eps) - break ; - - /* how many digits of tmp do we need in the sum? - */ - mcloc = new MathContext( err2prec(tmp.doubleValue(),eps) ) ; - BigDecimal c = pk.divide(k).BigDecimalValue(mcloc) ; - log7 = log7.subtract(c) ; - pk = pk.multiply(r) ; - } - return log7.round(mc) ; - - } - - else - { - /* At this point one could either forward to the log(BigDecimal) signature (implemented) - * or decompose n into Ifactors and use an implemenation of all the prime bases. - * Estimate of the result; convert the mc argument to an absolute error eps - * log(n+errn) = log(n)+errn/n = log(n)+eps - */ - double res = Math.log((double)n) ; - double eps = prec2err(res,mc.getPrecision() ) ; - /* errn = eps*n, convert absolute error in result to requirement on absolute error in input - */ - eps *= n ; - /* Convert this absolute requirement of error in n to a relative error in n - */ - final MathContext mcloc = new MathContext( 1+err2prec((double)n,eps ) ) ; - /* Padd n with a number of zeros to trigger the required accuracy in - * the standard signature method - */ - BigDecimal nb = scalePrec(new BigDecimal(n),mcloc) ; - return log(nb) ; - } - } /* log */ - - /** The natural logarithm. - * @param r The main argument, a strictly positive value. - * @param mc The requirements on the precision. - * @return ln(r). - * @since 2009-08-09 - * @author Richard J. Mathar - */ - static public BigDecimal log(final Rational r, final MathContext mc) - { - /* the value is undefined if x is negative. - */ - if ( r.compareTo(Rational.ZERO) <= 0 ) - throw new ArithmeticException("Cannot take log of negative "+ r.toString() ) ; - else if ( r.compareTo(Rational.ONE) == 0) - return BigDecimal.ZERO ; - else - { - - /* log(r+epsr) = log(r)+epsr/r. Convert the precision to an absolute error in the result. - * eps contains the required absolute error of the result, epsr/r. - */ - double eps = prec2err( Math.log(r.doubleValue()), mc.getPrecision()) ; - - /* Convert this further into a requirement of the relative precision in r, given that - * epsr/r is also the relative precision of r. Add one safety digit. - */ - MathContext mcloc = new MathContext( 1+err2prec(eps) ) ; - - final BigDecimal resul = log( r.BigDecimalValue(mcloc) ); - - return resul.round(mc) ; - } - } /* log */ - - /** Power function. - * @param x Base of the power. - * @param y Exponent of the power. - * @return x^y. - * The estimation of the relative error in the result is |log(x)*err(y)|+|y*err(x)/x| - * @since 2009-06-01 - */ - static public BigDecimal pow(final BigDecimal x, final BigDecimal y) - { - if( x.compareTo(BigDecimal.ZERO) < 0 ) - throw new ArithmeticException("Cannot power negative "+ x.toString()) ; - else if( x.compareTo(BigDecimal.ZERO) == 0 ) - return BigDecimal.ZERO ; - else - { - /* return x^y = exp(y*log(x)) ; - */ - BigDecimal logx = log(x) ; - BigDecimal ylogx = y.multiply(logx) ; - BigDecimal resul = exp(ylogx) ; - - /* The estimation of the relative error in the result is |log(x)*err(y)|+|y*err(x)/x| - */ - double errR = Math.abs(logx.doubleValue()*y.ulp().doubleValue()/2.) - + Math.abs(y.doubleValue()*x.ulp().doubleValue()/2./x.doubleValue()) ; - MathContext mcR = new MathContext( err2prec(1.0,errR) ) ; - return resul.round(mcR) ; - } - } /* BigDecimalMath.pow */ - - /** Raise to an integer power and round. - * @param x The base. - * @param n The exponent. - * @return x^n. - * @since 2009-08-13 - * @since 2010-05-26 handle also n<0 cases. - */ - static public BigDecimal powRound(final BigDecimal x, final int n) - { - /** Special cases: x^1=x and x^0 = 1 - */ - if ( n == 1 ) - return x; - else if ( n == 0 ) - return BigDecimal.ONE ; - else - { - /* The relative error in the result is n times the relative error in the input. - * The estimation is slightly optimistic due to the integer rounding of the logarithm. - * Since the standard BigDecimal.pow can only handle positive n, we split the algorithm. - */ - MathContext mc = new MathContext( x.precision() - (int)Math.log10((double)(Math.abs(n))) ) ; - if ( n > 0 ) - return x.pow(n,mc) ; - else - return BigDecimal.ONE.divide( x.pow(-n),mc) ; - } - } /* BigDecimalMath.powRound */ - - /** Raise to an integer power and round. - * @param x The base. - * @param n The exponent. - * The current implementation allows n only in the interval of the standard int values. - * @return x^n. - * @since 2010-05-26 - */ - static public BigDecimal powRound(final BigDecimal x, final BigInteger n) - { - /** For now, the implementation forwards to the cases where n - * is in the range of the standard integers. This might, however, be - * implemented to decompose larger powers into cascaded calls to smaller ones. - */ - if ( n.compareTo(Rational.MAX_INT) > 0 || n.compareTo(Rational.MIN_INT) < 0) - throw new ProviderException("Not implemented: big power "+n.toString() ) ; - else - return powRound(x,n.intValue() ) ; - } /* BigDecimalMath.powRound */ - - /** Raise to a fractional power and round. - * @param x The base. - * Generally enforced to be positive, with the exception of integer exponents where - * the sign is carried over according to the parity of the exponent. - * @param q The exponent. - * @return x^q. - * @since 2010-05-26 - */ - static public BigDecimal powRound(final BigDecimal x, final Rational q) - { - /** Special cases: x^1=x and x^0 = 1 - */ - if ( q.compareTo(BigInteger.ONE) == 0 ) - return x; - else if ( q.signum() == 0 ) - return BigDecimal.ONE ; - else if ( q.isInteger() ) - { - /* We are sure that the denominator is positive here, because normalize() has been - * called during constrution etc. - */ - return powRound(x,q.a) ; - } - /* Refuse to operate on the general negative basis. The integer q have already been handled above. - */ - else if ( x.compareTo(BigDecimal.ZERO) < 0 ) - throw new ArithmeticException("Cannot power negative "+ x.toString() ) ; - else - { - if ( q.isIntegerFrac() ) - { - /* Newton method with first estimate in double precision. - * The disadvantage of this first line here is that the result must fit in the - * standard range of double precision numbers exponents. - */ - double estim = Math.pow( x.doubleValue(),q.doubleValue() ) ; - BigDecimal res = new BigDecimal(estim) ; - - /* The error in x^q is q*x^(q-1)*Delta(x). - * The relative error is q*Delta(x)/x, q times the relative error of x. - */ - BigDecimal reserr = new BigDecimal( 0.5* q.abs().doubleValue() - * x.ulp().divide(x.abs(),MathContext.DECIMAL64).doubleValue() ) ; - - /* The main point in branching the cases above is that this conversion - * will succeed for numerator and denominator of q. - */ - int qa = q.a.intValue() ; - int qb = q.b.intValue() ; - - /* Newton iterations. */ - BigDecimal xpowa = powRound(x, qa) ; - for( ;; ) - { - /* numerator and denominator of the Newton term. The major - * disadvantage of this implementation is that the updates of the powers - * of the new estimate are done in full precision calling BigDecimal.pow(), - * which becomes slow if the denominator of q is large. - */ - BigDecimal nu = res.pow(qb) .subtract(xpowa) ; - BigDecimal de = multiplyRound( res.pow(qb-1),q.b) ; - - /* estimated correction */ - BigDecimal eps = nu.divide(de,MathContext.DECIMAL64) ; - - BigDecimal err = res.multiply(reserr,MathContext.DECIMAL64) ; - int precDiv = 2+err2prec(eps,err) ; - if ( precDiv <= 0 ) - { - /* The case when the precision is already reached and any precision - * will do. */ - eps = nu.divide(de,MathContext.DECIMAL32) ; - } - else - { - MathContext mc = new MathContext(precDiv) ; - eps = nu.divide(de,mc) ; - } - - res = subtractRound(res,eps) ; - /* reached final precision if the relative error fell below reserr, - * |eps/res| < reserr - */ - if ( eps.divide(res,MathContext.DECIMAL64).abs().compareTo(reserr) < 0 ) - { - /* delete the bits of extra precision kept in this - * working copy. - */ - MathContext mc = new MathContext(err2prec(reserr.doubleValue())) ; - return res.round(mc) ; - } - } - } - else - { - /* The error in x^q is q*x^(q-1)*Delta(x) + Delta(q)*x^q*log(x). - * The relative error is q/x*Delta(x) + Delta(q)*log(x). Convert q to a floating point - * number such that its relative error becomes negligible: Delta(q)/q << Delta(x)/x/log(x) . - */ - int precq = 3+err2prec( (x.ulp().divide(x,MathContext.DECIMAL64)).doubleValue() - / Math.log(x.doubleValue()) ) ; - MathContext mc = new MathContext(precq) ; - - /* Perform the actual calculation as exponentiation of two floating point numbers. - */ - return pow(x, q.BigDecimalValue(mc) ) ; - } - - - } - } /* BigDecimalMath.powRound */ - - /** Trigonometric sine. - * @param x The argument in radians. - * @return sin(x) in the range -1 to 1. - * @throws Errore - * @since 2009-06-01 - */ - static public BigDecimal sin(final BigDecimal x) throws Errore - { - if ( x.compareTo(BigDecimal.ZERO) < 0) - return sin(x.negate()).negate() ; - else if ( x.compareTo(BigDecimal.ZERO) == 0 ) - return BigDecimal.ZERO ; - else - { - /* reduce modulo 2pi - */ - BigDecimal res = mod2pi(x) ; - double errpi = 0.5*Math.abs(x.ulp().doubleValue()) ; - MathContext mc = new MathContext( 2+err2prec(3.14159,errpi) ) ; - BigDecimal p= pi(mc) ; - mc = new MathContext( x.precision() ) ; - if ( res.compareTo(p) > 0 ) - { - /* pi 0 ) - { - /* pi/2 0 ) - { - /* x>pi/4: sin(x) = cos(pi/2-x) - */ - return cos( subtractRound(p.divide(new BigDecimal("2")),res) ) ; - } - else - { - /* Simple Taylor expansion, sum_{i=1..infinity} (-1)^(..)res^(2i+1)/(2i+1)! */ - BigDecimal resul = res ; - - /* x^i */ - BigDecimal xpowi = res ; - - /* 2i+1 factorial */ - BigInteger ifac = BigInteger.ONE ; - - /* The error in the result is set by the error in x itself. - */ - double xUlpDbl = res.ulp().doubleValue() ; - - /* The error in the result is set by the error in x itself. - * We need at most k terms to squeeze x^(2k+1)/(2k+1)! below this value. - * x^(2k+1) < x.ulp; (2k+1)*log10(x) < -x.precision; 2k*log10(x)< -x.precision; - * 2k*(-log10(x)) > x.precision; 2k*log10(1/x) > x.precision - */ - int k = (int)(res.precision()/Math.log10(1.0/res.doubleValue()))/2 ; - MathContext mcTay = new MathContext( err2prec(res.doubleValue(),xUlpDbl/k) ) ; - for(int i=1 ; ; i++) - { - /* TBD: at which precision will 2*i or 2*i+1 overflow? - */ - ifac = ifac.multiply(new BigInteger(""+(2*i) ) ) ; - ifac = ifac.multiply( new BigInteger(""+(2*i+1)) ) ; - xpowi = xpowi.multiply(res).multiply(res).negate() ; - BigDecimal corr = xpowi.divide(new BigDecimal(ifac),mcTay) ; - resul = resul.add( corr ) ; - if ( corr.abs().doubleValue() < 0.5*xUlpDbl ) - break ; - } - /* The error in the result is set by the error in x itself. - */ - mc = new MathContext(res.precision() ) ; - return resul.round(mc) ; - } - } - } - } /* sin */ - - /** Trigonometric cosine. - * @param x The argument in radians. - * @return cos(x) in the range -1 to 1. - * @throws Errore - * @since 2009-06-01 - */ - static public BigDecimal cos(final BigDecimal x) throws Errore - { - if ( x.compareTo(BigDecimal.ZERO) < 0) - return cos(x.negate()); - else if ( x.compareTo(BigDecimal.ZERO) == 0 ) - return BigDecimal.ONE ; - else - { - /* reduce modulo 2pi - */ - BigDecimal res = mod2pi(x) ; - double errpi = 0.5*Math.abs(x.ulp().doubleValue()) ; - MathContext mc = new MathContext( 2+err2prec(3.14159,errpi) ) ; - BigDecimal p= pi(mc) ; - mc = new MathContext( x.precision() ) ; - if ( res.compareTo(p) > 0 ) - { - /* pi 0 ) - { - /* pi/2 0 ) - { - /* x>pi/4: cos(x) = sin(pi/2-x) - */ - return sin( subtractRound(p.divide(new BigDecimal("2")),res) ) ; - } - else - { - /* Simple Taylor expansion, sum_{i=0..infinity} (-1)^(..)res^(2i)/(2i)! */ - BigDecimal resul = BigDecimal.ONE ; - - /* x^i */ - BigDecimal xpowi = BigDecimal.ONE ; - - /* 2i factorial */ - BigInteger ifac = BigInteger.ONE ; - - /* The absolute error in the result is the error in x^2/2 which is x times the error in x. - */ - double xUlpDbl = 0.5*res.ulp().doubleValue()*res.doubleValue() ; - - /* The error in the result is set by the error in x^2/2 itself, xUlpDbl. - * We need at most k terms to push x^(2k+1)/(2k+1)! below this value. - * x^(2k) < xUlpDbl; (2k)*log(x) < log(xUlpDbl); - */ - int k = (int)(Math.log(xUlpDbl)/Math.log(res.doubleValue()) )/2 ; - MathContext mcTay = new MathContext( err2prec(1.,xUlpDbl/k) ) ; - for(int i=1 ; ; i++) - { - /* TBD: at which precision will 2*i-1 or 2*i overflow? - */ - ifac = ifac.multiply(new BigInteger(""+(2*i-1) ) ) ; - ifac = ifac.multiply( new BigInteger(""+(2*i)) ) ; - xpowi = xpowi.multiply(res).multiply(res).negate() ; - BigDecimal corr = xpowi.divide(new BigDecimal(ifac),mcTay) ; - resul = resul.add( corr ) ; - if ( corr.abs().doubleValue() < 0.5*xUlpDbl ) - break ; - } - /* The error in the result is governed by the error in x itself. - */ - mc = new MathContext( err2prec(resul.doubleValue(),xUlpDbl) ) ; - return resul.round(mc) ; - } - } - } - } /* BigDecimalMath.cos */ - - /** The trigonometric tangent. - * @param x the argument in radians. - * @return the tan(x) - * @throws Errore - */ - static public BigDecimal tan(final BigDecimal x) throws Errore - { - if ( x.compareTo(BigDecimal.ZERO) == 0 ) - return BigDecimal.ZERO ; - else if ( x.compareTo(BigDecimal.ZERO) < 0 ) - { - return tan(x.negate()).negate() ; - } - else - { - /* reduce modulo pi - */ - BigDecimal res = modpi(x) ; - - /* absolute error in the result is err(x)/cos^2(x) to lowest order - */ - final double xDbl = res.doubleValue() ; - final double xUlpDbl = x.ulp().doubleValue()/2. ; - final double eps = xUlpDbl/2./Math.pow(Math.cos(xDbl),2.) ; - - if ( xDbl > 0.8) - { - /* tan(x) = 1/cot(x) */ - BigDecimal co = cot(x) ; - MathContext mc = new MathContext( err2prec(1./co.doubleValue(),eps) ) ; - return BigDecimal.ONE.divide(co,mc) ; - } - else - { - final BigDecimal xhighpr = scalePrec(res,2) ; - final BigDecimal xhighprSq = multiplyRound(xhighpr,xhighpr) ; - - BigDecimal resul = xhighpr.plus() ; - - /* x^(2i+1) */ - BigDecimal xpowi = xhighpr ; - - Bernoulli b = new Bernoulli() ; - - /* 2^(2i) */ - BigInteger fourn = new BigInteger("4") ; - /* (2i)! */ - BigInteger fac = new BigInteger("2") ; - - for(int i= 2 ; ; i++) - { - Rational f = b.at(2*i).abs() ; - fourn = fourn.shiftLeft(2) ; - fac = fac.multiply(new BigInteger(""+(2*i))).multiply(new BigInteger(""+(2*i-1))) ; - f = f.multiply(fourn).multiply(fourn.subtract(BigInteger.ONE)).divide(fac) ; - xpowi = multiplyRound(xpowi,xhighprSq) ; - BigDecimal c = multiplyRound(xpowi,f) ; - resul = resul.add(c) ; - if ( Math.abs(c.doubleValue()) < 0.1*eps) - break; - } - MathContext mc = new MathContext( err2prec(resul.doubleValue(),eps) ) ; - return resul.round(mc) ; - } - } - } /* BigDecimalMath.tan */ - - /** The trigonometric co-tangent. - * @param x the argument in radians. - * @return the cot(x) - * @throws Errore - * @since 2009-07-31 - */ - static public BigDecimal cot(final BigDecimal x) throws Errore - { - if ( x.compareTo(BigDecimal.ZERO) == 0 ) - { - throw new ArithmeticException("Cannot take cot of zero "+ x.toString() ) ; - } - else if ( x.compareTo(BigDecimal.ZERO) < 0 ) - { - return cot(x.negate()).negate() ; - } - else - { - /* reduce modulo pi - */ - BigDecimal res = modpi(x) ; - - /* absolute error in the result is err(x)/sin^2(x) to lowest order - */ - final double xDbl = res.doubleValue() ; - final double xUlpDbl = x.ulp().doubleValue()/2. ; - final double eps = xUlpDbl/2./Math.pow(Math.sin(xDbl),2.) ; - - final BigDecimal xhighpr = scalePrec(res,2) ; - final BigDecimal xhighprSq = multiplyRound(xhighpr,xhighpr) ; - - MathContext mc = new MathContext( err2prec(xhighpr.doubleValue(),eps) ) ; - BigDecimal resul = BigDecimal.ONE.divide(xhighpr,mc) ; - - /* x^(2i-1) */ - BigDecimal xpowi = xhighpr ; - - Bernoulli b = new Bernoulli() ; - - /* 2^(2i) */ - BigInteger fourn = new BigInteger("4") ; - /* (2i)! */ - BigInteger fac = BigInteger.ONE ; - - for(int i= 1 ; ; i++) - { - Rational f = b.at(2*i) ; - fac = fac.multiply(new BigInteger(""+(2*i))).multiply(new BigInteger(""+(2*i-1))) ; - f = f.multiply(fourn).divide(fac) ; - BigDecimal c = multiplyRound(xpowi,f) ; - if ( i % 2 == 0 ) - resul = resul.add(c) ; - else - resul = resul.subtract(c) ; - if ( Math.abs(c.doubleValue()) < 0.1*eps) - break; - - fourn = fourn.shiftLeft(2) ; - xpowi = multiplyRound(xpowi,xhighprSq) ; - } - mc = new MathContext( err2prec(resul.doubleValue(),eps) ) ; - return resul.round(mc) ; - } - } /* BigDecimalMath.cot */ - - /** The inverse trigonometric sine. - * @param x the argument. - * @return the arcsin(x) in radians. - * @throws Errore - */ - static public BigDecimal asin(final BigDecimal x) throws Errore - { - if ( x.compareTo(BigDecimal.ONE) > 0 || x.compareTo(BigDecimal.ONE.negate()) < 0 ) - { - throw new ArithmeticException("Out of range argument "+ x.toString() + " of asin") ; - } - else if ( x.compareTo(BigDecimal.ZERO) == 0 ) - return BigDecimal.ZERO ; - else if ( x.compareTo(BigDecimal.ONE) == 0 ) - { - /* arcsin(1) = pi/2 - */ - double errpi = Math.sqrt(x.ulp().doubleValue()) ; - MathContext mc = new MathContext( err2prec(3.14159,errpi) ) ; - return pi(mc).divide(new BigDecimal(2)) ; - } - else if ( x.compareTo(BigDecimal.ZERO) < 0 ) - { - return asin(x.negate()).negate() ; - } - else if ( x.doubleValue() > 0.7) - { - final BigDecimal xCompl = BigDecimal.ONE.subtract(x) ; - final double xDbl = x.doubleValue() ; - final double xUlpDbl = x.ulp().doubleValue()/2. ; - final double eps = xUlpDbl/2./Math.sqrt(1.-Math.pow(xDbl,2.)) ; - - final BigDecimal xhighpr = scalePrec(xCompl,3) ; - final BigDecimal xhighprV = divideRound(xhighpr,4) ; - - BigDecimal resul = BigDecimal.ONE ; - - /* x^(2i+1) */ - BigDecimal xpowi = BigDecimal.ONE ; - - /* i factorial */ - BigInteger ifacN = BigInteger.ONE ; - BigInteger ifacD = BigInteger.ONE ; - - for(int i=1 ; ; i++) - { - ifacN = ifacN.multiply(new BigInteger(""+(2*i-1)) ) ; - ifacD = ifacD.multiply(new BigInteger(""+i) ) ; - if ( i == 1) - xpowi = xhighprV ; - else - xpowi = multiplyRound(xpowi,xhighprV) ; - BigDecimal c = divideRound( multiplyRound(xpowi,ifacN), - ifacD.multiply(new BigInteger(""+(2*i+1)) ) ) ; - resul = resul.add(c) ; - /* series started 1+x/12+... which yields an estimate of the sum's error - */ - if ( Math.abs(c.doubleValue()) < xUlpDbl/120.) - break; - } - /* sqrt(2*z)*(1+...) - */ - xpowi = sqrt(xhighpr.multiply(new BigDecimal(2))) ; - resul = multiplyRound(xpowi,resul) ; - - MathContext mc = new MathContext( resul.precision() ) ; - BigDecimal pihalf = pi(mc).divide(new BigDecimal(2)) ; - - mc = new MathContext( err2prec(resul.doubleValue(),eps) ) ; - return pihalf.subtract(resul,mc) ; - } - else - { - /* absolute error in the result is err(x)/sqrt(1-x^2) to lowest order - */ - final double xDbl = x.doubleValue() ; - final double xUlpDbl = x.ulp().doubleValue()/2. ; - final double eps = xUlpDbl/2./Math.sqrt(1.-Math.pow(xDbl,2.)) ; - - final BigDecimal xhighpr = scalePrec(x,2) ; - final BigDecimal xhighprSq = multiplyRound(xhighpr,xhighpr) ; - - BigDecimal resul = xhighpr.plus() ; - - /* x^(2i+1) */ - BigDecimal xpowi = xhighpr ; - - /* i factorial */ - BigInteger ifacN = BigInteger.ONE ; - BigInteger ifacD = BigInteger.ONE ; - - for(int i=1 ; ; i++) - { - ifacN = ifacN.multiply(new BigInteger(""+(2*i-1)) ) ; - ifacD = ifacD.multiply(new BigInteger(""+(2*i)) ) ; - xpowi = multiplyRound(xpowi,xhighprSq) ; - BigDecimal c = divideRound( multiplyRound(xpowi,ifacN), - ifacD.multiply(new BigInteger(""+(2*i+1)) ) ) ; - resul = resul.add(c) ; - if ( Math.abs(c.doubleValue()) < 0.1*eps) - break; - } - MathContext mc = new MathContext( err2prec(resul.doubleValue(),eps) ) ; - return resul.round(mc) ; - } - } /* BigDecimalMath.asin */ - - /** The inverse trigonometric cosine. - * @param x the argument. - * @return the arccos(x) in radians. - * @throws Errore - * @since 2009-09-29 - */ - static public BigDecimal acos(final BigDecimal x) throws Errore - { - /* Essentially forwarded to pi/2 - asin(x) - */ - final BigDecimal xhighpr = scalePrec(x,2) ; - BigDecimal resul = asin(xhighpr) ; - double eps = resul.ulp().doubleValue()/2. ; - - MathContext mc = new MathContext( err2prec(3.14159,eps) ) ; - BigDecimal pihalf = pi(mc).divide(new BigDecimal(2)) ; - resul = pihalf.subtract(resul) ; - - /* absolute error in the result is err(x)/sqrt(1-x^2) to lowest order - */ - final double xDbl = x.doubleValue() ; - final double xUlpDbl = x.ulp().doubleValue()/2. ; - eps = xUlpDbl/2./Math.sqrt(1.-Math.pow(xDbl,2.)) ; - - mc = new MathContext( err2prec(resul.doubleValue(),eps) ) ; - return resul.round(mc) ; - - } /* BigDecimalMath.acos */ - - /** The inverse trigonometric tangent. - * @param x the argument. - * @return the principal value of arctan(x) in radians in the range -pi/2 to +pi/2. - * @throws Errore - * @since 2009-08-03 - */ - static public BigDecimal atan(final BigDecimal x) throws Errore - { - if ( x.compareTo(BigDecimal.ZERO) < 0 ) - { - return atan(x.negate()).negate() ; - } - else if ( x.compareTo(BigDecimal.ZERO) == 0 ) - return BigDecimal.ZERO ; - else if ( x.doubleValue() >0.7 && x.doubleValue() <3.0) - { - /* Abramowitz-Stegun 4.4.34 convergence acceleration - * 2*arctan(x) = arctan(2x/(1-x^2)) = arctan(y). x=(sqrt(1+y^2)-1)/y - * This maps 0<=y<=3 to 0<=x<=0.73 roughly. Temporarily with 2 protectionist digits. - */ - BigDecimal y = scalePrec(x,2) ; - BigDecimal newx = divideRound( hypot(1,y).subtract(BigDecimal.ONE) , y); - - /* intermediate result with too optimistic error estimate*/ - BigDecimal resul = multiplyRound( atan(newx), 2) ; - - /* absolute error in the result is errx/(1+x^2), where errx = half of the ulp. */ - double eps = x.ulp().doubleValue()/( 2.0*Math.hypot(1.0,x.doubleValue()) ) ; - MathContext mc = new MathContext( err2prec(resul.doubleValue(),eps) ) ; - return resul.round(mc) ; - } - else if ( x.doubleValue() < 0.71 ) - { - /* Taylor expansion around x=0; Abramowitz-Stegun 4.4.42 */ - - final BigDecimal xhighpr = scalePrec(x,2) ; - final BigDecimal xhighprSq = multiplyRound(xhighpr,xhighpr).negate() ; - - BigDecimal resul = xhighpr.plus() ; - - /* signed x^(2i+1) */ - BigDecimal xpowi = xhighpr ; - - /* absolute error in the result is errx/(1+x^2), where errx = half of the ulp. - */ - double eps = x.ulp().doubleValue()/( 2.0*Math.hypot(1.0,x.doubleValue()) ) ; - - for(int i= 1 ; ; i++) - { - xpowi = multiplyRound(xpowi,xhighprSq) ; - BigDecimal c = divideRound(xpowi,2*i+1) ; - - resul = resul.add(c) ; - if ( Math.abs(c.doubleValue()) < 0.1*eps) - break; - } - MathContext mc = new MathContext( err2prec(resul.doubleValue(),eps) ) ; - return resul.round(mc) ; - } - else - { - /* Taylor expansion around x=infinity; Abramowitz-Stegun 4.4.42 */ - - /* absolute error in the result is errx/(1+x^2), where errx = half of the ulp. - */ - double eps = x.ulp().doubleValue()/( 2.0*Math.hypot(1.0,x.doubleValue()) ) ; - - /* start with the term pi/2; gather its precision relative to the expected result - */ - MathContext mc = new MathContext( 2+err2prec(3.1416,eps) ) ; - BigDecimal onepi= pi(mc) ; - BigDecimal resul = onepi.divide(new BigDecimal(2)) ; - - final BigDecimal xhighpr = divideRound(-1,scalePrec(x,2)) ; - final BigDecimal xhighprSq = multiplyRound(xhighpr,xhighpr).negate() ; - - /* signed x^(2i+1) */ - BigDecimal xpowi = xhighpr ; - - for(int i= 0 ; ; i++) - { - BigDecimal c = divideRound(xpowi,2*i+1) ; - - resul = resul.add(c) ; - if ( Math.abs(c.doubleValue()) < 0.1*eps) - break; - xpowi = multiplyRound(xpowi,xhighprSq) ; - } - mc = new MathContext( err2prec(resul.doubleValue(),eps) ) ; - return resul.round(mc) ; - } - } /* BigDecimalMath.atan */ - - /** The hyperbolic cosine. - * @param x The argument. - * @return The cosh(x) = (exp(x)+exp(-x))/2 . - * @author Richard J. Mathar - * @throws Errore - * @since 2009-08-19 - */ - static public BigDecimal cosh(final BigDecimal x) throws Errore - { - if ( x.compareTo(BigDecimal.ZERO) < 0) - return cos(x.negate()); - else if ( x.compareTo(BigDecimal.ZERO) == 0 ) - return BigDecimal.ONE ; - else - { - if ( x.doubleValue() > 1.5 ) - { - /* cosh^2(x) = 1+ sinh^2(x). - */ - return hypot(1, sinh(x) ) ; - } - else - { - BigDecimal xhighpr = scalePrec(x,2) ; - /* Simple Taylor expansion, sum_{0=1..infinity} x^(2i)/(2i)! */ - BigDecimal resul = BigDecimal.ONE ; - - /* x^i */ - BigDecimal xpowi = BigDecimal.ONE ; - - /* 2i factorial */ - BigInteger ifac = BigInteger.ONE ; - - /* The absolute error in the result is the error in x^2/2 which is x times the error in x. - */ - double xUlpDbl = 0.5*x.ulp().doubleValue()*x.doubleValue() ; - - /* The error in the result is set by the error in x^2/2 itself, xUlpDbl. - * We need at most k terms to push x^(2k)/(2k)! below this value. - * x^(2k) < xUlpDbl; (2k)*log(x) < log(xUlpDbl); - */ - int k = (int)(Math.log(xUlpDbl)/Math.log(x.doubleValue()) )/2 ; - - /* The individual terms are all smaller than 1, so an estimate of 1.0 for - * the absolute value will give a safe relative error estimate for the indivdual terms - */ - MathContext mcTay = new MathContext( err2prec(1.,xUlpDbl/k) ) ; - for(int i=1 ; ; i++) - { - /* TBD: at which precision will 2*i-1 or 2*i overflow? - */ - ifac = ifac.multiply(new BigInteger(""+(2*i-1) ) ) ; - ifac = ifac.multiply( new BigInteger(""+(2*i)) ) ; - xpowi = xpowi.multiply(xhighpr).multiply(xhighpr) ; - BigDecimal corr = xpowi.divide(new BigDecimal(ifac),mcTay) ; - resul = resul.add( corr ) ; - if ( corr.abs().doubleValue() < 0.5*xUlpDbl ) - break ; - } - /* The error in the result is governed by the error in x itself. - */ - MathContext mc = new MathContext( err2prec(resul.doubleValue(),xUlpDbl) ) ; - return resul.round(mc) ; - } - } - } /* BigDecimalMath.cosh */ - - /** The hyperbolic sine. - * @param x the argument. - * @return the sinh(x) = (exp(x)-exp(-x))/2 . - * @author Richard J. Mathar - * @throws Errore - * @since 2009-08-19 - */ - static public BigDecimal sinh(final BigDecimal x) throws Errore - { - if ( x.compareTo(BigDecimal.ZERO) < 0) - return sinh(x.negate()).negate() ; - else if ( x.compareTo(BigDecimal.ZERO) == 0 ) - return BigDecimal.ZERO ; - else - { - if ( x.doubleValue() > 2.4 ) - { - /* Move closer to zero with sinh(2x)= 2*sinh(x)*cosh(x). - */ - BigDecimal two = new BigDecimal(2) ; - BigDecimal xhalf = x.divide(two) ; - BigDecimal resul = sinh(xhalf).multiply(cosh(xhalf)).multiply(two) ; - /* The error in the result is set by the error in x itself. - * The first derivative of sinh(x) is cosh(x), so the absolute error - * in the result is cosh(x)*errx, and the relative error is coth(x)*errx = errx/tanh(x) - */ - double eps = Math.tanh(x.doubleValue()) ; - MathContext mc = new MathContext( err2prec(0.5*x.ulp().doubleValue()/eps) ) ; - return resul.round(mc) ; - } - else - { - BigDecimal xhighpr = scalePrec(x,2) ; - /* Simple Taylor expansion, sum_{i=0..infinity} x^(2i+1)/(2i+1)! */ - BigDecimal resul = xhighpr ; - - /* x^i */ - BigDecimal xpowi = xhighpr ; - - /* 2i+1 factorial */ - BigInteger ifac = BigInteger.ONE ; - - /* The error in the result is set by the error in x itself. - */ - double xUlpDbl = x.ulp().doubleValue() ; - - /* The error in the result is set by the error in x itself. - * We need at most k terms to squeeze x^(2k+1)/(2k+1)! below this value. - * x^(2k+1) < x.ulp; (2k+1)*log10(x) < -x.precision; 2k*log10(x)< -x.precision; - * 2k*(-log10(x)) > x.precision; 2k*log10(1/x) > x.precision - */ - int k = (int)(x.precision()/Math.log10(1.0/xhighpr.doubleValue()))/2 ; - MathContext mcTay = new MathContext( err2prec(x.doubleValue(),xUlpDbl/k) ) ; - for(int i=1 ; ; i++) - { - /* TBD: at which precision will 2*i or 2*i+1 overflow? - */ - ifac = ifac.multiply(new BigInteger(""+(2*i) ) ) ; - ifac = ifac.multiply( new BigInteger(""+(2*i+1)) ) ; - xpowi = xpowi.multiply(xhighpr).multiply(xhighpr) ; - BigDecimal corr = xpowi.divide(new BigDecimal(ifac),mcTay) ; - resul = resul.add( corr ) ; - if ( corr.abs().doubleValue() < 0.5*xUlpDbl ) - break ; - } - /* The error in the result is set by the error in x itself. - */ - MathContext mc = new MathContext(x.precision() ) ; - return resul.round(mc) ; - } - } - } /* BigDecimalMath.sinh */ - - /** The hyperbolic tangent. - * @param x The argument. - * @return The tanh(x) = sinh(x)/cosh(x). - * @author Richard J. Mathar - * @since 2009-08-20 - */ - static public BigDecimal tanh(final BigDecimal x) - { - if ( x.compareTo(BigDecimal.ZERO) < 0) - return tanh(x.negate()).negate() ; - else if ( x.compareTo(BigDecimal.ZERO) == 0 ) - return BigDecimal.ZERO ; - else - { - BigDecimal xhighpr = scalePrec(x,2) ; - - /* tanh(x) = (1-e^(-2x))/(1+e^(-2x)) . - */ - BigDecimal exp2x = exp( xhighpr.multiply(new BigDecimal(-2)) ) ; - - /* The error in tanh x is err(x)/cosh^2(x). - */ - double eps = 0.5*x.ulp().doubleValue()/Math.pow( Math.cosh(x.doubleValue()), 2.0 ) ; - MathContext mc = new MathContext( err2prec(Math.tanh(x.doubleValue()),eps) ) ; - return BigDecimal.ONE.subtract(exp2x).divide( BigDecimal.ONE.add(exp2x), mc) ; - } - } /* BigDecimalMath.tanh */ - - /** The inverse hyperbolic sine. - * @param x The argument. - * @return The arcsinh(x) . - * @author Richard J. Mathar - * @since 2009-08-20 - */ - static public BigDecimal asinh(final BigDecimal x) - { - if ( x.compareTo(BigDecimal.ZERO) == 0 ) - return BigDecimal.ZERO ; - else - { - BigDecimal xhighpr = scalePrec(x,2) ; - - /* arcsinh(x) = log(x+hypot(1,x)) - */ - BigDecimal logx = log(hypot(1,xhighpr).add(xhighpr)) ; - - /* The absolute error in arcsinh x is err(x)/sqrt(1+x^2) - */ - double xDbl = x.doubleValue() ; - double eps = 0.5*x.ulp().doubleValue()/Math.hypot(1.,xDbl ) ; - MathContext mc = new MathContext( err2prec(logx.doubleValue(),eps) ) ; - return logx.round(mc) ; - } - } /* BigDecimalMath.asinh */ - - /** The inverse hyperbolic cosine. - * @param x The argument. - * @return The arccosh(x) . - * @author Richard J. Mathar - * @since 2009-08-20 - */ - static public BigDecimal acosh(final BigDecimal x) - { - if ( x.compareTo(BigDecimal.ONE) < 0 ) - throw new ArithmeticException("Out of range argument cosh "+x.toString() ) ; - else if ( x.compareTo(BigDecimal.ONE) == 0 ) - return BigDecimal.ZERO ; - else - { - BigDecimal xhighpr = scalePrec(x,2) ; - - /* arccosh(x) = log(x+sqrt(x^2-1)) - */ - BigDecimal logx = log( sqrt(xhighpr.pow(2).subtract(BigDecimal.ONE) ) .add(xhighpr)) ; - - /* The absolute error in arcsinh x is err(x)/sqrt(x^2-1) - */ - double xDbl = x.doubleValue() ; - double eps = 0.5*x.ulp().doubleValue()/Math.sqrt(xDbl*xDbl-1.) ; - MathContext mc = new MathContext( err2prec(logx.doubleValue(),eps) ) ; - return logx.round(mc) ; - } - } /* BigDecimalMath.acosh */ - - /** The Gamma function. - * @param x The argument. - * @return Gamma(x). - * @throws Errore - * @since 2009-08-06 - */ - static public BigDecimal Gamma(final BigDecimal x) throws Errore - { - /* reduce to interval near 1.0 with the functional relation, Abramowitz-Stegun 6.1.33 - */ - if ( x.compareTo(BigDecimal.ZERO) < 0 ) - return divideRound(Gamma( x.add(BigDecimal.ONE) ),x) ; - else if ( x.doubleValue() > 1.5 ) - { - /* Gamma(x) = Gamma(xmin+n) = Gamma(xmin)*Pochhammer(xmin,n). - */ - int n = (int) ( x.doubleValue()-0.5 ); - BigDecimal xmin1 = x.subtract(new BigDecimal(n)) ; - return multiplyRound(Gamma(xmin1), pochhammer(xmin1,n) ) ; - } - else - { - /* apply Abramowitz-Stegun 6.1.33 - */ - BigDecimal z = x.subtract(BigDecimal.ONE) ; - - /* add intermediately 2 digits to the partial sum accumulation - */ - z = scalePrec(z,2) ; - MathContext mcloc = new MathContext(z.precision()) ; - - /* measure of the absolute error is the relative error in the first, logarithmic term - */ - double eps = x.ulp().doubleValue()/x.doubleValue() ; - - BigDecimal resul = log( scalePrec(x,2)).negate() ; - - if ( x.compareTo(BigDecimal.ONE) != 0 ) - { - - BigDecimal gammCompl = BigDecimal.ONE.subtract(gamma(mcloc) ) ; - resul = resul.add( multiplyRound(z,gammCompl) ) ; - for(int n=2; ;n++) - { - /* multiplying z^n/n by zeta(n-1) means that the two relative errors add. - * so the requirement in the relative error of zeta(n)-1 is that this is somewhat - * smaller than the relative error in z^n/n (the absolute error of thelatter is the - * absolute error in z) - */ - BigDecimal c = divideRound(z.pow(n,mcloc),n) ; - MathContext m = new MathContext( err2prec(n*z.ulp().doubleValue()/2./z.doubleValue()) ) ; - c = c.round(m) ; - - /* At larger n, zeta(n)-1 is roughly 1/2^n. The product is c/2^n. - * The relative error in c is c.ulp/2/c . The error in the product should be small versus eps/10. - * Error from 1/2^n is c*err(sigma-1). - * We need a relative error of zeta-1 of the order of c.ulp/50/c. This is an absolute - * error in zeta-1 of c.ulp/50/c/2^n, and also the absolute error in zeta, because zeta is - * of the order of 1. - */ - if ( eps/100./c.doubleValue() < 0.01 ) - m = new MathContext( err2prec(eps/100./c.doubleValue()) ) ; - else - m = new MathContext( 2) ; - /* zeta(n) -1 */ - BigDecimal zetm1 = zeta(n,m).subtract(BigDecimal.ONE) ; - c = multiplyRound(c,zetm1) ; - - if ( n % 2 == 0 ) - resul = resul.add(c) ; - else - resul = resul.subtract(c) ; - - /* alternating sum, so truncating as eps is reached suffices - */ - if ( Math.abs(c.doubleValue()) < eps) - break; - } - } - - /* The relative error in the result is the absolute error in the - * input variable times the digamma (psi) value at that point. - */ - double zdbl = z.doubleValue() ; - eps = psi(zdbl)* x.ulp().doubleValue()/2. ; - mcloc = new MathContext( err2prec(eps) ) ; - return exp(resul).round(mcloc) ; - } - } /* BigDecimalMath.gamma */ - - /** The Gamma function. - * @param q The argument. - * @param mc The required accuracy in the result. - * @return Gamma(x). - * @throws Errore - * @since 2010-05-26 - */ - static public BigDecimal Gamma(final Rational q, final MathContext mc) throws Errore - { - if ( q.isBigInteger() ) - { - if ( q.compareTo(Rational.ZERO) <= 0 ) - throw new ArithmeticException("Gamma at "+q.toString() ) ; - else - { - /* Gamma(n) = (n-1)! */ - Factorial f = new Factorial() ; - BigInteger g = f.at( q.trunc().intValue()-1 ) ; - return scalePrec(new BigDecimal(g),mc) ; - } - } - else if ( q.b.intValue() == 2 ) - { - /* half integer cases which are related to sqrt(pi) - */ - BigDecimal p = sqrt(pi(mc)) ; - if ( q.compareTo(Rational.ZERO) >= 0 ) - { - Rational pro = Rational.ONE ; - Rational f = q.subtract(1) ; - while ( f.compareTo(Rational.ZERO) > 0 ) - { - pro = pro.multiply(f) ; - f = f.subtract(1) ; - } - return multiplyRound(p,pro) ; - } - else - { - Rational pro = Rational.ONE ; - Rational f = q ; - while ( f.compareTo(Rational.ZERO) < 0 ) - { - pro = pro.divide(f) ; - f = f.add(1) ; - } - return multiplyRound(p,pro) ; - } - } - else - { - /* The relative error of the result is psi(x)*Delta(x). Tune Delta(x) such - * that this is equivalent to mc: Delta(x) = precision/psi(x). - */ - double qdbl = q.doubleValue() ; - double deltx = 5.*Math.pow(10.,-mc.getPrecision()) /psi(qdbl) ; - - MathContext mcx = new MathContext( err2prec(qdbl,deltx) ) ; - BigDecimal x = q.BigDecimalValue(mcx) ; - - /* forward calculation to the general floating point case */ - return Gamma(x) ; - } - } /* BigDecimalMath.Gamma */ - - /** Pochhammer's function. - * @param x The main argument. - * @param n The non-negative index. - * @return (x)_n = x(x+1)(x+2)*...*(x+n-1). - * @since 2009-08-19 - */ - static public BigDecimal pochhammer(final BigDecimal x, final int n) - { - /* reduce to interval near 1.0 with the functional relation, Abramowitz-Stegun 6.1.33 - */ - if ( n < 0 ) - throw new ProviderException("Not implemented: pochhammer with negative index "+n) ; - else if ( n == 0 ) - return BigDecimal.ONE ; - else - { - /* internally two safety digits - */ - BigDecimal xhighpr = scalePrec(x,2) ; - BigDecimal resul = xhighpr ; - - double xUlpDbl = x.ulp().doubleValue() ; - double xDbl = x.doubleValue() ; - /* relative error of the result is the sum of the relative errors of the factors - */ - double eps = 0.5*xUlpDbl/Math.abs(xDbl) ; - for (int i =1 ; i < n ; i++) - { - eps += 0.5*xUlpDbl/Math.abs(xDbl+i) ; - resul = resul.multiply( xhighpr.add(new BigDecimal(i)) ) ; - final MathContext mcloc = new MathContext(4+ err2prec(eps) ) ; - resul = resul.round(mcloc) ; - } - return resul.round(new MathContext(err2prec(eps)) ) ; - } - } /* BigDecimalMath.pochhammer */ - - /** Reduce value to the interval [0,2*Pi]. - * @param x the original value - * @return the value modulo 2*pi in the interval from 0 to 2*pi. - * @throws Errore - * @since 2009-06-01 - */ - static public BigDecimal mod2pi(BigDecimal x) throws Errore - { - /* write x= 2*pi*k+r with the precision in r defined by the precision of x and not - * compromised by the precision of 2*pi, so the ulp of 2*pi*k should match the ulp of x. - * First get a guess of k to figure out how many digits of 2*pi are needed. - */ - int k = (int)(0.5*x.doubleValue()/Math.PI) ; - - /* want to have err(2*pi*k)< err(x)=0.5*x.ulp, so err(pi) = err(x)/(4k) with two safety digits - */ - double err2pi ; - if ( k != 0 ) - err2pi = 0.25*Math.abs(x.ulp().doubleValue()/k) ; - else - err2pi = 0.5*Math.abs(x.ulp().doubleValue()) ; - MathContext mc = new MathContext( 2+err2prec(6.283,err2pi) ) ; - BigDecimal twopi= pi(mc).multiply(new BigDecimal(2)) ; - - /* Delegate the actual operation to the BigDecimal class, which may return - * a negative value of x was negative . - */ - BigDecimal res = x.remainder(twopi) ; - if ( res.compareTo(BigDecimal.ZERO) < 0 ) - res = res.add(twopi) ; - - /* The actual precision is set by the input value, its absolute value of x.ulp()/2. - */ - mc = new MathContext( err2prec(res.doubleValue(),x.ulp().doubleValue()/2.) ) ; - return res.round(mc) ; - } /* mod2pi */ - - /** Reduce value to the interval [-Pi/2,Pi/2]. - * @param x The original value - * @return The value modulo pi, shifted to the interval from -Pi/2 to Pi/2. - * @throws Errore - * @since 2009-07-31 - */ - static public BigDecimal modpi(BigDecimal x) throws Errore - { - /* write x= pi*k+r with the precision in r defined by the precision of x and not - * compromised by the precision of pi, so the ulp of pi*k should match the ulp of x. - * First get a guess of k to figure out how many digits of pi are needed. - */ - int k = (int)(x.doubleValue()/Math.PI) ; - - /* want to have err(pi*k)< err(x)=x.ulp/2, so err(pi) = err(x)/(2k) with two safety digits - */ - double errpi ; - if ( k != 0 ) - errpi = 0.5*Math.abs(x.ulp().doubleValue()/k) ; - else - errpi = 0.5*Math.abs(x.ulp().doubleValue()) ; - MathContext mc = new MathContext( 2+err2prec(3.1416,errpi) ) ; - BigDecimal onepi= pi(mc) ; - BigDecimal pihalf = onepi.divide(new BigDecimal(2)) ; - - /* Delegate the actual operation to the BigDecimal class, which may return - * a negative value of x was negative . - */ - BigDecimal res = x.remainder(onepi) ; - if ( res.compareTo(pihalf) > 0 ) - res = res.subtract(onepi) ; - else if ( res.compareTo(pihalf.negate()) < 0 ) - res = res.add(onepi) ; - - /* The actual precision is set by the input value, its absolute value of x.ulp()/2. - */ - mc = new MathContext( err2prec(res.doubleValue(),x.ulp().doubleValue()/2.) ) ; - return res.round(mc) ; - } /* modpi */ - - /** Riemann zeta function. - * @param n The positive integer argument. - * @param mc Specification of the accuracy of the result. - * @return zeta(n). - * @throws Errore - * @since 2009-08-05 - */ - static public BigDecimal zeta(final int n, final MathContext mc) throws Errore - { - if( n <= 0 ) - throw new ProviderException("Not implemented: zeta at negative argument "+n) ; - if( n == 1 ) - throw new ArithmeticException("Pole at zeta(1) ") ; - - if( n % 2 == 0 ) - { - /* Even indices. Abramowitz-Stegun 23.2.16. Start with 2^(n-1)*B(n)/n! - */ - Rational b = (new Bernoulli()).at(n).abs() ; - b = b.divide((new Factorial()).at(n)) ; - b = b.multiply( BigInteger.ONE.shiftLeft(n-1) ); - - /* to be multiplied by pi^n. Absolute error in the result of pi^n is n times - * error in pi times pi^(n-1). Relative error is n*error(pi)/pi, requested by mc. - * Need one more digit in pi if n=10, two digits if n=100 etc, and add one extra digit. - */ - MathContext mcpi = new MathContext( mc.getPrecision() + (int)(Math.log10(10.0*n)) ) ; - final BigDecimal piton = pi(mcpi).pow(n,mc) ; - return multiplyRound( piton, b) ; - } - else if ( n == 3) - { - /* Broadhurst BBP arXiv:math/9803067 - * Error propagation: S31 is roughly 0.087, S33 roughly 0.131 - */ - int[] a31 = {1,-7,-1,10,-1,-7,1,0} ; - int[] a33 = {1,1,-1,-2,-1,1,1,0} ; - BigDecimal S31 = broadhurstBBP(3,1,a31,mc) ; - BigDecimal S33 = broadhurstBBP(3,3,a33,mc) ; - S31 = S31.multiply(new BigDecimal(48)) ; - S33 = S33.multiply(new BigDecimal(32)) ; - return S31.add(S33).divide(new BigDecimal(7),mc) ; - } - else if ( n == 5) - { - /* Broadhurst BBP arXiv:math/9803067 - * Error propagation: S51 is roughly -11.15, S53 roughly 22.165, S55 is roughly 0.031 - * 9*2048*S51/6265 = -3.28. 7*2038*S53/61651= 5.07. 738*2048*S55/61651= 0.747. - * The result is of the order 1.03, so we add 2 digits to S51 and S52 and one digit to S55. - */ - int[] a51 = {31,-1614,-31,-6212,-31,-1614,31,74552} ; - int[] a53 = {173,284,-173,-457,-173,284,173,-111} ; - int[] a55 = {1,0,-1,-1,-1,0,1,1} ; - BigDecimal S51 = broadhurstBBP(5,1,a51, new MathContext(2+mc.getPrecision()) ) ; - BigDecimal S53 = broadhurstBBP(5,3,a53, new MathContext(2+mc.getPrecision()) ) ; - BigDecimal S55 = broadhurstBBP(5,5,a55, new MathContext(1+mc.getPrecision()) ) ; - S51 = S51.multiply(new BigDecimal(18432)) ; - S53 = S53.multiply(new BigDecimal(14336)) ; - S55 = S55.multiply(new BigDecimal(1511424)) ; - return S51.add(S53).subtract(S55).divide(new BigDecimal(62651),mc) ; - } - else - { - /* Cohen et al Exp Math 1 (1) (1992) 25 - */ - Rational betsum = new Rational() ; - Bernoulli bern = new Bernoulli() ; - Factorial fact = new Factorial() ; - for(int npr=0 ; npr <= (n+1)/2 ; npr++) - { - Rational b = bern.at(2*npr).multiply(bern.at(n+1-2*npr)) ; - b = b.divide(fact.at(2*npr)).divide(fact.at(n+1-2*npr)) ; - b = b.multiply(1-2*npr) ; - if ( npr % 2 ==0 ) - betsum = betsum.add(b) ; - else - betsum = betsum.subtract(b) ; - } - betsum = betsum.divide(n-1) ; - /* The first term, including the facor (2pi)^n, is essentially most - * of the result, near one. The second term below is roughly in the range 0.003 to 0.009. - * So the precision here is matching the precisionn requested by mc, and the precision - * requested for 2*pi is in absolute terms adjusted. - */ - MathContext mcloc = new MathContext( 2+mc.getPrecision() + (int)(Math.log10((double)(n))) ) ; - BigDecimal ftrm = pi(mcloc).multiply(new BigDecimal(2)) ; - ftrm = ftrm.pow(n) ; - ftrm = multiplyRound(ftrm, betsum.BigDecimalValue(mcloc) ) ; - BigDecimal exps = new BigDecimal(0) ; - - /* the basic accuracy of the accumulated terms before multiplication with 2 - */ - double eps = Math.pow(10.,-mc.getPrecision()) ; - - if ( n % 4 == 3) - { - /* since the argument n is at least 7 here, the drop - * of the terms is at rather constant pace at least 10^-3, for example - * 0.0018, 0.2e-7, 0.29e-11, 0.74e-15 etc for npr=1,2,3.... We want 2 times these terms - * fall below eps/10. - */ - int kmax = mc.getPrecision()/3 ; - eps /= kmax ; - /* need an error of eps for 2/(exp(2pi)-1) = 0.0037 - * The absolute error is 4*exp(2pi)*err(pi)/(exp(2pi)-1)^2=0.0075*err(pi) - */ - BigDecimal exp2p = pi( new MathContext(3+err2prec(3.14, eps/0.0075)) ) ; - exp2p = exp(exp2p.multiply(new BigDecimal(2))) ; - BigDecimal c = exp2p.subtract(BigDecimal.ONE) ; - exps = divideRound(1,c) ; - for(int npr=2 ; npr<= kmax ; npr++) - { - /* the error estimate above for npr=1 is the worst case of - * the absolute error created by an error in 2pi. So we can - * safely re-use the exp2p value computed above without - * reassessment of its error. - */ - c = powRound(exp2p,npr).subtract(BigDecimal.ONE) ; - c = multiplyRound(c, (new BigInteger(""+npr)).pow(n) ) ; - c = divideRound(1,c) ; - exps = exps.add(c) ; - } - } - else - { - /* since the argument n is at least 9 here, the drop - * of the terms is at rather constant pace at least 10^-3, for example - * 0.0096, 0.5e-7, 0.3e-11, 0.6e-15 etc. We want these terms - * fall below eps/10. - */ - int kmax = (1+mc.getPrecision())/3 ; - eps /= kmax ; - /* need an error of eps for 2/(exp(2pi)-1)*(1+4*Pi/8/(1-exp(-2pi)) = 0.0096 - * at k=7 or = 0.00766 at k=13 for example. - * The absolute error is 0.017*err(pi) at k=9, 0.013*err(pi) at k=13, 0.012 at k=17 - */ - BigDecimal twop = pi( new MathContext(3+err2prec(3.14, eps/0.017)) ) ; - twop = twop.multiply(new BigDecimal(2)) ; - BigDecimal exp2p = exp(twop) ; - BigDecimal c = exp2p.subtract(BigDecimal.ONE) ; - exps = divideRound(1,c) ; - c = BigDecimal.ONE.subtract(divideRound(1,exp2p)) ; - c = divideRound(twop,c).multiply(new BigDecimal(2)) ; - c = divideRound(c,n-1).add(BigDecimal.ONE) ; - exps = multiplyRound(exps,c) ; - for(int npr=2 ; npr<= kmax ; npr++) - { - c = powRound(exp2p,npr).subtract(BigDecimal.ONE) ; - c = multiplyRound(c, (new BigInteger(""+npr)).pow(n) ) ; - - BigDecimal d = divideRound(1, exp2p.pow(npr) ) ; - d = BigDecimal.ONE.subtract(d) ; - d = divideRound(twop,d).multiply(new BigDecimal(2*npr)) ; - d = divideRound(d,n-1).add(BigDecimal.ONE) ; - - d = divideRound(d,c) ; - - exps = exps.add(d) ; - } - } - exps = exps.multiply(new BigDecimal(2)) ; - return ftrm.subtract(exps,mc) ; - } - } /* zeta */ - - /** Riemann zeta function. - * @param n The positive integer argument. - * @return zeta(n)-1. - * @throws Errore - * @since 2009-08-20 - */ - static public double zeta1(final int n) throws Errore - { - /* precomputed static table in double precision - */ - final double[] zmin1 = {0.,0., -6.449340668482264364724151666e-01, -2.020569031595942853997381615e-01,8.232323371113819151600369654e-02, -3.692775514336992633136548646e-02,1.734306198444913971451792979e-02, -8.349277381922826839797549850e-03,4.077356197944339378685238509e-03, -2.008392826082214417852769232e-03,9.945751278180853371459589003e-04, -4.941886041194645587022825265e-04,2.460865533080482986379980477e-04, -1.227133475784891467518365264e-04,6.124813505870482925854510514e-05, -3.058823630702049355172851064e-05,1.528225940865187173257148764e-05, -7.637197637899762273600293563e-06,3.817293264999839856461644622e-06, -1.908212716553938925656957795e-06,9.539620338727961131520386834e-07, -4.769329867878064631167196044e-07,2.384505027277329900036481868e-07, -1.192199259653110730677887189e-07,5.960818905125947961244020794e-08, -2.980350351465228018606370507e-08,1.490155482836504123465850663e-08, -7.450711789835429491981004171e-09,3.725334024788457054819204018e-09, -1.862659723513049006403909945e-09,9.313274324196681828717647350e-10, -4.656629065033784072989233251e-10,2.328311833676505492001455976e-10, -1.164155017270051977592973835e-10,5.820772087902700889243685989e-11, -2.910385044497099686929425228e-11,1.455192189104198423592963225e-11, -7.275959835057481014520869012e-12,3.637979547378651190237236356e-12, -1.818989650307065947584832101e-12,9.094947840263889282533118387e-13, -4.547473783042154026799112029e-13,2.273736845824652515226821578e-13, -1.136868407680227849349104838e-13,5.684341987627585609277182968e-14, -2.842170976889301855455073705e-14,1.421085482803160676983430714e-14, -7.105427395210852712877354480e-15,3.552713691337113673298469534e-15, -1.776356843579120327473349014e-15,8.881784210930815903096091386e-16, -4.440892103143813364197770940e-16,2.220446050798041983999320094e-16, -1.110223025141066133720544570e-16,5.551115124845481243723736590e-17, -2.775557562136124172581632454e-17,1.387778780972523276283909491e-17, -6.938893904544153697446085326e-18,3.469446952165922624744271496e-18, -1.734723476047576572048972970e-18,8.673617380119933728342055067e-19, -4.336808690020650487497023566e-19,2.168404344997219785013910168e-19, -1.084202172494241406301271117e-19,5.421010862456645410918700404e-20, -2.710505431223468831954621312e-20,1.355252715610116458148523400e-20, -6.776263578045189097995298742e-21,3.388131789020796818085703100e-21, -1.694065894509799165406492747e-21,8.470329472546998348246992609e-22, -4.235164736272833347862270483e-22,2.117582368136194731844209440e-22, -1.058791184068023385226500154e-22,5.293955920339870323813912303e-23, -2.646977960169852961134116684e-23,1.323488980084899080309451025e-23, -6.617444900424404067355245332e-24,3.308722450212171588946956384e-24, -1.654361225106075646229923677e-24,8.271806125530344403671105617e-25, -4.135903062765160926009382456e-25,2.067951531382576704395967919e-25, -1.033975765691287099328409559e-25,5.169878828456431320410133217e-26, -2.584939414228214268127761771e-26,1.292469707114106670038112612e-26, -6.462348535570531803438002161e-27,3.231174267785265386134814118e-27, -1.615587133892632521206011406e-27,8.077935669463162033158738186e-28, -4.038967834731580825622262813e-28,2.019483917365790349158762647e-28, -1.009741958682895153361925070e-28,5.048709793414475696084771173e-29, -2.524354896707237824467434194e-29,1.262177448353618904375399966e-29, -6.310887241768094495682609390e-30,3.155443620884047239109841220e-30, -1.577721810442023616644432780e-30,7.888609052210118073520537800e-31 - } ; - if( n <= 0 ) - throw new ProviderException("Not implemented: zeta at negative argument "+n) ; - if( n == 1 ) - throw new ArithmeticException("Pole at zeta(1) ") ; - - if( n < zmin1.length ) - /* look it up if available */ - return zmin1[n] ; - else - { - /* Result is roughly 2^(-n), desired accuracy 18 digits. If zeta(n) is computed, the equivalent accuracy - * in relative units is higher, because zeta is around 1. - */ - double eps = 1.e-18*Math.pow(2.,(double)(-n) ) ; - MathContext mc = new MathContext( err2prec(eps) ) ; - return zeta(n,mc).subtract(BigDecimal.ONE).doubleValue() ; - } - } /* zeta */ - - - /** trigonometric cot. - * @param x The argument. - * @return cot(x) = 1/tan(x). - */ - static public double cot(final double x) - { - return 1./Math.tan(x) ; - } - - /** Digamma function. - * @param x The main argument. - * @return psi(x). - * The error is sometimes up to 10 ulp, where AS 6.3.15 suffers from cancellation of digits and psi=0 - * @throws Errore - * @since 2009-08-26 - */ - static public double psi(final double x) throws Errore - { - /* the single positive zero of psi(x) - */ - final double psi0 = 1.46163214496836234126265954232572132846819; - if ( x > 2.0) - { - /* Reduce to a value near x=1 with the standard recurrence formula. - * Abramowitz-Stegun 6.3.5 - */ - int m = (int) ( x-0.5 ); - double xmin1 = x-m ; - double resul = 0. ; - for(int i=1; i <= m ; i++) - resul += 1./(x-i) ; - return resul+psi(xmin1) ; - } - else if ( Math.abs(x-psi0) < 0.55) - { - /* Taylor approximation around the local zero - */ - final double [] psiT0 = { 9.67672245447621170427e-01, -4.42763168983592106093e-01, - 2.58499760955651010624e-01, -1.63942705442406527504e-01, 1.07824050691262365757e-01, - -7.21995612564547109261e-02, 4.88042881641431072251e-02, -3.31611264748473592923e-02, - 2.25976482322181046596e-02, -1.54247659049489591388e-02, 1.05387916166121753881e-02, - -7.20453438635686824097e-03, 4.92678139572985344635e-03, -3.36980165543932808279e-03, - 2.30512632673492783694e-03, -1.57693677143019725927e-03, 1.07882520191629658069e-03, - -7.38070938996005129566e-04, 5.04953265834602035177e-04, -3.45468025106307699556e-04, - 2.36356015640270527924e-04, -1.61706220919748034494e-04, 1.10633727687474109041e-04, - -7.56917958219506591924e-05, 5.17857579522208086899e-05, -3.54300709476596063157e-05, - 2.42400661186013176527e-05, -1.65842422718541333752e-05, 1.13463845846638498067e-05, - -7.76281766846209442527e-06, 5.31106092088986338732e-06, -3.63365078980104566837e-06, - 2.48602273312953794890e-06, -1.70085388543326065825e-06, 1.16366753635488427029e-06, - -7.96142543124197040035e-07, 5.44694193066944527850e-07, -3.72661612834382295890e-07, - 2.54962655202155425666e-07, -1.74436951177277452181e-07, 1.19343948298302427790e-07, - -8.16511518948840884084e-08, 5.58629968353217144428e-08, -3.82196006191749421243e-08, - 2.61485769519618662795e-08, -1.78899848649114926515e-08, 1.22397314032336619391e-08, - -8.37401629767179054290e-09, 5.72922285984999377160e-09} ; - final double xdiff = x-psi0 ; - double resul = 0. ; - for( int i = psiT0.length-1; i >=0 ; i--) - resul = resul*xdiff+psiT0[i] ; - return resul*xdiff ; - } - else if ( x < 0. ) - { - /* Reflection formula */ - double xmin = 1.-x ; - return psi(xmin) + Math.PI/Math.tan(Math.PI*xmin) ; - } - else - { - double xmin1 = x-1 ; - double resul = 0. ; - for(int k=26 ; k>= 1; k--) - { - resul -= zeta1(2*k+1) ; - resul *= xmin1*xmin1 ; - } - /* 0.422... = 1 -gamma */ - return resul + 0.422784335098467139393487909917597568 - + 0.5/xmin1-1./(1-xmin1*xmin1)- Math.PI/( 2.*Math.tan(Math.PI*xmin1) ); - } - } /* psi */ - - - /** Broadhurst ladder sequence. - * @param a The vector of 8 integer arguments - * @param mc Specification of the accuracy of the result - * @return S_(n,p)(a) - * @throws Errore - * @since 2009-08-09 - * @see arXiv:math/9803067 - */ - static protected BigDecimal broadhurstBBP(final int n, final int p, final int a[], MathContext mc) throws Errore - { - /* Explore the actual magnitude of the result first with a quick estimate. - */ - double x = 0.0 ; - for(int k=1; k < 10 ; k++) - x += a[ (k-1) % 8]/Math.pow(2., p*(k+1)/2)/Math.pow((double)k,n) ; - - /* Convert the relative precision and estimate of the result into an absolute precision. - */ - double eps = prec2err(x,mc.getPrecision()) ; - - /* Divide this through the number of terms in the sum to account for error accumulation - * The divisor 2^(p(k+1)/2) means that on the average each 8th term in k has shrunk by - * relative to the 8th predecessor by 1/2^(4p). 1/2^(4pc) = 10^(-precision) with c the 8term - * cycles yields c=log_2( 10^precision)/4p = 3.3*precision/4p with k=8c - */ - int kmax= (int)(6.6*mc.getPrecision()/p) ; - - /* Now eps is the absolute error in each term */ - eps /= kmax ; - BigDecimal res = BigDecimal.ZERO ; - for(int c =0 ; ; c++) - { - Rational r = new Rational() ; - for (int k=0; k < 8 ; k++) - { - Rational tmp = new Rational(new BigInteger(""+a[k]),(new BigInteger(""+(1+8*c+k))).pow(n)) ; - /* floor( (pk+p)/2) - */ - int pk1h = p*(2+8*c+k)/2 ; - tmp = tmp.divide( BigInteger.ONE.shiftLeft(pk1h) ) ; - r = r.add(tmp) ; - } - - if ( Math.abs(r.doubleValue()) < eps) - break; - MathContext mcloc = new MathContext( 1+err2prec(r.doubleValue(),eps) ) ; - res = res.add( r.BigDecimalValue(mcloc) ) ; - } - return res.round(mc) ; - } /* broadhurstBBP */ - - - - - - - - - /** Add a BigDecimal and a BigInteger. - * @param x The left summand - * @param y The right summand - * @return The sum x+y. - * @since 2012-03-02 - */ - static public BigDecimal add(final BigDecimal x, final BigInteger y) - { - return x.add(new BigDecimal(y)) ; - } /* add */ - - - /** Add and round according to the larger of the two ulp's. - * @param x The left summand - * @param y The right summand - * @return The sum x+y. - * @since 2009-07-30 - */ - static public BigDecimal addRound(final BigDecimal x, final BigDecimal y) - { - BigDecimal resul = x.add(y) ; - /* The estimation of the absolute error in the result is |err(y)|+|err(x)| - */ - double errR = Math.abs( y.ulp().doubleValue()/2. ) + Math.abs( x.ulp().doubleValue()/2. ) ; - int err2prec = err2prec(resul.doubleValue(),errR); - if (err2prec < 0) { - err2prec = 0; - } - MathContext mc = new MathContext(err2prec) ; - return resul.round(mc) ; - } /* addRound */ - - /** Add and round according to the larger of the two ulp's. - * @param x The left summand - * @param y The right summand - * @return The sum x+y. - * @since 2010-07-19 - */ - static public BigComplex addRound(final BigComplex x, final BigDecimal y) - { - final BigDecimal R = addRound(x.re,y) ; - return new BigComplex(R,x.im) ; - } /* addRound */ - - /** Add and round according to the larger of the two ulp's. - * @param x The left summand - * @param y The right summand - * @return The sum x+y. - * @since 2010-07-19 - */ - static public BigComplex addRound(final BigComplex x, final BigComplex y) - { - final BigDecimal R = addRound(x.re,y.re) ; - final BigDecimal I = addRound(x.im,y.im) ; - return new BigComplex(R,I) ; - } /* addRound */ - - /** Subtract and round according to the larger of the two ulp's. - * @param x The left term. - * @param y The right term. - * @return The difference x-y. - * @since 2009-07-30 - */ - static public BigDecimal subtractRound(final BigDecimal x, final BigDecimal y) - { - BigDecimal resul = x.subtract(y) ; - /* The estimation of the absolute error in the result is |err(y)|+|err(x)| - */ - double errR = Math.abs( y.ulp().doubleValue()/2. ) + Math.abs( x.ulp().doubleValue()/2. ) ; - MathContext mc = new MathContext( err2prec(resul.doubleValue(),errR) ) ; - return resul.round(mc) ; - } /* subtractRound */ - - /** Subtract and round according to the larger of the two ulp's. - * @param x The left summand - * @param y The right summand - * @return The difference x-y. - * @since 2010-07-19 - */ - static public BigComplex subtractRound(final BigComplex x, final BigComplex y) - { - final BigDecimal R = subtractRound(x.re,y.re) ; - final BigDecimal I = subtractRound(x.im,y.im) ; - return new BigComplex(R,I) ; - } /* subtractRound */ - - /** Multiply and round. - * @param x The left factor. - * @param y The right factor. - * @return The product x*y. - * @since 2009-07-30 - */ - static public BigDecimal multiplyRound(final BigDecimal x, final BigDecimal y) - { - BigDecimal resul = x.multiply(y) ; - /* The estimation of the relative error in the result is the sum of the relative - * errors |err(y)/y|+|err(x)/x| - */ - MathContext mc = new MathContext( Math.min(x.precision(),y.precision()) ) ; - return resul.round(mc) ; - } /* multiplyRound */ - - /** Multiply and round. - * @param x The left factor. - * @param y The right factor. - * @return The product x*y. - * @since 2010-07-19 - */ - static public BigComplex multiplyRound(final BigComplex x, final BigDecimal y) - { - BigDecimal R = multiplyRound(x.re,y) ; - BigDecimal I = multiplyRound(x.im,y) ; - return new BigComplex(R,I) ; - } /* multiplyRound */ - - /** Multiply and round. - * @param x The left factor. - * @param y The right factor. - * @return The product x*y. - * @since 2010-07-19 - */ - static public BigComplex multiplyRound(final BigComplex x, final BigComplex y) - { - BigDecimal R = subtractRound(multiplyRound(x.re,y.re), multiplyRound(x.im,y.im)) ; - BigDecimal I = addRound(multiplyRound(x.re,y.im), multiplyRound(x.im,y.re)) ; - return new BigComplex(R,I) ; - } /* multiplyRound */ - - /** Multiply and round. - * @param x The left factor. - * @param f The right factor. - * @return The product x*f. - * @since 2009-07-30 - */ - static public BigDecimal multiplyRound(final BigDecimal x, final Rational f) - { - if ( f.compareTo(BigInteger.ZERO) == 0 ) - return BigDecimal.ZERO ; - else - { - /* Convert the rational value with two digits of extra precision - */ - MathContext mc = new MathContext( 2+x.precision() ) ; - BigDecimal fbd = f.BigDecimalValue(mc) ; - - /* and the precision of the product is then dominated by the precision in x - */ - return multiplyRound(x,fbd) ; - } - } - - /** Multiply and round. - * @param x The left factor. - * @param n The right factor. - * @return The product x*n. - * @since 2009-07-30 - */ - static public BigDecimal multiplyRound(final BigDecimal x, final int n) - { - BigDecimal resul = x.multiply(new BigDecimal(n)) ; - /* The estimation of the absolute error in the result is |n*err(x)| - */ - MathContext mc = new MathContext( n != 0 ? x.precision(): 0 ) ; - return resul.round(mc) ; - } - - /** Multiply and round. - * @param x The left factor. - * @param n The right factor. - * @return the product x*n - * @since 2009-07-30 - */ - static public BigDecimal multiplyRound(final BigDecimal x, final BigInteger n) - { - BigDecimal resul = x.multiply(new BigDecimal(n)) ; - /* The estimation of the absolute error in the result is |n*err(x)| - */ - MathContext mc = new MathContext( n.compareTo(BigInteger.ZERO) != 0 ? x.precision(): 0 ) ; - return resul.round(mc) ; - } - - /** Divide and round. - * @param x The numerator - * @param y The denominator - * @return the divided x/y - * @since 2009-07-30 - */ - static public BigDecimal divideRound(final BigDecimal x, final BigDecimal y) - { - /* The estimation of the relative error in the result is |err(y)/y|+|err(x)/x| - */ - MathContext mc = new MathContext( Math.min(x.precision(),y.precision()) ) ; - BigDecimal resul = x.divide(y,mc) ; - /* If x and y are precise integer values that may have common factors, - * the method above will truncate trailing zeros, which may result in - * a smaller apparent accuracy than starte... add missing trailing zeros now. - */ - return scalePrec(resul,mc) ; - } - - /** Build the inverse and maintain the approximate accuracy. - * @param z The denominator - * @return The divided 1/z = [Re(z)-i*Im(z)]/ [Re^2 z + Im^2 z] - * @since 2010-07-19 - */ - static public BigComplex invertRound(final BigComplex z) - { - if (z.im.compareTo(BigDecimal.ZERO) == 0) - { - /* In this case with vanishing Im(x), the result is simply 1/Re z. - */ - final MathContext mc = new MathContext( z.re.precision() ) ; - return new BigComplex( BigDecimal.ONE.divide( z.re, mc) ) ; - } - else if (z.re.compareTo(BigDecimal.ZERO) == 0) - { - /* In this case with vanishing Re(z), the result is simply -i/Im z - */ - final MathContext mc = new MathContext( z.im.precision() ) ; - return new BigComplex(BigDecimal.ZERO, BigDecimal.ONE.divide( z.im, mc).negate() ) ; - } - else - { - /* 1/(x.re+I*x.im) = 1/(x.re+x.im^2/x.re) - I /(x.im +x.re^2/x.im) - */ - BigDecimal R = addRound(z.re, divideRound(multiplyRound(z.im,z.im), z.re) ) ; - BigDecimal I = addRound(z.im, divideRound(multiplyRound(z.re,z.re), z.im) ) ; - MathContext mc = new MathContext( 1+R.precision() ) ; - R = BigDecimal.ONE.divide(R,mc) ; - mc = new MathContext( 1+I.precision() ) ; - I = BigDecimal.ONE.divide(I,mc) ; - return new BigComplex(R,I.negate()) ; - } - } - - /** Divide and round. - * @param x The numerator - * @param y The denominator - * @return the divided x/y - * @since 2010-07-19 - */ - static public BigComplex divideRound(final BigComplex x, final BigComplex y) - { - return multiplyRound( x, invertRound(y) ) ; - } - - /** Divide and round. - * @param x The numerator - * @param n The denominator - * @return the divided x/n - * @since 2009-07-30 - */ - static public BigDecimal divideRound(final BigDecimal x, final int n) - { - /* The estimation of the relative error in the result is |err(x)/x| - */ - MathContext mc = new MathContext( x.precision() ) ; - return x.divide(new BigDecimal(n),mc) ; - } - - /** Divide and round. - * @param x The numerator - * @param n The denominator - * @return the divided x/n - * @since 2009-07-30 - */ - static public BigDecimal divideRound(final BigDecimal x, final BigInteger n) - { - /* The estimation of the relative error in the result is |err(x)/x| - */ - MathContext mc = new MathContext( x.precision() ) ; - return x.divide(new BigDecimal(n),mc) ; - } /* divideRound */ - - /** Divide and round. - * @param n The numerator - * @param x The denominator - * @return the divided n/x - * @since 2009-08-05 - */ - static public BigDecimal divideRound(final BigInteger n, final BigDecimal x) - { - /* The estimation of the relative error in the result is |err(x)/x| - */ - MathContext mc = new MathContext( x.precision() ) ; - return new BigDecimal(n).divide(x,mc) ; - } /* divideRound */ - - /** Divide and round. - * @param n The numerator - * @param x The denominator - * @return the divided n/x - * @since 2012-03-01 - */ - static public BigComplex divideRound(final BigInteger n, final BigComplex x) - { - /* catch case of real-valued denominator first - */ - if ( x.im.compareTo(BigDecimal.ZERO) == 0 ) - return new BigComplex( divideRound(n,x.re),BigDecimal.ZERO ) ; - else if ( x.re.compareTo(BigDecimal.ZERO) == 0 ) - return new BigComplex( BigDecimal.ZERO, divideRound(n,x.im).negate() ) ; - - BigComplex z = invertRound(x) ; - /* n/(x+iy) = nx/(x^2+y^2) -nyi/(x^2+y^2) - */ - BigDecimal repart = multiplyRound(z.re, n) ; - BigDecimal impart = multiplyRound(z.im, n) ; - return new BigComplex( repart, impart) ; - } /* divideRound */ - - /** Divide and round. - * @param n The numerator. - * @param x The denominator. - * @return the divided n/x. - * @since 2009-08-05 - */ - static public BigDecimal divideRound(final int n, final BigDecimal x) - { - /* The estimation of the relative error in the result is |err(x)/x| - */ - MathContext mc = new MathContext( x.precision() ) ; - return new BigDecimal(n).divide(x,mc) ; - } - - /** Append decimal zeros to the value. This returns a value which appears to have - * a higher precision than the input. - * @param x The input value - * @param d The (positive) value of zeros to be added as least significant digits. - * @return The same value as the input but with increased (pseudo) precision. - */ - static public BigDecimal scalePrec(final BigDecimal x, int d) - { - return x.setScale(d+x.scale()) ; - } - - /** Append decimal zeros to the value. This returns a value which appears to have - * a higher precision than the input. - * @param x The input value - * @param d The (positive) value of zeros to be added as least significant digits. - * @return The same value as the input but with increased (pseudo) precision. - */ - static public BigComplex scalePrec(final BigComplex x, int d) - { - return new BigComplex( scalePrec(x.re,d),scalePrec(x.im,d)) ; - } - - /** Boost the precision by appending decimal zeros to the value. This returns a value which appears to have - * a higher precision than the input. - * @param x The input value - * @param mc The requirement on the minimum precision on return. - * @return The same value as the input but with increased (pseudo) precision. - */ - static public BigDecimal scalePrec(final BigDecimal x, final MathContext mc) - { - final int diffPr = mc.getPrecision() - x.precision() ; - if ( diffPr > 0 ) - return scalePrec(x, diffPr) ; - else - return x ; - } /* BigDecimalMath.scalePrec */ - - /** Convert an absolute error to a precision. - * @param x The value of the variable - * @param xerr The absolute error in the variable - * @return The number of valid digits in x. - * The value is rounded down, and on the pessimistic side for that reason. - * @since 2009-06-25 - */ - static public int err2prec(BigDecimal x, BigDecimal xerr) - { - return err2prec( xerr.divide(x,MathContext.DECIMAL64).doubleValue() ); - } - - /** Convert an absolute error to a precision. - * @param x The value of the variable - * The value returned depends only on the absolute value, not on the sign. - * @param xerr The absolute error in the variable - * The value returned depends only on the absolute value, not on the sign. - * @return The number of valid digits in x. - * Derived from the representation x+- xerr, as if the error was represented - * in a "half width" (half of the error bar) form. - * The value is rounded down, and on the pessimistic side for that reason. - * @since 2009-05-30 - */ - static public int err2prec(double x, double xerr) - { - /* Example: an error of xerr=+-0.5 at x=100 represents 100+-0.5 with - * a precision = 3 (digits). - */ - return 1+(int)(Math.log10(Math.abs(0.5*x/xerr) ) ); - } - - /** Convert a relative error to a precision. - * @param xerr The relative error in the variable. - * The value returned depends only on the absolute value, not on the sign. - * @return The number of valid digits in x. - * The value is rounded down, and on the pessimistic side for that reason. - * @since 2009-08-05 - */ - static public int err2prec(double xerr) - { - /* Example: an error of xerr=+-0.5 a precision of 1 (digit), an error of - * +-0.05 a precision of 2 (digits) - */ - return 1+(int)(Math.log10(Math.abs(0.5/xerr) ) ); - } - - /** Convert a precision (relative error) to an absolute error. - * The is the inverse functionality of err2prec(). - * @param x The value of the variable - * The value returned depends only on the absolute value, not on the sign. - * @param prec The number of valid digits of the variable. - * @return the absolute error in x. - * Derived from the an accuracy of one half of the ulp. - * @since 2009-08-09 - */ - static public double prec2err(final double x, final int prec) - { - return 5.*Math.abs(x)*Math.pow(10.,-prec) ; - } +import org.warp.picalculator.Errore; + +/** + * BigDecimal special functions. + * A Java Math.BigDecimal + * Implementation of Core Mathematical Functions + * + * @since 2009-05-22 + * @author Richard J. Mathar + * @see apfloat + * @see dfp + * @see JScience + */ +public class BigDecimalMath { + + /** + * The base of the natural logarithm in a predefined accuracy. + * http://www.cs.arizona.edu/icon/oddsends/e.htm + * The precision of the predefined constant is one less than + * the string's length, taking into account the decimal dot. + * static int E_PRECISION = E.length()-1 ; + */ + static BigDecimal E = new BigDecimal("2.71828182845904523536028747135266249775724709369995957496696762772407663035354" + "759457138217852516642742746639193200305992181741359662904357290033429526059563" + "073813232862794349076323382988075319525101901157383418793070215408914993488416" + "750924476146066808226480016847741185374234544243710753907774499206955170276183" + "860626133138458300075204493382656029760673711320070932870912744374704723069697" + "720931014169283681902551510865746377211125238978442505695369677078544996996794" + "686445490598793163688923009879312773617821542499922957635148220826989519366803" + "318252886939849646510582093923982948879332036250944311730123819706841614039701" + "983767932068328237646480429531180232878250981945581530175671736133206981125099" + "618188159304169035159888851934580727386673858942287922849989208680582574927961" + "048419844436346324496848756023362482704197862320900216099023530436994184914631" + "409343173814364054625315209618369088870701676839642437814059271456354906130310" + "720851038375051011574770417189861068739696552126715468895703503540212340784981" + "933432106817012100562788023519303322474501585390473041995777709350366041699732" + "972508868769664035557071622684471625607988265178713419512466520103059212366771" + "943252786753985589448969709640975459185695638023637016211204774272283648961342" + "251644507818244235294863637214174023889344124796357437026375529444833799801612" + "549227850925778256209262264832627793338656648162772516401910590049164499828931"); + + /** + * Euler's constant Pi. + * http://www.cs.arizona.edu/icon/oddsends/pi.htm + */ + static BigDecimal PI = new BigDecimal("3.14159265358979323846264338327950288419716939937510582097494459230781640628620" + "899862803482534211706798214808651328230664709384460955058223172535940812848111" + "745028410270193852110555964462294895493038196442881097566593344612847564823378" + "678316527120190914564856692346034861045432664821339360726024914127372458700660" + "631558817488152092096282925409171536436789259036001133053054882046652138414695" + "194151160943305727036575959195309218611738193261179310511854807446237996274956" + "735188575272489122793818301194912983367336244065664308602139494639522473719070" + "217986094370277053921717629317675238467481846766940513200056812714526356082778" + "577134275778960917363717872146844090122495343014654958537105079227968925892354" + "201995611212902196086403441815981362977477130996051870721134999999837297804995" + "105973173281609631859502445945534690830264252230825334468503526193118817101000" + "313783875288658753320838142061717766914730359825349042875546873115956286388235" + "378759375195778185778053217122680661300192787661119590921642019893809525720106" + "548586327886593615338182796823030195203530185296899577362259941389124972177528" + "347913151557485724245415069595082953311686172785588907509838175463746493931925" + "506040092770167113900984882401285836160356370766010471018194295559619894676783" + "744944825537977472684710404753464620804668425906949129331367702898915210475216" + "205696602405803815019351125338243003558764024749647326391419927260426992279678" + "235478163600934172164121992458631503028618297455570674983850549458858692699569" + "092721079750930295532116534498720275596023648066549911988183479775356636980742" + "654252786255181841757467289097777279380008164706001614524919217321721477235014"); + + /** + * Euler-Mascheroni constant lower-case gamma. + * http://www.worldwideschool.org/library/books/sci/math/ + * MiscellaneousMathematicalConstants/chap35.html + */ + static BigDecimal GAMMA = new BigDecimal("0.577215664901532860606512090082402431" + "0421593359399235988057672348848677267776646709369470632917467495146314472498070" + "8248096050401448654283622417399764492353625350033374293733773767394279259525824" + "7094916008735203948165670853233151776611528621199501507984793745085705740029921" + "3547861466940296043254215190587755352673313992540129674205137541395491116851028" + "0798423487758720503843109399736137255306088933126760017247953783675927135157722" + "6102734929139407984301034177717780881549570661075010161916633401522789358679654" + "9725203621287922655595366962817638879272680132431010476505963703947394957638906" + "5729679296010090151251959509222435014093498712282479497471956469763185066761290" + "6381105182419744486783638086174945516989279230187739107294578155431600500218284" + "4096053772434203285478367015177394398700302370339518328690001558193988042707411" + "5422278197165230110735658339673487176504919418123000406546931429992977795693031" + "0050308630341856980323108369164002589297089098548682577736428825395492587362959" + "6133298574739302373438847070370284412920166417850248733379080562754998434590761" + "6431671031467107223700218107450444186647591348036690255324586254422253451813879" + "1243457350136129778227828814894590986384600629316947188714958752549236649352047" + "3243641097268276160877595088095126208404544477992299157248292516251278427659657" + "0832146102982146179519579590959227042089896279712553632179488737642106606070659" + "8256199010288075612519913751167821764361905705844078357350158005607745793421314" + "49885007864151716151945"); + + /** + * Natural logarithm of 2. + * http://www.worldwideschool.org/library/books/sci/math/ + * MiscellaneousMathematicalConstants/chap58.html + */ + static BigDecimal LOG2 = new BigDecimal("0.693147180559945309417232121458176568075" + "50013436025525412068000949339362196969471560586332699641868754200148102057068573" + "368552023575813055703267075163507596193072757082837143519030703862389167347112335" + "011536449795523912047517268157493206515552473413952588295045300709532636664265410" + "423915781495204374043038550080194417064167151864471283996817178454695702627163106" + "454615025720740248163777338963855069526066834113727387372292895649354702576265209" + "885969320196505855476470330679365443254763274495125040606943814710468994650622016" + "772042452452961268794654619316517468139267250410380254625965686914419287160829380" + "317271436778265487756648508567407764845146443994046142260319309673540257444607030" + "809608504748663852313818167675143866747664789088143714198549423151997354880375165" + "861275352916610007105355824987941472950929311389715599820565439287170007218085761" + "025236889213244971389320378439353088774825970171559107088236836275898425891853530" + "243634214367061189236789192372314672321720534016492568727477823445353476481149418" + "642386776774406069562657379600867076257199184734022651462837904883062033061144630" + "073719489002743643965002580936519443041191150608094879306786515887090060520346842" + "973619384128965255653968602219412292420757432175748909770675268711581705113700915" + "894266547859596489065305846025866838294002283300538207400567705304678700184162404" + "418833232798386349001563121889560650553151272199398332030751408426091479001265168" + "243443893572472788205486271552741877243002489794540196187233980860831664811490930" + "667519339312890431641370681397776498176974868903887789991296503619270710889264105" + "230924783917373501229842420499568935992206602204654941510613"); + + /** + * Euler's constant. + * + * @param mc + * The required precision of the result. + * @return 3.14159... + * @throws Errore + * @since 2009-05-29 + */ + static public BigDecimal pi(final MathContext mc) throws Errore { + /* look it up if possible */ + if (mc.getPrecision() < PI.precision()) + return PI.round(mc); + else { + /* + * Broadhurst arXiv:math/9803067 + */ + int[] a = { 1, 0, 0, -1, -1, -1, 0, 0 }; + BigDecimal S = broadhurstBBP(1, 1, a, mc); + return multiplyRound(S, 8); + } + } /* BigDecimalMath.pi */ + + /** + * Euler-Mascheroni constant. + * + * @param mc + * The required precision of the result. + * @return 0.577... + * @throws Errore + * @since 2009-08-13 + */ + static public BigDecimal gamma(MathContext mc) throws Errore { + /* look it up if possible */ + if (mc.getPrecision() < GAMMA.precision()) + return GAMMA.round(mc); + else { + double eps = prec2err(0.577, mc.getPrecision()); + + /* + * Euler-Stieltjes as shown in Dilcher, Aequat Math 48 (1) (1994) + * 55-85 + */ + MathContext mcloc = new MathContext(2 + mc.getPrecision()); + BigDecimal resul = BigDecimal.ONE; + resul = resul.add(log(2, mcloc)); + resul = resul.subtract(log(3, mcloc)); + + /* + * how many terms: zeta-1 falls as 1/2^(2n+1), so the + * terms drop faster than 1/2^(4n+2). Set 1/2^(4kmax+2) < eps. + * Leading term zeta(3)/(4^1*3) is 0.017. Leading zeta(3) is 1.2. + * Log(2) is 0.7 + */ + int kmax = (int) ((Math.log(eps / 0.7) - 2.) / 4.); + mcloc = new MathContext(1 + err2prec(1.2, eps / kmax)); + for (int n = 1;; n++) { + /* + * zeta is close to 1. Division of zeta-1 through + * 4^n*(2n+1) means divion through roughly 2^(2n+1) + */ + BigDecimal c = zeta(2 * n + 1, mcloc).subtract(BigDecimal.ONE); + BigInteger fourn = new BigInteger("" + (2 * n + 1)); + fourn = fourn.shiftLeft(2 * n); + c = divideRound(c, fourn); + resul = resul.subtract(c); + if (c.doubleValue() < 0.1 * eps) + break; + } + return resul.round(mc); + } + + } /* BigDecimalMath.gamma */ + + /** + * The square root. + * + * @param x + * the non-negative argument. + * @param mc + * @return the square root of the BigDecimal. + * @since 2008-10-27 + */ + static public BigDecimal sqrt(final BigDecimal x, final MathContext mc) { + if (x.compareTo(BigDecimal.ZERO) < 0) + throw new ArithmeticException("negative argument " + x.toString() + " of square root"); + if (x.abs().subtract(new BigDecimal(Math.pow(10., -mc.getPrecision()))).compareTo(BigDecimal.ZERO) < 0) + return BigDecimalMath.scalePrec(BigDecimal.ZERO, mc); + /* start the computation from a double precision estimate */ + BigDecimal s = new BigDecimal(Math.sqrt(x.doubleValue()), mc); + final BigDecimal half = new BigDecimal("2"); + + /* increase the local accuracy by 2 digits */ + MathContext locmc = new MathContext(mc.getPrecision() + 2, mc.getRoundingMode()); + + /* + * relative accuracy requested is 10^(-precision) + */ + final double eps = Math.pow(10.0, -mc.getPrecision()); + for (;;) { + /* + * s = s -(s/2-x/2s); test correction s-x/s for being + * smaller than the precision requested. The relative correction is + * 1-x/s^2, + * (actually half of this, which we use for a little bit of + * additional protection). + */ + if (Math.abs(BigDecimal.ONE.subtract(x.divide(s.pow(2, locmc), locmc)).doubleValue()) <= eps) + break; + s = s.add(x.divide(s, locmc)).divide(half, locmc); + /* debugging */ + // System.out.println("itr "+x.round(locmc).toString() + " " + + // s.round(locmc).toString()) ; + } + return s; + } /* BigDecimalMath.sqrt */ + + /** + * The square root. + * + * @param x + * the non-negative argument. + * @return the square root of the BigDecimal rounded to the precision + * implied by x. + * @since 2009-06-25 + */ + static public BigDecimal sqrt(final BigDecimal x) { + if (x.compareTo(BigDecimal.ZERO) < 0) + throw new ArithmeticException("negative argument " + x.toString() + " of square root"); + + return root(2, x); + } /* BigDecimalMath.sqrt */ + + /** + * The cube root. + * + * @param x + * The argument. + * @return The cubic root of the BigDecimal rounded to the precision implied + * by x. + * The sign of the result is the sign of the argument. + * @since 2009-08-16 + */ + static public BigDecimal cbrt(final BigDecimal x) { + if (x.compareTo(BigDecimal.ZERO) < 0) + return root(3, x.negate()).negate(); + else + return root(3, x); + } /* BigDecimalMath.cbrt */ + + /** + * The integer root. + * + * @param n + * the positive argument. + * @param x + * the non-negative argument. + * @return The n-th root of the BigDecimal rounded to the precision implied + * by x, x^(1/n). + * @since 2009-07-30 + */ + static public BigDecimal root(final int n, final BigDecimal x) { + if (x.compareTo(BigDecimal.ZERO) < 0) + throw new ArithmeticException("negative argument " + x.toString() + " of root"); + if (n <= 0) + throw new ArithmeticException("negative power " + n + " of root"); + + if (n == 1) + return x; + + /* start the computation from a double precision estimate */ + BigDecimal s = new BigDecimal(Math.pow(x.doubleValue(), 1.0 / n)); + + /* + * this creates nth with nominal precision of 1 digit + */ + final BigDecimal nth = new BigDecimal(n); + + /* + * Specify an internal accuracy within the loop which is + * slightly larger than what is demanded by 'eps' below. + */ + final BigDecimal xhighpr = scalePrec(x, 2); + MathContext mc = new MathContext(2 + x.precision()); + + /* + * Relative accuracy of the result is eps. + */ + final double eps = x.ulp().doubleValue() / (2 * n * x.doubleValue()); + for (;;) { + /* + * s = s -(s/n-x/n/s^(n-1)) = s-(s-x/s^(n-1))/n; test correction + * s/n-x/s for being + * smaller than the precision requested. The relative correction is + * (1-x/s^n)/n, + */ + BigDecimal c = xhighpr.divide(s.pow(n - 1), mc); + c = s.subtract(c); + MathContext locmc = new MathContext(c.precision()); + c = c.divide(nth, locmc); + s = s.subtract(c); + if (Math.abs(c.doubleValue() / s.doubleValue()) < eps) + break; + } + return s.round(new MathContext(err2prec(eps))); + } /* BigDecimalMath.root */ + + /** + * The hypotenuse. + * + * @param x + * the first argument. + * @param y + * the second argument. + * @return the square root of the sum of the squares of the two arguments, + * sqrt(x^2+y^2). + * @since 2009-06-25 + */ + static public BigDecimal hypot(final BigDecimal x, final BigDecimal y) { + /* + * compute x^2+y^2 + */ + BigDecimal z = x.pow(2).add(y.pow(2)); + + /* + * truncate to the precision set by x and y. Absolute error = + * 2*x*xerr+2*y*yerr, + * where the two errors are 1/2 of the ulp's. Two intermediate protectio + * digits. + */ + BigDecimal zerr = x.abs().multiply(x.ulp()).add(y.abs().multiply(y.ulp())); + MathContext mc = new MathContext(2 + err2prec(z, zerr)); + + /* Pull square root */ + z = sqrt(z.round(mc)); + + /* + * Final rounding. Absolute error in the square root is + * (y*yerr+x*xerr)/z, where zerr holds 2*(x*xerr+y*yerr). + */ + mc = new MathContext(err2prec(z.doubleValue(), 0.5 * zerr.doubleValue() / z.doubleValue())); + return z.round(mc); + } /* BigDecimalMath.hypot */ + + /** + * The hypotenuse. + * + * @param n + * the first argument. + * @param x + * the second argument. + * @return the square root of the sum of the squares of the two arguments, + * sqrt(n^2+x^2). + * @since 2009-08-05 + */ + static public BigDecimal hypot(final int n, final BigDecimal x) { + /* + * compute n^2+x^2 in infinite precision + */ + BigDecimal z = (new BigDecimal(n)).pow(2).add(x.pow(2)); + + /* + * Truncate to the precision set by x. Absolute error = in z (square of + * the result) is |2*x*xerr|, + * where the error is 1/2 of the ulp. Two intermediate protection + * digits. + * zerr is a signed value, but used only in conjunction with err2prec(), + * so this feature does not harm. + */ + double zerr = x.doubleValue() * x.ulp().doubleValue(); + MathContext mc = new MathContext(2 + err2prec(z.doubleValue(), zerr)); + + /* Pull square root */ + z = sqrt(z.round(mc)); + + /* + * Final rounding. Absolute error in the square root is x*xerr/z, where + * zerr holds 2*x*xerr. + */ + mc = new MathContext(err2prec(z.doubleValue(), 0.5 * zerr / z.doubleValue())); + return z.round(mc); + } /* BigDecimalMath.hypot */ + + /** + * A suggestion for the maximum numter of terms in the Taylor expansion of + * the exponential. + */ + static private int TAYLOR_NTERM = 8; + + /** + * The exponential function. + * + * @param x + * the argument. + * @return exp(x). + * The precision of the result is implicitly defined by the + * precision in the argument. + * In particular this means that "Invalid Operation" errors are + * thrown if catastrophic + * cancellation of digits causes the result to have no valid digits + * left. + * @since 2009-05-29 + * @author Richard J. Mathar + */ + static public BigDecimal exp(BigDecimal x) { + /* + * To calculate the value if x is negative, use exp(-x) = 1/exp(x) + */ + if (x.compareTo(BigDecimal.ZERO) < 0) { + final BigDecimal invx = exp(x.negate()); + /* + * Relative error in inverse of invx is the same as the relative + * errror in invx. + * This is used to define the precision of the result. + */ + MathContext mc = new MathContext(invx.precision()); + return BigDecimal.ONE.divide(invx, mc); + } else if (x.compareTo(BigDecimal.ZERO) == 0) { + /* + * recover the valid number of digits from x.ulp(), if x hits the + * zero. The x.precision() is 1 then, and does not provide this + * information. + */ + return scalePrec(BigDecimal.ONE, -(int) (Math.log10(x.ulp().doubleValue()))); + } else { + /* + * Push the number in the Taylor expansion down to a small + * value where TAYLOR_NTERM terms will do. If x<1, the n-th term is + * of the order + * x^n/n!, and equal to both the absolute and relative error of the + * result + * since the result is close to 1. The x.ulp() sets the relative and + * absolute error + * of the result, as estimated from the first Taylor term. + * We want x^TAYLOR_NTERM/TAYLOR_NTERM! < x.ulp, which is guaranteed + * if + * x^TAYLOR_NTERM < TAYLOR_NTERM*(TAYLOR_NTERM-1)*...*x.ulp. + */ + final double xDbl = x.doubleValue(); + final double xUlpDbl = x.ulp().doubleValue(); + if (Math.pow(xDbl, TAYLOR_NTERM) < TAYLOR_NTERM * (TAYLOR_NTERM - 1.0) * (TAYLOR_NTERM - 2.0) * xUlpDbl) { + /* + * Add TAYLOR_NTERM terms of the Taylor expansion (Euler's sum + * formula) + */ + BigDecimal resul = BigDecimal.ONE; + + /* x^i */ + BigDecimal xpowi = BigDecimal.ONE; + + /* i factorial */ + BigInteger ifac = BigInteger.ONE; + + /* + * TAYLOR_NTERM terms to be added means we move x.ulp() to the + * right + * for each power of 10 in TAYLOR_NTERM, so the addition won't + * add noise beyond + * what's already in x. + */ + MathContext mcTay = new MathContext(err2prec(1., xUlpDbl / TAYLOR_NTERM)); + for (int i = 1; i <= TAYLOR_NTERM; i++) { + ifac = ifac.multiply(new BigInteger("" + i)); + xpowi = xpowi.multiply(x); + final BigDecimal c = xpowi.divide(new BigDecimal(ifac), mcTay); + resul = resul.add(c); + if (Math.abs(xpowi.doubleValue()) < i && Math.abs(c.doubleValue()) < 0.5 * xUlpDbl) + break; + } + /* + * exp(x+deltax) = exp(x)(1+deltax) if deltax is <<1. So the + * relative error + * in the result equals the absolute error in the argument. + */ + MathContext mc = new MathContext(err2prec(xUlpDbl / 2.)); + return resul.round(mc); + } else { + /* + * Compute exp(x) = (exp(0.1*x))^10. Division by 10 does not + * lead + * to loss of accuracy. + */ + int exSc = (int) (1.0 - Math.log10(TAYLOR_NTERM * (TAYLOR_NTERM - 1.0) * (TAYLOR_NTERM - 2.0) * xUlpDbl / Math.pow(xDbl, TAYLOR_NTERM)) / (TAYLOR_NTERM - 1.0)); + BigDecimal xby10 = x.scaleByPowerOfTen(-exSc); + BigDecimal expxby10 = exp(xby10); + + /* + * Final powering by 10 means that the relative error of the + * result + * is 10 times the relative error of the base (First order + * binomial expansion). + * This looses one digit. + */ + MathContext mc = new MathContext(expxby10.precision() - exSc); + /* + * Rescaling the powers of 10 is done in chunks of a maximum of + * 8 to avoid an invalid operation + * response by the BigDecimal.pow library or integer overflow. + */ + while (exSc > 0) { + int exsub = Math.min(8, exSc); + exSc -= exsub; + MathContext mctmp = new MathContext(expxby10.precision() - exsub + 2); + int pex = 1; + while (exsub-- > 0) + pex *= 10; + expxby10 = expxby10.pow(pex, mctmp); + } + return expxby10.round(mc); + } + } + } /* BigDecimalMath.exp */ + + /** + * The base of the natural logarithm. + * + * @param mc + * the required precision of the result + * @return exp(1) = 2.71828.... + * @since 2009-05-29 + */ + static public BigDecimal exp(final MathContext mc) { + /* look it up if possible */ + if (mc.getPrecision() < E.precision()) + return E.round(mc); + else { + /* + * Instantiate a 1.0 with the requested pseudo-accuracy + * and delegate the computation to the public method above. + */ + BigDecimal uni = scalePrec(BigDecimal.ONE, mc.getPrecision()); + return exp(uni); + } + } /* BigDecimalMath.exp */ + + /** + * The natural logarithm. + * + * @param x + * the argument. + * @return ln(x). + * The precision of the result is implicitly defined by the + * precision in the argument. + * @since 2009-05-29 + * @author Richard J. Mathar + */ + static public BigDecimal log(BigDecimal x) { + /* + * the value is undefined if x is negative. + */ + if (x.compareTo(BigDecimal.ZERO) < 0) + throw new ArithmeticException("Cannot take log of negative " + x.toString()); + else if (x.compareTo(BigDecimal.ONE) == 0) { + /* log 1. = 0. */ + return scalePrec(BigDecimal.ZERO, x.precision() - 1); + } else if (Math.abs(x.doubleValue() - 1.0) <= 0.3) { + /* + * The standard Taylor series around x=1, z=0, z=x-1. + * Abramowitz-Stegun 4.124. + * The absolute error is err(z)/(1+z) = err(x)/x. + */ + BigDecimal z = scalePrec(x.subtract(BigDecimal.ONE), 2); + BigDecimal zpown = z; + double eps = 0.5 * x.ulp().doubleValue() / Math.abs(x.doubleValue()); + BigDecimal resul = z; + for (int k = 2;; k++) { + zpown = multiplyRound(zpown, z); + BigDecimal c = divideRound(zpown, k); + if (k % 2 == 0) + resul = resul.subtract(c); + else + resul = resul.add(c); + if (Math.abs(c.doubleValue()) < eps) + break; + } + MathContext mc = new MathContext(err2prec(resul.doubleValue(), eps)); + return resul.round(mc); + } else { + final double xDbl = x.doubleValue(); + final double xUlpDbl = x.ulp().doubleValue(); + + /* + * Map log(x) = log root[r](x)^r = r*log( root[r](x)) with the aim + * to move roor[r](x) near to 1.2 (that is, below the 0.3 appearing + * above), where log(1.2) is roughly 0.2. + */ + int r = (int) (Math.log(xDbl) / 0.2); + + /* + * Since the actual requirement is a function of the value 0.3 + * appearing above, + * we avoid the hypothetical case of endless recurrence by ensuring + * that r >= 2. + */ + r = Math.max(2, r); + + /* + * Compute r-th root with 2 additional digits of precision + */ + BigDecimal xhighpr = scalePrec(x, 2); + BigDecimal resul = root(r, xhighpr); + resul = log(resul).multiply(new BigDecimal(r)); + + /* + * error propagation: log(x+errx) = log(x)+errx/x, so the absolute + * error + * in the result equals the relative error in the input, + * xUlpDbl/xDbl . + */ + MathContext mc = new MathContext(err2prec(resul.doubleValue(), xUlpDbl / xDbl)); + return resul.round(mc); + } + } /* BigDecimalMath.log */ + + /** + * The natural logarithm. + * + * @param n + * The main argument, a strictly positive integer. + * @param mc + * The requirements on the precision. + * @return ln(n). + * @since 2009-08-08 + * @author Richard J. Mathar + * @throws Errore + */ + static public BigDecimal log(int n, final MathContext mc) throws Errore { + /* + * the value is undefined if x is negative. + */ + if (n <= 0) + throw new ArithmeticException("Cannot take log of negative " + n); + else if (n == 1) + return BigDecimal.ZERO; + else if (n == 2) { + if (mc.getPrecision() < LOG2.precision()) + return LOG2.round(mc); + else { + /* + * Broadhurst arXiv:math/9803067 + * Error propagation: the error in log(2) is twice the error in + * S(2,-5,...). + */ + int[] a = { 2, -5, -2, -7, -2, -5, 2, -3 }; + BigDecimal S = broadhurstBBP(2, 1, a, new MathContext(1 + mc.getPrecision())); + S = S.multiply(new BigDecimal(8)); + S = sqrt(divideRound(S, 3)); + return S.round(mc); + } + } else if (n == 3) { + /* + * summation of a series roughly proportional to (7/500)^k. Estimate + * count + * of terms to estimate the precision (drop the favorable additional + * 1/k here): 0.013^k <= 10^(-precision), so k*log10(0.013) <= + * -precision + * so k>= precision/1.87. + */ + int kmax = (int) (mc.getPrecision() / 1.87); + MathContext mcloc = new MathContext(mc.getPrecision() + 1 + (int) (Math.log10(kmax * 0.693 / 1.098))); + BigDecimal log3 = multiplyRound(log(2, mcloc), 19); + + /* + * log3 is roughly 1, so absolute and relative error are the same. + * The + * result will be divided by 12, so a conservative error is the one + * already found in mc + */ + double eps = prec2err(1.098, mc.getPrecision()) / kmax; + Rational r = new Rational(7153, 524288); + Rational pk = new Rational(7153, 524288); + for (int k = 1;; k++) { + Rational tmp = pk.divide(k); + if (tmp.doubleValue() < eps) + break; + + /* + * how many digits of tmp do we need in the sum? + */ + mcloc = new MathContext(err2prec(tmp.doubleValue(), eps)); + BigDecimal c = pk.divide(k).BigDecimalValue(mcloc); + if (k % 2 != 0) + log3 = log3.add(c); + else + log3 = log3.subtract(c); + pk = pk.multiply(r); + } + log3 = divideRound(log3, 12); + return log3.round(mc); + } else if (n == 5) { + /* + * summation of a series roughly proportional to (7/160)^k. Estimate + * count + * of terms to estimate the precision (drop the favorable additional + * 1/k here): 0.046^k <= 10^(-precision), so k*log10(0.046) <= + * -precision + * so k>= precision/1.33. + */ + int kmax = (int) (mc.getPrecision() / 1.33); + MathContext mcloc = new MathContext(mc.getPrecision() + 1 + (int) (Math.log10(kmax * 0.693 / 1.609))); + BigDecimal log5 = multiplyRound(log(2, mcloc), 14); + + /* + * log5 is roughly 1.6, so absolute and relative error are the same. + * The + * result will be divided by 6, so a conservative error is the one + * already found in mc + */ + double eps = prec2err(1.6, mc.getPrecision()) / kmax; + Rational r = new Rational(759, 16384); + Rational pk = new Rational(759, 16384); + for (int k = 1;; k++) { + Rational tmp = pk.divide(k); + if (tmp.doubleValue() < eps) + break; + + /* + * how many digits of tmp do we need in the sum? + */ + mcloc = new MathContext(err2prec(tmp.doubleValue(), eps)); + BigDecimal c = pk.divide(k).BigDecimalValue(mcloc); + log5 = log5.subtract(c); + pk = pk.multiply(r); + } + log5 = divideRound(log5, 6); + return log5.round(mc); + } else if (n == 7) { + /* + * summation of a series roughly proportional to (1/8)^k. Estimate + * count + * of terms to estimate the precision (drop the favorable additional + * 1/k here): 0.125^k <= 10^(-precision), so k*log10(0.125) <= + * -precision + * so k>= precision/0.903. + */ + int kmax = (int) (mc.getPrecision() / 0.903); + MathContext mcloc = new MathContext(mc.getPrecision() + 1 + (int) (Math.log10(kmax * 3 * 0.693 / 1.098))); + BigDecimal log7 = multiplyRound(log(2, mcloc), 3); + + /* + * log7 is roughly 1.9, so absolute and relative error are the same. + */ + double eps = prec2err(1.9, mc.getPrecision()) / kmax; + Rational r = new Rational(1, 8); + Rational pk = new Rational(1, 8); + for (int k = 1;; k++) { + Rational tmp = pk.divide(k); + if (tmp.doubleValue() < eps) + break; + + /* + * how many digits of tmp do we need in the sum? + */ + mcloc = new MathContext(err2prec(tmp.doubleValue(), eps)); + BigDecimal c = pk.divide(k).BigDecimalValue(mcloc); + log7 = log7.subtract(c); + pk = pk.multiply(r); + } + return log7.round(mc); + + } + + else { + /* + * At this point one could either forward to the log(BigDecimal) + * signature (implemented) + * or decompose n into Ifactors and use an implemenation of all the + * prime bases. + * Estimate of the result; convert the mc argument to an absolute + * error eps + * log(n+errn) = log(n)+errn/n = log(n)+eps + */ + double res = Math.log(n); + double eps = prec2err(res, mc.getPrecision()); + /* + * errn = eps*n, convert absolute error in result to requirement on + * absolute error in input + */ + eps *= n; + /* + * Convert this absolute requirement of error in n to a relative + * error in n + */ + final MathContext mcloc = new MathContext(1 + err2prec(n, eps)); + /* + * Padd n with a number of zeros to trigger the required accuracy in + * the standard signature method + */ + BigDecimal nb = scalePrec(new BigDecimal(n), mcloc); + return log(nb); + } + } /* log */ + + /** + * The natural logarithm. + * + * @param r + * The main argument, a strictly positive value. + * @param mc + * The requirements on the precision. + * @return ln(r). + * @since 2009-08-09 + * @author Richard J. Mathar + */ + static public BigDecimal log(final Rational r, final MathContext mc) { + /* + * the value is undefined if x is negative. + */ + if (r.compareTo(Rational.ZERO) <= 0) + throw new ArithmeticException("Cannot take log of negative " + r.toString()); + else if (r.compareTo(Rational.ONE) == 0) + return BigDecimal.ZERO; + else { + + /* + * log(r+epsr) = log(r)+epsr/r. Convert the precision to an absolute + * error in the result. + * eps contains the required absolute error of the result, epsr/r. + */ + double eps = prec2err(Math.log(r.doubleValue()), mc.getPrecision()); + + /* + * Convert this further into a requirement of the relative precision + * in r, given that + * epsr/r is also the relative precision of r. Add one safety digit. + */ + MathContext mcloc = new MathContext(1 + err2prec(eps)); + + final BigDecimal resul = log(r.BigDecimalValue(mcloc)); + + return resul.round(mc); + } + } /* log */ + + /** + * Power function. + * + * @param x + * Base of the power. + * @param y + * Exponent of the power. + * @return x^y. + * The estimation of the relative error in the result is + * |log(x)*err(y)|+|y*err(x)/x| + * @since 2009-06-01 + */ + static public BigDecimal pow(final BigDecimal x, final BigDecimal y) { + if (x.compareTo(BigDecimal.ZERO) < 0) + throw new ArithmeticException("Cannot power negative " + x.toString()); + else if (x.compareTo(BigDecimal.ZERO) == 0) + return BigDecimal.ZERO; + else { + /* + * return x^y = exp(y*log(x)) ; + */ + BigDecimal logx = log(x); + BigDecimal ylogx = y.multiply(logx); + BigDecimal resul = exp(ylogx); + + /* + * The estimation of the relative error in the result is + * |log(x)*err(y)|+|y*err(x)/x| + */ + double errR = Math.abs(logx.doubleValue() * y.ulp().doubleValue() / 2.) + Math.abs(y.doubleValue() * x.ulp().doubleValue() / 2. / x.doubleValue()); + MathContext mcR = new MathContext(err2prec(1.0, errR)); + return resul.round(mcR); + } + } /* BigDecimalMath.pow */ + + /** + * Raise to an integer power and round. + * + * @param x + * The base. + * @param n + * The exponent. + * @return x^n. + * @since 2009-08-13 + * @since 2010-05-26 handle also n<0 cases. + */ + static public BigDecimal powRound(final BigDecimal x, final int n) { + /** + * Special cases: x^1=x and x^0 = 1 + */ + if (n == 1) + return x; + else if (n == 0) + return BigDecimal.ONE; + else { + /* + * The relative error in the result is n times the relative error in + * the input. + * The estimation is slightly optimistic due to the integer rounding + * of the logarithm. + * Since the standard BigDecimal.pow can only handle positive n, we + * split the algorithm. + */ + MathContext mc = new MathContext(x.precision() - (int) Math.log10((Math.abs(n)))); + if (n > 0) + return x.pow(n, mc); + else + return BigDecimal.ONE.divide(x.pow(-n), mc); + } + } /* BigDecimalMath.powRound */ + + /** + * Raise to an integer power and round. + * + * @param x + * The base. + * @param n + * The exponent. + * The current implementation allows n only in the interval of + * the standard int values. + * @return x^n. + * @since 2010-05-26 + */ + static public BigDecimal powRound(final BigDecimal x, final BigInteger n) { + /** + * For now, the implementation forwards to the cases where n + * is in the range of the standard integers. This might, however, be + * implemented to decompose larger powers into cascaded calls to smaller + * ones. + */ + if (n.compareTo(Rational.MAX_INT) > 0 || n.compareTo(Rational.MIN_INT) < 0) + throw new ProviderException("Not implemented: big power " + n.toString()); + else + return powRound(x, n.intValue()); + } /* BigDecimalMath.powRound */ + + /** + * Raise to a fractional power and round. + * + * @param x + * The base. + * Generally enforced to be positive, with the exception of + * integer exponents where + * the sign is carried over according to the parity of the + * exponent. + * @param q + * The exponent. + * @return x^q. + * @since 2010-05-26 + */ + static public BigDecimal powRound(final BigDecimal x, final Rational q) { + /** + * Special cases: x^1=x and x^0 = 1 + */ + if (q.compareTo(BigInteger.ONE) == 0) + return x; + else if (q.signum() == 0) + return BigDecimal.ONE; + else if (q.isInteger()) { + /* + * We are sure that the denominator is positive here, because + * normalize() has been + * called during constrution etc. + */ + return powRound(x, q.a); + } + /* + * Refuse to operate on the general negative basis. The integer q have + * already been handled above. + */ + else if (x.compareTo(BigDecimal.ZERO) < 0) + throw new ArithmeticException("Cannot power negative " + x.toString()); + else { + if (q.isIntegerFrac()) { + /* + * Newton method with first estimate in double precision. + * The disadvantage of this first line here is that the result + * must fit in the + * standard range of double precision numbers exponents. + */ + double estim = Math.pow(x.doubleValue(), q.doubleValue()); + BigDecimal res = new BigDecimal(estim); + + /* + * The error in x^q is q*x^(q-1)*Delta(x). + * The relative error is q*Delta(x)/x, q times the relative + * error of x. + */ + BigDecimal reserr = new BigDecimal(0.5 * q.abs().doubleValue() * x.ulp().divide(x.abs(), MathContext.DECIMAL64).doubleValue()); + + /* + * The main point in branching the cases above is that this + * conversion + * will succeed for numerator and denominator of q. + */ + int qa = q.a.intValue(); + int qb = q.b.intValue(); + + /* Newton iterations. */ + BigDecimal xpowa = powRound(x, qa); + for (;;) { + /* + * numerator and denominator of the Newton term. The major + * disadvantage of this implementation is that the updates + * of the powers + * of the new estimate are done in full precision calling + * BigDecimal.pow(), + * which becomes slow if the denominator of q is large. + */ + BigDecimal nu = res.pow(qb).subtract(xpowa); + BigDecimal de = multiplyRound(res.pow(qb - 1), q.b); + + /* estimated correction */ + BigDecimal eps = nu.divide(de, MathContext.DECIMAL64); + + BigDecimal err = res.multiply(reserr, MathContext.DECIMAL64); + int precDiv = 2 + err2prec(eps, err); + if (precDiv <= 0) { + /* + * The case when the precision is already reached and + * any precision + * will do. + */ + eps = nu.divide(de, MathContext.DECIMAL32); + } else { + MathContext mc = new MathContext(precDiv); + eps = nu.divide(de, mc); + } + + res = subtractRound(res, eps); + /* + * reached final precision if the relative error fell below + * reserr, + * |eps/res| < reserr + */ + if (eps.divide(res, MathContext.DECIMAL64).abs().compareTo(reserr) < 0) { + /* + * delete the bits of extra precision kept in this + * working copy. + */ + MathContext mc = new MathContext(err2prec(reserr.doubleValue())); + return res.round(mc); + } + } + } else { + /* + * The error in x^q is q*x^(q-1)*Delta(x) + Delta(q)*x^q*log(x). + * The relative error is q/x*Delta(x) + Delta(q)*log(x). Convert + * q to a floating point + * number such that its relative error becomes negligible: + * Delta(q)/q << Delta(x)/x/log(x) . + */ + int precq = 3 + err2prec((x.ulp().divide(x, MathContext.DECIMAL64)).doubleValue() / Math.log(x.doubleValue())); + MathContext mc = new MathContext(precq); + + /* + * Perform the actual calculation as exponentiation of two + * floating point numbers. + */ + return pow(x, q.BigDecimalValue(mc)); + } + + } + } /* BigDecimalMath.powRound */ + + /** + * Trigonometric sine. + * + * @param x + * The argument in radians. + * @return sin(x) in the range -1 to 1. + * @throws Errore + * @since 2009-06-01 + */ + static public BigDecimal sin(final BigDecimal x) throws Errore { + if (x.compareTo(BigDecimal.ZERO) < 0) + return sin(x.negate()).negate(); + else if (x.compareTo(BigDecimal.ZERO) == 0) + return BigDecimal.ZERO; + else { + /* + * reduce modulo 2pi + */ + BigDecimal res = mod2pi(x); + double errpi = 0.5 * Math.abs(x.ulp().doubleValue()); + MathContext mc = new MathContext(2 + err2prec(3.14159, errpi)); + BigDecimal p = pi(mc); + mc = new MathContext(x.precision()); + if (res.compareTo(p) > 0) { + /* + * pi 0) { + /* + * pi/2 0) { + /* + * x>pi/4: sin(x) = cos(pi/2-x) + */ + return cos(subtractRound(p.divide(new BigDecimal("2")), res)); + } else { + /* + * Simple Taylor expansion, sum_{i=1..infinity} + * (-1)^(..)res^(2i+1)/(2i+1)! + */ + BigDecimal resul = res; + + /* x^i */ + BigDecimal xpowi = res; + + /* 2i+1 factorial */ + BigInteger ifac = BigInteger.ONE; + + /* + * The error in the result is set by the error in x itself. + */ + double xUlpDbl = res.ulp().doubleValue(); + + /* + * The error in the result is set by the error in x itself. + * We need at most k terms to squeeze x^(2k+1)/(2k+1)! below + * this value. + * x^(2k+1) < x.ulp; (2k+1)*log10(x) < -x.precision; + * 2k*log10(x)< -x.precision; + * 2k*(-log10(x)) > x.precision; 2k*log10(1/x) > x.precision + */ + int k = (int) (res.precision() / Math.log10(1.0 / res.doubleValue())) / 2; + MathContext mcTay = new MathContext(err2prec(res.doubleValue(), xUlpDbl / k)); + for (int i = 1;; i++) { + /* + * TBD: at which precision will 2*i or 2*i+1 overflow? + */ + ifac = ifac.multiply(new BigInteger("" + (2 * i))); + ifac = ifac.multiply(new BigInteger("" + (2 * i + 1))); + xpowi = xpowi.multiply(res).multiply(res).negate(); + BigDecimal corr = xpowi.divide(new BigDecimal(ifac), mcTay); + resul = resul.add(corr); + if (corr.abs().doubleValue() < 0.5 * xUlpDbl) + break; + } + /* + * The error in the result is set by the error in x itself. + */ + mc = new MathContext(res.precision()); + return resul.round(mc); + } + } + } + } /* sin */ + + /** + * Trigonometric cosine. + * + * @param x + * The argument in radians. + * @return cos(x) in the range -1 to 1. + * @throws Errore + * @since 2009-06-01 + */ + static public BigDecimal cos(final BigDecimal x) throws Errore { + if (x.compareTo(BigDecimal.ZERO) < 0) + return cos(x.negate()); + else if (x.compareTo(BigDecimal.ZERO) == 0) + return BigDecimal.ONE; + else { + /* + * reduce modulo 2pi + */ + BigDecimal res = mod2pi(x); + double errpi = 0.5 * Math.abs(x.ulp().doubleValue()); + MathContext mc = new MathContext(2 + err2prec(3.14159, errpi)); + BigDecimal p = pi(mc); + mc = new MathContext(x.precision()); + if (res.compareTo(p) > 0) { + /* + * pi 0) { + /* + * pi/2 0) { + /* + * x>pi/4: cos(x) = sin(pi/2-x) + */ + return sin(subtractRound(p.divide(new BigDecimal("2")), res)); + } else { + /* + * Simple Taylor expansion, sum_{i=0..infinity} + * (-1)^(..)res^(2i)/(2i)! + */ + BigDecimal resul = BigDecimal.ONE; + + /* x^i */ + BigDecimal xpowi = BigDecimal.ONE; + + /* 2i factorial */ + BigInteger ifac = BigInteger.ONE; + + /* + * The absolute error in the result is the error in x^2/2 + * which is x times the error in x. + */ + double xUlpDbl = 0.5 * res.ulp().doubleValue() * res.doubleValue(); + + /* + * The error in the result is set by the error in x^2/2 + * itself, xUlpDbl. + * We need at most k terms to push x^(2k+1)/(2k+1)! below + * this value. + * x^(2k) < xUlpDbl; (2k)*log(x) < log(xUlpDbl); + */ + int k = (int) (Math.log(xUlpDbl) / Math.log(res.doubleValue())) / 2; + MathContext mcTay = new MathContext(err2prec(1., xUlpDbl / k)); + for (int i = 1;; i++) { + /* + * TBD: at which precision will 2*i-1 or 2*i overflow? + */ + ifac = ifac.multiply(new BigInteger("" + (2 * i - 1))); + ifac = ifac.multiply(new BigInteger("" + (2 * i))); + xpowi = xpowi.multiply(res).multiply(res).negate(); + BigDecimal corr = xpowi.divide(new BigDecimal(ifac), mcTay); + resul = resul.add(corr); + if (corr.abs().doubleValue() < 0.5 * xUlpDbl) + break; + } + /* + * The error in the result is governed by the error in x + * itself. + */ + mc = new MathContext(err2prec(resul.doubleValue(), xUlpDbl)); + return resul.round(mc); + } + } + } + } /* BigDecimalMath.cos */ + + /** + * The trigonometric tangent. + * + * @param x + * the argument in radians. + * @return the tan(x) + * @throws Errore + */ + static public BigDecimal tan(final BigDecimal x) throws Errore { + if (x.compareTo(BigDecimal.ZERO) == 0) + return BigDecimal.ZERO; + else if (x.compareTo(BigDecimal.ZERO) < 0) { + return tan(x.negate()).negate(); + } else { + /* + * reduce modulo pi + */ + BigDecimal res = modpi(x); + + /* + * absolute error in the result is err(x)/cos^2(x) to lowest order + */ + final double xDbl = res.doubleValue(); + final double xUlpDbl = x.ulp().doubleValue() / 2.; + final double eps = xUlpDbl / 2. / Math.pow(Math.cos(xDbl), 2.); + + if (xDbl > 0.8) { + /* tan(x) = 1/cot(x) */ + BigDecimal co = cot(x); + MathContext mc = new MathContext(err2prec(1. / co.doubleValue(), eps)); + return BigDecimal.ONE.divide(co, mc); + } else { + final BigDecimal xhighpr = scalePrec(res, 2); + final BigDecimal xhighprSq = multiplyRound(xhighpr, xhighpr); + + BigDecimal resul = xhighpr.plus(); + + /* x^(2i+1) */ + BigDecimal xpowi = xhighpr; + + Bernoulli b = new Bernoulli(); + + /* 2^(2i) */ + BigInteger fourn = new BigInteger("4"); + /* (2i)! */ + BigInteger fac = new BigInteger("2"); + + for (int i = 2;; i++) { + Rational f = b.at(2 * i).abs(); + fourn = fourn.shiftLeft(2); + fac = fac.multiply(new BigInteger("" + (2 * i))).multiply(new BigInteger("" + (2 * i - 1))); + f = f.multiply(fourn).multiply(fourn.subtract(BigInteger.ONE)).divide(fac); + xpowi = multiplyRound(xpowi, xhighprSq); + BigDecimal c = multiplyRound(xpowi, f); + resul = resul.add(c); + if (Math.abs(c.doubleValue()) < 0.1 * eps) + break; + } + MathContext mc = new MathContext(err2prec(resul.doubleValue(), eps)); + return resul.round(mc); + } + } + } /* BigDecimalMath.tan */ + + /** + * The trigonometric co-tangent. + * + * @param x + * the argument in radians. + * @return the cot(x) + * @throws Errore + * @since 2009-07-31 + */ + static public BigDecimal cot(final BigDecimal x) throws Errore { + if (x.compareTo(BigDecimal.ZERO) == 0) { + throw new ArithmeticException("Cannot take cot of zero " + x.toString()); + } else if (x.compareTo(BigDecimal.ZERO) < 0) { + return cot(x.negate()).negate(); + } else { + /* + * reduce modulo pi + */ + BigDecimal res = modpi(x); + + /* + * absolute error in the result is err(x)/sin^2(x) to lowest order + */ + final double xDbl = res.doubleValue(); + final double xUlpDbl = x.ulp().doubleValue() / 2.; + final double eps = xUlpDbl / 2. / Math.pow(Math.sin(xDbl), 2.); + + final BigDecimal xhighpr = scalePrec(res, 2); + final BigDecimal xhighprSq = multiplyRound(xhighpr, xhighpr); + + MathContext mc = new MathContext(err2prec(xhighpr.doubleValue(), eps)); + BigDecimal resul = BigDecimal.ONE.divide(xhighpr, mc); + + /* x^(2i-1) */ + BigDecimal xpowi = xhighpr; + + Bernoulli b = new Bernoulli(); + + /* 2^(2i) */ + BigInteger fourn = new BigInteger("4"); + /* (2i)! */ + BigInteger fac = BigInteger.ONE; + + for (int i = 1;; i++) { + Rational f = b.at(2 * i); + fac = fac.multiply(new BigInteger("" + (2 * i))).multiply(new BigInteger("" + (2 * i - 1))); + f = f.multiply(fourn).divide(fac); + BigDecimal c = multiplyRound(xpowi, f); + if (i % 2 == 0) + resul = resul.add(c); + else + resul = resul.subtract(c); + if (Math.abs(c.doubleValue()) < 0.1 * eps) + break; + + fourn = fourn.shiftLeft(2); + xpowi = multiplyRound(xpowi, xhighprSq); + } + mc = new MathContext(err2prec(resul.doubleValue(), eps)); + return resul.round(mc); + } + } /* BigDecimalMath.cot */ + + /** + * The inverse trigonometric sine. + * + * @param x + * the argument. + * @return the arcsin(x) in radians. + * @throws Errore + */ + static public BigDecimal asin(final BigDecimal x) throws Errore { + if (x.compareTo(BigDecimal.ONE) > 0 || x.compareTo(BigDecimal.ONE.negate()) < 0) { + throw new ArithmeticException("Out of range argument " + x.toString() + " of asin"); + } else if (x.compareTo(BigDecimal.ZERO) == 0) + return BigDecimal.ZERO; + else if (x.compareTo(BigDecimal.ONE) == 0) { + /* + * arcsin(1) = pi/2 + */ + double errpi = Math.sqrt(x.ulp().doubleValue()); + MathContext mc = new MathContext(err2prec(3.14159, errpi)); + return pi(mc).divide(new BigDecimal(2)); + } else if (x.compareTo(BigDecimal.ZERO) < 0) { + return asin(x.negate()).negate(); + } else if (x.doubleValue() > 0.7) { + final BigDecimal xCompl = BigDecimal.ONE.subtract(x); + final double xDbl = x.doubleValue(); + final double xUlpDbl = x.ulp().doubleValue() / 2.; + final double eps = xUlpDbl / 2. / Math.sqrt(1. - Math.pow(xDbl, 2.)); + + final BigDecimal xhighpr = scalePrec(xCompl, 3); + final BigDecimal xhighprV = divideRound(xhighpr, 4); + + BigDecimal resul = BigDecimal.ONE; + + /* x^(2i+1) */ + BigDecimal xpowi = BigDecimal.ONE; + + /* i factorial */ + BigInteger ifacN = BigInteger.ONE; + BigInteger ifacD = BigInteger.ONE; + + for (int i = 1;; i++) { + ifacN = ifacN.multiply(new BigInteger("" + (2 * i - 1))); + ifacD = ifacD.multiply(new BigInteger("" + i)); + if (i == 1) + xpowi = xhighprV; + else + xpowi = multiplyRound(xpowi, xhighprV); + BigDecimal c = divideRound(multiplyRound(xpowi, ifacN), ifacD.multiply(new BigInteger("" + (2 * i + 1)))); + resul = resul.add(c); + /* + * series started 1+x/12+... which yields an estimate of the + * sum's error + */ + if (Math.abs(c.doubleValue()) < xUlpDbl / 120.) + break; + } + /* + * sqrt(2*z)*(1+...) + */ + xpowi = sqrt(xhighpr.multiply(new BigDecimal(2))); + resul = multiplyRound(xpowi, resul); + + MathContext mc = new MathContext(resul.precision()); + BigDecimal pihalf = pi(mc).divide(new BigDecimal(2)); + + mc = new MathContext(err2prec(resul.doubleValue(), eps)); + return pihalf.subtract(resul, mc); + } else { + /* + * absolute error in the result is err(x)/sqrt(1-x^2) to lowest + * order + */ + final double xDbl = x.doubleValue(); + final double xUlpDbl = x.ulp().doubleValue() / 2.; + final double eps = xUlpDbl / 2. / Math.sqrt(1. - Math.pow(xDbl, 2.)); + + final BigDecimal xhighpr = scalePrec(x, 2); + final BigDecimal xhighprSq = multiplyRound(xhighpr, xhighpr); + + BigDecimal resul = xhighpr.plus(); + + /* x^(2i+1) */ + BigDecimal xpowi = xhighpr; + + /* i factorial */ + BigInteger ifacN = BigInteger.ONE; + BigInteger ifacD = BigInteger.ONE; + + for (int i = 1;; i++) { + ifacN = ifacN.multiply(new BigInteger("" + (2 * i - 1))); + ifacD = ifacD.multiply(new BigInteger("" + (2 * i))); + xpowi = multiplyRound(xpowi, xhighprSq); + BigDecimal c = divideRound(multiplyRound(xpowi, ifacN), ifacD.multiply(new BigInteger("" + (2 * i + 1)))); + resul = resul.add(c); + if (Math.abs(c.doubleValue()) < 0.1 * eps) + break; + } + MathContext mc = new MathContext(err2prec(resul.doubleValue(), eps)); + return resul.round(mc); + } + } /* BigDecimalMath.asin */ + + /** + * The inverse trigonometric cosine. + * + * @param x + * the argument. + * @return the arccos(x) in radians. + * @throws Errore + * @since 2009-09-29 + */ + static public BigDecimal acos(final BigDecimal x) throws Errore { + /* + * Essentially forwarded to pi/2 - asin(x) + */ + final BigDecimal xhighpr = scalePrec(x, 2); + BigDecimal resul = asin(xhighpr); + double eps = resul.ulp().doubleValue() / 2.; + + MathContext mc = new MathContext(err2prec(3.14159, eps)); + BigDecimal pihalf = pi(mc).divide(new BigDecimal(2)); + resul = pihalf.subtract(resul); + + /* + * absolute error in the result is err(x)/sqrt(1-x^2) to lowest order + */ + final double xDbl = x.doubleValue(); + final double xUlpDbl = x.ulp().doubleValue() / 2.; + eps = xUlpDbl / 2. / Math.sqrt(1. - Math.pow(xDbl, 2.)); + + mc = new MathContext(err2prec(resul.doubleValue(), eps)); + return resul.round(mc); + + } /* BigDecimalMath.acos */ + + /** + * The inverse trigonometric tangent. + * + * @param x + * the argument. + * @return the principal value of arctan(x) in radians in the range -pi/2 to + * +pi/2. + * @throws Errore + * @since 2009-08-03 + */ + static public BigDecimal atan(final BigDecimal x) throws Errore { + if (x.compareTo(BigDecimal.ZERO) < 0) { + return atan(x.negate()).negate(); + } else if (x.compareTo(BigDecimal.ZERO) == 0) + return BigDecimal.ZERO; + else if (x.doubleValue() > 0.7 && x.doubleValue() < 3.0) { + /* + * Abramowitz-Stegun 4.4.34 convergence acceleration + * 2*arctan(x) = arctan(2x/(1-x^2)) = arctan(y). x=(sqrt(1+y^2)-1)/y + * This maps 0<=y<=3 to 0<=x<=0.73 roughly. Temporarily with 2 + * protectionist digits. + */ + BigDecimal y = scalePrec(x, 2); + BigDecimal newx = divideRound(hypot(1, y).subtract(BigDecimal.ONE), y); + + /* intermediate result with too optimistic error estimate */ + BigDecimal resul = multiplyRound(atan(newx), 2); + + /* + * absolute error in the result is errx/(1+x^2), where errx = half + * of the ulp. + */ + double eps = x.ulp().doubleValue() / (2.0 * Math.hypot(1.0, x.doubleValue())); + MathContext mc = new MathContext(err2prec(resul.doubleValue(), eps)); + return resul.round(mc); + } else if (x.doubleValue() < 0.71) { + /* Taylor expansion around x=0; Abramowitz-Stegun 4.4.42 */ + + final BigDecimal xhighpr = scalePrec(x, 2); + final BigDecimal xhighprSq = multiplyRound(xhighpr, xhighpr).negate(); + + BigDecimal resul = xhighpr.plus(); + + /* signed x^(2i+1) */ + BigDecimal xpowi = xhighpr; + + /* + * absolute error in the result is errx/(1+x^2), where errx = half + * of the ulp. + */ + double eps = x.ulp().doubleValue() / (2.0 * Math.hypot(1.0, x.doubleValue())); + + for (int i = 1;; i++) { + xpowi = multiplyRound(xpowi, xhighprSq); + BigDecimal c = divideRound(xpowi, 2 * i + 1); + + resul = resul.add(c); + if (Math.abs(c.doubleValue()) < 0.1 * eps) + break; + } + MathContext mc = new MathContext(err2prec(resul.doubleValue(), eps)); + return resul.round(mc); + } else { + /* Taylor expansion around x=infinity; Abramowitz-Stegun 4.4.42 */ + + /* + * absolute error in the result is errx/(1+x^2), where errx = half + * of the ulp. + */ + double eps = x.ulp().doubleValue() / (2.0 * Math.hypot(1.0, x.doubleValue())); + + /* + * start with the term pi/2; gather its precision relative to the + * expected result + */ + MathContext mc = new MathContext(2 + err2prec(3.1416, eps)); + BigDecimal onepi = pi(mc); + BigDecimal resul = onepi.divide(new BigDecimal(2)); + + final BigDecimal xhighpr = divideRound(-1, scalePrec(x, 2)); + final BigDecimal xhighprSq = multiplyRound(xhighpr, xhighpr).negate(); + + /* signed x^(2i+1) */ + BigDecimal xpowi = xhighpr; + + for (int i = 0;; i++) { + BigDecimal c = divideRound(xpowi, 2 * i + 1); + + resul = resul.add(c); + if (Math.abs(c.doubleValue()) < 0.1 * eps) + break; + xpowi = multiplyRound(xpowi, xhighprSq); + } + mc = new MathContext(err2prec(resul.doubleValue(), eps)); + return resul.round(mc); + } + } /* BigDecimalMath.atan */ + + /** + * The hyperbolic cosine. + * + * @param x + * The argument. + * @return The cosh(x) = (exp(x)+exp(-x))/2 . + * @author Richard J. Mathar + * @throws Errore + * @since 2009-08-19 + */ + static public BigDecimal cosh(final BigDecimal x) throws Errore { + if (x.compareTo(BigDecimal.ZERO) < 0) + return cos(x.negate()); + else if (x.compareTo(BigDecimal.ZERO) == 0) + return BigDecimal.ONE; + else { + if (x.doubleValue() > 1.5) { + /* + * cosh^2(x) = 1+ sinh^2(x). + */ + return hypot(1, sinh(x)); + } else { + BigDecimal xhighpr = scalePrec(x, 2); + /* Simple Taylor expansion, sum_{0=1..infinity} x^(2i)/(2i)! */ + BigDecimal resul = BigDecimal.ONE; + + /* x^i */ + BigDecimal xpowi = BigDecimal.ONE; + + /* 2i factorial */ + BigInteger ifac = BigInteger.ONE; + + /* + * The absolute error in the result is the error in x^2/2 which + * is x times the error in x. + */ + double xUlpDbl = 0.5 * x.ulp().doubleValue() * x.doubleValue(); + + /* + * The error in the result is set by the error in x^2/2 itself, + * xUlpDbl. + * We need at most k terms to push x^(2k)/(2k)! below this + * value. + * x^(2k) < xUlpDbl; (2k)*log(x) < log(xUlpDbl); + */ + int k = (int) (Math.log(xUlpDbl) / Math.log(x.doubleValue())) / 2; + + /* + * The individual terms are all smaller than 1, so an estimate + * of 1.0 for + * the absolute value will give a safe relative error estimate + * for the indivdual terms + */ + MathContext mcTay = new MathContext(err2prec(1., xUlpDbl / k)); + for (int i = 1;; i++) { + /* + * TBD: at which precision will 2*i-1 or 2*i overflow? + */ + ifac = ifac.multiply(new BigInteger("" + (2 * i - 1))); + ifac = ifac.multiply(new BigInteger("" + (2 * i))); + xpowi = xpowi.multiply(xhighpr).multiply(xhighpr); + BigDecimal corr = xpowi.divide(new BigDecimal(ifac), mcTay); + resul = resul.add(corr); + if (corr.abs().doubleValue() < 0.5 * xUlpDbl) + break; + } + /* + * The error in the result is governed by the error in x itself. + */ + MathContext mc = new MathContext(err2prec(resul.doubleValue(), xUlpDbl)); + return resul.round(mc); + } + } + } /* BigDecimalMath.cosh */ + + /** + * The hyperbolic sine. + * + * @param x + * the argument. + * @return the sinh(x) = (exp(x)-exp(-x))/2 . + * @author Richard J. Mathar + * @throws Errore + * @since 2009-08-19 + */ + static public BigDecimal sinh(final BigDecimal x) throws Errore { + if (x.compareTo(BigDecimal.ZERO) < 0) + return sinh(x.negate()).negate(); + else if (x.compareTo(BigDecimal.ZERO) == 0) + return BigDecimal.ZERO; + else { + if (x.doubleValue() > 2.4) { + /* + * Move closer to zero with sinh(2x)= 2*sinh(x)*cosh(x). + */ + BigDecimal two = new BigDecimal(2); + BigDecimal xhalf = x.divide(two); + BigDecimal resul = sinh(xhalf).multiply(cosh(xhalf)).multiply(two); + /* + * The error in the result is set by the error in x itself. + * The first derivative of sinh(x) is cosh(x), so the absolute + * error + * in the result is cosh(x)*errx, and the relative error is + * coth(x)*errx = errx/tanh(x) + */ + double eps = Math.tanh(x.doubleValue()); + MathContext mc = new MathContext(err2prec(0.5 * x.ulp().doubleValue() / eps)); + return resul.round(mc); + } else { + BigDecimal xhighpr = scalePrec(x, 2); + /* + * Simple Taylor expansion, sum_{i=0..infinity} x^(2i+1)/(2i+1)! + */ + BigDecimal resul = xhighpr; + + /* x^i */ + BigDecimal xpowi = xhighpr; + + /* 2i+1 factorial */ + BigInteger ifac = BigInteger.ONE; + + /* + * The error in the result is set by the error in x itself. + */ + double xUlpDbl = x.ulp().doubleValue(); + + /* + * The error in the result is set by the error in x itself. + * We need at most k terms to squeeze x^(2k+1)/(2k+1)! below + * this value. + * x^(2k+1) < x.ulp; (2k+1)*log10(x) < -x.precision; + * 2k*log10(x)< -x.precision; + * 2k*(-log10(x)) > x.precision; 2k*log10(1/x) > x.precision + */ + int k = (int) (x.precision() / Math.log10(1.0 / xhighpr.doubleValue())) / 2; + MathContext mcTay = new MathContext(err2prec(x.doubleValue(), xUlpDbl / k)); + for (int i = 1;; i++) { + /* + * TBD: at which precision will 2*i or 2*i+1 overflow? + */ + ifac = ifac.multiply(new BigInteger("" + (2 * i))); + ifac = ifac.multiply(new BigInteger("" + (2 * i + 1))); + xpowi = xpowi.multiply(xhighpr).multiply(xhighpr); + BigDecimal corr = xpowi.divide(new BigDecimal(ifac), mcTay); + resul = resul.add(corr); + if (corr.abs().doubleValue() < 0.5 * xUlpDbl) + break; + } + /* + * The error in the result is set by the error in x itself. + */ + MathContext mc = new MathContext(x.precision()); + return resul.round(mc); + } + } + } /* BigDecimalMath.sinh */ + + /** + * The hyperbolic tangent. + * + * @param x + * The argument. + * @return The tanh(x) = sinh(x)/cosh(x). + * @author Richard J. Mathar + * @since 2009-08-20 + */ + static public BigDecimal tanh(final BigDecimal x) { + if (x.compareTo(BigDecimal.ZERO) < 0) + return tanh(x.negate()).negate(); + else if (x.compareTo(BigDecimal.ZERO) == 0) + return BigDecimal.ZERO; + else { + BigDecimal xhighpr = scalePrec(x, 2); + + /* + * tanh(x) = (1-e^(-2x))/(1+e^(-2x)) . + */ + BigDecimal exp2x = exp(xhighpr.multiply(new BigDecimal(-2))); + + /* + * The error in tanh x is err(x)/cosh^2(x). + */ + double eps = 0.5 * x.ulp().doubleValue() / Math.pow(Math.cosh(x.doubleValue()), 2.0); + MathContext mc = new MathContext(err2prec(Math.tanh(x.doubleValue()), eps)); + return BigDecimal.ONE.subtract(exp2x).divide(BigDecimal.ONE.add(exp2x), mc); + } + } /* BigDecimalMath.tanh */ + + /** + * The inverse hyperbolic sine. + * + * @param x + * The argument. + * @return The arcsinh(x) . + * @author Richard J. Mathar + * @since 2009-08-20 + */ + static public BigDecimal asinh(final BigDecimal x) { + if (x.compareTo(BigDecimal.ZERO) == 0) + return BigDecimal.ZERO; + else { + BigDecimal xhighpr = scalePrec(x, 2); + + /* + * arcsinh(x) = log(x+hypot(1,x)) + */ + BigDecimal logx = log(hypot(1, xhighpr).add(xhighpr)); + + /* + * The absolute error in arcsinh x is err(x)/sqrt(1+x^2) + */ + double xDbl = x.doubleValue(); + double eps = 0.5 * x.ulp().doubleValue() / Math.hypot(1., xDbl); + MathContext mc = new MathContext(err2prec(logx.doubleValue(), eps)); + return logx.round(mc); + } + } /* BigDecimalMath.asinh */ + + /** + * The inverse hyperbolic cosine. + * + * @param x + * The argument. + * @return The arccosh(x) . + * @author Richard J. Mathar + * @since 2009-08-20 + */ + static public BigDecimal acosh(final BigDecimal x) { + if (x.compareTo(BigDecimal.ONE) < 0) + throw new ArithmeticException("Out of range argument cosh " + x.toString()); + else if (x.compareTo(BigDecimal.ONE) == 0) + return BigDecimal.ZERO; + else { + BigDecimal xhighpr = scalePrec(x, 2); + + /* + * arccosh(x) = log(x+sqrt(x^2-1)) + */ + BigDecimal logx = log(sqrt(xhighpr.pow(2).subtract(BigDecimal.ONE)).add(xhighpr)); + + /* + * The absolute error in arcsinh x is err(x)/sqrt(x^2-1) + */ + double xDbl = x.doubleValue(); + double eps = 0.5 * x.ulp().doubleValue() / Math.sqrt(xDbl * xDbl - 1.); + MathContext mc = new MathContext(err2prec(logx.doubleValue(), eps)); + return logx.round(mc); + } + } /* BigDecimalMath.acosh */ + + /** + * The Gamma function. + * + * @param x + * The argument. + * @return Gamma(x). + * @throws Errore + * @since 2009-08-06 + */ + static public BigDecimal Gamma(final BigDecimal x) throws Errore { + /* + * reduce to interval near 1.0 with the functional relation, + * Abramowitz-Stegun 6.1.33 + */ + if (x.compareTo(BigDecimal.ZERO) < 0) + return divideRound(Gamma(x.add(BigDecimal.ONE)), x); + else if (x.doubleValue() > 1.5) { + /* + * Gamma(x) = Gamma(xmin+n) = Gamma(xmin)*Pochhammer(xmin,n). + */ + int n = (int) (x.doubleValue() - 0.5); + BigDecimal xmin1 = x.subtract(new BigDecimal(n)); + return multiplyRound(Gamma(xmin1), pochhammer(xmin1, n)); + } else { + /* + * apply Abramowitz-Stegun 6.1.33 + */ + BigDecimal z = x.subtract(BigDecimal.ONE); + + /* + * add intermediately 2 digits to the partial sum accumulation + */ + z = scalePrec(z, 2); + MathContext mcloc = new MathContext(z.precision()); + + /* + * measure of the absolute error is the relative error in the first, + * logarithmic term + */ + double eps = x.ulp().doubleValue() / x.doubleValue(); + + BigDecimal resul = log(scalePrec(x, 2)).negate(); + + if (x.compareTo(BigDecimal.ONE) != 0) { + + BigDecimal gammCompl = BigDecimal.ONE.subtract(gamma(mcloc)); + resul = resul.add(multiplyRound(z, gammCompl)); + for (int n = 2;; n++) { + /* + * multiplying z^n/n by zeta(n-1) means that the two + * relative errors add. + * so the requirement in the relative error of zeta(n)-1 is + * that this is somewhat + * smaller than the relative error in z^n/n (the absolute + * error of thelatter is the + * absolute error in z) + */ + BigDecimal c = divideRound(z.pow(n, mcloc), n); + MathContext m = new MathContext(err2prec(n * z.ulp().doubleValue() / 2. / z.doubleValue())); + c = c.round(m); + + /* + * At larger n, zeta(n)-1 is roughly 1/2^n. The product is + * c/2^n. + * The relative error in c is c.ulp/2/c . The error in the + * product should be small versus eps/10. + * Error from 1/2^n is c*err(sigma-1). + * We need a relative error of zeta-1 of the order of + * c.ulp/50/c. This is an absolute + * error in zeta-1 of c.ulp/50/c/2^n, and also the absolute + * error in zeta, because zeta is + * of the order of 1. + */ + if (eps / 100. / c.doubleValue() < 0.01) + m = new MathContext(err2prec(eps / 100. / c.doubleValue())); + else + m = new MathContext(2); + /* zeta(n) -1 */ + BigDecimal zetm1 = zeta(n, m).subtract(BigDecimal.ONE); + c = multiplyRound(c, zetm1); + + if (n % 2 == 0) + resul = resul.add(c); + else + resul = resul.subtract(c); + + /* + * alternating sum, so truncating as eps is reached suffices + */ + if (Math.abs(c.doubleValue()) < eps) + break; + } + } + + /* + * The relative error in the result is the absolute error in the + * input variable times the digamma (psi) value at that point. + */ + double zdbl = z.doubleValue(); + eps = psi(zdbl) * x.ulp().doubleValue() / 2.; + mcloc = new MathContext(err2prec(eps)); + return exp(resul).round(mcloc); + } + } /* BigDecimalMath.gamma */ + + /** + * The Gamma function. + * + * @param q + * The argument. + * @param mc + * The required accuracy in the result. + * @return Gamma(x). + * @throws Errore + * @since 2010-05-26 + */ + static public BigDecimal Gamma(final Rational q, final MathContext mc) throws Errore { + if (q.isBigInteger()) { + if (q.compareTo(Rational.ZERO) <= 0) + throw new ArithmeticException("Gamma at " + q.toString()); + else { + /* Gamma(n) = (n-1)! */ + Factorial f = new Factorial(); + BigInteger g = f.at(q.trunc().intValue() - 1); + return scalePrec(new BigDecimal(g), mc); + } + } else if (q.b.intValue() == 2) { + /* + * half integer cases which are related to sqrt(pi) + */ + BigDecimal p = sqrt(pi(mc)); + if (q.compareTo(Rational.ZERO) >= 0) { + Rational pro = Rational.ONE; + Rational f = q.subtract(1); + while (f.compareTo(Rational.ZERO) > 0) { + pro = pro.multiply(f); + f = f.subtract(1); + } + return multiplyRound(p, pro); + } else { + Rational pro = Rational.ONE; + Rational f = q; + while (f.compareTo(Rational.ZERO) < 0) { + pro = pro.divide(f); + f = f.add(1); + } + return multiplyRound(p, pro); + } + } else { + /* + * The relative error of the result is psi(x)*Delta(x). Tune + * Delta(x) such + * that this is equivalent to mc: Delta(x) = precision/psi(x). + */ + double qdbl = q.doubleValue(); + double deltx = 5. * Math.pow(10., -mc.getPrecision()) / psi(qdbl); + + MathContext mcx = new MathContext(err2prec(qdbl, deltx)); + BigDecimal x = q.BigDecimalValue(mcx); + + /* forward calculation to the general floating point case */ + return Gamma(x); + } + } /* BigDecimalMath.Gamma */ + + /** + * Pochhammer's function. + * + * @param x + * The main argument. + * @param n + * The non-negative index. + * @return (x)_n = x(x+1)(x+2)*...*(x+n-1). + * @since 2009-08-19 + */ + static public BigDecimal pochhammer(final BigDecimal x, final int n) { + /* + * reduce to interval near 1.0 with the functional relation, + * Abramowitz-Stegun 6.1.33 + */ + if (n < 0) + throw new ProviderException("Not implemented: pochhammer with negative index " + n); + else if (n == 0) + return BigDecimal.ONE; + else { + /* + * internally two safety digits + */ + BigDecimal xhighpr = scalePrec(x, 2); + BigDecimal resul = xhighpr; + + double xUlpDbl = x.ulp().doubleValue(); + double xDbl = x.doubleValue(); + /* + * relative error of the result is the sum of the relative errors of + * the factors + */ + double eps = 0.5 * xUlpDbl / Math.abs(xDbl); + for (int i = 1; i < n; i++) { + eps += 0.5 * xUlpDbl / Math.abs(xDbl + i); + resul = resul.multiply(xhighpr.add(new BigDecimal(i))); + final MathContext mcloc = new MathContext(4 + err2prec(eps)); + resul = resul.round(mcloc); + } + return resul.round(new MathContext(err2prec(eps))); + } + } /* BigDecimalMath.pochhammer */ + + /** + * Reduce value to the interval [0,2*Pi]. + * + * @param x + * the original value + * @return the value modulo 2*pi in the interval from 0 to 2*pi. + * @throws Errore + * @since 2009-06-01 + */ + static public BigDecimal mod2pi(BigDecimal x) throws Errore { + /* + * write x= 2*pi*k+r with the precision in r defined by the precision of + * x and not + * compromised by the precision of 2*pi, so the ulp of 2*pi*k should + * match the ulp of x. + * First get a guess of k to figure out how many digits of 2*pi are + * needed. + */ + int k = (int) (0.5 * x.doubleValue() / Math.PI); + + /* + * want to have err(2*pi*k)< err(x)=0.5*x.ulp, so err(pi) = err(x)/(4k) + * with two safety digits + */ + double err2pi; + if (k != 0) + err2pi = 0.25 * Math.abs(x.ulp().doubleValue() / k); + else + err2pi = 0.5 * Math.abs(x.ulp().doubleValue()); + MathContext mc = new MathContext(2 + err2prec(6.283, err2pi)); + BigDecimal twopi = pi(mc).multiply(new BigDecimal(2)); + + /* + * Delegate the actual operation to the BigDecimal class, which may + * return + * a negative value of x was negative . + */ + BigDecimal res = x.remainder(twopi); + if (res.compareTo(BigDecimal.ZERO) < 0) + res = res.add(twopi); + + /* + * The actual precision is set by the input value, its absolute value of + * x.ulp()/2. + */ + mc = new MathContext(err2prec(res.doubleValue(), x.ulp().doubleValue() / 2.)); + return res.round(mc); + } /* mod2pi */ + + /** + * Reduce value to the interval [-Pi/2,Pi/2]. + * + * @param x + * The original value + * @return The value modulo pi, shifted to the interval from -Pi/2 to Pi/2. + * @throws Errore + * @since 2009-07-31 + */ + static public BigDecimal modpi(BigDecimal x) throws Errore { + /* + * write x= pi*k+r with the precision in r defined by the precision of x + * and not + * compromised by the precision of pi, so the ulp of pi*k should match + * the ulp of x. + * First get a guess of k to figure out how many digits of pi are + * needed. + */ + int k = (int) (x.doubleValue() / Math.PI); + + /* + * want to have err(pi*k)< err(x)=x.ulp/2, so err(pi) = err(x)/(2k) with + * two safety digits + */ + double errpi; + if (k != 0) + errpi = 0.5 * Math.abs(x.ulp().doubleValue() / k); + else + errpi = 0.5 * Math.abs(x.ulp().doubleValue()); + MathContext mc = new MathContext(2 + err2prec(3.1416, errpi)); + BigDecimal onepi = pi(mc); + BigDecimal pihalf = onepi.divide(new BigDecimal(2)); + + /* + * Delegate the actual operation to the BigDecimal class, which may + * return + * a negative value of x was negative . + */ + BigDecimal res = x.remainder(onepi); + if (res.compareTo(pihalf) > 0) + res = res.subtract(onepi); + else if (res.compareTo(pihalf.negate()) < 0) + res = res.add(onepi); + + /* + * The actual precision is set by the input value, its absolute value of + * x.ulp()/2. + */ + mc = new MathContext(err2prec(res.doubleValue(), x.ulp().doubleValue() / 2.)); + return res.round(mc); + } /* modpi */ + + /** + * Riemann zeta function. + * + * @param n + * The positive integer argument. + * @param mc + * Specification of the accuracy of the result. + * @return zeta(n). + * @throws Errore + * @since 2009-08-05 + */ + static public BigDecimal zeta(final int n, final MathContext mc) throws Errore { + if (n <= 0) + throw new ProviderException("Not implemented: zeta at negative argument " + n); + if (n == 1) + throw new ArithmeticException("Pole at zeta(1) "); + + if (n % 2 == 0) { + /* + * Even indices. Abramowitz-Stegun 23.2.16. Start with + * 2^(n-1)*B(n)/n! + */ + Rational b = (new Bernoulli()).at(n).abs(); + b = b.divide((new Factorial()).at(n)); + b = b.multiply(BigInteger.ONE.shiftLeft(n - 1)); + + /* + * to be multiplied by pi^n. Absolute error in the result of pi^n is + * n times + * error in pi times pi^(n-1). Relative error is n*error(pi)/pi, + * requested by mc. + * Need one more digit in pi if n=10, two digits if n=100 etc, and + * add one extra digit. + */ + MathContext mcpi = new MathContext(mc.getPrecision() + (int) (Math.log10(10.0 * n))); + final BigDecimal piton = pi(mcpi).pow(n, mc); + return multiplyRound(piton, b); + } else if (n == 3) { + /* + * Broadhurst BBP arXiv:math/9803067 + * Error propagation: S31 is roughly 0.087, S33 roughly 0.131 + */ + int[] a31 = { 1, -7, -1, 10, -1, -7, 1, 0 }; + int[] a33 = { 1, 1, -1, -2, -1, 1, 1, 0 }; + BigDecimal S31 = broadhurstBBP(3, 1, a31, mc); + BigDecimal S33 = broadhurstBBP(3, 3, a33, mc); + S31 = S31.multiply(new BigDecimal(48)); + S33 = S33.multiply(new BigDecimal(32)); + return S31.add(S33).divide(new BigDecimal(7), mc); + } else if (n == 5) { + /* + * Broadhurst BBP arXiv:math/9803067 + * Error propagation: S51 is roughly -11.15, S53 roughly 22.165, S55 + * is roughly 0.031 + * 9*2048*S51/6265 = -3.28. 7*2038*S53/61651= 5.07. + * 738*2048*S55/61651= 0.747. + * The result is of the order 1.03, so we add 2 digits to S51 and + * S52 and one digit to S55. + */ + int[] a51 = { 31, -1614, -31, -6212, -31, -1614, 31, 74552 }; + int[] a53 = { 173, 284, -173, -457, -173, 284, 173, -111 }; + int[] a55 = { 1, 0, -1, -1, -1, 0, 1, 1 }; + BigDecimal S51 = broadhurstBBP(5, 1, a51, new MathContext(2 + mc.getPrecision())); + BigDecimal S53 = broadhurstBBP(5, 3, a53, new MathContext(2 + mc.getPrecision())); + BigDecimal S55 = broadhurstBBP(5, 5, a55, new MathContext(1 + mc.getPrecision())); + S51 = S51.multiply(new BigDecimal(18432)); + S53 = S53.multiply(new BigDecimal(14336)); + S55 = S55.multiply(new BigDecimal(1511424)); + return S51.add(S53).subtract(S55).divide(new BigDecimal(62651), mc); + } else { + /* + * Cohen et al Exp Math 1 (1) (1992) 25 + */ + Rational betsum = new Rational(); + Bernoulli bern = new Bernoulli(); + Factorial fact = new Factorial(); + for (int npr = 0; npr <= (n + 1) / 2; npr++) { + Rational b = bern.at(2 * npr).multiply(bern.at(n + 1 - 2 * npr)); + b = b.divide(fact.at(2 * npr)).divide(fact.at(n + 1 - 2 * npr)); + b = b.multiply(1 - 2 * npr); + if (npr % 2 == 0) + betsum = betsum.add(b); + else + betsum = betsum.subtract(b); + } + betsum = betsum.divide(n - 1); + /* + * The first term, including the facor (2pi)^n, is essentially most + * of the result, near one. The second term below is roughly in the + * range 0.003 to 0.009. + * So the precision here is matching the precisionn requested by mc, + * and the precision + * requested for 2*pi is in absolute terms adjusted. + */ + MathContext mcloc = new MathContext(2 + mc.getPrecision() + (int) (Math.log10((n)))); + BigDecimal ftrm = pi(mcloc).multiply(new BigDecimal(2)); + ftrm = ftrm.pow(n); + ftrm = multiplyRound(ftrm, betsum.BigDecimalValue(mcloc)); + BigDecimal exps = new BigDecimal(0); + + /* + * the basic accuracy of the accumulated terms before multiplication + * with 2 + */ + double eps = Math.pow(10., -mc.getPrecision()); + + if (n % 4 == 3) { + /* + * since the argument n is at least 7 here, the drop + * of the terms is at rather constant pace at least 10^-3, for + * example + * 0.0018, 0.2e-7, 0.29e-11, 0.74e-15 etc for npr=1,2,3.... We + * want 2 times these terms + * fall below eps/10. + */ + int kmax = mc.getPrecision() / 3; + eps /= kmax; + /* + * need an error of eps for 2/(exp(2pi)-1) = 0.0037 + * The absolute error is + * 4*exp(2pi)*err(pi)/(exp(2pi)-1)^2=0.0075*err(pi) + */ + BigDecimal exp2p = pi(new MathContext(3 + err2prec(3.14, eps / 0.0075))); + exp2p = exp(exp2p.multiply(new BigDecimal(2))); + BigDecimal c = exp2p.subtract(BigDecimal.ONE); + exps = divideRound(1, c); + for (int npr = 2; npr <= kmax; npr++) { + /* + * the error estimate above for npr=1 is the worst case of + * the absolute error created by an error in 2pi. So we can + * safely re-use the exp2p value computed above without + * reassessment of its error. + */ + c = powRound(exp2p, npr).subtract(BigDecimal.ONE); + c = multiplyRound(c, (new BigInteger("" + npr)).pow(n)); + c = divideRound(1, c); + exps = exps.add(c); + } + } else { + /* + * since the argument n is at least 9 here, the drop + * of the terms is at rather constant pace at least 10^-3, for + * example + * 0.0096, 0.5e-7, 0.3e-11, 0.6e-15 etc. We want these terms + * fall below eps/10. + */ + int kmax = (1 + mc.getPrecision()) / 3; + eps /= kmax; + /* + * need an error of eps for + * 2/(exp(2pi)-1)*(1+4*Pi/8/(1-exp(-2pi)) = 0.0096 + * at k=7 or = 0.00766 at k=13 for example. + * The absolute error is 0.017*err(pi) at k=9, 0.013*err(pi) at + * k=13, 0.012 at k=17 + */ + BigDecimal twop = pi(new MathContext(3 + err2prec(3.14, eps / 0.017))); + twop = twop.multiply(new BigDecimal(2)); + BigDecimal exp2p = exp(twop); + BigDecimal c = exp2p.subtract(BigDecimal.ONE); + exps = divideRound(1, c); + c = BigDecimal.ONE.subtract(divideRound(1, exp2p)); + c = divideRound(twop, c).multiply(new BigDecimal(2)); + c = divideRound(c, n - 1).add(BigDecimal.ONE); + exps = multiplyRound(exps, c); + for (int npr = 2; npr <= kmax; npr++) { + c = powRound(exp2p, npr).subtract(BigDecimal.ONE); + c = multiplyRound(c, (new BigInteger("" + npr)).pow(n)); + + BigDecimal d = divideRound(1, exp2p.pow(npr)); + d = BigDecimal.ONE.subtract(d); + d = divideRound(twop, d).multiply(new BigDecimal(2 * npr)); + d = divideRound(d, n - 1).add(BigDecimal.ONE); + + d = divideRound(d, c); + + exps = exps.add(d); + } + } + exps = exps.multiply(new BigDecimal(2)); + return ftrm.subtract(exps, mc); + } + } /* zeta */ + + /** + * Riemann zeta function. + * + * @param n + * The positive integer argument. + * @return zeta(n)-1. + * @throws Errore + * @since 2009-08-20 + */ + static public double zeta1(final int n) throws Errore { + /* + * precomputed static table in double precision + */ + final double[] zmin1 = { 0., 0., 6.449340668482264364724151666e-01, 2.020569031595942853997381615e-01, 8.232323371113819151600369654e-02, 3.692775514336992633136548646e-02, 1.734306198444913971451792979e-02, 8.349277381922826839797549850e-03, 4.077356197944339378685238509e-03, 2.008392826082214417852769232e-03, 9.945751278180853371459589003e-04, 4.941886041194645587022825265e-04, 2.460865533080482986379980477e-04, 1.227133475784891467518365264e-04, 6.124813505870482925854510514e-05, 3.058823630702049355172851064e-05, 1.528225940865187173257148764e-05, 7.637197637899762273600293563e-06, 3.817293264999839856461644622e-06, 1.908212716553938925656957795e-06, 9.539620338727961131520386834e-07, 4.769329867878064631167196044e-07, 2.384505027277329900036481868e-07, 1.192199259653110730677887189e-07, 5.960818905125947961244020794e-08, 2.980350351465228018606370507e-08, 1.490155482836504123465850663e-08, 7.450711789835429491981004171e-09, 3.725334024788457054819204018e-09, 1.862659723513049006403909945e-09, 9.313274324196681828717647350e-10, 4.656629065033784072989233251e-10, 2.328311833676505492001455976e-10, 1.164155017270051977592973835e-10, 5.820772087902700889243685989e-11, 2.910385044497099686929425228e-11, 1.455192189104198423592963225e-11, 7.275959835057481014520869012e-12, 3.637979547378651190237236356e-12, 1.818989650307065947584832101e-12, 9.094947840263889282533118387e-13, 4.547473783042154026799112029e-13, 2.273736845824652515226821578e-13, 1.136868407680227849349104838e-13, 5.684341987627585609277182968e-14, 2.842170976889301855455073705e-14, 1.421085482803160676983430714e-14, 7.105427395210852712877354480e-15, 3.552713691337113673298469534e-15, 1.776356843579120327473349014e-15, 8.881784210930815903096091386e-16, 4.440892103143813364197770940e-16, 2.220446050798041983999320094e-16, 1.110223025141066133720544570e-16, 5.551115124845481243723736590e-17, 2.775557562136124172581632454e-17, 1.387778780972523276283909491e-17, 6.938893904544153697446085326e-18, 3.469446952165922624744271496e-18, 1.734723476047576572048972970e-18, 8.673617380119933728342055067e-19, 4.336808690020650487497023566e-19, 2.168404344997219785013910168e-19, 1.084202172494241406301271117e-19, 5.421010862456645410918700404e-20, 2.710505431223468831954621312e-20, 1.355252715610116458148523400e-20, 6.776263578045189097995298742e-21, 3.388131789020796818085703100e-21, 1.694065894509799165406492747e-21, 8.470329472546998348246992609e-22, 4.235164736272833347862270483e-22, 2.117582368136194731844209440e-22, 1.058791184068023385226500154e-22, 5.293955920339870323813912303e-23, 2.646977960169852961134116684e-23, 1.323488980084899080309451025e-23, 6.617444900424404067355245332e-24, 3.308722450212171588946956384e-24, 1.654361225106075646229923677e-24, 8.271806125530344403671105617e-25, 4.135903062765160926009382456e-25, 2.067951531382576704395967919e-25, 1.033975765691287099328409559e-25, 5.169878828456431320410133217e-26, 2.584939414228214268127761771e-26, 1.292469707114106670038112612e-26, 6.462348535570531803438002161e-27, 3.231174267785265386134814118e-27, 1.615587133892632521206011406e-27, 8.077935669463162033158738186e-28, 4.038967834731580825622262813e-28, 2.019483917365790349158762647e-28, 1.009741958682895153361925070e-28, 5.048709793414475696084771173e-29, 2.524354896707237824467434194e-29, 1.262177448353618904375399966e-29, 6.310887241768094495682609390e-30, 3.155443620884047239109841220e-30, 1.577721810442023616644432780e-30, 7.888609052210118073520537800e-31 }; + if (n <= 0) + throw new ProviderException("Not implemented: zeta at negative argument " + n); + if (n == 1) + throw new ArithmeticException("Pole at zeta(1) "); + + if (n < zmin1.length) + /* look it up if available */ + return zmin1[n]; + else { + /* + * Result is roughly 2^(-n), desired accuracy 18 digits. If zeta(n) + * is computed, the equivalent accuracy + * in relative units is higher, because zeta is around 1. + */ + double eps = 1.e-18 * Math.pow(2., (-n)); + MathContext mc = new MathContext(err2prec(eps)); + return zeta(n, mc).subtract(BigDecimal.ONE).doubleValue(); + } + } /* zeta */ + + /** + * trigonometric cot. + * + * @param x + * The argument. + * @return cot(x) = 1/tan(x). + */ + static public double cot(final double x) { + return 1. / Math.tan(x); + } + + /** + * Digamma function. + * + * @param x + * The main argument. + * @return psi(x). + * The error is sometimes up to 10 ulp, where AS 6.3.15 suffers from + * cancellation of digits and psi=0 + * @throws Errore + * @since 2009-08-26 + */ + static public double psi(final double x) throws Errore { + /* + * the single positive zero of psi(x) + */ + final double psi0 = 1.46163214496836234126265954232572132846819; + if (x > 2.0) { + /* + * Reduce to a value near x=1 with the standard recurrence formula. + * Abramowitz-Stegun 6.3.5 + */ + int m = (int) (x - 0.5); + double xmin1 = x - m; + double resul = 0.; + for (int i = 1; i <= m; i++) + resul += 1. / (x - i); + return resul + psi(xmin1); + } else if (Math.abs(x - psi0) < 0.55) { + /* + * Taylor approximation around the local zero + */ + final double[] psiT0 = { 9.67672245447621170427e-01, -4.42763168983592106093e-01, 2.58499760955651010624e-01, -1.63942705442406527504e-01, 1.07824050691262365757e-01, -7.21995612564547109261e-02, 4.88042881641431072251e-02, -3.31611264748473592923e-02, 2.25976482322181046596e-02, -1.54247659049489591388e-02, 1.05387916166121753881e-02, -7.20453438635686824097e-03, 4.92678139572985344635e-03, -3.36980165543932808279e-03, 2.30512632673492783694e-03, -1.57693677143019725927e-03, 1.07882520191629658069e-03, -7.38070938996005129566e-04, 5.04953265834602035177e-04, -3.45468025106307699556e-04, 2.36356015640270527924e-04, -1.61706220919748034494e-04, 1.10633727687474109041e-04, -7.56917958219506591924e-05, 5.17857579522208086899e-05, -3.54300709476596063157e-05, 2.42400661186013176527e-05, -1.65842422718541333752e-05, 1.13463845846638498067e-05, -7.76281766846209442527e-06, 5.31106092088986338732e-06, -3.63365078980104566837e-06, 2.48602273312953794890e-06, -1.70085388543326065825e-06, 1.16366753635488427029e-06, -7.96142543124197040035e-07, 5.44694193066944527850e-07, -3.72661612834382295890e-07, 2.54962655202155425666e-07, -1.74436951177277452181e-07, 1.19343948298302427790e-07, -8.16511518948840884084e-08, 5.58629968353217144428e-08, -3.82196006191749421243e-08, 2.61485769519618662795e-08, -1.78899848649114926515e-08, 1.22397314032336619391e-08, -8.37401629767179054290e-09, 5.72922285984999377160e-09 }; + final double xdiff = x - psi0; + double resul = 0.; + for (int i = psiT0.length - 1; i >= 0; i--) + resul = resul * xdiff + psiT0[i]; + return resul * xdiff; + } else if (x < 0.) { + /* Reflection formula */ + double xmin = 1. - x; + return psi(xmin) + Math.PI / Math.tan(Math.PI * xmin); + } else { + double xmin1 = x - 1; + double resul = 0.; + for (int k = 26; k >= 1; k--) { + resul -= zeta1(2 * k + 1); + resul *= xmin1 * xmin1; + } + /* 0.422... = 1 -gamma */ + return resul + 0.422784335098467139393487909917597568 + 0.5 / xmin1 - 1. / (1 - xmin1 * xmin1) - Math.PI / (2. * Math.tan(Math.PI * xmin1)); + } + } /* psi */ + + /** + * Broadhurst ladder sequence. + * + * @param a + * The vector of 8 integer arguments + * @param mc + * Specification of the accuracy of the result + * @return S_(n,p)(a) + * @throws Errore + * @since 2009-08-09 + * @see arXiv:math/9803067 + */ + static protected BigDecimal broadhurstBBP(final int n, final int p, final int a[], MathContext mc) throws Errore { + /* + * Explore the actual magnitude of the result first with a quick + * estimate. + */ + double x = 0.0; + for (int k = 1; k < 10; k++) + x += a[(k - 1) % 8] / Math.pow(2., p * (k + 1) / 2) / Math.pow(k, n); + + /* + * Convert the relative precision and estimate of the result into an + * absolute precision. + */ + double eps = prec2err(x, mc.getPrecision()); + + /* + * Divide this through the number of terms in the sum to account for + * error accumulation + * The divisor 2^(p(k+1)/2) means that on the average each 8th term in k + * has shrunk by + * relative to the 8th predecessor by 1/2^(4p). 1/2^(4pc) = + * 10^(-precision) with c the 8term + * cycles yields c=log_2( 10^precision)/4p = 3.3*precision/4p with k=8c + */ + int kmax = (int) (6.6 * mc.getPrecision() / p); + + /* Now eps is the absolute error in each term */ + eps /= kmax; + BigDecimal res = BigDecimal.ZERO; + for (int c = 0;; c++) { + Rational r = new Rational(); + for (int k = 0; k < 8; k++) { + Rational tmp = new Rational(new BigInteger("" + a[k]), (new BigInteger("" + (1 + 8 * c + k))).pow(n)); + /* + * floor( (pk+p)/2) + */ + int pk1h = p * (2 + 8 * c + k) / 2; + tmp = tmp.divide(BigInteger.ONE.shiftLeft(pk1h)); + r = r.add(tmp); + } + + if (Math.abs(r.doubleValue()) < eps) + break; + MathContext mcloc = new MathContext(1 + err2prec(r.doubleValue(), eps)); + res = res.add(r.BigDecimalValue(mcloc)); + } + return res.round(mc); + } /* broadhurstBBP */ + + /** + * Add a BigDecimal and a BigInteger. + * + * @param x + * The left summand + * @param y + * The right summand + * @return The sum x+y. + * @since 2012-03-02 + */ + static public BigDecimal add(final BigDecimal x, final BigInteger y) { + return x.add(new BigDecimal(y)); + } /* add */ + + /** + * Add and round according to the larger of the two ulp's. + * + * @param x + * The left summand + * @param y + * The right summand + * @return The sum x+y. + * @since 2009-07-30 + */ + static public BigDecimal addRound(final BigDecimal x, final BigDecimal y) { + BigDecimal resul = x.add(y); + /* + * The estimation of the absolute error in the result is + * |err(y)|+|err(x)| + */ + double errR = Math.abs(y.ulp().doubleValue() / 2.) + Math.abs(x.ulp().doubleValue() / 2.); + int err2prec = err2prec(resul.doubleValue(), errR); + if (err2prec < 0) { + err2prec = 0; + } + MathContext mc = new MathContext(err2prec); + return resul.round(mc); + } /* addRound */ + + /** + * Add and round according to the larger of the two ulp's. + * + * @param x + * The left summand + * @param y + * The right summand + * @return The sum x+y. + * @since 2010-07-19 + */ + static public BigComplex addRound(final BigComplex x, final BigDecimal y) { + final BigDecimal R = addRound(x.re, y); + return new BigComplex(R, x.im); + } /* addRound */ + + /** + * Add and round according to the larger of the two ulp's. + * + * @param x + * The left summand + * @param y + * The right summand + * @return The sum x+y. + * @since 2010-07-19 + */ + static public BigComplex addRound(final BigComplex x, final BigComplex y) { + final BigDecimal R = addRound(x.re, y.re); + final BigDecimal I = addRound(x.im, y.im); + return new BigComplex(R, I); + } /* addRound */ + + /** + * Subtract and round according to the larger of the two ulp's. + * + * @param x + * The left term. + * @param y + * The right term. + * @return The difference x-y. + * @since 2009-07-30 + */ + static public BigDecimal subtractRound(final BigDecimal x, final BigDecimal y) { + BigDecimal resul = x.subtract(y); + /* + * The estimation of the absolute error in the result is + * |err(y)|+|err(x)| + */ + double errR = Math.abs(y.ulp().doubleValue() / 2.) + Math.abs(x.ulp().doubleValue() / 2.); + MathContext mc = new MathContext(err2prec(resul.doubleValue(), errR)); + return resul.round(mc); + } /* subtractRound */ + + /** + * Subtract and round according to the larger of the two ulp's. + * + * @param x + * The left summand + * @param y + * The right summand + * @return The difference x-y. + * @since 2010-07-19 + */ + static public BigComplex subtractRound(final BigComplex x, final BigComplex y) { + final BigDecimal R = subtractRound(x.re, y.re); + final BigDecimal I = subtractRound(x.im, y.im); + return new BigComplex(R, I); + } /* subtractRound */ + + /** + * Multiply and round. + * + * @param x + * The left factor. + * @param y + * The right factor. + * @return The product x*y. + * @since 2009-07-30 + */ + static public BigDecimal multiplyRound(final BigDecimal x, final BigDecimal y) { + BigDecimal resul = x.multiply(y); + /* + * The estimation of the relative error in the result is the sum of the + * relative + * errors |err(y)/y|+|err(x)/x| + */ + MathContext mc = new MathContext(Math.min(x.precision(), y.precision())); + return resul.round(mc); + } /* multiplyRound */ + + /** + * Multiply and round. + * + * @param x + * The left factor. + * @param y + * The right factor. + * @return The product x*y. + * @since 2010-07-19 + */ + static public BigComplex multiplyRound(final BigComplex x, final BigDecimal y) { + BigDecimal R = multiplyRound(x.re, y); + BigDecimal I = multiplyRound(x.im, y); + return new BigComplex(R, I); + } /* multiplyRound */ + + /** + * Multiply and round. + * + * @param x + * The left factor. + * @param y + * The right factor. + * @return The product x*y. + * @since 2010-07-19 + */ + static public BigComplex multiplyRound(final BigComplex x, final BigComplex y) { + BigDecimal R = subtractRound(multiplyRound(x.re, y.re), multiplyRound(x.im, y.im)); + BigDecimal I = addRound(multiplyRound(x.re, y.im), multiplyRound(x.im, y.re)); + return new BigComplex(R, I); + } /* multiplyRound */ + + /** + * Multiply and round. + * + * @param x + * The left factor. + * @param f + * The right factor. + * @return The product x*f. + * @since 2009-07-30 + */ + static public BigDecimal multiplyRound(final BigDecimal x, final Rational f) { + if (f.compareTo(BigInteger.ZERO) == 0) + return BigDecimal.ZERO; + else { + /* + * Convert the rational value with two digits of extra precision + */ + MathContext mc = new MathContext(2 + x.precision()); + BigDecimal fbd = f.BigDecimalValue(mc); + + /* + * and the precision of the product is then dominated by the + * precision in x + */ + return multiplyRound(x, fbd); + } + } + + /** + * Multiply and round. + * + * @param x + * The left factor. + * @param n + * The right factor. + * @return The product x*n. + * @since 2009-07-30 + */ + static public BigDecimal multiplyRound(final BigDecimal x, final int n) { + BigDecimal resul = x.multiply(new BigDecimal(n)); + /* + * The estimation of the absolute error in the result is |n*err(x)| + */ + MathContext mc = new MathContext(n != 0 ? x.precision() : 0); + return resul.round(mc); + } + + /** + * Multiply and round. + * + * @param x + * The left factor. + * @param n + * The right factor. + * @return the product x*n + * @since 2009-07-30 + */ + static public BigDecimal multiplyRound(final BigDecimal x, final BigInteger n) { + BigDecimal resul = x.multiply(new BigDecimal(n)); + /* + * The estimation of the absolute error in the result is |n*err(x)| + */ + MathContext mc = new MathContext(n.compareTo(BigInteger.ZERO) != 0 ? x.precision() : 0); + return resul.round(mc); + } + + /** + * Divide and round. + * + * @param x + * The numerator + * @param y + * The denominator + * @return the divided x/y + * @since 2009-07-30 + */ + static public BigDecimal divideRound(final BigDecimal x, final BigDecimal y) { + /* + * The estimation of the relative error in the result is + * |err(y)/y|+|err(x)/x| + */ + MathContext mc = new MathContext(Math.min(x.precision(), y.precision())); + BigDecimal resul = x.divide(y, mc); + /* + * If x and y are precise integer values that may have common factors, + * the method above will truncate trailing zeros, which may result in + * a smaller apparent accuracy than starte... add missing trailing zeros + * now. + */ + return scalePrec(resul, mc); + } + + /** + * Build the inverse and maintain the approximate accuracy. + * + * @param z + * The denominator + * @return The divided 1/z = [Re(z)-i*Im(z)]/ [Re^2 z + Im^2 z] + * @since 2010-07-19 + */ + static public BigComplex invertRound(final BigComplex z) { + if (z.im.compareTo(BigDecimal.ZERO) == 0) { + /* + * In this case with vanishing Im(x), the result is simply 1/Re z. + */ + final MathContext mc = new MathContext(z.re.precision()); + return new BigComplex(BigDecimal.ONE.divide(z.re, mc)); + } else if (z.re.compareTo(BigDecimal.ZERO) == 0) { + /* + * In this case with vanishing Re(z), the result is simply -i/Im z + */ + final MathContext mc = new MathContext(z.im.precision()); + return new BigComplex(BigDecimal.ZERO, BigDecimal.ONE.divide(z.im, mc).negate()); + } else { + /* + * 1/(x.re+I*x.im) = 1/(x.re+x.im^2/x.re) - I /(x.im +x.re^2/x.im) + */ + BigDecimal R = addRound(z.re, divideRound(multiplyRound(z.im, z.im), z.re)); + BigDecimal I = addRound(z.im, divideRound(multiplyRound(z.re, z.re), z.im)); + MathContext mc = new MathContext(1 + R.precision()); + R = BigDecimal.ONE.divide(R, mc); + mc = new MathContext(1 + I.precision()); + I = BigDecimal.ONE.divide(I, mc); + return new BigComplex(R, I.negate()); + } + } + + /** + * Divide and round. + * + * @param x + * The numerator + * @param y + * The denominator + * @return the divided x/y + * @since 2010-07-19 + */ + static public BigComplex divideRound(final BigComplex x, final BigComplex y) { + return multiplyRound(x, invertRound(y)); + } + + /** + * Divide and round. + * + * @param x + * The numerator + * @param n + * The denominator + * @return the divided x/n + * @since 2009-07-30 + */ + static public BigDecimal divideRound(final BigDecimal x, final int n) { + /* + * The estimation of the relative error in the result is |err(x)/x| + */ + MathContext mc = new MathContext(x.precision()); + return x.divide(new BigDecimal(n), mc); + } + + /** + * Divide and round. + * + * @param x + * The numerator + * @param n + * The denominator + * @return the divided x/n + * @since 2009-07-30 + */ + static public BigDecimal divideRound(final BigDecimal x, final BigInteger n) { + /* + * The estimation of the relative error in the result is |err(x)/x| + */ + MathContext mc = new MathContext(x.precision()); + return x.divide(new BigDecimal(n), mc); + } /* divideRound */ + + /** + * Divide and round. + * + * @param n + * The numerator + * @param x + * The denominator + * @return the divided n/x + * @since 2009-08-05 + */ + static public BigDecimal divideRound(final BigInteger n, final BigDecimal x) { + /* + * The estimation of the relative error in the result is |err(x)/x| + */ + MathContext mc = new MathContext(x.precision()); + return new BigDecimal(n).divide(x, mc); + } /* divideRound */ + + /** + * Divide and round. + * + * @param n + * The numerator + * @param x + * The denominator + * @return the divided n/x + * @since 2012-03-01 + */ + static public BigComplex divideRound(final BigInteger n, final BigComplex x) { + /* + * catch case of real-valued denominator first + */ + if (x.im.compareTo(BigDecimal.ZERO) == 0) + return new BigComplex(divideRound(n, x.re), BigDecimal.ZERO); + else if (x.re.compareTo(BigDecimal.ZERO) == 0) + return new BigComplex(BigDecimal.ZERO, divideRound(n, x.im).negate()); + + BigComplex z = invertRound(x); + /* + * n/(x+iy) = nx/(x^2+y^2) -nyi/(x^2+y^2) + */ + BigDecimal repart = multiplyRound(z.re, n); + BigDecimal impart = multiplyRound(z.im, n); + return new BigComplex(repart, impart); + } /* divideRound */ + + /** + * Divide and round. + * + * @param n + * The numerator. + * @param x + * The denominator. + * @return the divided n/x. + * @since 2009-08-05 + */ + static public BigDecimal divideRound(final int n, final BigDecimal x) { + /* + * The estimation of the relative error in the result is |err(x)/x| + */ + MathContext mc = new MathContext(x.precision()); + return new BigDecimal(n).divide(x, mc); + } + + /** + * Append decimal zeros to the value. This returns a value which appears to + * have + * a higher precision than the input. + * + * @param x + * The input value + * @param d + * The (positive) value of zeros to be added as least significant + * digits. + * @return The same value as the input but with increased (pseudo) + * precision. + */ + static public BigDecimal scalePrec(final BigDecimal x, int d) { + return x.setScale(d + x.scale()); + } + + /** + * Append decimal zeros to the value. This returns a value which appears to + * have + * a higher precision than the input. + * + * @param x + * The input value + * @param d + * The (positive) value of zeros to be added as least significant + * digits. + * @return The same value as the input but with increased (pseudo) + * precision. + */ + static public BigComplex scalePrec(final BigComplex x, int d) { + return new BigComplex(scalePrec(x.re, d), scalePrec(x.im, d)); + } + + /** + * Boost the precision by appending decimal zeros to the value. This returns + * a value which appears to have + * a higher precision than the input. + * + * @param x + * The input value + * @param mc + * The requirement on the minimum precision on return. + * @return The same value as the input but with increased (pseudo) + * precision. + */ + static public BigDecimal scalePrec(final BigDecimal x, final MathContext mc) { + final int diffPr = mc.getPrecision() - x.precision(); + if (diffPr > 0) + return scalePrec(x, diffPr); + else + return x; + } /* BigDecimalMath.scalePrec */ + + /** + * Convert an absolute error to a precision. + * + * @param x + * The value of the variable + * @param xerr + * The absolute error in the variable + * @return The number of valid digits in x. + * The value is rounded down, and on the pessimistic side for that + * reason. + * @since 2009-06-25 + */ + static public int err2prec(BigDecimal x, BigDecimal xerr) { + return err2prec(xerr.divide(x, MathContext.DECIMAL64).doubleValue()); + } + + /** + * Convert an absolute error to a precision. + * + * @param x + * The value of the variable + * The value returned depends only on the absolute value, not on + * the sign. + * @param xerr + * The absolute error in the variable + * The value returned depends only on the absolute value, not on + * the sign. + * @return The number of valid digits in x. + * Derived from the representation x+- xerr, as if the error was + * represented + * in a "half width" (half of the error bar) form. + * The value is rounded down, and on the pessimistic side for that + * reason. + * @since 2009-05-30 + */ + static public int err2prec(double x, double xerr) { + /* + * Example: an error of xerr=+-0.5 at x=100 represents 100+-0.5 with + * a precision = 3 (digits). + */ + return 1 + (int) (Math.log10(Math.abs(0.5 * x / xerr))); + } + + /** + * Convert a relative error to a precision. + * + * @param xerr + * The relative error in the variable. + * The value returned depends only on the absolute value, not on + * the sign. + * @return The number of valid digits in x. + * The value is rounded down, and on the pessimistic side for that + * reason. + * @since 2009-08-05 + */ + static public int err2prec(double xerr) { + /* + * Example: an error of xerr=+-0.5 a precision of 1 (digit), an error of + * +-0.05 a precision of 2 (digits) + */ + return 1 + (int) (Math.log10(Math.abs(0.5 / xerr))); + } + + /** + * Convert a precision (relative error) to an absolute error. + * The is the inverse functionality of err2prec(). + * + * @param x + * The value of the variable + * The value returned depends only on the absolute value, not on + * the sign. + * @param prec + * The number of valid digits of the variable. + * @return the absolute error in x. + * Derived from the an accuracy of one half of the ulp. + * @since 2009-08-09 + */ + static public double prec2err(final double x, final int prec) { + return 5. * Math.abs(x) * Math.pow(10., -prec); + } } /* BigDecimalMath */ diff --git a/src/org/nevec/rjm/BigIntegerMath.java b/src/org/nevec/rjm/BigIntegerMath.java index 0c04f86a..fa90d4d4 100644 --- a/src/org/nevec/rjm/BigIntegerMath.java +++ b/src/org/nevec/rjm/BigIntegerMath.java @@ -1,550 +1,610 @@ -package org.nevec.rjm ; +package org.nevec.rjm; import java.math.BigInteger; import java.util.Vector; -import org.warpgate.pi.calculator.Errore; +import org.warp.picalculator.Errore; +/** + * BigInteger special functions and Number theory. + * + * @since 2009-08-06 + * @author Richard J. Mathar + */ +public class BigIntegerMath { -/** BigInteger special functions and Number theory. -* @since 2009-08-06 -* @author Richard J. Mathar -*/ -public class BigIntegerMath -{ + /** + * Evaluate binomial(n,k). + * + * @param n + * The upper index + * @param k + * The lower index + * @return The binomial coefficient + */ + static public BigInteger binomial(final int n, final int k) { + if (k == 0) + return (BigInteger.ONE); + BigInteger bin = new BigInteger("" + n); + BigInteger n2 = bin; + for (BigInteger i = new BigInteger("" + (k - 1)); i.compareTo(BigInteger.ONE) >= 0; i = i.subtract(BigInteger.ONE)) + bin = bin.multiply(n2.subtract(i)); + for (BigInteger i = new BigInteger("" + k); i.compareTo(BigInteger.ONE) == 1; i = i.subtract(BigInteger.ONE)) + bin = bin.divide(i); + return (bin); + } /* binomial */ + /** + * Evaluate binomial(n,k). + * + * @param n + * The upper index + * @param k + * The lower index + * @return The binomial coefficient + * @since 2008-10-15 + */ + static public BigInteger binomial(final BigInteger n, final BigInteger k) { + /* + * binomial(n,0) =1 + */ + if (k.compareTo(BigInteger.ZERO) == 0) + return (BigInteger.ONE); + BigInteger bin = new BigInteger("" + n); + /* + * the following version first calculates n(n-1)(n-2)..(n-k+1) + * in the first loop, and divides this product through k(k-1)(k-2)....2 + * in the second loop. This is rather slow and replaced by a faster + * version + * below + * BigInteger n2 = bin ; + * BigInteger i= k.subtract(BigInteger.ONE) ; + * for( ; i.compareTo(BigInteger.ONE) >= 0 ; i = + * i.subtract(BigInteger.ONE) ) + * bin = bin.multiply(n2.subtract(i)) ; + * i= new BigInteger(""+k) ; + * for( ; i.compareTo(BigInteger.ONE) == 1 ; i = + * i.subtract(BigInteger.ONE) ) + * bin = bin.divide(i) ; + */ + /* + * calculate n then n(n-1)/2 then n(n-1)(n-2)(2*3) etc up to + * n(n-1)..(n-k+1)/(2*3*..k) + * This is roughly the best way to keep the individual intermediate + * products small + * and in the integer domain. First replace C(n,k) by C(n,n-k) if n-k= 0 ; i = i.subtract(BigInteger.ONE) ) - bin = bin.multiply(n2.subtract(i)) ; - for(BigInteger i= new BigInteger(""+k) ; i.compareTo(BigInteger.ONE) == 1 ; i = i.subtract(BigInteger.ONE) ) - bin = bin.divide(i) ; - return ( bin) ; - } /* binomial */ + /* + * Calculate C(num,truek) where num=n and truek is the smaller of n-k + * and k. + * Have already initialized bin=n=C(n,1) above. Start definining the + * factorial + * in the denominator, named fden + */ + BigInteger i = new BigInteger("2"); + BigInteger num = new BigInteger(n.toString()); + /* + * a for-loop (i=2;i<= truek;i++) + */ + for (; i.compareTo(truek) <= 0; i = i.add(BigInteger.ONE)) { + /* + * num = n-i+1 after this operation + */ + num = num.subtract(BigInteger.ONE); + /* + * multiply by (n-i+1)/i + */ + bin = (bin.multiply(num)).divide(i); + } + return (bin); + } /* binomial */ - /** Evaluate binomial(n,k). - * @param n The upper index - * @param k The lower index - * @return The binomial coefficient - * @since 2008-10-15 - */ - static public BigInteger binomial(final BigInteger n, final BigInteger k) - { - /* binomial(n,0) =1 - */ - if ( k.compareTo(BigInteger.ZERO) == 0 ) - return(BigInteger.ONE) ; + /** + * Evaluate sigma_k(n). + * + * @param n + * the main argument which defines the divisors + * @param k + * the lower index, which defines the power + * @return The sum of the k-th powers of the positive divisors + */ + static public BigInteger sigmak(final BigInteger n, final int k) { + return (new Ifactor(n.abs())).sigma(k).n; + } /* sigmak */ - BigInteger bin = new BigInteger(""+n) ; + /** + * Evaluate sigma(n). + * + * @param n + * the argument for which divisors will be searched. + * @return the sigma function. Sum of the positive divisors of the argument. + * @since 2006-08-14 + * @author Richard J. Mathar + */ + static public BigInteger sigma(int n) { + return (new Ifactor(Math.abs(n))).sigma().n; + } - /* the following version first calculates n(n-1)(n-2)..(n-k+1) - * in the first loop, and divides this product through k(k-1)(k-2)....2 - * in the second loop. This is rather slow and replaced by a faster version - * below - * BigInteger n2 = bin ; - * BigInteger i= k.subtract(BigInteger.ONE) ; - * for( ; i.compareTo(BigInteger.ONE) >= 0 ; i = i.subtract(BigInteger.ONE) ) - * bin = bin.multiply(n2.subtract(i)) ; - * i= new BigInteger(""+k) ; - * for( ; i.compareTo(BigInteger.ONE) == 1 ; i = i.subtract(BigInteger.ONE) ) - * bin = bin.divide(i) ; - */ + /** + * Compute the list of positive divisors. + * + * @param n + * The integer of which the divisors are to be found. + * @return The sorted list of positive divisors. + * @since 2010-08-27 + * @author Richard J. Mathar + */ + static public Vector divisors(final BigInteger n) { + return (new Ifactor(n.abs())).divisors(); + } - /* calculate n then n(n-1)/2 then n(n-1)(n-2)(2*3) etc up to n(n-1)..(n-k+1)/(2*3*..k) - * This is roughly the best way to keep the individual intermediate products small - * and in the integer domain. First replace C(n,k) by C(n,n-k) if n-k 120) + x = n.shiftRight(bl / 2 - 1); + else { + final double resul = Math.sqrt(n.doubleValue()); + x = new BigInteger("" + Math.round(resul)); + } - /** Compute the list of positive divisors. - * @param n The integer of which the divisors are to be found. - * @return The sorted list of positive divisors. - * @since 2010-08-27 - * @author Richard J. Mathar - */ - static public Vector divisors(final BigInteger n) - { - return (new Ifactor(n.abs())).divisors() ; - } + final BigInteger two = new BigInteger("2"); + while (true) { + /* + * check whether the result is accurate, x^2 =n + */ + BigInteger x2 = x.pow(2); + BigInteger xplus2 = x.add(BigInteger.ONE).pow(2); + if (x2.compareTo(n) <= 0 && xplus2.compareTo(n) > 0) + return x; + xplus2 = xplus2.subtract(x.shiftLeft(2)); + if (xplus2.compareTo(n) <= 0 && x2.compareTo(n) > 0) + return x.subtract(BigInteger.ONE); + /* + * Newton algorithm. This correction is on the + * low side caused by the integer divisions. So the value required + * may end up by one unit too large by the bare algorithm, and this + * is caught above by comparing x^2, (x+-1)^2 with n. + */ + xplus2 = x2.subtract(n).divide(x).divide(two); + x = x.subtract(xplus2); + } + } - /** Evaluate sigma(n). - * @param n the argument for which divisors will be searched. - * @return the sigma function. Sum of the divisors of the argument. - * @since 2006-08-14 - * @author Richard J. Mathar - */ - static public BigInteger sigma(final BigInteger n) - { - return (new Ifactor(n.abs())).sigma().n ; - } + /** + * Evaluate core(n). + * Returns the smallest positive integer m such that n/m is a perfect + * square. + * + * @param n + * The non-negative argument. + * @return The square-free part of n. + * @since 2011-02-12 + * @author Richard J. Mathar + */ + static public BigInteger core(final BigInteger n) { + if (n.compareTo(BigInteger.ZERO) < 0) + throw new ArithmeticException("Negative argument " + n); + final Ifactor i = new Ifactor(n); + return i.core(); + } - /** Evaluate floor(sqrt(n)). - * @param n The non-negative argument. - * @return The integer square root. The square root rounded down. - * @since 2010-08-27 - * @author Richard J. Mathar - */ - static public int isqrt(final int n) - { - if ( n < 0 ) - throw new ArithmeticException("Negative argument "+ n) ; - final double resul= Math.sqrt((double)n) ; - return (int)Math.round(resul) ; - } + /** + * Minor of an integer matrix. + * + * @param A + * The matrix. + * @param r + * The row index of the row to be removed (0-based). + * An exception is thrown if this is outside the range 0 to the + * upper row index of A. + * @param c + * The column index of the column to be removed (0-based). + * An exception is thrown if this is outside the range 0 to the + * upper column index of A. + * @return The depleted matrix. This is not a deep copy but contains + * references to the original. + * @since 2010-08-27 + * @author Richard J. Mathar + */ + static public BigInteger[][] minor(final BigInteger[][] A, final int r, final int c) throws ArithmeticException { + /* original row count */ + final int rL = A.length; + if (rL == 0) + throw new ArithmeticException("zero row count in matrix"); + if (r < 0 || r >= rL) + throw new ArithmeticException("row number " + r + " out of range 0.." + (rL - 1)); + /* original column count */ + final int cL = A[0].length; + if (cL == 0) + throw new ArithmeticException("zero column count in matrix"); + if (c < 0 || c >= cL) + throw new ArithmeticException("column number " + c + " out of range 0.." + (cL - 1)); + BigInteger M[][] = new BigInteger[rL - 1][cL - 1]; + int imrow = 0; + for (int row = 0; row < rL; row++) { + if (row != r) { + int imcol = 0; + for (int col = 0; col < cL; col++) { + if (col != c) { + M[imrow][imcol] = A[row][col]; + imcol++; + } + } + imrow++; + } + } + return M; + } - /** Evaluate floor(sqrt(n)). - * @param n The non-negative argument. - * Arguments less than zero throw an ArithmeticException. - * @return The integer square root, the square root rounded down. - * @since 2010-08-27 - * @author Richard J. Mathar - */ - static public long isqrt(final long n) - { - if ( n < 0 ) - throw new ArithmeticException("Negative argument "+ n) ; - final double resul= Math.sqrt((double)n) ; - return Math.round(resul) ; - } + /** + * Replace column of a matrix with a column vector. + * + * @param A + * The matrix. + * @param c + * The column index of the column to be substituted (0-based). + * @param v + * The column vector to be inserted. + * With the current implementation, it must be at least as long + * as the row count, and + * its elements that exceed that count are ignored. + * @return The modified matrix. This is not a deep copy but contains + * references to the original. + * @since 2010-08-27 + * @author Richard J. Mathar + */ + @SuppressWarnings("unused") + static private BigInteger[][] colSubs(final BigInteger[][] A, final int c, final BigInteger[] v) + throws ArithmeticException { + /* original row count */ + final int rL = A.length; + if (rL == 0) + throw new ArithmeticException("zero row count in matrix"); + /* original column count */ + final int cL = A[0].length; + if (cL == 0) + throw new ArithmeticException("zero column count in matrix"); + if (c < 0 || c >= cL) + throw new ArithmeticException("column number " + c + " out of range 0.." + (cL - 1)); + BigInteger M[][] = new BigInteger[rL][cL]; + for (int row = 0; row < rL; row++) { + for (int col = 0; col < cL; col++) { + /* + * currently, v may just be longer than the row count, and + * surplus + * elements will be ignored. Shorter v lead to an exception. + */ + if (col != c) + M[row][col] = A[row][col]; + else + M[row][col] = v[row]; + } + } + return M; + } - /** Evaluate floor(sqrt(n)). - * @param n The non-negative argument. - * Arguments less than zero throw an ArithmeticException. - * @return The integer square root, the square root rounded down. - * @since 2011-02-12 - * @author Richard J. Mathar - */ - static public BigInteger isqrt(final BigInteger n) - { - if ( n.compareTo(BigInteger.ZERO) < 0 ) - throw new ArithmeticException("Negative argument "+ n.toString()) ; - /* Start with an estimate from a floating point reduction. - */ - BigInteger x ; - final int bl = n.bitLength() ; - if ( bl > 120) - x = n.shiftRight(bl/2-1) ; - else - { - final double resul= Math.sqrt(n.doubleValue()) ; - x = new BigInteger(""+Math.round(resul)) ; - } + /** + * Determinant of an integer square matrix. + * + * @param A + * The square matrix. + * If column and row dimensions are unequal, an + * ArithmeticException is thrown. + * @return The determinant. + * @since 2010-08-27 + * @author Richard J. Mathar + */ + static public BigInteger det(final BigInteger[][] A) throws ArithmeticException { + BigInteger d = BigInteger.ZERO; + /* row size */ + final int rL = A.length; + if (rL == 0) + throw new ArithmeticException("zero row count in matrix"); + /* column size */ + final int cL = A[0].length; + if (cL != rL) + throw new ArithmeticException("Non-square matrix dim " + rL + " by " + cL); - final BigInteger two = new BigInteger("2") ; - while ( true) - { - /* check whether the result is accurate, x^2 =n - */ - BigInteger x2 = x.pow(2) ; - BigInteger xplus2 = x.add(BigInteger.ONE).pow(2) ; - if ( x2.compareTo(n) <= 0 && xplus2.compareTo(n) > 0) - return x ; - xplus2 = xplus2.subtract(x.shiftLeft(2)) ; - if ( xplus2.compareTo(n) <= 0 && x2.compareTo(n) > 0) - return x.subtract(BigInteger.ONE) ; - /* Newton algorithm. This correction is on the - * low side caused by the integer divisions. So the value required - * may end up by one unit too large by the bare algorithm, and this - * is caught above by comparing x^2, (x+-1)^2 with n. - */ - xplus2 = x2.subtract(n).divide(x).divide(two) ; - x = x.subtract(xplus2) ; - } - } + /* + * Compute the low-order cases directly. + */ + if (rL == 1) + return A[0][0]; - /** Evaluate core(n). - * Returns the smallest positive integer m such that n/m is a perfect square. - * @param n The non-negative argument. - * @return The square-free part of n. - * @since 2011-02-12 - * @author Richard J. Mathar - */ - static public BigInteger core(final BigInteger n) - { - if ( n.compareTo(BigInteger.ZERO) < 0 ) - throw new ArithmeticException("Negative argument "+ n) ; - final Ifactor i = new Ifactor(n) ; - return i.core() ; - } + else if (rL == 2) { + d = A[0][0].multiply(A[1][1]); + return d.subtract(A[0][1].multiply(A[1][0])); + } else { + /* Work arbitrarily along the first column of the matrix */ + for (int r = 0; r < rL; r++) { + /* + * Do not consider minors that do no contribute anyway + */ + if (A[r][0].compareTo(BigInteger.ZERO) != 0) { + final BigInteger M[][] = minor(A, r, 0); + final BigInteger m = A[r][0].multiply(det(M)); + /* recursive call */ + if (r % 2 == 0) + d = d.add(m); + else + d = d.subtract(m); + } + } + } + return d; + } - /** Minor of an integer matrix. - * @param A The matrix. - * @param r The row index of the row to be removed (0-based). - * An exception is thrown if this is outside the range 0 to the upper row index of A. - * @param c The column index of the column to be removed (0-based). - * An exception is thrown if this is outside the range 0 to the upper column index of A. - * @return The depleted matrix. This is not a deep copy but contains references to the original. - * @since 2010-08-27 - * @author Richard J. Mathar - */ - static public BigInteger[][] minor(final BigInteger[][] A, final int r, final int c) throws ArithmeticException - { - /* original row count */ - final int rL = A.length ; - if ( rL == 0 ) - throw new ArithmeticException("zero row count in matrix") ; - if ( r < 0 || r >= rL) - throw new ArithmeticException("row number "+r + " out of range 0.." + (rL-1)) ; - /* original column count */ - final int cL = A[0].length ; - if ( cL == 0 ) - throw new ArithmeticException("zero column count in matrix") ; - if ( c < 0 || c >= cL) - throw new ArithmeticException("column number "+c + " out of range 0.." + (cL-1)) ; - BigInteger M[][] = new BigInteger[rL-1][cL-1] ; - int imrow =0 ; - for (int row = 0 ; row < rL ; row++) - { - if ( row != r) - { - int imcol = 0 ; - for(int col = 0 ; col < cL ;col++) - { - if ( col != c ) - { - M[imrow][imcol] = A[row][col] ; - imcol ++ ; - } - } - imrow++ ; - } - } - return M ; - } + /** + * Solve a linear system of equations. + * + * @param A + * The square matrix. + * If it is not of full rank, an ArithmeticException is thrown. + * @param rhs + * The right hand side. The length of this vector must match the + * matrix size; + * else an ArithmeticException is thrown. + * @return The vector of x in A*x=rhs. + * @since 2010-08-28 + * @author Richard J. Mathar + * @throws Errore + */ + static public Rational[] solve(final BigInteger[][] A, final BigInteger[] rhs) throws ArithmeticException, Errore { - /** Replace column of a matrix with a column vector. - * @param A The matrix. - * @param c The column index of the column to be substituted (0-based). - * @param v The column vector to be inserted. - * With the current implementation, it must be at least as long as the row count, and - * its elements that exceed that count are ignored. - * @return The modified matrix. This is not a deep copy but contains references to the original. - * @since 2010-08-27 - * @author Richard J. Mathar - */ - @SuppressWarnings("unused") - static private BigInteger[][] colSubs(final BigInteger[][] A, final int c, final BigInteger[] v) throws ArithmeticException - { - /* original row count */ - final int rL = A.length ; - if ( rL == 0 ) - throw new ArithmeticException("zero row count in matrix") ; - /* original column count */ - final int cL = A[0].length ; - if ( cL == 0 ) - throw new ArithmeticException("zero column count in matrix") ; - if ( c < 0 || c >= cL) - throw new ArithmeticException("column number "+c + " out of range 0.." + (cL-1)) ; - BigInteger M[][] = new BigInteger[rL][cL] ; - for (int row = 0 ; row < rL ; row++) - { - for(int col = 0 ; col < cL ;col++) - { - /* currently, v may just be longer than the row count, and surplus - * elements will be ignored. Shorter v lead to an exception. - */ - if ( col != c ) - M[row][col] = A[row][col] ; - else - M[row][col] = v[row] ; - } - } - return M ; - } + final int rL = A.length; + if (rL == 0) + throw new ArithmeticException("zero row count in matrix"); - /** Determinant of an integer square matrix. - * @param A The square matrix. - * If column and row dimensions are unequal, an ArithmeticException is thrown. - * @return The determinant. - * @since 2010-08-27 - * @author Richard J. Mathar - */ - static public BigInteger det(final BigInteger[][] A) throws ArithmeticException - { - BigInteger d = BigInteger.ZERO ; - /* row size */ - final int rL = A.length ; - if ( rL == 0 ) - throw new ArithmeticException("zero row count in matrix") ; - /* column size */ - final int cL = A[0].length ; - if ( cL != rL ) - throw new ArithmeticException("Non-square matrix dim "+rL + " by " + cL) ; + /* column size */ + final int cL = A[0].length; + if (cL != rL) + throw new ArithmeticException("Non-square matrix dim " + rL + " by " + cL); + if (rhs.length != rL) + throw new ArithmeticException("Right hand side dim " + rhs.length + " unequal matrix dim " + rL); - /* Compute the low-order cases directly. - */ - if ( rL == 1 ) - return A[0][0] ; + /* + * Gauss elimination + */ + Rational x[] = new Rational[rL]; - else if ( rL == 2) - { - d = A[0][0].multiply(A[1][1]) ; - return d.subtract( A[0][1].multiply(A[1][0])) ; - } - else - { - /* Work arbitrarily along the first column of the matrix */ - for (int r = 0 ; r < rL ; r++) - { - /* Do not consider minors that do no contribute anyway - */ - if ( A[r][0].compareTo(BigInteger.ZERO) != 0 ) - { - final BigInteger M[][] = minor(A,r,0) ; - final BigInteger m = A[r][0].multiply( det(M)) ; - /* recursive call */ - if ( r % 2 == 0) - d = d.add(m) ; - else - d = d.subtract(m) ; - } - } - } - return d; - } + /* + * copy of r.h.s ito a mutable Rationalright hand side + */ + for (int c = 0; c < cL; c++) + x[c] = new Rational(rhs[c]); - /** Solve a linear system of equations. - * @param A The square matrix. - * If it is not of full rank, an ArithmeticException is thrown. - * @param rhs The right hand side. The length of this vector must match the matrix size; - * else an ArithmeticException is thrown. - * @return The vector of x in A*x=rhs. - * @since 2010-08-28 - * @author Richard J. Mathar - * @throws Errore - */ - static public Rational[] solve(final BigInteger[][]A, final BigInteger[] rhs) throws ArithmeticException, Errore - { + /* + * Create zeros downwards column c by linear combination of row c and + * row r. + */ + for (int c = 0; c < cL - 1; c++) { + /* + * zero on the diagonal? swap with a non-zero row, searched with + * index r + */ + if (A[c][c].compareTo(BigInteger.ZERO) == 0) { + boolean swpd = false; + for (int r = c + 1; r < rL; r++) { + if (A[r][c].compareTo(BigInteger.ZERO) != 0) { + for (int cpr = c; cpr < cL; cpr++) { + BigInteger tmp = A[c][cpr]; + A[c][cpr] = A[r][cpr]; + A[r][cpr] = tmp; + } + Rational tmp = x[c]; + x[c] = x[r]; + x[r] = tmp; + swpd = true; + break; + } + } + /* + * not swapped with a non-zero row: determinant zero and no + * solution + */ + if (!swpd) + throw new ArithmeticException("Zero determinant of main matrix"); + } + /* create zero at A[c+1..cL-1][c] */ + for (int r = c + 1; r < rL; r++) { + /* + * skip the cpr=c which actually sets the zero: this element is + * not visited again + */ + for (int cpr = c + 1; cpr < cL; cpr++) { + BigInteger tmp = A[c][c].multiply(A[r][cpr]).subtract(A[c][cpr].multiply(A[r][c])); + A[r][cpr] = tmp; + } + Rational tmp = x[r].multiply(A[c][c]).subtract(x[c].multiply(A[r][c])); + x[r] = tmp; + } + } + if (A[cL - 1][cL - 1].compareTo(BigInteger.ZERO) == 0) + throw new ArithmeticException("Zero determinant of main matrix"); + /* backward elimination */ + for (int r = cL - 1; r >= 0; r--) { + x[r] = x[r].divide(A[r][r]); + for (int rpr = r - 1; rpr >= 0; rpr--) + x[rpr] = x[rpr].subtract(x[r].multiply(A[rpr][r])); + } - final int rL = A.length ; - if ( rL == 0 ) - throw new ArithmeticException("zero row count in matrix") ; + return x; + } - /* column size */ - final int cL = A[0].length ; - if ( cL != rL ) - throw new ArithmeticException("Non-square matrix dim "+rL + " by " + cL) ; - if ( rhs.length != rL ) - throw new ArithmeticException("Right hand side dim "+ rhs.length + " unequal matrix dim " + rL) ; + /** + * The lowest common multiple + * + * @param a + * The first argument + * @param b + * The second argument + * @return lcm(|a|,|b|) + * @since 2010-08-27 + * @author Richard J. Mathar + */ + static public BigInteger lcm(final BigInteger a, final BigInteger b) { + BigInteger g = a.gcd(b); + return a.multiply(b).abs().divide(g); + } - /* Gauss elimination - */ - Rational x[] = new Rational[rL] ; + /** + * Evaluate the value of an integer polynomial at some integer argument. + * + * @param c + * Represents the coefficients c[0]+c[1]*x+c[2]*x^2+.. of the + * polynomial + * @param x + * The abscissa point of the evaluation + * @return The polynomial value. + * @since 2010-08-27 + * @author Richard J. Mathar + */ + static public BigInteger valueOf(final Vector c, final BigInteger x) { + if (c.size() == 0) + return BigInteger.ZERO; + BigInteger res = c.lastElement(); + for (int i = c.size() - 2; i >= 0; i--) + res = res.multiply(x).add(c.elementAt(i)); + return res; + } - /* copy of r.h.s ito a mutable Rationalright hand side - */ - for(int c = 0 ; c < cL ; c++) - x[c] = new Rational(rhs[c]) ; - - /* Create zeros downwards column c by linear combination of row c and row r. - */ - for(int c = 0 ; c < cL-1 ; c++) - { - /* zero on the diagonal? swap with a non-zero row, searched with index r */ - if ( A[c][c].compareTo(BigInteger.ZERO) == 0) - { - boolean swpd = false ; - for(int r=c+1; r< rL ; r++) - { - if ( A[r][c].compareTo(BigInteger.ZERO) != 0) - { - for(int cpr =c ; cpr < cL; cpr++) - { - BigInteger tmp = A[c][cpr] ; - A[c][cpr] = A[r][cpr] ; - A[r][cpr] = tmp ; - } - Rational tmp = x[c] ; - x[c] = x[r] ; - x[r] = tmp ; - swpd = true ; - break; - } - } - /* not swapped with a non-zero row: determinant zero and no solution - */ - if ( ! swpd) - throw new ArithmeticException("Zero determinant of main matrix") ; - } - /* create zero at A[c+1..cL-1][c] */ - for( int r=c+1; r < rL ; r++) - { - /* skip the cpr=c which actually sets the zero: this element is not visited again - */ - for(int cpr = c+1; cpr < cL; cpr++) - { - BigInteger tmp = A[c][c].multiply(A[r][cpr]) .subtract ( A[c][cpr].multiply(A[r][c])) ; - A[r][cpr] = tmp ; - } - Rational tmp = x[r].multiply(A[c][c]) .subtract ( x[c].multiply(A[r][c])) ; - x[r] = tmp ; - } - } - if ( A[cL-1][cL-1].compareTo(BigInteger.ZERO) == 0) - throw new ArithmeticException("Zero determinant of main matrix") ; - /* backward elimination */ - for( int r = cL-1 ; r >= 0 ; r--) - { - x[r] = x[r].divide(A[r][r]) ; - for(int rpr = r-1 ; rpr >=0 ; rpr--) - x[rpr] = x[rpr].subtract( x[r].multiply(A[rpr][r]) ) ; - } - - return x ; - } - - /** The lowest common multiple - * @param a The first argument - * @param b The second argument - * @return lcm(|a|,|b|) - * @since 2010-08-27 - * @author Richard J. Mathar - */ - static public BigInteger lcm(final BigInteger a, final BigInteger b) - { - BigInteger g = a.gcd(b) ; - return a.multiply(b).abs().divide(g) ; - } - - - /** Evaluate the value of an integer polynomial at some integer argument. - * @param c Represents the coefficients c[0]+c[1]*x+c[2]*x^2+.. of the polynomial - * @param x The abscissa point of the evaluation - * @return The polynomial value. - * @since 2010-08-27 - * @author Richard J. Mathar - */ - static public BigInteger valueOf(final Vectorc, final BigInteger x) - { - if (c.size() == 0) - return BigInteger.ZERO ; - BigInteger res = c.lastElement() ; - for(int i= c.size()-2 ; i >=0 ; i--) - res = res.multiply(x).add( c.elementAt(i) ) ; - return res ; - } - - /** The central factorial number t(n,k) number at the indices provided. - * @param n the first parameter, non-negative. - * @param k the second index, non-negative. - * @return t(n,k) - * @since 2009-08-06 - * @author Richard J. Mathar - * @throws Errore - * @see P. L. Butzer et al, Num. Funct. Anal. Opt. 10 (5)( 1989) 419-488 - */ - static public Rational centrlFactNumt(int n,int k) throws Errore - { - if ( k > n || k < 0 || ( k % 2 ) != (n % 2) ) - return Rational.ZERO ; - else if ( k == n) - return Rational.ONE ; - else - { - /* Proposition 6.2.6 */ - Factorial f = new Factorial() ; - Rational jsum = new Rational(0,1) ; - int kprime = n-k ; - for ( int j =0 ; j <= kprime ; j++) - { - Rational nusum = new Rational(0,1) ; - for(int nu =0 ; nu <= j ; nu++) - { - Rational t = new Rational(j-2*nu,2) ; - t = t.pow(kprime+j) ; - t = t.multiply( binomial(j,nu) ) ; - if ( nu % 2 != 0 ) - nusum = nusum.subtract(t) ; - else - nusum = nusum.add(t) ; - } - nusum = nusum.divide( f.at(j) ).divide(n+j) ; - nusum = nusum.multiply( binomial(2*kprime,kprime-j) ) ; - if ( j % 2 != 0 ) - jsum = jsum.subtract(nusum) ; - else - jsum = jsum.add(nusum) ; - } - return jsum.multiply(k).multiply( binomial(n+kprime,k) ) ; - } - } /* CentralFactNumt */ - - /** The central factorial number T(n,k) number at the indices provided. - * @param n the first parameter, non-negative. - * @param k the second index, non-negative. - * @return T(n,k) - * @since 2009-08-06 - * @author Richard J. Mathar - * @see P. L. Butzer et al, Num. Funct. Anal. Opt. 10 (5)( 1989) 419-488 - */ - static public Rational centrlFactNumT(int n,int k) - { - if ( k > n || k < 0 || ( k % 2 ) != (n % 2) ) - return Rational.ZERO ; - else if ( k == n) - return Rational.ONE ; - else - { - /* Proposition 2.1 */ - return centrlFactNumT(n-2,k-2).add( centrlFactNumT(n-2,k).multiply(new Rational(k*k,4)) ) ; - } - } /* CentralFactNumT */ + /** + * The central factorial number t(n,k) number at the indices provided. + * + * @param n + * the first parameter, non-negative. + * @param k + * the second index, non-negative. + * @return t(n,k) + * @since 2009-08-06 + * @author Richard J. Mathar + * @throws Errore + * @see P. L. Butzer + * et al, Num. Funct. Anal. Opt. 10 (5)( 1989) 419-488 + */ + static public Rational centrlFactNumt(int n, int k) throws Errore { + if (k > n || k < 0 || (k % 2) != (n % 2)) + return Rational.ZERO; + else if (k == n) + return Rational.ONE; + else { + /* Proposition 6.2.6 */ + Factorial f = new Factorial(); + Rational jsum = new Rational(0, 1); + int kprime = n - k; + for (int j = 0; j <= kprime; j++) { + Rational nusum = new Rational(0, 1); + for (int nu = 0; nu <= j; nu++) { + Rational t = new Rational(j - 2 * nu, 2); + t = t.pow(kprime + j); + t = t.multiply(binomial(j, nu)); + if (nu % 2 != 0) + nusum = nusum.subtract(t); + else + nusum = nusum.add(t); + } + nusum = nusum.divide(f.at(j)).divide(n + j); + nusum = nusum.multiply(binomial(2 * kprime, kprime - j)); + if (j % 2 != 0) + jsum = jsum.subtract(nusum); + else + jsum = jsum.add(nusum); + } + return jsum.multiply(k).multiply(binomial(n + kprime, k)); + } + } /* CentralFactNumt */ + /** + * The central factorial number T(n,k) number at the indices provided. + * + * @param n + * the first parameter, non-negative. + * @param k + * the second index, non-negative. + * @return T(n,k) + * @since 2009-08-06 + * @author Richard J. Mathar + * @see P. L. Butzer + * et al, Num. Funct. Anal. Opt. 10 (5)( 1989) 419-488 + */ + static public Rational centrlFactNumT(int n, int k) { + if (k > n || k < 0 || (k % 2) != (n % 2)) + return Rational.ZERO; + else if (k == n) + return Rational.ONE; + else { + /* Proposition 2.1 */ + return centrlFactNumT(n - 2, k - 2).add(centrlFactNumT(n - 2, k).multiply(new Rational(k * k, 4))); + } + } /* CentralFactNumT */ } /* BigIntegerMath */ diff --git a/src/org/nevec/rjm/BigIntegerPoly.java b/src/org/nevec/rjm/BigIntegerPoly.java index 444234c9..ab72e974 100644 --- a/src/org/nevec/rjm/BigIntegerPoly.java +++ b/src/org/nevec/rjm/BigIntegerPoly.java @@ -1,672 +1,729 @@ -package org.nevec.rjm ; +package org.nevec.rjm; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Scanner; import java.util.Vector; -import org.warpgate.pi.calculator.Errore; +import org.warp.picalculator.Errore; -/** Polynomial with integer coefficients. -* Alternatively to be interpreted as a sequence which has the polynomial as an (approximate) -* generating function. -* @since 2010-08-27 -* @author Richard J. Mathar -*/ -public class BigIntegerPoly implements Cloneable -{ - /** The list of all coefficients, starting with a0, then a1, as in - * poly=a0+a1*x+a2*x^2+a3*x^3+... - */ - Vector a ; +/** + * Polynomial with integer coefficients. + * Alternatively to be interpreted as a sequence which has the polynomial as an + * (approximate) + * generating function. + * + * @since 2010-08-27 + * @author Richard J. Mathar + */ +public class BigIntegerPoly implements Cloneable { + /** + * The list of all coefficients, starting with a0, then a1, as in + * poly=a0+a1*x+a2*x^2+a3*x^3+... + */ + Vector a; - /** Default ctor. - * Creates the polynomial p(x)=0. - */ - public BigIntegerPoly() - { - a = new Vector() ; - } + /** + * Default ctor. + * Creates the polynomial p(x)=0. + */ + public BigIntegerPoly() { + a = new Vector(); + } - /** Ctor with a comma-separated list as the list of coefficients. - * @param L the string of the form a0,a1,a2,a3 with the coefficients - */ - public BigIntegerPoly(final String L) throws NumberFormatException - { - a = new Vector() ; - Scanner sc = new Scanner(L) ; - sc.useDelimiter(",") ; - while ( sc.hasNextBigInteger()) - a.add(sc.nextBigInteger()) ; - simplify() ; - sc.close(); - } /* ctor */ + /** + * Ctor with a comma-separated list as the list of coefficients. + * + * @param L + * the string of the form a0,a1,a2,a3 with the coefficients + */ + public BigIntegerPoly(final String L) throws NumberFormatException { + a = new Vector(); + Scanner sc = new Scanner(L); + sc.useDelimiter(","); + while (sc.hasNextBigInteger()) + a.add(sc.nextBigInteger()); + simplify(); + sc.close(); + } /* ctor */ - /** Ctor with a list of coefficients. - * @param c The coefficients a0, a1, a2 etc in a0+a1*x+a2*x^2+... - */ - @SuppressWarnings("unchecked") - public BigIntegerPoly(final Vector c) - { - a = (Vector)c.clone() ; - simplify() ; - } /* ctor */ + /** + * Ctor with a list of coefficients. + * + * @param c + * The coefficients a0, a1, a2 etc in a0+a1*x+a2*x^2+... + */ + @SuppressWarnings("unchecked") + public BigIntegerPoly(final Vector c) { + a = (Vector) c.clone(); + simplify(); + } /* ctor */ - /** Ctor with a list of coefficients. - * @param c The coefficients a0, a1, a2 etc in a0+a1*x+a2*x^2+... - */ - public BigIntegerPoly(final BigInteger[] c) - { - for(int i=0 ; i < c.length; i++) - a.add( c[i].add(BigInteger.ZERO) ) ; - simplify() ; - } /* ctor */ + /** + * Ctor with a list of coefficients. + * + * @param c + * The coefficients a0, a1, a2 etc in a0+a1*x+a2*x^2+... + */ + public BigIntegerPoly(final BigInteger[] c) { + for (int i = 0; i < c.length; i++) + a.add(c[i].add(BigInteger.ZERO)); + simplify(); + } /* ctor */ - /** Create a copy of this. - * @since 2010-08-27 - */ - public BigIntegerPoly clone() - { - return new BigIntegerPoly(a) ; - } /* clone */ + /** + * Create a copy of this. + * + * @since 2010-08-27 + */ + @Override + public BigIntegerPoly clone() { + return new BigIntegerPoly(a); + } /* clone */ + /** + * Translate into a RatPoly copy. + * + * @since 2012-03-02 + */ + public RatPoly toRatPoly() { + RatPoly bd = new RatPoly(); + for (int i = 0; i < a.size(); i++) + bd.set(i, a.elementAt(i)); + return bd; + } /* toRatPoly */ - /** Translate into a RatPoly copy. - * @since 2012-03-02 - */ - public RatPoly toRatPoly() - { - RatPoly bd = new RatPoly() ; - for(int i=0 ; i < a.size() ; i++) - bd.set(i, a.elementAt(i) ) ; - return bd; - } /* toRatPoly */ + /** + * Retrieve a polynomial coefficient. + * + * @param n + * the zero-based index of the coefficient. n=0 for the constant + * term. + * @return the polynomial coefficient in front of x^n. + */ + public BigInteger at(final int n) { + if (n < a.size()) + return (a.elementAt(n)); + else + return (BigInteger.ZERO); + } /* at */ - /** Retrieve a polynomial coefficient. - * @param n the zero-based index of the coefficient. n=0 for the constant term. - * @return the polynomial coefficient in front of x^n. - */ - public BigInteger at(final int n) - { - if ( n < a.size()) - return( a.elementAt(n) ) ; - else - return( BigInteger.ZERO ) ; - } /* at */ + /** + * Evaluate at some integer argument. + * + * @param x + * The abscissa point of the evaluation + * @return The polynomial value. + * @since 2010-08-27 + * @author Richard J. Mathar + */ + public BigInteger valueOf(final BigInteger x) { + if (a.size() == 0) + return BigInteger.ZERO; + BigInteger res = a.lastElement(); + /* + * Heron casted form + */ + for (int i = a.size() - 2; i >= 0; i--) + res = res.multiply(x).add(a.elementAt(i)); + return res; + } /* valueOf */ - /** Evaluate at some integer argument. - * @param x The abscissa point of the evaluation - * @return The polynomial value. - * @since 2010-08-27 - * @author Richard J. Mathar - */ - public BigInteger valueOf(final BigInteger x) - { - if (a.size() == 0) - return BigInteger.ZERO ; - BigInteger res = a.lastElement() ; - /* Heron casted form - */ - for(int i= a.size()-2 ; i >=0 ; i--) - res = res.multiply(x).add( a.elementAt(i) ) ; - return res ; - } /* valueOf */ + /** + * Horner scheme to find the function value at the argument x + * + * @param x + * The argument x. + * @return Value of the polynomial at x. + * @since 2008-11-13 + */ + public BigInteger valueOf(int x) { + return valueOf(new BigInteger("" + x)); + } /* valueOf */ + /** + * Set a polynomial coefficient. + * + * @param n + * the zero-based index of the coefficient. n=0 for the constant + * term. + * If the polynomial has not yet the degree to need this + * coefficient, + * the intermediate coefficients are set to zero. + * @param value + * the new value of the coefficient. + */ + public void set(final int n, final BigInteger value) { + if (n < a.size()) + a.set(n, value); + else { + /* + * fill intermediate powers with coefficients of zero + */ + while (a.size() < n) { + a.add(BigInteger.ZERO); + } + a.add(value); + } + } /* set */ - /** Horner scheme to find the function value at the argument x - * @param x The argument x. - * @return Value of the polynomial at x. - * @since 2008-11-13 - */ - public BigInteger valueOf( int x) - { - return valueOf(new BigInteger(""+x)) ; - } /* valueOf */ + /** + * Set a polynomial coefficient. + * + * @param n + * the zero-based index of the coefficient. n=0 for the constant + * term. + * If the polynomial has not yet the degree to need this + * coefficient, + * the intermediate coefficients are implicitly set to zero. + * @param value + * the new value of the coefficient. + */ + public void set(final int n, final int value) { + BigInteger val2 = new BigInteger("" + value); + set(n, val2); + } /* set */ - /** Set a polynomial coefficient. - * @param n the zero-based index of the coefficient. n=0 for the constant term. - * If the polynomial has not yet the degree to need this coefficient, - * the intermediate coefficients are set to zero. - * @param value the new value of the coefficient. - */ - public void set(final int n, final BigInteger value) - { - if ( n < a.size()) - a.set(n,value) ; - else - { - /* fill intermediate powers with coefficients of zero - */ - while ( a.size() < n ) - { - a.add(BigInteger.ZERO ) ; - } - a.add(value) ; - } - } /* set */ + /** + * Count of coefficients. + * + * @return the number of polynomial coefficients. + * Differs from the polynomial degree by one. + */ + public int size() { + return a.size(); + } /* size */ - /** Set a polynomial coefficient. - * @param n the zero-based index of the coefficient. n=0 for the constant term. - * If the polynomial has not yet the degree to need this coefficient, - * the intermediate coefficients are implicitly set to zero. - * @param value the new value of the coefficient. - */ - public void set(final int n, final int value) - { - BigInteger val2 = new BigInteger(""+value) ; - set(n,val2) ; - } /* set */ + /** + * Polynomial degree. + * + * @return the polynomial degree. + */ + public int degree() { + return a.size() - 1; + } /* degree */ - /** Count of coefficients. - * @return the number of polynomial coefficients. - * Differs from the polynomial degree by one. - */ - public int size() - { - return a.size() ; - } /* size */ + /** + * Polynomial lower degree. + * + * @return power of the smallest non-zero coefficient. + * If the polynomial is identical to 0, 0 is returned. + */ + public int ldegree() { + for (int n = 0; n < a.size(); n++) + if (a.elementAt(n).compareTo(BigInteger.ZERO) != 0) + return n; + return 0; + } /* ldegree */ - /** Polynomial degree. - * @return the polynomial degree. - */ - public int degree() - { - return a.size()-1 ; - } /* degree */ + /** + * Multiply by a constant factor. + * + * @param val + * the factor + * @return the product of this with the factor. + * All coefficients of this have been multiplied individually by the + * factor. + * @since 2010-08-27 + */ + public BigIntegerPoly multiply(final BigInteger val) { + BigIntegerPoly resul = new BigIntegerPoly(); + if (val.compareTo(BigInteger.ZERO) != 0) + for (int n = 0; n < a.size(); n++) + resul.set(n, a.elementAt(n).multiply(val)); + return resul; + } /* multiply */ - /** Polynomial lower degree. - * @return power of the smallest non-zero coefficient. - * If the polynomial is identical to 0, 0 is returned. - */ - public int ldegree() - { - for(int n=0 ; n < a.size() ; n++) - if ( a.elementAt(n).compareTo(BigInteger.ZERO) != 0 ) - return n; - return 0 ; - } /* ldegree */ + /** + * Multiply by another polynomial + * + * @param val + * the other polynomial + * @return the product of this with the other polynomial + */ + public BigIntegerPoly multiply(final BigIntegerPoly val) { + BigIntegerPoly resul = new BigIntegerPoly(); + /* + * the degree of the result is the sum of the two degrees. + */ + final int nmax = degree() + val.degree(); + for (int n = 0; n <= nmax; n++) { + BigInteger coef = BigInteger.ZERO; + for (int nleft = 0; nleft <= n; nleft++) + coef = coef.add(at(nleft).multiply(val.at(n - nleft))); + resul.set(n, coef); + } + resul.simplify(); + return resul; + } /* multiply */ - /** Multiply by a constant factor. - * @param val the factor - * @return the product of this with the factor. - * All coefficients of this have been multiplied individually by the factor. - * @since 2010-08-27 - */ - public BigIntegerPoly multiply(final BigInteger val) - { - BigIntegerPoly resul = new BigIntegerPoly() ; - if ( val.compareTo(BigInteger.ZERO) != 0 ) - for(int n=0; n < a.size() ; n++) - resul.set(n,a.elementAt(n).multiply(val) ) ; - return resul ; - } /* multiply */ + /** + * Raise to a positive power. + * + * @param n + * the exponent of the power + * @return the n-th power of this. + */ + public BigIntegerPoly pow(final int n) throws ArithmeticException { + BigIntegerPoly resul = new BigIntegerPoly("1"); + if (n < 0) + throw new ArithmeticException("negative polynomial power " + n); + else { + for (int i = 1; i <= n; i++) + resul = resul.multiply(this); + resul.simplify(); + return resul; + } + } /* pow */ - /** Multiply by another polynomial - * @param val the other polynomial - * @return the product of this with the other polynomial - */ - public BigIntegerPoly multiply(final BigIntegerPoly val) - { - BigIntegerPoly resul = new BigIntegerPoly() ; - /* the degree of the result is the sum of the two degrees. - */ - final int nmax = degree()+val.degree() ; - for(int n=0; n <= nmax ; n++) - { - BigInteger coef = BigInteger.ZERO ; - for(int nleft=0; nleft <= n ; nleft++) - coef = coef.add(at(nleft).multiply(val.at(n-nleft))) ; - resul.set(n,coef) ; - } - resul.simplify() ; - return resul ; - } /* multiply */ + /** + * Add another polynomial + * + * @param val + * the other polynomial + * @return the sum of this with the other polynomial + * @since 2010-08-27 + */ + public BigIntegerPoly add(final BigIntegerPoly val) { + BigIntegerPoly resul = new BigIntegerPoly(); + /* + * the degree of the result is the larger of the two degrees (before + * simplify() at least). + */ + final int nmax = (degree() > val.degree()) ? degree() : val.degree(); + for (int n = 0; n <= nmax; n++) { + BigInteger coef = at(n).add(val.at(n)); + resul.set(n, coef); + } + resul.simplify(); + return resul; + } /* add */ - /** Raise to a positive power. - * @param n the exponent of the power - * @return the n-th power of this. - */ - public BigIntegerPoly pow(final int n) throws ArithmeticException - { - BigIntegerPoly resul = new BigIntegerPoly("1") ; - if ( n < 0 ) - throw new ArithmeticException("negative polynomial power "+n) ; - else - { - for(int i=1 ; i <= n ; i++) - resul = resul.multiply(this) ; - resul.simplify() ; - return resul ; - } - } /* pow */ + /** + * Subtract another polynomial + * + * @param val + * the other polynomial + * @return the difference between this and the other polynomial + * @since 2008-10-25 + */ + public BigIntegerPoly subtract(final BigIntegerPoly val) { + BigIntegerPoly resul = new BigIntegerPoly(); + /* + * the degree of the result is the larger of the two degrees (before + * simplify() at least). + */ + final int nmax = (degree() > val.degree()) ? degree() : val.degree(); + for (int n = 0; n <= nmax; n++) { + BigInteger coef = at(n).subtract(val.at(n)); + resul.set(n, coef); + } + resul.simplify(); + return resul; + } /* subtract */ - /** Add another polynomial - * @param val the other polynomial - * @return the sum of this with the other polynomial - * @since 2010-08-27 - */ - public BigIntegerPoly add(final BigIntegerPoly val) - { - BigIntegerPoly resul = new BigIntegerPoly() ; - /* the degree of the result is the larger of the two degrees (before simplify() at least). - */ - final int nmax = (degree()>val.degree()) ? degree() : val.degree() ; - for(int n=0; n <= nmax ; n++) - { - BigInteger coef = at(n).add(val.at(n)) ; - resul.set(n,coef) ; - } - resul.simplify() ; - return resul ; - } /* add */ + /** + * Divide by another polynomial. + * + * @param val + * the other polynomial + * @return A vector with [0] containg the polynomial of degree which is the + * difference of the degree of this and the degree of val. [1] the + * remainder polynomial. + * This = returnvalue[0] + returnvalue[1]/val . + * @since 2012-03-01 + */ + public BigIntegerPoly[] divideAndRemainder(final BigIntegerPoly val) { + BigIntegerPoly[] ret = new BigIntegerPoly[2]; + /* + * remove any high-order zeros. note that the clone() operation calls + * simplify(). + */ + BigIntegerPoly valSimpl = val.clone(); + BigIntegerPoly thisSimpl = clone(); - /** Subtract another polynomial - * @param val the other polynomial - * @return the difference between this and the other polynomial - * @since 2008-10-25 - */ - public BigIntegerPoly subtract(final BigIntegerPoly val) - { - BigIntegerPoly resul = new BigIntegerPoly() ; - /* the degree of the result is the larger of the two degrees (before simplify() at least). - */ - final int nmax = (degree()>val.degree()) ? degree() : val.degree() ; - for(int n=0; n <= nmax ; n++) - { - BigInteger coef = at(n).subtract(val.at(n)) ; - resul.set(n,coef) ; - } - resul.simplify() ; - return resul ; - } /* subtract */ + /* + * catch the case with val equal to zero + */ + if (valSimpl.degree() == 0 && valSimpl.a.firstElement().compareTo(BigInteger.ZERO) == 0) + throw new ArithmeticException("Division through zero polynomial"); + /* + * degree of this smaller than degree of val: remainder is this + */ + if (thisSimpl.degree() < valSimpl.degree()) { + /* + * leading polynomial equals zero + */ + ret[0] = new BigIntegerPoly(); + ret[1] = thisSimpl; + } else { + /* + * long division. Highest degree by dividing the highest degree + * of this thru val. At this point an exception is thrown if the + * polynomial division cannot be done with integer coefficients. + */ + ret[0] = new BigIntegerPoly(); + BigInteger[] newc = thisSimpl.a.lastElement().divideAndRemainder(valSimpl.a.lastElement()); + if (newc[1].compareTo(BigInteger.ZERO) != 0) + throw new ArithmeticException("Incompatible leading term in " + this + " / " + val); + ret[0].set(thisSimpl.degree() - valSimpl.degree(), newc[0]); + /* + * recurrences: build this - val*(1-termresult) and feed this + * into another round of division. Have intermediate + * ret[0]+ret[1]/val. + */ + ret[1] = thisSimpl.subtract(ret[0].multiply(valSimpl)); - /** Divide by another polynomial. - * @param val the other polynomial - * @return A vector with [0] containg the polynomial of degree which is the - * difference of the degree of this and the degree of val. [1] the remainder polynomial. - * This = returnvalue[0] + returnvalue[1]/val . - * @since 2012-03-01 - */ - public BigIntegerPoly[] divideAndRemainder(final BigIntegerPoly val) - { - BigIntegerPoly[] ret = new BigIntegerPoly[2] ; - /* remove any high-order zeros. note that the clone() operation calls simplify(). - */ - BigIntegerPoly valSimpl = val.clone() ; - BigIntegerPoly thisSimpl = clone() ; + /* + * any remainder left ? + */ + if (ret[1].degree() < valSimpl.degree()) + ; + else { + BigIntegerPoly rem[] = ret[1].divideAndRemainder(val); + ret[0] = ret[0].add(rem[0]); + ret[1] = rem[1]; + } + } + return ret; + } /* divideAndRemainder */ - /* catch the case with val equal to zero - */ - if ( valSimpl.degree() == 0 && valSimpl.a.firstElement().compareTo(BigInteger.ZERO) == 0) - throw new ArithmeticException("Division through zero polynomial") ; - /* degree of this smaller than degree of val: remainder is this - */ - if ( thisSimpl.degree() < valSimpl.degree() ) - { - /* leading polynomial equals zero - */ - ret[0] = new BigIntegerPoly() ; - ret[1] = thisSimpl ; - } - else - { - /* long division. Highest degree by dividing the highest degree - * of this thru val. At this point an exception is thrown if the - * polynomial division cannot be done with integer coefficients. - */ - ret[0] = new BigIntegerPoly() ; - BigInteger[] newc = thisSimpl.a.lastElement().divideAndRemainder( valSimpl.a.lastElement()) ; - if ( newc[1].compareTo(BigInteger.ZERO) != 0) - throw new ArithmeticException("Incompatible leading term in " + this + " / " + val) ; - ret[0].set( thisSimpl.degree()-valSimpl.degree(), newc[0]) ; + /** + * Print as a comma-separated list of coefficients. + * + * @return the representation a0,a1,a2,a3,... + * @since 2010-08-27 + */ + @Override + public String toString() { + String str = new String(); + for (int n = 0; n < a.size(); n++) { + if (n == 0) + str += a.elementAt(n).toString(); + else + str += "," + a.elementAt(n).toString(); + } + if (str.length() == 0) + str = "0"; + return str; + } /* toString */ - /* recurrences: build this - val*(1-termresult) and feed this - * into another round of division. Have intermediate ret[0]+ret[1]/val. - */ - ret[1] = thisSimpl.subtract( ret[0].multiply( valSimpl) ); + /** + * Print as a polyomial in x. + * + * @return The representation a0+a1*x+a2*x^2+... + * The terms with zero coefficients are not mentioned. + * @since 2008-10-26 + */ + public String toPString() { + String str = new String(); + for (int n = 0; n < a.size(); n++) { + final BigInteger num = a.elementAt(n); + if (num.compareTo(BigInteger.ZERO) != 0) { + str += " "; + if (num.compareTo(BigInteger.ZERO) > 0 && n > 0) + str += "+"; + str += a.elementAt(n).toString(); + if (n > 0) { + str += "*x"; + if (n > 1) + str += "^" + n; + } + } + } + if (str.length() == 0) + str = "0"; + return str; + } /* toPString */ - /* any remainder left ? - */ - if ( ret[1].degree() < valSimpl.degree() ) - ; - else - { - BigIntegerPoly rem[] = ret[1].divideAndRemainder(val) ; - ret[0] = ret[0].add(rem[0]) ; - ret[1] = rem[1] ; - } - } - return ret ; - } /* divideAndRemainder */ + /** + * Simplify the representation. + * Trailing values with zero coefficients (at high powers) are deleted. + */ + protected void simplify() { + int n = a.size() - 1; + if (n >= 0) + while (a.elementAt(n).compareTo(BigInteger.ZERO) == 0) { + a.removeElementAt(n); + if (--n < 0) + break; + } + } /* simplify */ - /** Print as a comma-separated list of coefficients. - * @return the representation a0,a1,a2,a3,... - * @since 2010-08-27 - */ - public String toString() - { - String str = new String(); - for(int n=0; n < a.size() ; n++) - { - if ( n == 0 ) - str += a.elementAt(n).toString() ; - else - str += ","+a.elementAt(n).toString() ; - } - if ( str.length() == 0 ) - str = "0" ; - return str ; - } /* toString */ + /** + * First derivative. + * + * @return The first derivative with respect to the indeterminate variable. + * @since 2008-10-26 + */ + public BigIntegerPoly derive() { + if (a.size() <= 1) { + /* + * derivative of the constant is just zero + */ + return new BigIntegerPoly(); + } else { + BigIntegerPoly d = new BigIntegerPoly(); + for (int i = 1; i <= degree(); i++) { + final BigInteger c = a.elementAt(i).multiply(new BigInteger("" + i)); + d.set(i - 1, c); + } + return d; + } + } /* derive */ - /** Print as a polyomial in x. - * @return The representation a0+a1*x+a2*x^2+... - * The terms with zero coefficients are not mentioned. - * @since 2008-10-26 - */ - public String toPString() - { - String str = new String(); - for(int n=0; n < a.size() ; n++) - { - final BigInteger num = a.elementAt(n) ; - if ( num.compareTo(BigInteger.ZERO) != 0 ) - { - str += " " ; - if ( num.compareTo(BigInteger.ZERO) > 0 && n> 0) - str += "+" ; - str += a.elementAt(n).toString() ; - if ( n > 0 ) - { - str += "*x" ; - if ( n > 1 ) - str += "^"+n ; - } - } - } - if ( str.length() == 0 ) - str = "0" ; - return str ; - } /* toPString */ + /** + * Truncate polynomial degree. + * + * @return The polynomial with all coefficients beyond deg set to zero. + * @since 2010-08-27 + */ + public BigIntegerPoly trunc(int newdeg) { + BigIntegerPoly t = new BigIntegerPoly(); + for (int i = 0; i <= newdeg; i++) + t.set(i, at(i)); + t.simplify(); + return t; + } /* trunc */ - /** Simplify the representation. - * Trailing values with zero coefficients (at high powers) are deleted. - */ - protected void simplify() - { - int n = a.size()-1 ; - if ( n >= 0) - while( a.elementAt(n).compareTo(BigInteger.ZERO) == 0 ) - { - a.removeElementAt(n) ; - if( --n <0) - break ; - } - } /* simplify */ + /** + * Inverse Binomial transform. + * + * @param maxdeg + * the maximum polynomial degree of the result + * @return the sequence of coefficients is the inverse binomial transform of + * the original sequence. + * @since 2010-08-29 + */ + public BigIntegerPoly binomialTInv(int maxdeg) { + BigIntegerPoly r = new BigIntegerPoly(); + for (int i = 0; i <= maxdeg; i++) { + BigInteger c = BigInteger.ZERO; + for (int j = 0; j <= i && j < a.size(); j++) + if ((j + i) % 2 != 0) + c = c.subtract(a.elementAt(j).multiply(BigIntegerMath.binomial(i, j))); + else + c = c.add(a.elementAt(j).multiply(BigIntegerMath.binomial(i, j))); + r.set(i, c); + } + r.simplify(); + return r; + } /* binomialTInv */ - /** First derivative. - * @return The first derivative with respect to the indeterminate variable. - * @since 2008-10-26 - */ - public BigIntegerPoly derive() - { - if ( a.size() <= 1) - { - /* derivative of the constant is just zero - */ - return new BigIntegerPoly() ; - } - else - { - BigIntegerPoly d = new BigIntegerPoly() ; - for(int i=1 ; i <= degree() ; i++) - { - final BigInteger c = a.elementAt(i).multiply(new BigInteger(""+i)) ; - d.set(i-1,c) ; - } - return d ; - } - } /* derive */ + /** + * Compute the order of the root r. + * + * @return 1 for simple roots, 2 for order 2 etc., 0 if not a root + * @since 2010-08-27 + */ + public int rootDeg(final BigInteger r) { + int o = 0; + BigIntegerPoly d = clone(); + BigInteger roo = d.valueOf(r); + while (roo.compareTo(BigInteger.ZERO) == 0) { + o++; + d = d.derive(); + roo = d.valueOf(r); + } + return o; + } /* rootDeg */ - /** Truncate polynomial degree. - * @return The polynomial with all coefficients beyond deg set to zero. - * @since 2010-08-27 - */ - public BigIntegerPoly trunc(int newdeg) - { - BigIntegerPoly t = new BigIntegerPoly() ; - for(int i=0; i <= newdeg; i++) - t.set(i,at(i)) ; - t.simplify() ; - return t ; - } /* trunc */ + /** + * Generate the integer roots of the polynomial. + * + * @return The vector of integer roots, without their multiplicity. + * @since 2010-08-27 + */ + public Vector iroots() { + /* The vector of the roots */ + Vector res = new Vector(); - /** Inverse Binomial transform. - * @param maxdeg the maximum polynomial degree of the result - * @return the sequence of coefficients is the inverse binomial transform of the original sequence. - * @since 2010-08-29 - */ - public BigIntegerPoly binomialTInv(int maxdeg) - { - BigIntegerPoly r = new BigIntegerPoly() ; - for(int i=0; i <= maxdeg; i++) - { - BigInteger c = BigInteger.ZERO ; - for(int j=0; j <= i && j < a.size(); j++) - if ( (j+i) % 2 != 0 ) - c = c.subtract( a.elementAt(j).multiply(BigIntegerMath.binomial(i,j)) ) ; - else - c = c.add( a.elementAt(j).multiply(BigIntegerMath.binomial(i,j)) ) ; - r.set(i,c) ; - } - r.simplify() ; - return r ; - } /* binomialTInv */ + /* + * collect the zero + */ + if (a.firstElement().compareTo(BigInteger.ZERO) == 0) + res.add(BigInteger.ZERO); + /* + * collect the divisors of the constant element (or the reduced + * polynomial) + */ + int l = ldegree(); + if (a.elementAt(l).compareTo(BigInteger.ZERO) != 0) { + Vector cand = BigIntegerMath.divisors(a.elementAt(l).abs()); - /** Compute the order of the root r. - * @return 1 for simple roots, 2 for order 2 etc., 0 if not a root - * @since 2010-08-27 - */ - public int rootDeg(final BigInteger r) - { - int o = 0 ; - BigIntegerPoly d = clone() ; - BigInteger roo = d.valueOf(r) ; - while ( roo.compareTo(BigInteger.ZERO) == 0 ) - { - o++ ; - d = d.derive() ; - roo = d.valueOf(r) ; - } - return o ; - } /* rootDeg */ + /* check the divisors (both signs) */ + for (int i = 0; i < cand.size(); i++) { + BigInteger roo = valueOf(cand.elementAt(i)); + if (roo.compareTo(BigInteger.ZERO) == 0) + /* found a root cand[i] */ + res.add(cand.elementAt(i)); + roo = valueOf(cand.elementAt(i).negate()); + if (roo.compareTo(BigInteger.ZERO) == 0) + res.add(cand.elementAt(i).negate()); + } + } + return res; + } /* iroots */ - /** Generate the integer roots of the polynomial. - * @return The vector of integer roots, without their multiplicity. - * @since 2010-08-27 - */ - public Vector iroots() - { - /* The vector of the roots */ - Vector res =new Vector() ; + /** + * Generate the factors which are 2nd degree polynomials. + * + * @return A (potentially empty) vector of factors, without multiplicity. + * Only factors with non-zero absolute coefficient are generated. + * This means the factors are of the form x^2+a*x+b=0 with nonzero + * b. + * @throws Errore + * @since 2012-03-01 + */ + protected Vector i2roots() throws Errore { + /* + * The vector of the factors to be returned + */ + Vector res = new Vector(); - /* collect the zero - */ - if ( a.firstElement().compareTo(BigInteger.ZERO) == 0 ) - res.add(BigInteger.ZERO) ; + if (degree() < 2) + return res; - /* collect the divisors of the constant element (or the reduced polynomial) */ - int l = ldegree() ; - if ( a.elementAt(l).compareTo(BigInteger.ZERO) != 0 ) - { - Vector cand = BigIntegerMath.divisors(a.elementAt(l).abs()) ; - - /* check the divisors (both signs) */ - for(int i=0 ; i < cand.size() ; i++) - { - BigInteger roo = valueOf( cand.elementAt(i) ) ; - if ( roo.compareTo(BigInteger.ZERO) == 0 ) - /* found a root cand[i] */ - res.add(cand.elementAt(i)) ; - roo = valueOf( cand.elementAt(i).negate() ) ; - if ( roo.compareTo(BigInteger.ZERO) == 0 ) - res.add(cand.elementAt(i).negate()) ; - } - } - return res; - } /* iroots */ + BigInteger bsco = a.firstElement().abs(); + Vector b = BigIntegerMath.divisors(bsco); + BigInteger csco = a.lastElement().abs(); + Vector c = BigIntegerMath.divisors(csco); - /** Generate the factors which are 2nd degree polynomials. - * @return A (potentially empty) vector of factors, without multiplicity. - * Only factors with non-zero absolute coefficient are generated. - * This means the factors are of the form x^2+a*x+b=0 with nonzero b. - * @throws Errore - * @since 2012-03-01 - */ - protected Vector i2roots() throws Errore - { - /* The vector of the factors to be returned - */ - Vector res =new Vector() ; + /* + * Generate the floating point values of roots. To have some reasonable + * accuracy in the results, add zeros to the integer coefficients, + * scaled + * by the expected division with values of b (which are all <= + * a.firstele). + * Number of decimal digits in bsco by using a log2->log10 rough + * estimate + * and adding 6 safety digits + */ + RatPoly thisDec = toRatPoly(); + Vector roo = thisDec.roots(6 + (int) (0.3 * bsco.bitCount())); - if ( degree() < 2) - return res ; + final BigDecimal half = new BigDecimal("0.5"); - BigInteger bsco = a.firstElement().abs() ; - Vector b = BigIntegerMath.divisors(bsco) ; - BigInteger csco = a.lastElement().abs() ; - Vector c = BigIntegerMath.divisors(csco) ; + /* + * for each of the roots z try to see whether c*z^2+a*z+b=0 with integer + * a, b and c + * where b is restricted to a signed divisor of the constant + * coefficient. + * Solve z*(c*z+a)=-b or c*z+a = -b/z or -b/z-c*z = some integer a. + */ + for (BigComplex z : roo) { + for (BigInteger bco : b) + for (BigInteger cco : c) { + /* + * the major reason to avoid the case b=0 is that this would + * require precaution of double counting below. Note that + * this + * case is already covered by using iroots(). + */ + if (bco.signum() != 0) { + for (int sig = -1; sig <= 1; sig += 2) { + BigInteger bcosig = (sig > 0) ? bco : bco.negate(); + /* + * -a = b/z+c*z has real part b*Re(z)/|z|^2+c*Re(z) + * = Re z *( b/|z|^2+c) + */ + BigDecimal negA = BigDecimalMath.add(BigDecimalMath.divideRound(bcosig, z.norm()), cco); + negA = negA.multiply(z.re); + /* + * convert to a with round-to-nearest + */ + BigInteger a = negA.negate().add(half).toBigInteger(); - /* Generate the floating point values of roots. To have some reasonable - * accuracy in the results, add zeros to the integer coefficients, scaled - * by the expected division with values of b (which are all <= a.firstele). - * Number of decimal digits in bsco by using a log2->log10 rough estimate - * and adding 6 safety digits - */ - RatPoly thisDec = toRatPoly() ; - Vector roo = thisDec.roots(6+(int)(0.3*bsco.bitCount()) ) ; + /* + * test the polynomial remainder. if zero, add the + * term + * to the results. + */ + BigIntegerPoly dtst = new BigIntegerPoly("" + bcosig + "," + a + "," + cco); + try { + BigIntegerPoly[] rm = divideAndRemainder(dtst); + if (rm[1].isZero()) + res.add(dtst); + } catch (ArithmeticException ex) {} + } + } + } + } - final BigDecimal half = new BigDecimal("0.5") ; + return res; + } /* i2roots */ - /* for each of the roots z try to see whether c*z^2+a*z+b=0 with integer a, b and c - * where b is restricted to a signed divisor of the constant coefficient. - * Solve z*(c*z+a)=-b or c*z+a = -b/z or -b/z-c*z = some integer a. - */ - for( BigComplex z : roo) - { - for(BigInteger bco : b) - for(BigInteger cco : c) - { - /* the major reason to avoid the case b=0 is that this would - * require precaution of double counting below. Note that this - * case is already covered by using iroots(). - */ - if ( bco.signum() != 0 ) - { - for(int sig = -1 ; sig <=1 ; sig +=2) - { - BigInteger bcosig = (sig > 0 )? bco : bco.negate() ; - /* -a = b/z+c*z has real part b*Re(z)/|z|^2+c*Re(z) = Re z *( b/|z|^2+c) - */ - BigDecimal negA = BigDecimalMath.add(BigDecimalMath.divideRound(bcosig,z.norm()),cco) ; - negA = negA.multiply(z.re) ; - /* convert to a with round-to-nearest - */ - BigInteger a = negA.negate().add(half).toBigInteger() ; - - /* test the polynomial remainder. if zero, add the term - * to the results. - */ - BigIntegerPoly dtst = new BigIntegerPoly(""+bcosig+","+a+","+cco) ; - try - { - BigIntegerPoly[] rm = divideAndRemainder(dtst) ; - if ( rm[1].isZero() ) - res.add(dtst) ; - } - catch ( ArithmeticException ex) - { - } - } - } - } - } + /** + * Test whether this polynomial value is zero. + * + * @return If this is a polynomial p(x)=0 for all x. + */ + public boolean isZero() { + simplify(); + return (a.size() == 0); + } - return res; - } /* i2roots */ + /** + * Factorization into integer polynomials. + * The current factorization detects only factors which are polynomials of + * order up to 2. + * + * @return The vector of factors. Factors with higher multiplicity are + * represented by repetition. + * @throws Errore + * @since 2012-03-01 + */ + public Vector ifactor() throws Errore { + /* + * this ought be entirely rewritten in terms of the LLL algorithm + */ + Vector fac = new Vector(); - /** Test whether this polynomial value is zero. - * @return If this is a polynomial p(x)=0 for all x. - */ - public boolean isZero() - { - simplify() ; - return (a.size() ==0 ) ; - } + /* collect integer roots (polynomial factors of degree 1) */ + Vector r = iroots(); + BigIntegerPoly[] res = new BigIntegerPoly[2]; + res[0] = this; + for (BigInteger i : r) { + int deg = rootDeg(i); + /* construct the factor x-i */ + BigIntegerPoly f = new BigIntegerPoly("" + i.negate() + ",1"); + for (int mu = 0; mu < deg; mu++) { + fac.add(f); + res = res[0].divideAndRemainder(f); + } + } - /** Factorization into integer polynomials. - * The current factorization detects only factors which are polynomials of order up to 2. - * @return The vector of factors. Factors with higher multiplicity are represented by repetition. - * @throws Errore - * @since 2012-03-01 - */ - public Vector ifactor() throws Errore - { - /* this ought be entirely rewritten in terms of the LLL algorithm - */ - Vector fac = new Vector() ; - - /* collect integer roots (polynomial factors of degree 1) */ - Vector r = iroots() ; - BigIntegerPoly[] res = new BigIntegerPoly[2] ; - res[0] = this ; - for( BigInteger i : r) - { - int deg = rootDeg(i) ; - /* construct the factor x-i */ - BigIntegerPoly f = new BigIntegerPoly(""+i.negate()+",1") ; - for(int mu =0 ; mu < deg ; mu++) - { - fac.add(f) ; - res = res[0].divideAndRemainder(f) ; - } - } - - /* collect factors which are polynomials of degree 2 - */ - Vector pol2 = i2roots() ; - for( BigIntegerPoly i : pol2) - { - /* the internal loop catches cases with higher - * powers of individual polynomials (of actual degree 2 or 4...) - */ - while ( res[0].degree() >= 2) - { - try - { - BigIntegerPoly[] dtst = res[0].divideAndRemainder(i) ; - if ( dtst[1].isZero() ) - { - fac.add(i) ; - res = dtst ; - } - else - break ; - } - catch(ArithmeticException ex) - { - break ; - } - } - } - - /* add remaining factor, if not equal to 1 - */ - if ( res[0].degree() >0 || res[0].a.firstElement().compareTo (BigInteger.ONE) != 0 ) - fac.add(res[0]) ; - return fac ; - } /* ifactor */ + /* + * collect factors which are polynomials of degree 2 + */ + Vector pol2 = i2roots(); + for (BigIntegerPoly i : pol2) { + /* + * the internal loop catches cases with higher + * powers of individual polynomials (of actual degree 2 or 4...) + */ + while (res[0].degree() >= 2) { + try { + BigIntegerPoly[] dtst = res[0].divideAndRemainder(i); + if (dtst[1].isZero()) { + fac.add(i); + res = dtst; + } else + break; + } catch (ArithmeticException ex) { + break; + } + } + } + /* + * add remaining factor, if not equal to 1 + */ + if (res[0].degree() > 0 || res[0].a.firstElement().compareTo(BigInteger.ONE) != 0) + fac.add(res[0]); + return fac; + } /* ifactor */ } /* BigIntegerPoly */ diff --git a/src/org/nevec/rjm/BigSurd.java b/src/org/nevec/rjm/BigSurd.java index a40097c2..8bdc3690 100644 --- a/src/org/nevec/rjm/BigSurd.java +++ b/src/org/nevec/rjm/BigSurd.java @@ -1,488 +1,557 @@ -package org.nevec.rjm ; +package org.nevec.rjm; import java.math.BigDecimal; import java.math.BigInteger; import java.math.MathContext; import java.security.ProviderException; -import org.warpgate.pi.calculator.Errore; -import org.warpgate.pi.calculator.Utils; +import org.warp.picalculator.Errore; +import org.warp.picalculator.Utils; -/** Square roots on the real line. -* These represent numbers which are a product of a (signed) fraction by -* a square root of a non-negative fraction. -* This might be extended to values on the imaginary axis by allowing negative -* values underneath the square root, but this is not yet implemented. -* @since 2011-02-12 -* @author Richard J. Mathar -*/ -public class BigSurd implements Cloneable, Comparable -{ - /** The value of zero. - */ - static public BigSurd ZERO = new BigSurd() ; +/** + * Square roots on the real line. + * These represent numbers which are a product of a (signed) fraction by + * a square root of a non-negative fraction. + * This might be extended to values on the imaginary axis by allowing negative + * values underneath the square root, but this is not yet implemented. + * + * @since 2011-02-12 + * @author Richard J. Mathar + */ +public class BigSurd implements Cloneable, Comparable { + /** + * The value of zero. + */ + static public BigSurd ZERO = new BigSurd(); - /** The value of one. - */ - static public BigSurd ONE = new BigSurd(Rational.ONE,Rational.ONE) ; - /** Prefactor - */ - Rational pref ; + /** + * The value of one. + */ + static public BigSurd ONE = new BigSurd(Rational.ONE, Rational.ONE); + /** + * Prefactor + */ + Rational pref; - /** The number underneath the square root, always non-negative. - * The mathematical object has the value pref*sqrt(disc). - */ - Rational disc ; + /** + * The number underneath the square root, always non-negative. + * The mathematical object has the value pref*sqrt(disc). + */ + Rational disc; - /** Default ctor, which represents the zero. - * @since 2011-02-12 - */ - public BigSurd() - { - pref = Rational.ZERO ; - disc = Rational.ZERO ; - } + /** + * Default ctor, which represents the zero. + * + * @since 2011-02-12 + */ + public BigSurd() { + pref = Rational.ZERO; + disc = Rational.ZERO; + } - /** ctor given the prefactor and the basis of the root. - * This creates an object of value a*sqrt(b). - * @param a the prefactor. - * @param b the discriminant. - * @since 2011-02-12 - */ - public BigSurd(Rational a, Rational b) - { - this.pref = a ; - /* reject attempts to use a negative b - */ - if ( b.signum() < 0 ) - throw new ProviderException("Not implemented: imaginary surds") ; - this.disc = b ; - try { - normalize() ; - normalizeG() ; - } catch (Errore e) { - e.printStackTrace(); - } - } + /** + * ctor given the prefactor and the basis of the root. + * This creates an object of value a*sqrt(b). + * + * @param a + * the prefactor. + * @param b + * the discriminant. + * @since 2011-02-12 + */ + public BigSurd(Rational a, Rational b) { + this.pref = a; + /* + * reject attempts to use a negative b + */ + if (b.signum() < 0) + throw new ProviderException("Not implemented: imaginary surds"); + this.disc = b; + try { + normalize(); + normalizeG(); + } catch (Errore e) { + e.printStackTrace(); + } + } - /** ctor given the numerator and denominator of the root. - * This creates an object of value sqrt(a/b). - * @param a the numerator - * @param b the denominator. - * @since 2011-02-12 - */ - public BigSurd(int a, int b) - { - this( Rational.ONE, new Rational(a,b) ) ; - } + /** + * ctor given the numerator and denominator of the root. + * This creates an object of value sqrt(a/b). + * + * @param a + * the numerator + * @param b + * the denominator. + * @since 2011-02-12 + */ + public BigSurd(int a, int b) { + this(Rational.ONE, new Rational(a, b)); + } - /** ctor given the value under the root. - * This creates an object of value sqrt(a). - * @param a the discriminant. - * @since 2011-02-12 - */ - public BigSurd(BigInteger a) - { - this( Rational.ONE, new Rational(a,BigInteger.ONE) ) ; - } - - public BigSurd(Rational a) - { - this( Rational.ONE, a) ; - } + /** + * ctor given the value under the root. + * This creates an object of value sqrt(a). + * + * @param a + * the discriminant. + * @since 2011-02-12 + */ + public BigSurd(BigInteger a) { + this(Rational.ONE, new Rational(a, BigInteger.ONE)); + } - /** Create a deep copy. - * @since 2011-02-12 - */ - public BigSurd clone() - { - Rational fclon = pref.clone() ; - Rational dclon = disc.clone() ; - /* the main intent here is to bypass any attempt to reduce the discriminant - * by figuring out the square-free part in normalize(), which has already done - * in the current copy of the number. - */ - BigSurd cl = new BigSurd() ; - cl.pref = fclon ; - cl.disc = dclon ; - return cl ; - } /* BigSurd.clone */ - - /** Add two surds of compatible discriminant. - * @param val The value to be added to this. - */ - - public BigSurdVec add(final BigSurd val) - { - //zero plus somethings yields something - if ( signum() == 0 ) - return new BigSurdVec(val) ; - else if (val.signum() == 0 ) - return new BigSurdVec(this) ; - else - // let the ctor of BigSurdVec to the work - return new BigSurdVec(this,val) ; - } /* BigSurd.add */ + public BigSurd(Rational a) { + this(Rational.ONE, a); + } - /** Multiply by another square root. - * @param val a second number of this type. - * @return the product of this with the val. - * @since 2011-02-12 - */ - public BigSurd multiply(final BigSurd val) - { - return new BigSurd( pref.multiply(val.pref), disc.multiply(val.disc) ) ; - } /* BigSurd.multiply */ + /** + * Create a deep copy. + * + * @since 2011-02-12 + */ + @Override + public BigSurd clone() { + Rational fclon = pref.clone(); + Rational dclon = disc.clone(); + /* + * the main intent here is to bypass any attempt to reduce the + * discriminant + * by figuring out the square-free part in normalize(), which has + * already done + * in the current copy of the number. + */ + BigSurd cl = new BigSurd(); + cl.pref = fclon; + cl.disc = dclon; + return cl; + } /* BigSurd.clone */ - /** Multiply by a rational number. - * @param val the factor. - * @return the product of this with the val. - * @since 2011-02-15 - */ - public BigSurd multiply(final Rational val) - { - return new BigSurd( pref.multiply(val), disc) ; - } /* BigSurd.multiply */ + /** + * Add two surds of compatible discriminant. + * + * @param val + * The value to be added to this. + */ - /** Multiply by a BigInteger. - * @param val a second number. - * @return the product of this with the value. - * @since 2011-02-12 - */ - public BigSurd multiply(final BigInteger val) - { - return new BigSurd(pref.multiply(val), disc) ; - } /* BigSurd.multiply */ + public BigSurdVec add(final BigSurd val) { + // zero plus somethings yields something + if (signum() == 0) + return new BigSurdVec(val); + else if (val.signum() == 0) + return new BigSurdVec(this); + else + // let the ctor of BigSurdVec to the work + return new BigSurdVec(this, val); + } /* BigSurd.add */ - /** Multiply by an integer. - * @param val a second number. - * @return the product of this with the value. - * @since 2011-02-12 - */ - public BigSurd multiply(final int val) - { - BigInteger tmp = new BigInteger(""+val) ; - return multiply(tmp) ; - } /* BigSurd.multiply */ + /** + * Multiply by another square root. + * + * @param val + * a second number of this type. + * @return the product of this with the val. + * @since 2011-02-12 + */ + public BigSurd multiply(final BigSurd val) { + return new BigSurd(pref.multiply(val.pref), disc.multiply(val.disc)); + } /* BigSurd.multiply */ + /** + * Multiply by a rational number. + * + * @param val + * the factor. + * @return the product of this with the val. + * @since 2011-02-15 + */ + public BigSurd multiply(final Rational val) { + return new BigSurd(pref.multiply(val), disc); + } /* BigSurd.multiply */ - /** Compute the square. - * @return this value squared. - * @since 2011-02-12 - */ - public Rational sqr() - { - Rational res = pref.pow(2) ; - res = res.multiply(disc) ; - return res; - } /* BigSurd.sqr */ + /** + * Multiply by a BigInteger. + * + * @param val + * a second number. + * @return the product of this with the value. + * @since 2011-02-12 + */ + public BigSurd multiply(final BigInteger val) { + return new BigSurd(pref.multiply(val), disc); + } /* BigSurd.multiply */ - /** Divide by another square root. - * @param val A second number of this type. - * @return The value of this/val - * @throws Errore - * @since 2011-02-12 - */ - public BigSurd divide(final BigSurd val) throws Errore - { - if( val.signum() == 0 ) - throw new ArithmeticException("Dividing "+ toFancyString() + " through zero.") ; - return new BigSurd( pref.divide(val.pref), disc.divide(val.disc) ) ; - } /* BigSurd.divide */ + /** + * Multiply by an integer. + * + * @param val + * a second number. + * @return the product of this with the value. + * @since 2011-02-12 + */ + public BigSurd multiply(final int val) { + BigInteger tmp = new BigInteger("" + val); + return multiply(tmp); + } /* BigSurd.multiply */ - private String toFancyString() { - BigSurd bs = this; - BigInteger denominator = pref.b; - String s = ""; - if (denominator.compareTo(BigInteger.ONE) != 0) { + /** + * Compute the square. + * + * @return this value squared. + * @since 2011-02-12 + */ + public Rational sqr() { + Rational res = pref.pow(2); + res = res.multiply(disc); + return res; + } /* BigSurd.sqr */ + + /** + * Divide by another square root. + * + * @param val + * A second number of this type. + * @return The value of this/val + * @throws Errore + * @since 2011-02-12 + */ + public BigSurd divide(final BigSurd val) throws Errore { + if (val.signum() == 0) + throw new ArithmeticException("Dividing " + toFancyString() + " through zero."); + return new BigSurd(pref.divide(val.pref), disc.divide(val.disc)); + } /* BigSurd.divide */ + + private String toFancyString() { + BigSurd bs = this; + BigInteger denominator = pref.b; + String s = ""; + if (denominator.compareTo(BigInteger.ONE) != 0) { + s += "("; + } + if (bs.isBigInteger()) { + s += bs.BigDecimalValue(new MathContext(Utils.scale, Utils.scaleMode2)).toBigInteger().toString(); + } else if (bs.isRational()) { + s += bs.toRational().toString(); + } else { + BigInteger numerator = bs.pref.a; + if (numerator.compareTo(BigInteger.ONE) != 0) { + s += numerator.toString(); + s += "*"; s += "("; } - if (bs.isBigInteger()) { - s += bs.BigDecimalValue(new MathContext(Utils.scale, Utils.scaleMode2)).toBigInteger().toString(); - } else if (bs.isRational()) { - s += bs.toRational().toString(); + s += "2√"; + if (bs.disc.isInteger()) { + s += bs.disc.toString(); } else { - BigInteger numerator = bs.pref.a; - if (numerator.compareTo(BigInteger.ONE) != 0) { - s += numerator.toString(); - s += "*"; - s += "("; - } - s += "2√"; - if (bs.disc.isInteger()) { - s += bs.disc.toString(); - } else { - s += "("+bs.disc.toString()+")"; - } - if (numerator.compareTo(BigInteger.ONE) != 0) { - s += ")"; - } + s += "(" + bs.disc.toString() + ")"; + } + if (numerator.compareTo(BigInteger.ONE) != 0) { + s += ")"; } - return s; } + return s; + } - /** Divide by an integer. - * @param val a second number. - * @return the value of this/val - * @throws Errore - * @since 2011-02-12 - */ - public BigSurd divide(final BigInteger val) throws Errore - { - if( val.signum() == 0 ) - throw new ArithmeticException("Dividing "+ toFancyString() + " through zero.") ; - return new BigSurd( pref.divide(val), disc ) ; - } /* BigSurd.divide */ + /** + * Divide by an integer. + * + * @param val + * a second number. + * @return the value of this/val + * @throws Errore + * @since 2011-02-12 + */ + public BigSurd divide(final BigInteger val) throws Errore { + if (val.signum() == 0) + throw new ArithmeticException("Dividing " + toFancyString() + " through zero."); + return new BigSurd(pref.divide(val), disc); + } /* BigSurd.divide */ - /** Divide by an integer. - * @param val A second number. - * @return The value of this/val - * @throws Errore - * @since 2011-02-12 - */ - public BigSurd divide(int val) throws Errore - { - if( val == 0 ) - throw new ArithmeticException("Dividing "+ toFancyString() + " through zero.") ; - return new BigSurd( pref.divide(val), disc ) ; - } /* BigSurd.divide */ + /** + * Divide by an integer. + * + * @param val + * A second number. + * @return The value of this/val + * @throws Errore + * @since 2011-02-12 + */ + public BigSurd divide(int val) throws Errore { + if (val == 0) + throw new ArithmeticException("Dividing " + toFancyString() + " through zero."); + return new BigSurd(pref.divide(val), disc); + } /* BigSurd.divide */ - /** Compute the negative. - * @return -this. - * @since 2011-02-12 - */ - public BigSurd negate() - { - /* This is trying to be quick, avoiding normalize(), by toggling - * the sign in a clone() - */ - BigSurd n = clone() ; - n.pref = n.pref.negate() ; - return n ; - } /* BigSurd.negate */ + /** + * Compute the negative. + * + * @return -this. + * @since 2011-02-12 + */ + public BigSurd negate() { + /* + * This is trying to be quick, avoiding normalize(), by toggling + * the sign in a clone() + */ + BigSurd n = clone(); + n.pref = n.pref.negate(); + return n; + } /* BigSurd.negate */ - /** Absolute value. - * @return The absolute (non-negative) value of this. - * @since 2011-02-12 - */ - public BigSurd abs() - { - return new BigSurd(pref.abs(),disc) ; - } + /** + * Absolute value. + * + * @return The absolute (non-negative) value of this. + * @since 2011-02-12 + */ + public BigSurd abs() { + return new BigSurd(pref.abs(), disc); + } - /** Compares the value of this with another constant. - * @param val the other constant to compare with - * @return -1, 0 or 1 if this number is numerically less than, equal to, - * or greater than val. - * @since 2011-02-12 - */ - public int compareTo(final BigSurd val) - { - /* Since we keep the discriminant positive, the rough estimate - * comes from comparing the signs of the prefactors. - */ - final int sig = signum() ; - final int sigv = val.signum() ; - if ( sig < 0 && sigv >= 0 ) - return -1 ; - if ( sig > 0 && sigv <= 0 ) - return 1 ; - if ( sig == 0 && sigv == 0 ) - return 0 ; - if ( sig == 0 && sigv > 0 ) - return -1 ; - if ( sig == 0 && sigv < 0 ) - return 1 ; + /** + * Compares the value of this with another constant. + * + * @param val + * the other constant to compare with + * @return -1, 0 or 1 if this number is numerically less than, equal to, + * or greater than val. + * @since 2011-02-12 + */ + @Override + public int compareTo(final BigSurd val) { + /* + * Since we keep the discriminant positive, the rough estimate + * comes from comparing the signs of the prefactors. + */ + final int sig = signum(); + final int sigv = val.signum(); + if (sig < 0 && sigv >= 0) + return -1; + if (sig > 0 && sigv <= 0) + return 1; + if (sig == 0 && sigv == 0) + return 0; + if (sig == 0 && sigv > 0) + return -1; + if (sig == 0 && sigv < 0) + return 1; - /* Work out the cases of equal sign. Compare absolute values by comparison - * of the squares which is forwarded to the comparison of the Rational class. - */ - final Rational this2 = sqr() ; - final Rational val2 = val.sqr() ; - final int c = this2.compareTo(val2) ; - if ( c == 0 ) - return 0 ; - /* If both values have negative sign, the one with the smaller square is the larger number. - */ - else if ( sig >0 && c >0 || sig <0 && c <0 ) - return 1; - else - return -1 ; - } /* BigSurd.compareTo */ + /* + * Work out the cases of equal sign. Compare absolute values by + * comparison + * of the squares which is forwarded to the comparison of the Rational + * class. + */ + final Rational this2 = sqr(); + final Rational val2 = val.sqr(); + final int c = this2.compareTo(val2); + if (c == 0) + return 0; + /* + * If both values have negative sign, the one with the smaller square is + * the larger number. + */ + else if (sig > 0 && c > 0 || sig < 0 && c < 0) + return 1; + else + return -1; + } /* BigSurd.compareTo */ - /** Return a string in the format (number/denom)*()^(1/2). - * If the discriminant equals 1, print just the prefactor. - * @return the human-readable version in base 10 - * @since 2011-02-12 - */ - public String toString() - { - if ( disc.compareTo(Rational.ONE) != 0 && disc.compareTo(Rational.ZERO) != 0) - return( "("+pref.toString()+")*("+disc.toString()+")^(1/2)" ) ; - else - return pref.toString() ; - } /* BigSurd.toString */ + /** + * Return a string in the format (number/denom)*()^(1/2). + * If the discriminant equals 1, print just the prefactor. + * + * @return the human-readable version in base 10 + * @since 2011-02-12 + */ + @Override + public String toString() { + if (disc.compareTo(Rational.ONE) != 0 && disc.compareTo(Rational.ZERO) != 0) + return ("(" + pref.toString() + ")*(" + disc.toString() + ")^(1/2)"); + else + return pref.toString(); + } /* BigSurd.toString */ - /** Return a double value representation. - * @return The value with double precision. - * @since 2011-02-12 - */ - public double doubleValue() - { - /* First compute the square to prevent overflows if the two pieces of - * the prefactor and the discriminant are of very different magnitude. - */ - Rational p2 = pref.pow(2).multiply(disc) ; -System.out.println("dv sq " + p2.toString()) ; - double res = p2.doubleValue() ; -System.out.println("dv sq " + res) ; - return (pref.signum() >= 0) ? Math.sqrt(res) : -Math.sqrt(res) ; - } /* BigSurd.doubleValue */ + /** + * Return a double value representation. + * + * @return The value with double precision. + * @since 2011-02-12 + */ + public double doubleValue() { + /* + * First compute the square to prevent overflows if the two pieces of + * the prefactor and the discriminant are of very different magnitude. + */ + Rational p2 = pref.pow(2).multiply(disc); + System.out.println("dv sq " + p2.toString()); + double res = p2.doubleValue(); + System.out.println("dv sq " + res); + return (pref.signum() >= 0) ? Math.sqrt(res) : -Math.sqrt(res); + } /* BigSurd.doubleValue */ - /** Return a float value representation. - * @return The value with single precision. - * @since 2011-02-12 - */ - public float floatValue() - { - return (float)(doubleValue()) ; - } /* BigSurd.floatValue */ + /** + * Return a float value representation. + * + * @return The value with single precision. + * @since 2011-02-12 + */ + public float floatValue() { + return (float) (doubleValue()); + } /* BigSurd.floatValue */ - /** True if the value is integer. - * Equivalent to the indication whether a conversion to an integer - * can be exact. - * @since 2011-02-12 - */ - public boolean isBigInteger() - { - return pref.isBigInteger() && ( disc.signum() ==0 || disc.compareTo(Rational.ONE) == 0 ) ; - } /* BigSurd.isBigInteger */ + /** + * True if the value is integer. + * Equivalent to the indication whether a conversion to an integer + * can be exact. + * + * @since 2011-02-12 + */ + public boolean isBigInteger() { + return pref.isBigInteger() && (disc.signum() == 0 || disc.compareTo(Rational.ONE) == 0); + } /* BigSurd.isBigInteger */ - /** True if the value is rational. - * Equivalent to the indication whether a conversion to a Rational can be exact. - * @since 2011-02-12 - */ - public boolean isRational() - { - return ( disc.signum() ==0 || disc.compareTo(Rational.ONE) == 0 ) ; - } /* BigSurd.isRational */ + /** + * True if the value is rational. + * Equivalent to the indication whether a conversion to a Rational can be + * exact. + * + * @since 2011-02-12 + */ + public boolean isRational() { + return (disc.signum() == 0 || disc.compareTo(Rational.ONE) == 0); + } /* BigSurd.isRational */ - /** Convert to a rational value if possible - * @since 2012-02-15 - */ - public Rational toRational() - { - if ( isRational() ) - return pref ; - else - throw new ArithmeticException("Undefined conversion "+ toFancyString() + " to Rational.") ; - } /* BigSurd.toRational */ + /** + * Convert to a rational value if possible + * + * @since 2012-02-15 + */ + public Rational toRational() { + if (isRational()) + return pref; + else + throw new ArithmeticException("Undefined conversion " + toFancyString() + " to Rational."); + } /* BigSurd.toRational */ - /** The sign: 1 if the number is >0, 0 if ==0, -1 if <0 - * @return the signum of the value. - * @since 2011-02-12 - */ - public int signum() - { - /* Since the disc is kept positive, this is the same - * as the sign of the prefactor. This works because a zero discriminant - * is always copied over to the prefactor, not hidden. - */ - return pref.signum() ; - } /* BigSurd.signum */ + /** + * The sign: 1 if the number is >0, 0 if ==0, -1 if <0 + * + * @return the signum of the value. + * @since 2011-02-12 + */ + public int signum() { + /* + * Since the disc is kept positive, this is the same + * as the sign of the prefactor. This works because a zero discriminant + * is always copied over to the prefactor, not hidden. + */ + return pref.signum(); + } /* BigSurd.signum */ - /** Normalize to squarefree discriminant. - * @throws Errore - * @since 2011-02-12 - */ - protected void normalize() throws Errore - { - /* Move squares out of the numerator and denominator of the discriminant - */ - if ( disc.signum() != 0 ) - { - /* square-free part of the numerator: numer = numC*some^2 - */ - BigInteger numC = BigIntegerMath.core(disc.numer()) ; - /* extract the perfect square of the numerator - */ - BigInteger sq = disc.numer().divide(numC) ; - /* extract the associated square root - */ - BigInteger sqf = BigIntegerMath.isqrt(sq) ; + /** + * Normalize to squarefree discriminant. + * + * @throws Errore + * @since 2011-02-12 + */ + protected void normalize() throws Errore { + /* + * Move squares out of the numerator and denominator of the discriminant + */ + if (disc.signum() != 0) { + /* + * square-free part of the numerator: numer = numC*some^2 + */ + BigInteger numC = BigIntegerMath.core(disc.numer()); + /* + * extract the perfect square of the numerator + */ + BigInteger sq = disc.numer().divide(numC); + /* + * extract the associated square root + */ + BigInteger sqf = BigIntegerMath.isqrt(sq); - /* move sqf over to the pre-factor - */ - pref = pref.multiply(sqf) ; + /* + * move sqf over to the pre-factor + */ + pref = pref.multiply(sqf); - BigInteger denC = BigIntegerMath.core(disc.denom()) ; - sq = disc.denom().divide(denC) ; - sqf = BigIntegerMath.isqrt(sq) ; - pref = pref.divide(sqf) ; + BigInteger denC = BigIntegerMath.core(disc.denom()); + sq = disc.denom().divide(denC); + sqf = BigIntegerMath.isqrt(sq); + pref = pref.divide(sqf); - disc = new Rational(numC,denC) ; - } - else - pref = Rational.ZERO ; - } /* BigSurd.normalize */ - - /** Normalize to coprime numerator and denominator in prefactor and discriminant - * @throws Errore - * @since 2011-02-12 - */ - protected void normalizeG() throws Errore - { - /* Is there a common factor between the numerator of the prefactor - * and the denominator of the discriminant ? - */ - BigInteger d = pref.numer().abs().gcd( disc.denom()) ; - if ( d.compareTo(BigInteger.ONE) > 0 ) - { - pref = pref.divide(d) ; - /* instead of multiplying with the square of d, using two steps - * offers a change to recognize the common factor.. - */ - disc = disc.multiply(d) ; - disc = disc.multiply(d) ; - } - /* Is there a common factor between the denominator of the prefactor - * and the numerator of the discriminant ? - */ - d = pref.denom().gcd( disc.numer()) ; - if ( d.compareTo(BigInteger.ONE) > 0 ) - { - pref = pref.multiply(d) ; - /* instead of dividing through the square of d, using two steps - * offers a change to recognize the common factor.. - */ - disc = disc.divide(d) ; - disc = disc.divide(d) ; - } - } /* BigSurd.normalizeG */ - - /** Return the approximate floating point representation. - * @param mc Description of the accuracy needed. - * @return A representation with digits valid as described by mc - * @since 2012-02-15 - */ - public BigDecimal BigDecimalValue(MathContext mc) - { - /* the relative error of the result equals the relative error of the - * prefactor plus half of the relative error of the discriminant. - * So adding 3 digits temporarily is sufficient. - */ - final MathContext locmc = new MathContext(mc.getPrecision()+3,mc.getRoundingMode()) ; - /* first the square root of the discriminant - */ - BigDecimal sqrdis = BigDecimalMath.sqrt(disc.BigDecimalValue(locmc),locmc ) ; - /* Then multiply by the prefactor. If sqrdis is a terminating decimal fraction, - * we prevent early truncation of the result by truncating later. - */ - BigDecimal res = sqrdis.multiply(pref.BigDecimalValue(mc)) ; - return BigDecimalMath.scalePrec(res,mc) ; - } /* BigDecimalValue */ + disc = new Rational(numC, denC); + } else + pref = Rational.ZERO; + } /* BigSurd.normalize */ + /** + * Normalize to coprime numerator and denominator in prefactor and + * discriminant + * + * @throws Errore + * @since 2011-02-12 + */ + protected void normalizeG() throws Errore { + /* + * Is there a common factor between the numerator of the prefactor + * and the denominator of the discriminant ? + */ + BigInteger d = pref.numer().abs().gcd(disc.denom()); + if (d.compareTo(BigInteger.ONE) > 0) { + pref = pref.divide(d); + /* + * instead of multiplying with the square of d, using two steps + * offers a change to recognize the common factor.. + */ + disc = disc.multiply(d); + disc = disc.multiply(d); + } + /* + * Is there a common factor between the denominator of the prefactor + * and the numerator of the discriminant ? + */ + d = pref.denom().gcd(disc.numer()); + if (d.compareTo(BigInteger.ONE) > 0) { + pref = pref.multiply(d); + /* + * instead of dividing through the square of d, using two steps + * offers a change to recognize the common factor.. + */ + disc = disc.divide(d); + disc = disc.divide(d); + } + } /* BigSurd.normalizeG */ + /** + * Return the approximate floating point representation. + * + * @param mc + * Description of the accuracy needed. + * @return A representation with digits valid as described by mc + * @since 2012-02-15 + */ + public BigDecimal BigDecimalValue(MathContext mc) { + /* + * the relative error of the result equals the relative error of the + * prefactor plus half of the relative error of the discriminant. + * So adding 3 digits temporarily is sufficient. + */ + final MathContext locmc = new MathContext(mc.getPrecision() + 3, mc.getRoundingMode()); + /* + * first the square root of the discriminant + */ + BigDecimal sqrdis = BigDecimalMath.sqrt(disc.BigDecimalValue(locmc), locmc); + /* + * Then multiply by the prefactor. If sqrdis is a terminating decimal + * fraction, + * we prevent early truncation of the result by truncating later. + */ + BigDecimal res = sqrdis.multiply(pref.BigDecimalValue(mc)); + return BigDecimalMath.scalePrec(res, mc); + } /* BigDecimalValue */ } /* BigSurd */ - diff --git a/src/org/nevec/rjm/BigSurdVec.java b/src/org/nevec/rjm/BigSurdVec.java index c917a863..b23c5795 100644 --- a/src/org/nevec/rjm/BigSurdVec.java +++ b/src/org/nevec/rjm/BigSurdVec.java @@ -5,8 +5,8 @@ import java.math.BigInteger; import java.math.MathContext; import java.util.Vector; -import org.warpgate.pi.calculator.Errore; -import org.warpgate.pi.calculator.Utils; +import org.warp.picalculator.Errore; +import org.warp.picalculator.Utils; /** * A BigSurdVec represents an algebraic sum or differences of values which each @@ -78,7 +78,8 @@ public class BigSurdVec implements Comparable { /** * Combine terms that can be written as a single surd. This unites for * example the terms sqrt(90) and sqrt(10) to 4*sqrt(10). - * @throws Errore + * + * @throws Errore * * @since 2012-02-15 */ @@ -141,6 +142,7 @@ public class BigSurdVec implements Comparable { * @return 0 or +-1. * @since 2012-02-15 */ + @Override public int compareTo(BigSurdVec oth) { BigSurdVec diff; try { @@ -158,7 +160,7 @@ public class BigSurdVec implements Comparable { * equal to or larger than zero. * * @return 0 or +-1. - * @throws Errore + * @throws Errore * @since 2012-02-15 */ public int signum() throws Errore { @@ -209,12 +211,14 @@ public class BigSurdVec implements Comparable { else lhs = new BigSurdVec(terms.elementAt(0), terms.elementAt(2)); lhs = lhs.sqr(); - /* - * Strange line: this line isn't used, but it's present in this code! + /* + * Strange line: this line isn't used, but it's present in this + * code! * * * - BigSurd rhs = new BigSurd(terms.elementAt(offsig).sqr(), Rational.ONE); + * BigSurd rhs = new BigSurd(terms.elementAt(offsig).sqr(), + * Rational.ONE); * * * @@ -304,7 +308,7 @@ public class BigSurdVec implements Comparable { * @param val * The value to be added to this. * @return The new value representing this+val. - * @throws Errore + * @throws Errore */ public BigSurdVec add(final BigSurdVec val) throws Errore { BigSurdVec sum = new BigSurdVec(); @@ -331,7 +335,7 @@ public class BigSurdVec implements Comparable { * @param val * The value to be added to this. * @return The new value representing this+val. - * @throws Errore + * @throws Errore */ public BigSurdVec add(final BigSurd val) throws Errore { BigSurdVec sum = new BigSurdVec(); @@ -350,7 +354,7 @@ public class BigSurdVec implements Comparable { * @param val * The value to be subtracted from this. * @return The new value representing this-val. - * @throws Errore + * @throws Errore */ public BigSurdVec subtract(final BigSurdVec val) throws Errore { BigSurdVec sum = new BigSurdVec(); @@ -370,7 +374,7 @@ public class BigSurdVec implements Comparable { * @param val * The value to be subtracted from this. * @return The new value representing this-val. - * @throws Errore + * @throws Errore */ public BigSurdVec subtract(final BigSurd val) throws Errore { BigSurdVec sum = new BigSurdVec(); @@ -407,7 +411,7 @@ public class BigSurdVec implements Comparable { * Compute the square. * * @return this value squared. - * @throws Errore + * @throws Errore * @since 2012-02-15 */ public BigSurdVec sqr() throws Errore { @@ -431,7 +435,7 @@ public class BigSurdVec implements Comparable { * @param val * a second number of this type. * @return the product of this with the val. - * @throws Errore + * @throws Errore * @since 2011-02-12 */ public BigSurdVec multiply(final BigSurd val) throws Errore { @@ -554,6 +558,7 @@ public class BigSurdVec implements Comparable { * @return the human-readable version in base 10 * @since 2012-02-16 */ + @Override public String toString() { /* * simple cases with one term forwarded to the BigSurd class @@ -597,16 +602,16 @@ public class BigSurdVec implements Comparable { if (numerator.compareTo(BigInteger.ONE) != 0) { s += numerator.toString(); s += "*"; - //s += "("; Radice quadrata. non servono le parentesi. + // s += "("; Radice quadrata. non servono le parentesi. } s += "Ⓐ"; if (bs.disc.isInteger()) { s += bs.disc.toString(); } else { - s += "("+bs.disc.toString()+")"; + s += "(" + bs.disc.toString() + ")"; } if (numerator.compareTo(BigInteger.ONE) != 0) { - //s += ")"; Radice quadrata. non servono le parentesi. + // s += ")"; Radice quadrata. non servono le parentesi. } } } diff --git a/src/org/nevec/rjm/Euler.java b/src/org/nevec/rjm/Euler.java index e72ece73..93e7174e 100644 --- a/src/org/nevec/rjm/Euler.java +++ b/src/org/nevec/rjm/Euler.java @@ -1,69 +1,71 @@ -package org.nevec.rjm ; +package org.nevec.rjm; import java.math.BigInteger; import java.util.Vector; -/** Euler numbers -* @see A000364 in the OEIS. -* @since 2008-10-30 -* @author Richard J. Mathar -*/ -public class Euler -{ - /* - * The list of all Euler numbers as a vector, n=0,2,4,.... - */ - static protected Vector a = new Vector() ; +/** + * Euler numbers + * + * @see A000364 in the OEIS. + * @since 2008-10-30 + * @author Richard J. Mathar + */ +public class Euler { + /* + * The list of all Euler numbers as a vector, n=0,2,4,.... + */ + static protected Vector a = new Vector(); - /** Ctor(). Fill the hash list initially with E_0 to E_3. - */ - public Euler() - { - if ( a.size() == 0 ) - { - a.add(BigInteger.ONE) ; - a.add(BigInteger.ONE) ; - a.add(new BigInteger("5")) ; - a.add(new BigInteger("61")) ; - } - } + /** + * Ctor(). Fill the hash list initially with E_0 to E_3. + */ + public Euler() { + if (a.size() == 0) { + a.add(BigInteger.ONE); + a.add(BigInteger.ONE); + a.add(new BigInteger("5")); + a.add(new BigInteger("61")); + } + } - /** Compute a coefficient in the internal table. - * @param n the zero-based index of the coefficient. n=0 for the E_0 term. - */ - protected void set(final int n) - { - while ( n >= a.size()) - { - BigInteger val = BigInteger.ZERO ; - boolean sigPos = true; - int thisn = a.size() ; - for(int i= thisn-1 ; i > 0 ; i--) - { - BigInteger f = new BigInteger(""+ a.elementAt(i).toString() ) ; - f = f.multiply( BigIntegerMath.binomial(2*thisn,2*i) ); - if ( sigPos ) - val = val.add(f) ; - else - val = val.subtract(f) ; - sigPos = ! sigPos ; - } - if ( thisn % 2 ==0 ) - val = val.subtract(BigInteger.ONE) ; - else - val = val.add(BigInteger.ONE) ; - a.add(val) ; - } - } + /** + * Compute a coefficient in the internal table. + * + * @param n + * the zero-based index of the coefficient. n=0 for the E_0 term. + */ + protected void set(final int n) { + while (n >= a.size()) { + BigInteger val = BigInteger.ZERO; + boolean sigPos = true; + int thisn = a.size(); + for (int i = thisn - 1; i > 0; i--) { + BigInteger f = new BigInteger("" + a.elementAt(i).toString()); + f = f.multiply(BigIntegerMath.binomial(2 * thisn, 2 * i)); + if (sigPos) + val = val.add(f); + else + val = val.subtract(f); + sigPos = !sigPos; + } + if (thisn % 2 == 0) + val = val.subtract(BigInteger.ONE); + else + val = val.add(BigInteger.ONE); + a.add(val); + } + } - /** The Euler number at the index provided. - * @param n the index, non-negative. - * @return the E_0=E_1=1 , E_2=5, E_3=61 etc - */ - public BigInteger at(int n) - { - set(n) ; - return(a.elementAt(n)) ; - } + /** + * The Euler number at the index provided. + * + * @param n + * the index, non-negative. + * @return the E_0=E_1=1 , E_2=5, E_3=61 etc + */ + public BigInteger at(int n) { + set(n); + return (a.elementAt(n)); + } } /* Euler */ diff --git a/src/org/nevec/rjm/EulerPhi.java b/src/org/nevec/rjm/EulerPhi.java index 56386fc4..973e6944 100644 --- a/src/org/nevec/rjm/EulerPhi.java +++ b/src/org/nevec/rjm/EulerPhi.java @@ -1,60 +1,64 @@ -package org.nevec.rjm ; +package org.nevec.rjm; import java.math.BigInteger; -/** Euler totient function. -* @see A000010 in the OEIS. -* @since 2008-10-14 -* @since 2012-03-04 Adapted to new Ifactor representation. -* @author Richard J. Mathar -*/ -public class EulerPhi -{ - /** Default constructor. - * Does nothing(). - */ - public EulerPhi() - { - } +/** + * Euler totient function. + * + * @see A000010 in the OEIS. + * @since 2008-10-14 + * @since 2012-03-04 Adapted to new Ifactor representation. + * @author Richard J. Mathar + */ +public class EulerPhi { + /** + * Default constructor. + * Does nothing(). + */ + public EulerPhi() {} - /** Compute phi(n). - * @param n The positive argument of the function. - * @return phi(n) - */ - public BigInteger at(int n) - { - return at(new BigInteger(""+n) ) ; - } /* at */ + /** + * Compute phi(n). + * + * @param n + * The positive argument of the function. + * @return phi(n) + */ + public BigInteger at(int n) { + return at(new BigInteger("" + n)); + } /* at */ - /** Compute phi(n). - * @param n The positive argument of the function. - * @return phi(n) - */ - public BigInteger at(BigInteger n) - { - if ( n.compareTo(BigInteger.ZERO) <= 0 ) - throw new ArithmeticException("negative argument "+n+ " of EulerPhi") ; - Ifactor prFact = new Ifactor(n) ; - BigInteger phi = n ; - if ( n.compareTo(BigInteger.ONE) > 0 ) - for(int i=0 ; i < prFact.primeexp.size() ; i += 2) - { - BigInteger p = new BigInteger(prFact.primeexp.elementAt(i).toString()) ; - BigInteger p_1 = p.subtract(BigInteger.ONE) ; - phi = phi.multiply(p_1).divide(p) ; - } - return phi ; - } /* at */ + /** + * Compute phi(n). + * + * @param n + * The positive argument of the function. + * @return phi(n) + */ + public BigInteger at(BigInteger n) { + if (n.compareTo(BigInteger.ZERO) <= 0) + throw new ArithmeticException("negative argument " + n + " of EulerPhi"); + Ifactor prFact = new Ifactor(n); + BigInteger phi = n; + if (n.compareTo(BigInteger.ONE) > 0) + for (int i = 0; i < prFact.primeexp.size(); i += 2) { + BigInteger p = new BigInteger(prFact.primeexp.elementAt(i).toString()); + BigInteger p_1 = p.subtract(BigInteger.ONE); + phi = phi.multiply(p_1).divide(p); + } + return phi; + } /* at */ - /** Test program. - * It takes one argument n and prints the value phi(n).
- * java -cp . org.nevec.rjm.EulerPhi n
- * @since 2006-08-14 - */ - public static void main(String[] args) throws ArithmeticException - { - EulerPhi a = new EulerPhi() ; - int n = (new Integer(args[0])).intValue() ; - System.out.println("phi("+ n + ") = " + a.at(n)) ; - } + /** + * Test program. + * It takes one argument n and prints the value phi(n).
+ * java -cp . org.nevec.rjm.EulerPhi n
+ * + * @since 2006-08-14 + */ + public static void main(String[] args) throws ArithmeticException { + EulerPhi a = new EulerPhi(); + int n = (new Integer(args[0])).intValue(); + System.out.println("phi(" + n + ") = " + a.at(n)); + } } /* EulerPhi */ diff --git a/src/org/nevec/rjm/Factorial.java b/src/org/nevec/rjm/Factorial.java index 154a1904..bfb19788 100644 --- a/src/org/nevec/rjm/Factorial.java +++ b/src/org/nevec/rjm/Factorial.java @@ -1,70 +1,79 @@ -package org.nevec.rjm ; +package org.nevec.rjm; import java.math.BigInteger; import java.util.Vector; +/** + * Factorials. + * + * @since 2006-06-25 + * @since 2012-02-15 Storage of the values based on Ifactor, not BigInteger. + * @author Richard J. Mathar + */ +public class Factorial { + /** + * The list of all factorials as a vector. + */ + static Vector a = new Vector(); -/** Factorials. -* @since 2006-06-25 -* @since 2012-02-15 Storage of the values based on Ifactor, not BigInteger. -* @author Richard J. Mathar -*/ -public class Factorial -{ - /** The list of all factorials as a vector. - */ - static Vector a = new Vector() ; + /** + * ctor(). + * Initialize the vector of the factorials with 0!=1 and 1!=1. + */ + public Factorial() { + if (a.size() == 0) { + a.add(Ifactor.ONE); + a.add(Ifactor.ONE); + } + } /* ctor */ - /** ctor(). - * Initialize the vector of the factorials with 0!=1 and 1!=1. - */ - public Factorial() - { - if ( a.size() == 0 ) - { - a.add(Ifactor.ONE) ; - a.add(Ifactor.ONE) ; - } - } /* ctor */ + /** + * Compute the factorial of the non-negative integer. + * + * @param n + * the argument to the factorial, non-negative. + * @return the factorial of n. + */ + public BigInteger at(int n) { + /* + * extend the internal list if needed. + */ + growto(n); + return a.elementAt(n).n; + } /* at */ - /** Compute the factorial of the non-negative integer. - * @param n the argument to the factorial, non-negative. - * @return the factorial of n. - */ - public BigInteger at(int n) - { - /* extend the internal list if needed. - */ - growto(n) ; - return a.elementAt(n).n ; - } /* at */ + /** + * Compute the factorial of the non-negative integer. + * + * @param n + * the argument to the factorial, non-negative. + * @return the factorial of n. + */ + public Ifactor toIfactor(int n) { + /* + * extend the internal list if needed. + */ + growto(n); + return a.elementAt(n); + } /* at */ - /** Compute the factorial of the non-negative integer. - * @param n the argument to the factorial, non-negative. - * @return the factorial of n. - */ - public Ifactor toIfactor(int n) - { - /* extend the internal list if needed. - */ - growto(n) ; - return a.elementAt(n) ; - } /* at */ - - /** Extend the internal table to cover up to n! - * @param n The maximum factorial to be supported. - * @since 2012-02-15 - */ - private void growto(int n) - { - /* extend the internal list if needed. Size to be 2 for n<=1, 3 for n<=2 etc. - */ - while ( a.size() <=n ) - { - final int lastn = a.size()-1 ; - final Ifactor nextn = new Ifactor(lastn+1) ; - a.add(a.elementAt(lastn).multiply(nextn) ) ; - } - } /* growto */ + /** + * Extend the internal table to cover up to n! + * + * @param n + * The maximum factorial to be supported. + * @since 2012-02-15 + */ + private void growto(int n) { + /* + * extend the internal list if needed. Size to be 2 for n<=1, 3 for n<=2 + * etc. + */ + while (a.size() <= n) { + final int lastn = a.size() - 1; + final Ifactor nextn = new Ifactor(lastn + 1); + a.add(a.elementAt(lastn).multiply(nextn)); + } + } /* growto */ } /* Factorial */ diff --git a/src/org/nevec/rjm/Harmonic.java b/src/org/nevec/rjm/Harmonic.java index b4ef627d..37365b92 100644 --- a/src/org/nevec/rjm/Harmonic.java +++ b/src/org/nevec/rjm/Harmonic.java @@ -1,39 +1,42 @@ -package org.nevec.rjm ; +package org.nevec.rjm; -/** Harmonic numbers. -* H(n) is the sum of the inverses of the integers from 1 to n. -* @since 2008-10-19 -* @author Richard J. Mathar -*/ -public class Harmonic -{ - /** ctor() - * Does nothing. - */ - public Harmonic() - { - } +/** + * Harmonic numbers. + * H(n) is the sum of the inverses of the integers from 1 to n. + * + * @since 2008-10-19 + * @author Richard J. Mathar + */ +public class Harmonic { + /** + * ctor() + * Does nothing. + */ + public Harmonic() {} - /** The Harmonic number at the index specified - * @param n the index, non-negative. - * @return the H_1=1 for n=1, H_2=3/2 for n=2 etc. - * For values of n less than 1, zero is returned. - */ - public Rational at(int n) - { - if ( n < 1) - return(new Rational(0,1)) ; - else - { - /* start with 1 as the result - */ - Rational a = new Rational(1,1) ; + /** + * The Harmonic number at the index specified + * + * @param n + * the index, non-negative. + * @return the H_1=1 for n=1, H_2=3/2 for n=2 etc. + * For values of n less than 1, zero is returned. + */ + public Rational at(int n) { + if (n < 1) + return (new Rational(0, 1)); + else { + /* + * start with 1 as the result + */ + Rational a = new Rational(1, 1); - /* add 1/i for i=2..n - */ - for( int i=2 ; i <=n ; i++) - a = a.add(new Rational(1,i)) ; - return a ; - } - } + /* + * add 1/i for i=2..n + */ + for (int i = 2; i <= n; i++) + a = a.add(new Rational(1, i)); + return a; + } + } } /* Harmonic */ diff --git a/src/org/nevec/rjm/Ifactor.java b/src/org/nevec/rjm/Ifactor.java index 2b958be9..d1feba75 100644 --- a/src/org/nevec/rjm/Ifactor.java +++ b/src/org/nevec/rjm/Ifactor.java @@ -1,750 +1,813 @@ -package org.nevec.rjm ; +package org.nevec.rjm; import java.math.BigInteger; import java.util.Collections; import java.util.Vector; -import org.warpgate.pi.calculator.Errore; +import org.warp.picalculator.Errore; -/** Factored integers. -* This class contains a non-negative integer with the prime factor decomposition attached. -* @since 2006-08-14 -* @since 2012-02-14 The internal representation contains the bases, and becomes sparser if few -* prime factors are present. -* @author Richard J. Mathar -*/ -public class Ifactor implements Cloneable, Comparable -{ - /** - * The standard representation of the number - */ - public BigInteger n ; +/** + * Factored integers. + * This class contains a non-negative integer with the prime factor + * decomposition attached. + * + * @since 2006-08-14 + * @since 2012-02-14 The internal representation contains the bases, and becomes + * sparser if few + * prime factors are present. + * @author Richard J. Mathar + */ +public class Ifactor implements Cloneable, Comparable { + /** + * The standard representation of the number + */ + public BigInteger n; - /* - * The bases and powers of the prime factorization. - * representation n = primeexp[0]^primeexp[1]*primeexp[2]^primeexp[3]*... - * The value 0 is represented by an empty vector, the value 1 by a vector of length 1 - * with a single power of 0. - */ - public Vector primeexp ; + /* + * The bases and powers of the prime factorization. + * representation n = primeexp[0]^primeexp[1]*primeexp[2]^primeexp[3]*... + * The value 0 is represented by an empty vector, the value 1 by a vector of + * length 1 + * with a single power of 0. + */ + public Vector primeexp; - final public static Ifactor ONE = new Ifactor(1) ; + final public static Ifactor ONE = new Ifactor(1); - final public static Ifactor ZERO = new Ifactor(0) ; + final public static Ifactor ZERO = new Ifactor(0); - /** Constructor given an integer. - * constructor with an ordinary integer - * @param number the standard representation of the integer - */ - public Ifactor(int number) - { - n = new BigInteger(""+number) ; - primeexp = new Vector() ; - if( number > 1 ) - { - int primindx = 0 ; - Prime primes = new Prime() ; - /* Test division against all primes. - */ - while(number > 1) - { - int ex=0 ; - /* primindx=0 refers to 2, =1 to 3, =2 to 5, =3 to 7 etc - */ - int p = primes.at(primindx).intValue() ; - while( number % p == 0 ) - { - ex++ ; - number /= p ; - if ( number == 1 ) - break ; - } - if ( ex > 0 ) - { - primeexp.add(new Integer(p)) ; - primeexp.add(new Integer(ex)) ; - } - primindx++ ; - } - } - else if ( number == 1) - { - primeexp.add(new Integer(1)) ; - primeexp.add(new Integer(0)) ; - } - } /* Ifactor */ - - /** Constructor given a BigInteger . - * Constructor with an ordinary integer, calling a prime factor decomposition. - * @param number the BigInteger representation of the integer - */ - public Ifactor(BigInteger number) - { - n = number ; - primeexp = new Vector() ; - if ( number.compareTo(BigInteger.ONE) == 0 ) - { - primeexp.add(new Integer(1)) ; - primeexp.add(new Integer(0)) ; - } - else - { - int primindx = 0 ; - Prime primes = new Prime() ; - /* Test for division against all primes. - */ - while(number.compareTo(BigInteger.ONE) == 1) - { - int ex=0 ; - BigInteger p = primes.at(primindx) ; - while( number.remainder(p).compareTo(BigInteger.ZERO) == 0 ) - { - ex++ ; - number = number.divide(p) ; - if ( number.compareTo(BigInteger.ONE) == 0 ) - break ; - } - if ( ex > 0 ) - { - primeexp.add(new Integer(p.intValue()) ) ; - primeexp.add(new Integer(ex) ) ; - } - primindx++ ; - } - } - } /* Ifactor */ - - /** Constructor given a list of exponents of the prime factor decomposition. - * @param pows the vector with the sorted list of exponents. - * pows[0] is the exponent of 2, pows[1] the exponent of 3, pows[2] the exponent of 5 etc. - * Note that this list does not include the primes, but assumes a continuous prime-smooth basis. - */ - public Ifactor(Vector pows) - { - primeexp = new Vector(2* pows.size()) ; - if ( pows.size() > 0 ) - { - n = BigInteger.ONE ; - Prime primes = new Prime() ; - /* Build the full number by the product of all powers of the primes. - */ - for(int primindx=0 ; primindx < pows.size() ; primindx++) - { - int ex= pows.elementAt(primindx).intValue() ; - final BigInteger p = primes.at(primindx) ; - n = n.multiply( p.pow(ex) ) ; - primeexp.add(new Integer(p.intValue()) ) ; - primeexp.add(new Integer(ex) ) ; - } - } - else - n = BigInteger.ZERO ; - } /* Ifactor */ - - /** Copy constructor. - * @param oth the value to be copied - */ - public Ifactor(Ifactor oth) - { - n = oth.n ; - primeexp = oth.primeexp ; - } /* Ifactor */ - - /** Deep copy. - * @since 2009-08-14 - */ - public Ifactor clone() - { + /** + * Constructor given an integer. + * constructor with an ordinary integer + * + * @param number + * the standard representation of the integer + */ + public Ifactor(int number) { + n = new BigInteger("" + number); + primeexp = new Vector(); + if (number > 1) { + int primindx = 0; + Prime primes = new Prime(); + /* + * Test division against all primes. + */ + while (number > 1) { + int ex = 0; /* - * Line not used: - * - Vector p = (Vector)primeexp.clone(); - * + * primindx=0 refers to 2, =1 to 3, =2 to 5, =3 to 7 etc */ - Ifactor cl = new Ifactor(0) ; - cl.n = new BigInteger(""+n) ; - return cl ; - } /* Ifactor.clone */ + int p = primes.at(primindx).intValue(); + while (number % p == 0) { + ex++; + number /= p; + if (number == 1) + break; + } + if (ex > 0) { + primeexp.add(new Integer(p)); + primeexp.add(new Integer(ex)); + } + primindx++; + } + } else if (number == 1) { + primeexp.add(new Integer(1)); + primeexp.add(new Integer(0)); + } + } /* Ifactor */ - /** Comparison of two numbers. - * The value of this method is in allowing the Vector<>.contains() calls that use the value, - * not the reference for comparison. - * @param oth the number to compare this with. - * @return true if both are the same numbers, false otherwise. - */ - public boolean equals(final Ifactor oth) - { - return ( n.compareTo(oth.n) == 0 ) ; - } /* Ifactor.equals */ + /** + * Constructor given a BigInteger . + * Constructor with an ordinary integer, calling a prime factor + * decomposition. + * + * @param number + * the BigInteger representation of the integer + */ + public Ifactor(BigInteger number) { + n = number; + primeexp = new Vector(); + if (number.compareTo(BigInteger.ONE) == 0) { + primeexp.add(new Integer(1)); + primeexp.add(new Integer(0)); + } else { + int primindx = 0; + Prime primes = new Prime(); + /* + * Test for division against all primes. + */ + while (number.compareTo(BigInteger.ONE) == 1) { + int ex = 0; + BigInteger p = primes.at(primindx); + while (number.remainder(p).compareTo(BigInteger.ZERO) == 0) { + ex++; + number = number.divide(p); + if (number.compareTo(BigInteger.ONE) == 0) + break; + } + if (ex > 0) { + primeexp.add(new Integer(p.intValue())); + primeexp.add(new Integer(ex)); + } + primindx++; + } + } + } /* Ifactor */ - /** Multiply with another positive integer. - * @param oth the second factor. - * @return the product of both numbers. - */ - public Ifactor multiply(final BigInteger oth) - { - /* the optimization is to factorize oth _before_ multiplying - */ - return( multiply(new Ifactor(oth)) ) ; - } /* Ifactor.multiply */ + /** + * Constructor given a list of exponents of the prime factor decomposition. + * + * @param pows + * the vector with the sorted list of exponents. + * pows[0] is the exponent of 2, pows[1] the exponent of 3, + * pows[2] the exponent of 5 etc. + * Note that this list does not include the primes, but assumes a + * continuous prime-smooth basis. + */ + public Ifactor(Vector pows) { + primeexp = new Vector(2 * pows.size()); + if (pows.size() > 0) { + n = BigInteger.ONE; + Prime primes = new Prime(); + /* + * Build the full number by the product of all powers of the primes. + */ + for (int primindx = 0; primindx < pows.size(); primindx++) { + int ex = pows.elementAt(primindx).intValue(); + final BigInteger p = primes.at(primindx); + n = n.multiply(p.pow(ex)); + primeexp.add(new Integer(p.intValue())); + primeexp.add(new Integer(ex)); + } + } else + n = BigInteger.ZERO; + } /* Ifactor */ - /** Multiply with another positive integer. - * @param oth the second factor. - * @return the product of both numbers. - */ - public Ifactor multiply(final int oth) - { - /* the optimization is to factorize oth _before_ multiplying - */ - return( multiply(new Ifactor(oth)) ) ; - } /* Ifactor.multiply */ + /** + * Copy constructor. + * + * @param oth + * the value to be copied + */ + public Ifactor(Ifactor oth) { + n = oth.n; + primeexp = oth.primeexp; + } /* Ifactor */ - /** Multiply with another positive integer. - * @param oth the second factor. - * @return the product of both numbers. - */ - public Ifactor multiply(final Ifactor oth) - { - /* This might be done similar to the lcm() implementation by adding - * the powers of the components and calling the constructor with the - * list of exponents. This here is the simplest implementation, but slow because - * it calls another prime factorization of the product: - * return( new Ifactor(n.multiply(oth.n))) ; - */ - return multGcdLcm(oth,0) ; - } + /** + * Deep copy. + * + * @since 2009-08-14 + */ + @Override + public Ifactor clone() { + /* + * Line not used: + * + * Vector p = (Vector)primeexp.clone(); + * + */ + Ifactor cl = new Ifactor(0); + cl.n = new BigInteger("" + n); + return cl; + } /* Ifactor.clone */ - /** Lowest common multiple of this with oth. - * @param oth the second parameter of lcm(this,oth) - * @return the lowest common multiple of both numbers. Returns zero - * if any of both arguments is zero. - */ - public Ifactor lcm(final Ifactor oth) - { - return multGcdLcm(oth,2) ; - } + /** + * Comparison of two numbers. + * The value of this method is in allowing the Vector<>.contains() calls + * that use the value, + * not the reference for comparison. + * + * @param oth + * the number to compare this with. + * @return true if both are the same numbers, false otherwise. + */ + public boolean equals(final Ifactor oth) { + return (n.compareTo(oth.n) == 0); + } /* Ifactor.equals */ - /** Greatest common divisor of this and oth. - * @param oth the second parameter of gcd(this,oth) - * @return the lowest common multiple of both numbers. Returns zero - * if any of both arguments is zero. - */ - public Ifactor gcd(final Ifactor oth) - { - return multGcdLcm(oth,1) ; - } + /** + * Multiply with another positive integer. + * + * @param oth + * the second factor. + * @return the product of both numbers. + */ + public Ifactor multiply(final BigInteger oth) { + /* + * the optimization is to factorize oth _before_ multiplying + */ + return (multiply(new Ifactor(oth))); + } /* Ifactor.multiply */ - /** Multiply with another positive integer. - * @param oth the second factor. - * @param type 0 to multiply, 1 for gcd, 2 for lcm - * @return the product, gcd or lcm of both numbers. - */ - protected Ifactor multGcdLcm(final Ifactor oth, int type) - { - Ifactor prod = new Ifactor(0) ; - /* skip the case where 0*something =0, falling thru to the empty representation for 0 - */ - if( primeexp.size() != 0 && oth.primeexp.size() != 0) - { - /* Cases of 1 times something return something. - * Cases of lcm(1, something) return something. - * Cases of gcd(1, something) return 1. - */ - if ( primeexp.firstElement().intValue() == 1 && type == 0) - return oth ; - else if ( primeexp.firstElement().intValue() == 1 && type == 2) - return oth ; - else if ( primeexp.firstElement().intValue() == 1 && type == 1) - return this ; - else if ( oth.primeexp.firstElement().intValue() == 1 && type ==0) - return this ; - else if ( oth.primeexp.firstElement().intValue() == 1 && type ==2) - return this ; - else if ( oth.primeexp.firstElement().intValue() == 1 && type ==1) - return oth ; - else - { - int idxThis = 0 ; - int idxOth = 0 ; - switch(type) - { - case 0 : - prod.n = n.multiply(oth.n) ; - break; - case 1 : - prod.n = n.gcd(oth.n) ; - break; - case 2 : - /* the awkward way, lcm = product divided by gcd - */ - prod.n = n.multiply(oth.n).divide( n.gcd(oth.n) ) ; - break; - } + /** + * Multiply with another positive integer. + * + * @param oth + * the second factor. + * @return the product of both numbers. + */ + public Ifactor multiply(final int oth) { + /* + * the optimization is to factorize oth _before_ multiplying + */ + return (multiply(new Ifactor(oth))); + } /* Ifactor.multiply */ - /* scan both representations left to right, increasing prime powers - */ - while( idxOth < oth.primeexp.size() || idxThis < primeexp.size() ) - { - if ( idxOth >= oth.primeexp.size() ) - { - /* exhausted the list in oth.primeexp; copy over the remaining 'this' - * if multiplying or lcm, discard if gcd. - */ - if ( type == 0 || type == 2) - { - prod.primeexp.add( primeexp.elementAt(idxThis) ) ; - prod.primeexp.add( primeexp.elementAt(idxThis+1) ) ; - } - idxThis += 2 ; - } - else if ( idxThis >= primeexp.size() ) - { - /* exhausted the list in primeexp; copy over the remaining 'oth' - */ - if ( type == 0 || type == 2) - { - prod.primeexp.add( oth.primeexp.elementAt(idxOth) ) ; - prod.primeexp.add( oth.primeexp.elementAt(idxOth+1) ) ; - } - idxOth += 2 ; - } - else - { - Integer p ; - int ex ; - switch ( primeexp.elementAt(idxThis).compareTo(oth.primeexp.elementAt(idxOth) ) ) - { - case 0 : - /* same prime bases p in both factors */ - p = primeexp.elementAt(idxThis) ; - switch(type) - { - case 0 : - /* product means adding exponents */ - ex = primeexp.elementAt(idxThis+1).intValue() + - oth.primeexp.elementAt(idxOth+1).intValue() ; - break; - case 1 : - /* gcd means minimum of exponents */ - ex = Math.min( primeexp.elementAt(idxThis+1).intValue() , - oth.primeexp.elementAt(idxOth+1).intValue()) ; - break; - default : - /* lcm means maximum of exponents */ - ex = Math.max( primeexp.elementAt(idxThis+1).intValue() , - oth.primeexp.elementAt(idxOth+1).intValue()) ; - break; - } - prod.primeexp.add( p ) ; - prod.primeexp.add( new Integer(ex) ) ; - idxOth += 2 ; - idxThis += 2 ; - break ; - case 1: - /* this prime base bigger than the other and taken later */ - if ( type == 0 || type == 2) - { - prod.primeexp.add( oth.primeexp.elementAt(idxOth) ) ; - prod.primeexp.add( oth.primeexp.elementAt(idxOth+1) ) ; - } - idxOth += 2 ; - break ; - default: - /* this prime base smaller than the other and taken now */ - if ( type == 0 || type == 2) - { - prod.primeexp.add( primeexp.elementAt(idxThis) ) ; - prod.primeexp.add( primeexp.elementAt(idxThis+1) ) ; - } - idxThis += 2 ; - } - } - } - } - } - return prod ; - } /* Ifactor.multGcdLcm */ + /** + * Multiply with another positive integer. + * + * @param oth + * the second factor. + * @return the product of both numbers. + */ + public Ifactor multiply(final Ifactor oth) { + /* + * This might be done similar to the lcm() implementation by adding + * the powers of the components and calling the constructor with the + * list of exponents. This here is the simplest implementation, but slow + * because + * it calls another prime factorization of the product: + * return( new Ifactor(n.multiply(oth.n))) ; + */ + return multGcdLcm(oth, 0); + } - /** Integer division through another positive integer. - * @param oth the denominator. - * @return the division of this through the oth, discarding the remainder. - */ - public Ifactor divide(final Ifactor oth) - { - /* todo: it'd probably be faster to cancel the gcd(this,oth) first in the prime power - * representation, which would avoid a more strenous factorization of the integer ratio - */ - return new Ifactor(n.divide(oth.n)) ; - } /* Ifactor.divide */ + /** + * Lowest common multiple of this with oth. + * + * @param oth + * the second parameter of lcm(this,oth) + * @return the lowest common multiple of both numbers. Returns zero + * if any of both arguments is zero. + */ + public Ifactor lcm(final Ifactor oth) { + return multGcdLcm(oth, 2); + } - /** Summation with another positive integer - * @param oth the other term. - * @return the sum of both numbers - */ - public Ifactor add(final BigInteger oth) - { - /* avoid refactorization if oth is zero... - */ - if ( oth.compareTo(BigInteger.ZERO) != 0 ) - return new Ifactor(n.add(oth)) ; - else - return this ; - } /* Ifactor.add */ + /** + * Greatest common divisor of this and oth. + * + * @param oth + * the second parameter of gcd(this,oth) + * @return the lowest common multiple of both numbers. Returns zero + * if any of both arguments is zero. + */ + public Ifactor gcd(final Ifactor oth) { + return multGcdLcm(oth, 1); + } - /** Exponentiation with a positive integer. - * @param exponent the non-negative exponent - * @return n^exponent. If exponent=0, the result is 1. - */ - public Ifactor pow(final int exponent) throws ArithmeticException - { - /* three simple cases first - */ - if ( exponent < 0 ) - throw new ArithmeticException("Cannot raise "+ toString() + " to negative " + exponent) ; - else if ( exponent == 0) - return new Ifactor(1) ; - else if ( exponent == 1) - return this ; + /** + * Multiply with another positive integer. + * + * @param oth + * the second factor. + * @param type + * 0 to multiply, 1 for gcd, 2 for lcm + * @return the product, gcd or lcm of both numbers. + */ + protected Ifactor multGcdLcm(final Ifactor oth, int type) { + Ifactor prod = new Ifactor(0); + /* + * skip the case where 0*something =0, falling thru to the empty + * representation for 0 + */ + if (primeexp.size() != 0 && oth.primeexp.size() != 0) { + /* + * Cases of 1 times something return something. + * Cases of lcm(1, something) return something. + * Cases of gcd(1, something) return 1. + */ + if (primeexp.firstElement().intValue() == 1 && type == 0) + return oth; + else if (primeexp.firstElement().intValue() == 1 && type == 2) + return oth; + else if (primeexp.firstElement().intValue() == 1 && type == 1) + return this; + else if (oth.primeexp.firstElement().intValue() == 1 && type == 0) + return this; + else if (oth.primeexp.firstElement().intValue() == 1 && type == 2) + return this; + else if (oth.primeexp.firstElement().intValue() == 1 && type == 1) + return oth; + else { + int idxThis = 0; + int idxOth = 0; + switch (type) { + case 0: + prod.n = n.multiply(oth.n); + break; + case 1: + prod.n = n.gcd(oth.n); + break; + case 2: + /* + * the awkward way, lcm = product divided by gcd + */ + prod.n = n.multiply(oth.n).divide(n.gcd(oth.n)); + break; + } - /* general case, the vector with the prime factor powers, which are component-wise - * exponentiation of the individual prime factor powers. - */ - Ifactor pows = new Ifactor(0) ; - for(int i=0 ; i < primeexp.size() ; i += 2) - { - Integer p = primeexp.elementAt(i) ; - int ex = primeexp.elementAt(i+1).intValue() ; - pows.primeexp.add( p ) ; - pows.primeexp.add( new Integer(ex*exponent) ) ; - } - return pows ; - } /* Ifactor.pow */ + /* + * scan both representations left to right, increasing prime + * powers + */ + while (idxOth < oth.primeexp.size() || idxThis < primeexp.size()) { + if (idxOth >= oth.primeexp.size()) { + /* + * exhausted the list in oth.primeexp; copy over the + * remaining 'this' + * if multiplying or lcm, discard if gcd. + */ + if (type == 0 || type == 2) { + prod.primeexp.add(primeexp.elementAt(idxThis)); + prod.primeexp.add(primeexp.elementAt(idxThis + 1)); + } + idxThis += 2; + } else if (idxThis >= primeexp.size()) { + /* + * exhausted the list in primeexp; copy over the + * remaining 'oth' + */ + if (type == 0 || type == 2) { + prod.primeexp.add(oth.primeexp.elementAt(idxOth)); + prod.primeexp.add(oth.primeexp.elementAt(idxOth + 1)); + } + idxOth += 2; + } else { + Integer p; + int ex; + switch (primeexp.elementAt(idxThis).compareTo(oth.primeexp.elementAt(idxOth))) { + case 0: + /* same prime bases p in both factors */ + p = primeexp.elementAt(idxThis); + switch (type) { + case 0: + /* product means adding exponents */ + ex = primeexp.elementAt(idxThis + 1).intValue() + oth.primeexp.elementAt(idxOth + 1).intValue(); + break; + case 1: + /* gcd means minimum of exponents */ + ex = Math.min(primeexp.elementAt(idxThis + 1).intValue(), oth.primeexp.elementAt(idxOth + 1).intValue()); + break; + default: + /* lcm means maximum of exponents */ + ex = Math.max(primeexp.elementAt(idxThis + 1).intValue(), oth.primeexp.elementAt(idxOth + 1).intValue()); + break; + } + prod.primeexp.add(p); + prod.primeexp.add(new Integer(ex)); + idxOth += 2; + idxThis += 2; + break; + case 1: + /* + * this prime base bigger than the other and + * taken later + */ + if (type == 0 || type == 2) { + prod.primeexp.add(oth.primeexp.elementAt(idxOth)); + prod.primeexp.add(oth.primeexp.elementAt(idxOth + 1)); + } + idxOth += 2; + break; + default: + /* + * this prime base smaller than the other and + * taken now + */ + if (type == 0 || type == 2) { + prod.primeexp.add(primeexp.elementAt(idxThis)); + prod.primeexp.add(primeexp.elementAt(idxThis + 1)); + } + idxThis += 2; + } + } + } + } + } + return prod; + } /* Ifactor.multGcdLcm */ - /** Pulling the r-th root. - * @param r the positive or negative (nonzero) root. - * @return n^(1/r). - * The return value falls into the Ifactor class if r is positive, but if r is negative - * a Rational type is needed. - * @throws Errore - * @since 2009-05-18 - */ - public Rational root(final int r) throws ArithmeticException, Errore - { - if ( r == 0 ) - throw new ArithmeticException("Cannot pull zeroth root of "+ toString()) ; - else if ( r < 0 ) - { - /* a^(-1/b)= 1/(a^(1/b)) - */ - final Rational invRoot = root(-r) ; - return Rational.ONE.divide(invRoot) ; - } - else - { - BigInteger pows = BigInteger.ONE ; - for(int i=0 ; i < primeexp.size() ; i += 2) - { - /* all exponents must be multiples of r to succeed (that is, to - * stay in the range of rational results). - */ - int ex = primeexp.elementAt(i+1).intValue() ; - if ( ex % r != 0 ) - throw new ArithmeticException("Cannot pull "+ r+"th root of "+ toString()) ; + /** + * Integer division through another positive integer. + * + * @param oth + * the denominator. + * @return the division of this through the oth, discarding the remainder. + */ + public Ifactor divide(final Ifactor oth) { + /* + * todo: it'd probably be faster to cancel the gcd(this,oth) first in + * the prime power + * representation, which would avoid a more strenous factorization of + * the integer ratio + */ + return new Ifactor(n.divide(oth.n)); + } /* Ifactor.divide */ - pows.multiply( new BigInteger(""+primeexp.elementAt(i)).pow(ex/r) ) ; - } - /* convert result to a Rational; unfortunately this will loose the prime factorization */ - return new Rational(pows) ; - } - } /* Ifactor.root */ + /** + * Summation with another positive integer + * + * @param oth + * the other term. + * @return the sum of both numbers + */ + public Ifactor add(final BigInteger oth) { + /* + * avoid refactorization if oth is zero... + */ + if (oth.compareTo(BigInteger.ZERO) != 0) + return new Ifactor(n.add(oth)); + else + return this; + } /* Ifactor.add */ + /** + * Exponentiation with a positive integer. + * + * @param exponent + * the non-negative exponent + * @return n^exponent. If exponent=0, the result is 1. + */ + public Ifactor pow(final int exponent) throws ArithmeticException { + /* + * three simple cases first + */ + if (exponent < 0) + throw new ArithmeticException("Cannot raise " + toString() + " to negative " + exponent); + else if (exponent == 0) + return new Ifactor(1); + else if (exponent == 1) + return this; - /** The set of positive divisors. - * @return the vector of divisors of the absolute value, sorted. - * @since 2010-08-27 - */ - public Vector divisors() - { - /* Recursive approach: the divisors of p1^e1*p2^e2*..*py^ey*pz^ez are - * the divisors that don't contain the factor pz, and the - * the divisors that contain any power of pz between 1 and up to ez multiplied - * by 1 or by a product that contains the factors p1..py. - */ - Vector d=new Vector() ; - if ( n.compareTo(BigInteger.ZERO) == 0 ) - return d ; - d.add(BigInteger.ONE) ; - if ( n.compareTo(BigInteger.ONE) > 0 ) - { - /* Computes sigmaIncopml(p1^e*p2^e2...*py^ey) */ - Ifactor dp = dropPrime() ; + /* + * general case, the vector with the prime factor powers, which are + * component-wise + * exponentiation of the individual prime factor powers. + */ + Ifactor pows = new Ifactor(0); + for (int i = 0; i < primeexp.size(); i += 2) { + Integer p = primeexp.elementAt(i); + int ex = primeexp.elementAt(i + 1).intValue(); + pows.primeexp.add(p); + pows.primeexp.add(new Integer(ex * exponent)); + } + return pows; + } /* Ifactor.pow */ - /* get ez */ - final int ez = primeexp.lastElement().intValue() ; + /** + * Pulling the r-th root. + * + * @param r + * the positive or negative (nonzero) root. + * @return n^(1/r). + * The return value falls into the Ifactor class if r is positive, + * but if r is negative + * a Rational type is needed. + * @throws Errore + * @since 2009-05-18 + */ + public Rational root(final int r) throws ArithmeticException, Errore { + if (r == 0) + throw new ArithmeticException("Cannot pull zeroth root of " + toString()); + else if (r < 0) { + /* + * a^(-1/b)= 1/(a^(1/b)) + */ + final Rational invRoot = root(-r); + return Rational.ONE.divide(invRoot); + } else { + BigInteger pows = BigInteger.ONE; + for (int i = 0; i < primeexp.size(); i += 2) { + /* + * all exponents must be multiples of r to succeed (that is, to + * stay in the range of rational results). + */ + int ex = primeexp.elementAt(i + 1).intValue(); + if (ex % r != 0) + throw new ArithmeticException("Cannot pull " + r + "th root of " + toString()); - Vector partd = dp.divisors() ; + pows.multiply(new BigInteger("" + primeexp.elementAt(i)).pow(ex / r)); + } + /* + * convert result to a Rational; unfortunately this will loose the + * prime factorization + */ + return new Rational(pows); + } + } /* Ifactor.root */ - /* obtain pz by lookup in the prime list */ - final BigInteger pz = new BigInteger( primeexp.elementAt(primeexp.size()-2).toString()) ; + /** + * The set of positive divisors. + * + * @return the vector of divisors of the absolute value, sorted. + * @since 2010-08-27 + */ + public Vector divisors() { + /* + * Recursive approach: the divisors of p1^e1*p2^e2*..*py^ey*pz^ez are + * the divisors that don't contain the factor pz, and the + * the divisors that contain any power of pz between 1 and up to ez + * multiplied + * by 1 or by a product that contains the factors p1..py. + */ + Vector d = new Vector(); + if (n.compareTo(BigInteger.ZERO) == 0) + return d; + d.add(BigInteger.ONE); + if (n.compareTo(BigInteger.ONE) > 0) { + /* Computes sigmaIncopml(p1^e*p2^e2...*py^ey) */ + Ifactor dp = dropPrime(); - /* the output contains all products of the form partd[]*pz^ez, ez>0, - * and with the exception of the 1, all these are appended. - */ - for(int i =1 ; i < partd.size() ; i++) - d.add( partd.elementAt(i) ) ; - for(int e =1 ; e <= ez ; e++) - { - final BigInteger pzez = pz.pow(e) ; - for(int i =0 ; i < partd.size() ; i++) - d.add( partd.elementAt(i).multiply(pzez) ) ; - } - } - Collections.sort(d) ; - return d ; - } /* Ifactor.divisors */ + /* get ez */ + final int ez = primeexp.lastElement().intValue(); - /** Sum of the divisors of the number. - * @return the sum of all divisors of the number, 1+....+n. - */ - public Ifactor sigma() - { - return sigma(1) ; - } /* Ifactor.sigma */ + Vector partd = dp.divisors(); - /** Sum of the k-th powers of divisors of the number. - * @return the sum of all divisors of the number, 1^k+....+n^k. - */ - public Ifactor sigma(int k) - { - /* the question is whether keeping a factorization is worth the effort - * or whether one should simply multiply these to return a BigInteger... - */ - if( n.compareTo(BigInteger.ONE) == 0 ) - return ONE ; - else if( n.compareTo(BigInteger.ZERO) == 0 ) - return ZERO ; - else - { - /* multiplicative: sigma_k(p^e) = [p^(k*(e+1))-1]/[p^k-1] - * sigma_0(p^e) = e+1. - */ - Ifactor resul = Ifactor.ONE ; - for(int i=0 ; i < primeexp.size() ; i += 2) - { - int ex = primeexp.elementAt(i+1).intValue() ; - if ( k == 0 ) - resul = resul.multiply(ex+1) ; - else - { - Integer p = primeexp.elementAt(i) ; - BigInteger num = (new BigInteger(p.toString())).pow(k*(ex+1)).subtract(BigInteger.ONE) ; - BigInteger deno = (new BigInteger(p.toString())).pow(k).subtract(BigInteger.ONE) ; - /* This division is of course exact, no remainder - * The costly prime factorization is hidden here. - */ - Ifactor f = new Ifactor(num.divide(deno)) ; - resul = resul.multiply(f) ; - } - } - return resul ; - } - } /* Ifactor.sigma */ + /* obtain pz by lookup in the prime list */ + final BigInteger pz = new BigInteger(primeexp.elementAt(primeexp.size() - 2).toString()); - /** Divide through the highest possible power of the highest prime. - * If the current number is the prime factor product p1^e1 * p2*e2* p3^e3*...*py^ey * pz^ez, - * the value returned has the final factor pz^ez eliminated, which gives - * p1^e1 * p2*e2* p3^e3*...*py^ey. - * @return the new integer obtained by removing the highest prime power. - * If this here represents 0 or 1, it is returned without change. - * @since 2006-08-20 - */ - public Ifactor dropPrime() - { - /* the cases n==1 or n ==0 - */ - if ( n.compareTo(BigInteger.ONE) <= 0 ) - return this ; + /* + * the output contains all products of the form partd[]*pz^ez, ez>0, + * and with the exception of the 1, all these are appended. + */ + for (int i = 1; i < partd.size(); i++) + d.add(partd.elementAt(i)); + for (int e = 1; e <= ez; e++) { + final BigInteger pzez = pz.pow(e); + for (int i = 0; i < partd.size(); i++) + d.add(partd.elementAt(i).multiply(pzez)); + } + } + Collections.sort(d); + return d; + } /* Ifactor.divisors */ - /* The cases n>1 - * Start empty. Copy all but the last factor over to the result - * the vector with the new prime factor powers, which contain the - * old prime factor powers up to but not including the last one. - */ - Ifactor pows=new Ifactor(0) ; - pows.n = BigInteger.ONE ; - for(int i = 0 ; i < primeexp.size()-2 ; i += 2) - { - pows.primeexp.add( primeexp.elementAt(i)) ; - pows.primeexp.add( primeexp.elementAt(i+1)) ; - BigInteger p = new BigInteger( primeexp.elementAt(i).toString() ) ; - int ex = primeexp.elementAt(i+1).intValue() ; - pows.n = pows.n.multiply( p.pow(ex) ) ; - } - return pows ; - } /* Ifactor.dropPrime */ + /** + * Sum of the divisors of the number. + * + * @return the sum of all divisors of the number, 1+....+n. + */ + public Ifactor sigma() { + return sigma(1); + } /* Ifactor.sigma */ - /** Test whether this is a square of an integer (perfect square). - * @return true if this is an integer squared (including 0), else false - */ - public boolean issquare() - { - /* check the exponents, located at the odd-indexed positions - */ - for(int i=1 ; i < primeexp.size() ; i += 2) - { - if ( primeexp.elementAt(i).intValue() % 2 != 0) - return false ; - } - return true ; - } /* Ifactor.issquare */ + /** + * Sum of the k-th powers of divisors of the number. + * + * @return the sum of all divisors of the number, 1^k+....+n^k. + */ + public Ifactor sigma(int k) { + /* + * the question is whether keeping a factorization is worth the effort + * or whether one should simply multiply these to return a BigInteger... + */ + if (n.compareTo(BigInteger.ONE) == 0) + return ONE; + else if (n.compareTo(BigInteger.ZERO) == 0) + return ZERO; + else { + /* + * multiplicative: sigma_k(p^e) = [p^(k*(e+1))-1]/[p^k-1] + * sigma_0(p^e) = e+1. + */ + Ifactor resul = Ifactor.ONE; + for (int i = 0; i < primeexp.size(); i += 2) { + int ex = primeexp.elementAt(i + 1).intValue(); + if (k == 0) + resul = resul.multiply(ex + 1); + else { + Integer p = primeexp.elementAt(i); + BigInteger num = (new BigInteger(p.toString())).pow(k * (ex + 1)).subtract(BigInteger.ONE); + BigInteger deno = (new BigInteger(p.toString())).pow(k).subtract(BigInteger.ONE); + /* + * This division is of course exact, no remainder + * The costly prime factorization is hidden here. + */ + Ifactor f = new Ifactor(num.divide(deno)); + resul = resul.multiply(f); + } + } + return resul; + } + } /* Ifactor.sigma */ - /** The sum of the prime factor exponents, with multiplicity. - * @return the sum over the primeexp numbers - */ - public int bigomega() - { - int resul= 0 ; - for(int i=1 ; i < primeexp.size() ; i += 2) - resul += primeexp.elementAt(i).intValue() ; - return(resul) ; - } /* Ifactor.bigomega */ + /** + * Divide through the highest possible power of the highest prime. + * If the current number is the prime factor product p1^e1 * p2*e2* + * p3^e3*...*py^ey * pz^ez, + * the value returned has the final factor pz^ez eliminated, which gives + * p1^e1 * p2*e2* p3^e3*...*py^ey. + * + * @return the new integer obtained by removing the highest prime power. + * If this here represents 0 or 1, it is returned without change. + * @since 2006-08-20 + */ + public Ifactor dropPrime() { + /* + * the cases n==1 or n ==0 + */ + if (n.compareTo(BigInteger.ONE) <= 0) + return this; - /** The sum of the prime factor exponents, without multiplicity. - * @return the number of distinct prime factors. - * @since 2008-10-16 - */ - public int omega() - { - return primeexp.size()/2 ; - } /* Ifactor.omega */ + /* + * The cases n>1 + * Start empty. Copy all but the last factor over to the result + * the vector with the new prime factor powers, which contain the + * old prime factor powers up to but not including the last one. + */ + Ifactor pows = new Ifactor(0); + pows.n = BigInteger.ONE; + for (int i = 0; i < primeexp.size() - 2; i += 2) { + pows.primeexp.add(primeexp.elementAt(i)); + pows.primeexp.add(primeexp.elementAt(i + 1)); + BigInteger p = new BigInteger(primeexp.elementAt(i).toString()); + int ex = primeexp.elementAt(i + 1).intValue(); + pows.n = pows.n.multiply(p.pow(ex)); + } + return pows; + } /* Ifactor.dropPrime */ - /** The square-free part. - * @return the minimum m such that m times this number is a square. - * @since 2008-10-16 - */ - public BigInteger core() - { - BigInteger resul = BigInteger.ONE ; - for(int i=0 ; i < primeexp.size() ; i += 2) - if ( primeexp.elementAt(i+1).intValue() % 2 != 0) - resul = resul.multiply( new BigInteger(primeexp.elementAt(i).toString()) ); - return resul ; - } /* Ifactor.core */ + /** + * Test whether this is a square of an integer (perfect square). + * + * @return true if this is an integer squared (including 0), else false + */ + public boolean issquare() { + /* + * check the exponents, located at the odd-indexed positions + */ + for (int i = 1; i < primeexp.size(); i += 2) { + if (primeexp.elementAt(i).intValue() % 2 != 0) + return false; + } + return true; + } /* Ifactor.issquare */ - /** The Moebius function. - * 1 if n=1, else, if k is the number of distinct prime factors, return (-1)^k, - * else, if k has repeated prime factors, return 0. - * @return the moebius function. - */ - public int moebius() - { - if( n.compareTo(BigInteger.ONE) <= 0 ) - return 1 ; - /* accumulate number of different primes in k */ - int k=1 ; - for(int i=0 ; i < primeexp.size() ; i += 2) - { - final int e = primeexp.elementAt(i+1).intValue() ; - if ( e > 1 ) - return 0 ; - else if ( e == 1) - /* accumulates (-1)^k */ - k *= -1 ; - - } - return( k ) ; - } /* Ifactor.moebius */ + /** + * The sum of the prime factor exponents, with multiplicity. + * + * @return the sum over the primeexp numbers + */ + public int bigomega() { + int resul = 0; + for (int i = 1; i < primeexp.size(); i += 2) + resul += primeexp.elementAt(i).intValue(); + return (resul); + } /* Ifactor.bigomega */ - /** Maximum of two values. - * @param oth the number to compare this with. - * @return the larger of the two values. - */ - public Ifactor max(final Ifactor oth) - { - if( n.compareTo(oth.n) >= 0 ) - return this ; - else - return oth ; - } /* Ifactor.max */ + /** + * The sum of the prime factor exponents, without multiplicity. + * + * @return the number of distinct prime factors. + * @since 2008-10-16 + */ + public int omega() { + return primeexp.size() / 2; + } /* Ifactor.omega */ - /** Minimum of two values. - * @param oth the number to compare this with. - * @return the smaller of the two values. - */ - public Ifactor min(final Ifactor oth) - { - if( n.compareTo(oth.n) <= 0 ) - return this ; - else - return oth ; - } /* Ifactor.min */ + /** + * The square-free part. + * + * @return the minimum m such that m times this number is a square. + * @since 2008-10-16 + */ + public BigInteger core() { + BigInteger resul = BigInteger.ONE; + for (int i = 0; i < primeexp.size(); i += 2) + if (primeexp.elementAt(i + 1).intValue() % 2 != 0) + resul = resul.multiply(new BigInteger(primeexp.elementAt(i).toString())); + return resul; + } /* Ifactor.core */ - /** Maximum of a list of values. - * @param set list of numbers. - * @return the largest in the list. - */ - public static Ifactor max(final Vector set) - { - Ifactor resul = set.elementAt(0) ; - for(int i=1; i < set.size() ; i++) - resul = resul.max(set.elementAt(i)) ; - return resul ; - } /* Ifactor.max */ + /** + * The Moebius function. + * 1 if n=1, else, if k is the number of distinct prime factors, return + * (-1)^k, + * else, if k has repeated prime factors, return 0. + * + * @return the moebius function. + */ + public int moebius() { + if (n.compareTo(BigInteger.ONE) <= 0) + return 1; + /* accumulate number of different primes in k */ + int k = 1; + for (int i = 0; i < primeexp.size(); i += 2) { + final int e = primeexp.elementAt(i + 1).intValue(); + if (e > 1) + return 0; + else if (e == 1) + /* accumulates (-1)^k */ + k *= -1; - /** Minimum of a list of values. - * @param set list of numbers. - * @return the smallest in the list. - */ - public static Ifactor min(final Vector set) - { - Ifactor resul = set.elementAt(0) ; - for(int i=1; i < set.size() ; i++) - resul = resul.min(set.elementAt(i)) ; - return resul ; - } /* Ifactor.min */ + } + return (k); + } /* Ifactor.moebius */ - /** Compare value against another Ifactor - * @param oth The value to be compared agains. - * @return 1, 0 or -1 according to being larger, equal to or smaller than oth. - * @since 2012-02-15 - */ - public int compareTo( final Ifactor oth) - { - return n.compareTo(oth.n) ; - } /* compareTo */ + /** + * Maximum of two values. + * + * @param oth + * the number to compare this with. + * @return the larger of the two values. + */ + public Ifactor max(final Ifactor oth) { + if (n.compareTo(oth.n) >= 0) + return this; + else + return oth; + } /* Ifactor.max */ - /** Convert to printable format - * @return a string of the form n:prime^pow*prime^pow*prime^pow... - */ - public String toString() - { - String resul = new String(n.toString()+":") ; - if ( n.compareTo(BigInteger.ONE) == 0 ) - resul += "1" ; - else - { - boolean firstMul = true ; - for(int i=0 ; i < primeexp.size() ; i += 2) - { - if ( ! firstMul) - resul += "*" ; - if ( primeexp.elementAt(i+1).intValue() > 1 ) - resul += primeexp.elementAt(i).toString()+"^"+primeexp.elementAt(i+1).toString() ; - else - resul += primeexp.elementAt(i).toString() ; - firstMul = false ; - } - } - return resul ; - } /* Ifactor.toString */ + /** + * Minimum of two values. + * + * @param oth + * the number to compare this with. + * @return the smaller of the two values. + */ + public Ifactor min(final Ifactor oth) { + if (n.compareTo(oth.n) <= 0) + return this; + else + return oth; + } /* Ifactor.min */ - /** Test program. - * It takes a single argument n and prints the integer factorizaton.
- * java -cp . org.nevec.rjm.Ifactor n
- */ - public static void main(String[] args) throws Exception - { - BigInteger n = new BigInteger(args[0]) ; - System.out.println( new Ifactor(n)) ; - } /* Ifactor.main */ + /** + * Maximum of a list of values. + * + * @param set + * list of numbers. + * @return the largest in the list. + */ + public static Ifactor max(final Vector set) { + Ifactor resul = set.elementAt(0); + for (int i = 1; i < set.size(); i++) + resul = resul.max(set.elementAt(i)); + return resul; + } /* Ifactor.max */ + + /** + * Minimum of a list of values. + * + * @param set + * list of numbers. + * @return the smallest in the list. + */ + public static Ifactor min(final Vector set) { + Ifactor resul = set.elementAt(0); + for (int i = 1; i < set.size(); i++) + resul = resul.min(set.elementAt(i)); + return resul; + } /* Ifactor.min */ + + /** + * Compare value against another Ifactor + * + * @param oth + * The value to be compared agains. + * @return 1, 0 or -1 according to being larger, equal to or smaller than + * oth. + * @since 2012-02-15 + */ + @Override + public int compareTo(final Ifactor oth) { + return n.compareTo(oth.n); + } /* compareTo */ + + /** + * Convert to printable format + * + * @return a string of the form n:prime^pow*prime^pow*prime^pow... + */ + @Override + public String toString() { + String resul = new String(n.toString() + ":"); + if (n.compareTo(BigInteger.ONE) == 0) + resul += "1"; + else { + boolean firstMul = true; + for (int i = 0; i < primeexp.size(); i += 2) { + if (!firstMul) + resul += "*"; + if (primeexp.elementAt(i + 1).intValue() > 1) + resul += primeexp.elementAt(i).toString() + "^" + primeexp.elementAt(i + 1).toString(); + else + resul += primeexp.elementAt(i).toString(); + firstMul = false; + } + } + return resul; + } /* Ifactor.toString */ + + /** + * Test program. + * It takes a single argument n and prints the integer factorizaton.
+ * java -cp . org.nevec.rjm.Ifactor n
+ */ + public static void main(String[] args) throws Exception { + BigInteger n = new BigInteger(args[0]); + System.out.println(new Ifactor(n)); + } /* Ifactor.main */ } /* Ifactor */ diff --git a/src/org/nevec/rjm/NumeroAvanzato.java b/src/org/nevec/rjm/NumeroAvanzato.java index dc5240ba..392a8c13 100644 --- a/src/org/nevec/rjm/NumeroAvanzato.java +++ b/src/org/nevec/rjm/NumeroAvanzato.java @@ -5,10 +5,8 @@ import java.math.BigInteger; import java.math.MathContext; import java.security.ProviderException; -import org.warpgate.pi.calculator.Errore; -import org.warpgate.pi.calculator.Incognita; -import org.warpgate.pi.calculator.Incognite; -import org.warpgate.pi.calculator.Utils; +import org.warp.picalculator.Errore; +import org.warp.picalculator.Incognite; /** * Square roots on the real line. These represent numbers which are a product of @@ -67,7 +65,7 @@ public class NumeroAvanzato implements Cloneable { * the prefactor. * @param b * the discriminant. - * @throws Errore + * @throws Errore * @since 2011-02-12 */ public NumeroAvanzato(Rational a, Rational b) { @@ -83,7 +81,7 @@ public class NumeroAvanzato implements Cloneable { incognitez = new Incognite(); try { normalize(); - normalizeG(); + normalizeG(); } catch (Errore e) { e.printStackTrace(); } @@ -101,7 +99,7 @@ public class NumeroAvanzato implements Cloneable { incognitey = y; incognitez = z; try { - normalize(); + normalize(); normalizeG(); } catch (Errore e) { e.printStackTrace(); @@ -143,6 +141,7 @@ public class NumeroAvanzato implements Cloneable { * * @since 2011-02-12 */ + @Override public NumeroAvanzato clone() { Rational fclon = pref.clone(); Rational dclon = disc.clone(); @@ -190,8 +189,7 @@ public class NumeroAvanzato implements Cloneable { * @since 2011-02-12 */ public NumeroAvanzato multiply(final NumeroAvanzato val) { - return new NumeroAvanzato(pref.multiply(val.pref), disc.multiply(val.disc), incognitex.multiply(val.incognitex), - incognitey.multiply(val.incognitey), incognitez.multiply(val.incognitez)); + return new NumeroAvanzato(pref.multiply(val.pref), disc.multiply(val.disc), incognitex.multiply(val.incognitex), incognitey.multiply(val.incognitey), incognitez.multiply(val.incognitez)); } /* NumeroAvanzato.multiply */ /** @@ -235,7 +233,7 @@ public class NumeroAvanzato implements Cloneable { * Compute the square. * * @return this value squared. - * @throws Errore + * @throws Errore * @since 2011-02-12 */ public NumeroAvanzato pow2() throws Errore { @@ -259,7 +257,7 @@ public class NumeroAvanzato implements Cloneable { * @param val * A second number of this type. * @return The value of this/val - * @throws Errore + * @throws Errore * @since 2011-02-12 */ public NumeroAvanzato divide(final NumeroAvanzato val) throws Errore { @@ -283,7 +281,7 @@ public class NumeroAvanzato implements Cloneable { * @param val * a second number. * @return the value of this/val - * @throws Errore + * @throws Errore * @since 2011-02-12 */ public NumeroAvanzato divide(final BigInteger val) throws Errore { @@ -298,7 +296,7 @@ public class NumeroAvanzato implements Cloneable { * @param val * A second number. * @return The value of this/val - * @throws Errore + * @throws Errore * @since 2011-02-12 */ public NumeroAvanzato divide(int val) throws Errore { @@ -340,7 +338,7 @@ public class NumeroAvanzato implements Cloneable { * the other constant to compare with * @return -1, 0 or 1 if this number is numerically less than, equal to, or * greater than val. - * @throws Errore + * @throws Errore * @since 2011-02-12 */ public int signumComparedTo(final NumeroAvanzato val) throws Errore { @@ -388,6 +386,7 @@ public class NumeroAvanzato implements Cloneable { * @return the human-readable version in base 10 * @since 2011-02-12 */ + @Override public String toString() { try { return toFancyString(); @@ -396,11 +395,12 @@ public class NumeroAvanzato implements Cloneable { e.printStackTrace(); } /* - if (disc.compareTo(Rational.ONE) != 0 && disc.compareTo(Rational.ZERO) != 0) - return ("(" + pref.toString() + ")*(" + disc.toString() + ")^(1/2)"); - else - return pref.toString(); - */ + * if (disc.compareTo(Rational.ONE) != 0 && + * disc.compareTo(Rational.ZERO) != 0) + * return ("(" + pref.toString() + ")*(" + disc.toString() + ")^(1/2)"); + * else + * return pref.toString(); + */ return "err"; } /* NumeroAvanzato.toString */ @@ -435,7 +435,8 @@ public class NumeroAvanzato implements Cloneable { /** * True if the value is integer. Equivalent to the indication whether a * conversion to an integer can be exact. - * @param hasBigIntegerVariables + * + * @param hasBigIntegerVariables * * @since 2011-02-12 */ @@ -464,16 +465,16 @@ public class NumeroAvanzato implements Cloneable { return false; } } - + if (this.pref.b.compareTo(new BigInteger("10000")) > 0) { return false; } return true; } return false; - + } /* NumeroAvanzato.isRational */ - + public boolean isTooPreciseRational(boolean hasRationalVariables) { if (disc.signum() == 0 || disc.compareTo(new Rational(1, 1)) == 0) { if (incognitex.count() > 0) { @@ -483,7 +484,7 @@ public class NumeroAvanzato implements Cloneable { return false; } } - + if (this.pref.b.compareTo(new BigInteger("10000")) > 0) { return true; } @@ -494,7 +495,8 @@ public class NumeroAvanzato implements Cloneable { /** * Convert to a rational value if possible - * @throws Errore + * + * @throws Errore * * @since 2012-02-15 */ @@ -511,7 +513,7 @@ public class NumeroAvanzato implements Cloneable { else throw new ArithmeticException("Undefined conversion " + toFancyString() + " to Rational."); } - + /** * The sign: 1 if the number is >0, 0 if ==0, -1 if <0 * @@ -529,7 +531,8 @@ public class NumeroAvanzato implements Cloneable { /** * Normalize to squarefree discriminant. - * @throws Errore + * + * @throws Errore * * @since 2011-02-12 */ @@ -567,23 +570,23 @@ public class NumeroAvanzato implements Cloneable { } disc = new Rational(numC, denC); - + if (disc.b.compareTo(BigInteger.ZERO) == 0 || disc.a.compareTo(BigInteger.ZERO) == 0 || pref.a.compareTo(BigInteger.ZERO) == 0) { incognitex = new Incognite(); incognitey = new Incognite(); } - + if (disc.b.compareTo(BigInteger.ZERO) == 0 || pref.b.compareTo(BigInteger.ZERO) == 0) { incognitez = new Incognite(); } - + if (incognitey.compareTo(incognitez)) { incognitey = new Incognite(); incognitez = new Incognite(); } - + /**/ - Incognite[] incognitetemp = new Incognite[]{incognitex, incognitey, incognitez}; + Incognite[] incognitetemp = new Incognite[] { incognitex, incognitey, incognitez }; incognitetemp = Incognite.normalizeBigSurdVariables(incognitetemp); incognitex = incognitetemp[0]; incognitey = incognitetemp[1]; @@ -600,7 +603,8 @@ public class NumeroAvanzato implements Cloneable { /** * Normalize to coprime numerator and denominator in prefactor and * discriminant - * @throws Errore + * + * @throws Errore * * @since 2011-02-12 */ diff --git a/src/org/nevec/rjm/NumeroAvanzatoVec.java b/src/org/nevec/rjm/NumeroAvanzatoVec.java index 6fd6568e..114a53bf 100644 --- a/src/org/nevec/rjm/NumeroAvanzatoVec.java +++ b/src/org/nevec/rjm/NumeroAvanzatoVec.java @@ -6,15 +6,17 @@ import java.math.MathContext; import java.util.Comparator; import java.util.Vector; -import org.warpgate.pi.calculator.Errore; -import org.warpgate.pi.calculator.Incognita; -import org.warpgate.pi.calculator.Incognite; -import org.warpgate.pi.calculator.Utils; +import org.warp.picalculator.Errore; +import org.warp.picalculator.Incognite; +import org.warp.picalculator.Utils; /** - * A NumeroAvanzatoVec represents an algebraic sum or differences of values which each - * term an instance of NumeroAvanzato. This mainly means that sums or differences of - * two NumeroAvanzato (or two NumeroAvanzatoVec) can be represented (exactly) as a NumeroAvanzatoVec. + * A NumeroAvanzatoVec represents an algebraic sum or differences of values + * which each + * term an instance of NumeroAvanzato. This mainly means that sums or + * differences of + * two NumeroAvanzato (or two NumeroAvanzatoVec) can be represented (exactly) as + * a NumeroAvanzatoVec. * * @since 2012-02-15 * @author Richard J. Mathar @@ -31,7 +33,8 @@ public class NumeroAvanzatoVec implements Comparable { public static final NumeroAvanzatoVec ONE = new NumeroAvanzatoVec(NumeroAvanzato.ONE); /** - * Internal representation: Each term as a single NumeroAvanzato. The value zero is + * Internal representation: Each term as a single NumeroAvanzato. The value + * zero is * represented by an empty vector. */ Vector terms; @@ -80,7 +83,8 @@ public class NumeroAvanzatoVec implements Comparable { /** * Combine terms that can be written as a single surd. This unites for * example the terms sqrt(90) and sqrt(10) to 4*sqrt(10). - * @throws Errore + * + * @throws Errore * * @since 2012-02-15 */ @@ -129,15 +133,15 @@ public class NumeroAvanzatoVec implements Comparable { if (!merged) newter.add(todo); } - + newter.sort(new Comparator() { - @Override - public int compare(NumeroAvanzato o1, NumeroAvanzato o2) { - int index1 = Incognite.priorità(o1.getIncognitex().sqrt().multiply(o1.getIncognitey()).divide(o1.getIncognitez())); - int index2 = Incognite.priorità(o2.getIncognitex().sqrt().multiply(o2.getIncognitey()).divide(o2.getIncognitez())); - return index2-index1; - } - }); + @Override + public int compare(NumeroAvanzato o1, NumeroAvanzato o2) { + int index1 = Incognite.priorità(o1.getIncognitex().sqrt().multiply(o1.getIncognitey()).divide(o1.getIncognitez())); + int index2 = Incognite.priorità(o2.getIncognitex().sqrt().multiply(o2.getIncognitey()).divide(o2.getIncognitez())); + return index2 - index1; + } + }); /* overwrite old version */ terms = newter; @@ -152,6 +156,7 @@ public class NumeroAvanzatoVec implements Comparable { * @return 0 or +-1. * @since 2012-02-15 */ + @Override public int compareTo(NumeroAvanzatoVec oth) { NumeroAvanzatoVec diff; try { @@ -168,7 +173,7 @@ public class NumeroAvanzatoVec implements Comparable { * equal to or larger than zero. * * @return 0 or +-1. - * @throws Errore + * @throws Errore * @since 2012-02-15 */ public int signum() throws Errore { @@ -180,7 +185,8 @@ public class NumeroAvanzatoVec implements Comparable { return 0; /* - * if there is one term: forward to the signum function of NumeroAvanzato + * if there is one term: forward to the signum function of + * NumeroAvanzato */ if (terms.size() == 1) return terms.firstElement().signum(); @@ -219,12 +225,14 @@ public class NumeroAvanzatoVec implements Comparable { else lhs = new NumeroAvanzatoVec(terms.elementAt(0), terms.elementAt(2)); lhs = lhs.sqr(); - /* - * Strange line: this line isn't used, but it's present in this code! + /* + * Strange line: this line isn't used, but it's present in this + * code! * * * - NumeroAvanzato rhs = new NumeroAvanzato(terms.elementAt(offsig).sqr(), Rational.ONE); + * NumeroAvanzato rhs = new + * NumeroAvanzato(terms.elementAt(offsig).sqr(), Rational.ONE); * * * @@ -317,7 +325,7 @@ public class NumeroAvanzatoVec implements Comparable { * @param val * The value to be added to this. * @return The new value representing this+val. - * @throws Errore + * @throws Errore */ public NumeroAvanzatoVec add(final NumeroAvanzatoVec val) throws Errore { NumeroAvanzatoVec sum = new NumeroAvanzatoVec(); @@ -344,7 +352,7 @@ public class NumeroAvanzatoVec implements Comparable { * @param val * The value to be added to this. * @return The new value representing this+val. - * @throws Errore + * @throws Errore */ public NumeroAvanzatoVec add(final NumeroAvanzato val) throws Errore { NumeroAvanzatoVec sum = new NumeroAvanzatoVec(); @@ -363,7 +371,7 @@ public class NumeroAvanzatoVec implements Comparable { * @param val * The value to be subtracted from this. * @return The new value representing this-val. - * @throws Errore + * @throws Errore */ public NumeroAvanzatoVec subtract(final NumeroAvanzatoVec val) throws Errore { NumeroAvanzatoVec sum = new NumeroAvanzatoVec(); @@ -383,7 +391,7 @@ public class NumeroAvanzatoVec implements Comparable { * @param val * The value to be subtracted from this. * @return The new value representing this-val. - * @throws Errore + * @throws Errore */ public NumeroAvanzatoVec subtract(final NumeroAvanzato val) throws Errore { NumeroAvanzatoVec sum = new NumeroAvanzatoVec(); @@ -420,7 +428,7 @@ public class NumeroAvanzatoVec implements Comparable { * Compute the square. * * @return this value squared. - * @throws Errore + * @throws Errore * @since 2012-02-15 */ public NumeroAvanzatoVec sqr() throws Errore { @@ -444,7 +452,7 @@ public class NumeroAvanzatoVec implements Comparable { * @param val * a second number of this type. * @return the product of this with the val. - * @throws Errore + * @throws Errore * @since 2011-02-12 */ public NumeroAvanzatoVec multiply(final NumeroAvanzato val) throws Errore { @@ -499,7 +507,7 @@ public class NumeroAvanzatoVec implements Comparable { } return val; } /* NumeroAvanzatoVec.isRational */ - + public boolean isNumeroAvanzato() { return (this.terms.size() <= 1); } @@ -549,7 +557,7 @@ public class NumeroAvanzatoVec implements Comparable { public BigInteger toBigInteger(boolean hasBigIntegerVariables) { BigDecimal tmp = BigDecimal.ZERO.setScale(Utils.scale, Utils.scaleMode); if (isBigInteger(hasBigIntegerVariables) == false) - throw new ArithmeticException("Undefined conversion " + toString() + " to Rational."); + throw new ArithmeticException("Undefined conversion " + toString() + " to BigInteger."); for (NumeroAvanzato s : terms) { tmp = BigDecimalMath.addRound(tmp, s.pref.BigDecimalValue(new MathContext(Utils.scale, Utils.scaleMode2))); } @@ -569,14 +577,13 @@ public class NumeroAvanzatoVec implements Comparable { return tmp; } /* NumeroAvanzato.toBigDecimal */ - public NumeroAvanzato toNumeroAvanzato() { if (this.terms.size() == 0) { return NumeroAvanzato.ZERO; } return this.terms.get(0); } - + /** * Return a string in the format (number/denom)*()^(1/2). If the * discriminant equals 1, print just the prefactor. @@ -584,25 +591,26 @@ public class NumeroAvanzatoVec implements Comparable { * @return the human-readable version in base 10 * @since 2012-02-16 */ + @Override public String toString() { /* * simple cases with one term forwarded to the NumeroAvanzato class */ return toFancyString(); /* - if (terms.size() == 0) - return new String("0"); - else { - String s = new String(); - for (int t = 0; t < terms.size(); t++) { - NumeroAvanzato bs = terms.elementAt(t); - if (bs.signum() > 0) - s += "+"; - s += bs.toString(); - } - return s; - } - */ + * if (terms.size() == 0) + * return new String("0"); + * else { + * String s = new String(); + * for (int t = 0; t < terms.size(); t++) { + * NumeroAvanzato bs = terms.elementAt(t); + * if (bs.signum() > 0) + * s += "+"; + * s += bs.toString(); + * } + * return s; + * } + */ } /* toString */ public String toFancyString() { @@ -613,7 +621,7 @@ public class NumeroAvanzatoVec implements Comparable { NumeroAvanzato bs = terms.elementAt(0).clone(); String num = bs.BigDecimalValue(new MathContext(Utils.resultScale, Utils.scaleMode2)).toEngineeringString(); if (num.contains("E")) { - s += "("+num.replace("E+", "*10^")+")"; + s += "(" + num.replace("E+", "*10^") + ")"; } else { s += num; } @@ -624,12 +632,13 @@ public class NumeroAvanzatoVec implements Comparable { Incognite incognitedenom = new Incognite(); for (int i = 0; i < terms.size(); i++) { denominator = BigIntegerMath.lcm(denominator, terms.elementAt(i).pref.b); - //denominator = denominator.multiply(terms.elementAt(i).pref.b); + // denominator = + // denominator.multiply(terms.elementAt(i).pref.b); Incognite iz = terms.elementAt(i).getIncognitez(); incognitedenom = Incognite.lcm(incognitedenom, iz); } String s = ""; - + if (denominator.abs().compareTo(new BigInteger("10000")) > 0) { for (int i = 0; i < terms.size(); i++) { NumeroAvanzato bs = terms.elementAt(i).clone(); @@ -638,11 +647,11 @@ public class NumeroAvanzatoVec implements Comparable { if (bs.signum() > 0) { s += "+"; } - num = num.replace("E+", "*10^")+")"; + num = num.replace("E+", "*10^") + ")"; if (num.contains("*10^1)")) { num = num.replace("*10^1)", ")"); } else { - num = "("+num; + num = "(" + num; } s += num; } else { @@ -655,7 +664,7 @@ public class NumeroAvanzatoVec implements Comparable { } return s; } - + if (denominator.compareTo(BigInteger.ONE) != 0 || incognitedenom.count() > 0) { if (terms.size() == 1 && terms.get(0).multiply(denominator).isBigInteger(true) == false) { s += "("; @@ -663,12 +672,12 @@ public class NumeroAvanzatoVec implements Comparable { } for (int t = 0; t < terms.size(); t++) { NumeroAvanzato bs = terms.elementAt(t).clone(); - + bs = bs.setIncognitey(bs.getIncognitey().divide(bs.getIncognitez())); bs = bs.setIncognitey(bs.getIncognitey().multiply(incognitedenom)); bs = bs.multiply(denominator); bs = bs.setIncognitez(incognitedenom); - + bs.pref = new Rational(bs.pref.a, BigInteger.ONE); if (bs.signum() > 0 && t > 0) s += "+"; @@ -687,7 +696,7 @@ public class NumeroAvanzatoVec implements Comparable { } else if (numb.equals("-1")) { s += "-"; } - s +=incognite; + s += incognite; } else if (bs.isRational(true) || bs.isTooPreciseRational(true)) { try { s += bs.toRational(true).toString(); @@ -706,13 +715,13 @@ public class NumeroAvanzatoVec implements Comparable { s += "-"; } } - //s += "("; + // s += "("; } if (bs.disc.isInteger() && bs.getIncognitex().count() == 0) { s += "Ⓐ("; s += bs.disc.toString(); s += ")"; - } else if((bs.disc.toString().equals("1") || bs.disc.toString().equals("-1")) && bs.getIncognitex().count() > 0) { + } else if ((bs.disc.toString().equals("1") || bs.disc.toString().equals("-1")) && bs.getIncognitex().count() > 0) { s += "Ⓐ("; if (bs.disc.toString().equals("-1")) { s += "-"; @@ -720,16 +729,16 @@ public class NumeroAvanzatoVec implements Comparable { s += bs.getIncognitex().toString(); s += ")"; } else { - s += "Ⓐ("+bs.disc.toString()+bs.getIncognitex().toString()+")"; + s += "Ⓐ(" + bs.disc.toString() + bs.getIncognitex().toString() + ")"; } if ((numerator.compareTo(BigInteger.ONE) != 0 || bs.getIncognitey().count() > 0) && (bs.disc.compareTo(BigInteger.ONE) != 0 || bs.getIncognitex().count() > 0)) { if (((bs.getIncognitey().count() > 0) || (bs.getIncognitey().count() == 0 && numerator.toString().length() > 0))) { s += bs.getIncognitey().toString(); } - //s += "("; + // s += "("; } if ((numerator.compareTo(BigInteger.ONE) != 0 || bs.getIncognitey().count() > 0) && (bs.disc.compareTo(BigInteger.ONE) != 0 || bs.getIncognitex().count() > 0)) { - //s += ")"; + // s += ")"; } } } diff --git a/src/org/nevec/rjm/PartitionsP.java b/src/org/nevec/rjm/PartitionsP.java index bb169146..07e48902 100644 --- a/src/org/nevec/rjm/PartitionsP.java +++ b/src/org/nevec/rjm/PartitionsP.java @@ -1,85 +1,88 @@ -package org.nevec.rjm ; +package org.nevec.rjm; import java.math.BigInteger; import java.util.Vector; -/** Number of partitions. -* @since 2008-10-15 -* @author Richard J. Mathar -*/ -public class PartitionsP -{ - /** - * The list of all partitions as a vector. - */ - static protected Vector a = new Vector() ; +/** + * Number of partitions. + * + * @since 2008-10-15 + * @author Richard J. Mathar + */ +public class PartitionsP { + /** + * The list of all partitions as a vector. + */ + static protected Vector a = new Vector(); - /** - * The maximum integer covered by the high end of the list. - */ - static protected BigInteger nMax =new BigInteger("-1") ; + /** + * The maximum integer covered by the high end of the list. + */ + static protected BigInteger nMax = new BigInteger("-1"); - /** - * Default constructor initializing a list of partitions up to 7. - */ - public PartitionsP() - { - if ( a.size() == 0 ) - { - a.add(new BigInteger(""+1)) ; - a.add(new BigInteger(""+1)) ; - a.add(new BigInteger(""+2)) ; - a.add(new BigInteger(""+3)) ; - a.add(new BigInteger(""+5)) ; - a.add(new BigInteger(""+7)) ; - } - nMax = new BigInteger(""+(a.size()-1)) ; - } /* ctor */ + /** + * Default constructor initializing a list of partitions up to 7. + */ + public PartitionsP() { + if (a.size() == 0) { + a.add(new BigInteger("" + 1)); + a.add(new BigInteger("" + 1)); + a.add(new BigInteger("" + 2)); + a.add(new BigInteger("" + 3)); + a.add(new BigInteger("" + 5)); + a.add(new BigInteger("" + 7)); + } + nMax = new BigInteger("" + (a.size() - 1)); + } /* ctor */ - /** return the number of partitions of i - * @param i the zero-based index into the list of partitions - * @return the ith partition number. This is 1 if i=0 or 1, 2 if i=2 and so forth. - */ - public BigInteger at(int i) - { - /* If the current list is too small, increase in intervals - * of 3 until the list has at least i elements. - */ - while ( i > nMax.intValue() ) - { - growto(nMax.add(new BigInteger(""+3))) ; - } - return ( a.elementAt(i) ) ; - } /* at */ + /** + * return the number of partitions of i + * + * @param i + * the zero-based index into the list of partitions + * @return the ith partition number. This is 1 if i=0 or 1, 2 if i=2 and so + * forth. + */ + public BigInteger at(int i) { + /* + * If the current list is too small, increase in intervals + * of 3 until the list has at least i elements. + */ + while (i > nMax.intValue()) { + growto(nMax.add(new BigInteger("" + 3))); + } + return (a.elementAt(i)); + } /* at */ - /** extend the list of known partitions up to n - * @param n the maximum integer hashed after the call. - */ - private void growto(BigInteger n) - { - while( a.size() <= n.intValue() ) - { - BigInteger per = new BigInteger("0") ; - BigInteger cursiz = new BigInteger(""+a.size()) ; - for(int k=0; k < a.size() ; k++) - { - BigInteger tmp = a.elementAt(k).multiply(BigIntegerMath.sigma(a.size()-k)) ; - per = per.add(tmp) ; - } - a.add(per.divide(cursiz)) ; - } - nMax = new BigInteger(""+(a.size()-1)) ; - } /* growto */ + /** + * extend the list of known partitions up to n + * + * @param n + * the maximum integer hashed after the call. + */ + private void growto(BigInteger n) { + while (a.size() <= n.intValue()) { + BigInteger per = new BigInteger("0"); + BigInteger cursiz = new BigInteger("" + a.size()); + for (int k = 0; k < a.size(); k++) { + BigInteger tmp = a.elementAt(k).multiply(BigIntegerMath.sigma(a.size() - k)); + per = per.add(tmp); + } + a.add(per.divide(cursiz)); + } + nMax = new BigInteger("" + (a.size() - 1)); + } /* growto */ - /** Test program. - * It takes one integer argument n and prints P(n).
- * java -cp . org.nevec.rjm.PartitionsP n
- * @since 2008-10-15 - */ - public static void main(String[] args) throws Exception - { - PartitionsP a = new PartitionsP() ; - int n = (new Integer(args[0])).intValue() ; - System.out.println("P("+ n +")=" + a.at(n)) ; - } + /** + * Test program. + * It takes one integer argument n and prints P(n).
+ * java -cp . org.nevec.rjm.PartitionsP n
+ * + * @since 2008-10-15 + */ + public static void main(String[] args) throws Exception { + PartitionsP a = new PartitionsP(); + int n = (new Integer(args[0])).intValue(); + System.out.println("P(" + n + ")=" + a.at(n)); + } } diff --git a/src/org/nevec/rjm/Prime.java b/src/org/nevec/rjm/Prime.java index 4e40dcd6..a5197102 100644 --- a/src/org/nevec/rjm/Prime.java +++ b/src/org/nevec/rjm/Prime.java @@ -1,279 +1,312 @@ -package org.nevec.rjm ; +package org.nevec.rjm; import java.math.BigInteger; import java.util.Vector; -/** Prime numbers. -* The implementation is a very basic computation of the set of all primes -* on demand, growing infinitely without any defined upper limit. -* The effects of such scheme are (i) the lookup-times become shorter after -* a while as more and more primes have been used and stored. The applications -* appear to become faster. (ii) Using the implementation for factorizations -* may easily require all available memory and stall finally, because indeed -* a dense list of primes with growing upper bound is kept without any hashing or lagging scheme. -* @since 2006-08-11 -* @author Richard J. Mathar -*/ -public class Prime -{ - /** The list of all numbers as a vector. - */ - static Vector a = new Vector(); +/** + * Prime numbers. + * The implementation is a very basic computation of the set of all primes + * on demand, growing infinitely without any defined upper limit. + * The effects of such scheme are (i) the lookup-times become shorter after + * a while as more and more primes have been used and stored. The applications + * appear to become faster. (ii) Using the implementation for factorizations + * may easily require all available memory and stall finally, because indeed + * a dense list of primes with growing upper bound is kept without any hashing + * or lagging scheme. + * + * @since 2006-08-11 + * @author Richard J. Mathar + */ +public class Prime { + /** + * The list of all numbers as a vector. + */ + static Vector a = new Vector(); - /** The maximum integer covered by the high end of the list. - */ - static protected BigInteger nMax = new BigInteger("-1"); + /** + * The maximum integer covered by the high end of the list. + */ + static protected BigInteger nMax = new BigInteger("-1"); - /** Default constructor initializing a list of primes up to 17. - * 17 is enough to call the Miller-Rabin tests on the first 7 primes without further - * action. - */ - public Prime() - { - if ( a.size() == 0 ) - { - a.add(new BigInteger(""+2)) ; - a.add(new BigInteger(""+3)) ; - a.add(new BigInteger(""+5)) ; - a.add(new BigInteger(""+7)) ; - a.add(new BigInteger(""+11)) ; - a.add(new BigInteger(""+13)) ; - a.add(new BigInteger(""+17)) ; - } - nMax = a.lastElement() ; - } + /** + * Default constructor initializing a list of primes up to 17. + * 17 is enough to call the Miller-Rabin tests on the first 7 primes without + * further + * action. + */ + public Prime() { + if (a.size() == 0) { + a.add(new BigInteger("" + 2)); + a.add(new BigInteger("" + 3)); + a.add(new BigInteger("" + 5)); + a.add(new BigInteger("" + 7)); + a.add(new BigInteger("" + 11)); + a.add(new BigInteger("" + 13)); + a.add(new BigInteger("" + 17)); + } + nMax = a.lastElement(); + } - /** Test if a number is a prime. - * @param n the integer to be tested for primality - * @return true if prime, false if not - */ - public boolean contains(BigInteger n) - { - /* not documented - * return ( n.isProbablePrime() ) ; - */ - switch ( millerRabin(n) ) - { - case -1: - return false ; - case 1: - return true ; - } - growto(n) ; - return( a.contains(n) ) ; - } + /** + * Test if a number is a prime. + * + * @param n + * the integer to be tested for primality + * @return true if prime, false if not + */ + public boolean contains(BigInteger n) { + /* + * not documented + * return ( n.isProbablePrime() ) ; + */ + switch (millerRabin(n)) { + case -1: + return false; + case 1: + return true; + } + growto(n); + return (a.contains(n)); + } - /** Test whether a number n is a strong pseudoprime to base a. - * @param n the integer to be tested for primality - * @param a the base - * @return true if the test is passed, so n may be a prime. - * false if the test is not passed, so n is not a prime. - * @since 2010-02-25 - */ - public boolean isSPP(final BigInteger n, final BigInteger a) - { - final BigInteger two = new BigInteger(""+2) ; + /** + * Test whether a number n is a strong pseudoprime to base a. + * + * @param n + * the integer to be tested for primality + * @param a + * the base + * @return true if the test is passed, so n may be a prime. + * false if the test is not passed, so n is not a prime. + * @since 2010-02-25 + */ + public boolean isSPP(final BigInteger n, final BigInteger a) { + final BigInteger two = new BigInteger("" + 2); + /* + * numbers less than 2 are not prime + */ + if (n.compareTo(two) == -1) + return false; + /* + * 2 is prime + */ + else if (n.compareTo(two) == 0) + return true; + /* + * even numbers >2 are not prime + */ + else if (n.remainder(two).compareTo(BigInteger.ZERO) == 0) + return false; + else { + /* + * q= n- 1 = d *2^s with d odd + */ + final BigInteger q = n.subtract(BigInteger.ONE); + int s = q.getLowestSetBit(); + BigInteger d = q.shiftRight(s); - /* numbers less than 2 are not prime - */ - if ( n.compareTo(two) == -1 ) - return false ; - /* 2 is prime - */ - else if ( n.compareTo(two) == 0 ) - return true ; - /* even numbers >2 are not prime - */ - else if ( n.remainder(two).compareTo(BigInteger.ZERO) == 0 ) - return false ; - else - { - /* q= n- 1 = d *2^s with d odd - */ - final BigInteger q = n.subtract(BigInteger.ONE) ; - int s = q.getLowestSetBit() ; - BigInteger d = q.shiftRight(s) ; + /* + * test whether a^d = 1 (mod n) + */ + if (a.modPow(d, n).compareTo(BigInteger.ONE) == 0) + return true; - /* test whether a^d = 1 (mod n) - */ - if ( a.modPow(d,n).compareTo(BigInteger.ONE) == 0 ) - return true ; + /* + * test whether a^(d*2^r) = -1 (mod n), 0<=r= a.size()) { + growto(nMax.add(new BigInteger("" + 5))); + } + return (a.elementAt(i)); + } - /** return the ithprime - * @param i the zero-based index into the list of primes - * @return the ith prime. This is 2 if i=0, 3 if i=1 and so forth. - */ - public BigInteger at(int i) - { - /* If the current list is too small, increase in intervals - * of 5 until the list has at least i elements. - */ - while ( i >= a.size() ) - { - growto(nMax.add(new BigInteger(""+5))) ; - } - return ( a.elementAt(i) ) ; - } + /** + * return the count of primes <= n + * + * @param n + * the upper limit of the scan + * @return the ith prime. This is 2 if i=0, 3 if i=1 and so forth. + */ + public BigInteger pi(BigInteger n) { + /* + * If the current list is too small, increase in intervals + * of 5 until the list has at least i elements. + */ + growto(n); + BigInteger r = new BigInteger("0"); + for (int i = 0; i < a.size(); i++) + if (a.elementAt(i).compareTo(n) <= 0) + r = r.add(BigInteger.ONE); + return r; + } - /** return the count of primes <= n - * @param n the upper limit of the scan - * @return the ith prime. This is 2 if i=0, 3 if i=1 and so forth. - */ - public BigInteger pi(BigInteger n) - { - /* If the current list is too small, increase in intervals - * of 5 until the list has at least i elements. - */ - growto(n) ; - BigInteger r = new BigInteger("0") ; - for(int i=0 ; i= 0) + return (a.elementAt(i - 1)); + return (a.lastElement()); + } - for(int i=0 ; i < a.size() ; i++) - if ( a.elementAt(i).compareTo(n) >= 0) - return ( a.elementAt(i-1) ) ; - return ( a.lastElement() ) ; - } + /** + * extend the list of known primes up to n + * + * @param n + * the maximum integer known to be prime or not prime after the + * call. + */ + protected void growto(BigInteger n) { + while (nMax.compareTo(n) == -1) { + nMax = nMax.add(BigInteger.ONE); + boolean isp = true; + for (int p = 0; p < a.size(); p++) { + /* + * Test the list of known primes only up to sqrt(n) + */ + if (a.get(p).multiply(a.get(p)).compareTo(nMax) == 1) + break; - /** extend the list of known primes up to n - * @param n the maximum integer known to be prime or not prime after the call. - */ - protected void growto(BigInteger n) - { - while( nMax.compareTo(n) == -1) - { - nMax = nMax.add(BigInteger.ONE) ; - boolean isp = true ; - for(int p=0; p < a.size() ; p++) - { - /* - * Test the list of known primes only up to sqrt(n) - */ - if ( a.get(p).multiply(a.get(p)).compareTo(nMax) == 1 ) - break ; + /* + * The next case means that the p'th number in the list of known + * primes divides + * nMax and nMax cannot be a prime. + */ + if (nMax.remainder(a.get(p)).compareTo(BigInteger.ZERO) == 0) { + isp = false; + break; + } + } + if (isp) + a.add(nMax); + } + } - /* - * The next case means that the p'th number in the list of known primes divides - * nMax and nMax cannot be a prime. - */ - if ( nMax.remainder(a.get(p)).compareTo(BigInteger.ZERO) == 0 ) - { - isp = false ; - break ; - } - } - if( isp ) - a.add(nMax) ; - } - } - /** Test program. - * Usage: java -cp . org.nevec.rjm.Prime n
- * This takes a single argument (n) and prints prime(n), the previous and next prime, and pi(n). - * @since 2006-08-14 - */ - public static void main(String[] args) throws Exception - { - Prime a = new Prime() ; - int n = (new Integer(args[0])).intValue() ; - if ( n >= 1 ) - { - if ( n >= 2) - System.out.println("prime("+(n-1)+") = " + a.at(n-1)) ; - System.out.println("prime("+n+") = " + a.at(n)) ; - System.out.println("prime("+(n+1)+") = " + a.at(n+1)) ; - System.out.println("pi(" + n +") = " + a.pi(new BigInteger(""+n) )) ; - } - } + /** + * Test program. + * Usage: java -cp . org.nevec.rjm.Prime n
+ * This takes a single argument (n) and prints prime(n), the previous and + * next prime, and pi(n). + * + * @since 2006-08-14 + */ + public static void main(String[] args) throws Exception { + Prime a = new Prime(); + int n = (new Integer(args[0])).intValue(); + if (n >= 1) { + if (n >= 2) + System.out.println("prime(" + (n - 1) + ") = " + a.at(n - 1)); + System.out.println("prime(" + n + ") = " + a.at(n)); + System.out.println("prime(" + (n + 1) + ") = " + a.at(n + 1)); + System.out.println("pi(" + n + ") = " + a.pi(new BigInteger("" + n))); + } + } } /* Prime */ diff --git a/src/org/nevec/rjm/RatPoly.java b/src/org/nevec/rjm/RatPoly.java index 027c77b9..9c86297f 100644 --- a/src/org/nevec/rjm/RatPoly.java +++ b/src/org/nevec/rjm/RatPoly.java @@ -1,4 +1,4 @@ -package org.nevec.rjm ; +package org.nevec.rjm; import java.math.BigInteger; import java.math.MathContext; @@ -7,907 +7,1003 @@ import java.util.Random; import java.util.Scanner; import java.util.Vector; -import org.warpgate.pi.calculator.Errore; - -/** A one-parameter polynomial with rational coefficients. -* Alternatively to be interpreted as a sequence which has the polynomial as an (approximate) -* generating function. -* @since 2006-06-25 -* @author Richard J. Mathar -*/ -class RatPoly -{ - /** The list of all coefficients, ascending exponents. Starting with a0, then a1, representing - * a value a0+a1*x+a2*x^2+a3*x^3+... - */ - protected Vector a ; - - /** Default ctor. - * Initializes the zero-valued polynomial x=0. - */ - public RatPoly() - { - a = new Vector() ; - } /* ctor */ - - /** Constructor with an explicit list of coefficients. - * @param L the coefficients a0, a1, a2, a3,.., A deep copy of the these is created. - */ - public RatPoly(final Vector L) - { - a = new Vector() ; - for(int i=0 ; i < L.size() ; i++) - a.add( L.elementAt(i).clone() ) ; - simplify() ; - } /* ctor */ - - /** Constructor with a comma-separated list as the list of coefficients. - * @param L the string of the form a0,a1,a2,a3 with the coefficients - */ - public RatPoly(final String L) throws NumberFormatException - { - a = new Vector() ; - Scanner sc = new Scanner(L) ; - sc.useDelimiter(",") ; - while ( sc.hasNext()) - { - String tok =sc.next() ; - a.add(new Rational(tok)) ; - } - simplify() ; - sc.close(); - } /* ctor */ - - - /** Constructor from a hypergeometric series. - * @param A the list of values in the numerator of AFB - * @param B the list of values in the denominator of AFB - * @param nmax the order of the truncated polynomial representation - * @throws Errore - * @since 2008-11-13 - */ - public RatPoly(final Vector A, final Vector B, int nmax) throws Errore - { - /* To allow common initialization with the signature below, - * the main body is assembled in a separate function. - */ - init(A,B,nmax) ; - } - - - /** Constructor from a hypergeometric series. - * @param A the list of values in the numerator of AFB. - * At least one of these values must be a negative integer, which implicitly determines - * the order of the new polynomial. - * @param B the list of values in the denominator of AFB - * @throws Errore - * @since 2009-08-05 - */ - public RatPoly(final Vector A, final Vector B) throws Errore - { - BigInteger Nmax = BigInteger.ONE.negate() ; - for(int j=0; j < A.size() ; j++) - { - if ( A.elementAt(j).compareTo(BigInteger.ZERO) <= 0) - { - if ( Nmax.compareTo(BigInteger.ZERO) < 0 ) - Nmax = A.elementAt(j).negate() ; - else - Nmax = Nmax.min( A.elementAt(j).negate() ) ; - } - } - if ( Nmax.compareTo(BigInteger.ZERO) < 0 ) - throw new ArithmeticException("Infinite Number of Terms in Series "+Nmax.toString()) ; - - int nmax = Nmax.intValue()-1 ; - init(A,B,nmax) ; - } /* ctor */ - - /** Constructor from a hypergeometric series. - * @param A the list of values in the numerator of AFB - * @param B the list of values in the denominator of AFB - * @param nmax the order of the truncated polynomial representation - * @throws Errore - * @since 2008-11-13 - */ - protected void init(final Vector A, final Vector B, int nmax) throws Errore - { - a = new Vector() ; - Factorial f=new Factorial() ; - for( int n=0; n <= nmax ; n++) - { - Rational c = new Rational(1,1) ; - for(int j=0; j < A.size() ; j++) - { - Rational aEl = new Rational(A.elementAt(j)) ; - c = c.multiply(aEl.Pochhammer(n)) ; - } - for(int j=0; j < B.size() ; j++) - { - Rational bEl = new Rational(B.elementAt(j)) ; - c = c.divide(bEl.Pochhammer(n)) ; - } - c =c.divide(f.at(n)) ; - a.add(c) ; - } - simplify() ; - } /* init */ - - - /** Create a copy of this. - * @since 2008-11-07 - */ - @SuppressWarnings("unchecked") - public RatPoly clone() - { - RatPoly clo = new RatPoly() ; - clo.a = (Vector)a.clone() ; - return clo ; - } /* clone */ - - /** Retrieve a polynomial coefficient. - * @param n the zero-based index of the coefficient. n=0 for the constant term. - * @return the polynomial coefficient in front of x^n. - */ - public Rational at(final int n) - { - if ( n < a.size()) - return( a.elementAt(n) ) ; - else - return( new Rational(0,1) ) ; - } /* at */ - - /** Horner scheme to find the function value at the argument x - * @param x The argument of the polynomial - * @param mc The context determining the precision of the value returned. - * @since 2008-10-26 - */ - public BigComplex valueOf( BigComplex x, MathContext mc) - { - /* result is initialized to zero */ - BigComplex f = new BigComplex() ; - for(int i=degree() ; i >= 0 ; i--) - f = f.multiply(x,mc).add(a.elementAt(i).BigDecimalValue(mc)) ; - return f ; - } /* valueOf */ - - /** Horner scheme to find the function value at the argument x - * @param x The argument of the polynomial - * @since 2008-11-13 - */ - public Rational valueOf( Rational x) - { - /* result is initialized to zero */ - Rational f = new Rational(0,1) ; - for(int i=degree() ; i >= 0 ; i--) - f = f.multiply(x).add(a.elementAt(i)) ; - return f ; - } /* valueOf */ - - /** Horner scheme to find the function value at the argument x - * @param x The argument of the polynomial - * @since 2008-11-13 - */ - public Rational valueOf( int x) - { - return valueOf(new Rational(x,1)) ; - } /* valueOf */ - - /** Horner scheme to evaluate the function at the argument x - * @param x The argument of the polynomial - * @since 2010-08-27 - */ - public Rational valueOf( BigInteger x) - { - return valueOf(new Rational(x)) ; - } /* valueOf */ - - /* Set a polynomial coefficient. - * @param n the zero-based index of the coefficient. n=0 for the constant term. - * If the polynomial has not yet the degree to need this coefficient, - * the intermediate coefficients are implicitly set to zero. - * @param value the new value of the coefficient. - */ - public void set(final int n, final Rational value) - { - if ( n < a.size()) - a.set(n,value) ; - else - { - /* fill intermediate powers with coefficients of zero - */ - while ( a.size() < n ) - a.add(new Rational(0,1)) ; - a.add(value) ; - } - } /* set */ - - /** Set a polynomial coefficient. - * @param n the zero-based index of the coefficient. n=0 for the constant term. - * If the polynomial has not yet the degree to need this coefficient, - * the intermediate coefficients are implicitly set to zero. - * @param value the new value of the coefficient. - */ - public void set(final int n, final BigInteger value) - { - Rational val2 = new Rational(value,BigInteger.ONE) ; - set(n,val2) ; - } /* set */ - - /** Set a polynomial coefficient. - * @param n the zero-based index of the coefficient. n=0 for the constant term. - * If the polynomial has not yet the degree to need this coefficient, - * the intermediate coefficients are implicitly set to zero. - * @param value the new value of the coefficient. - */ - public void set(final int n, final int value) - { - Rational val2 = new Rational(value,1) ; - set(n,val2) ; - } /* set */ - - /* Set to the taylor series of exp(x) up to degree nmax. - * @param nmax the maximum polynomial degree - */ - public void setExp(final int nmax) - { - a.clear() ; - Factorial factorial=new Factorial() ; - for(int n=0; n <= nmax ; n++) - set(n, new Rational(BigInteger.ONE,factorial.at(n))) ; - } /* setExp */ - - /** Set to the taylor series representing 0+x. - */ - public void setx() - { - a.clear() ; - /* coefficient 0/1=0 */ - a.add(new Rational(0,1)) ; - /* coefficient 1/1=1 */ - a.add(new Rational(1,1)) ; - } /* setx */ - - /** Count of coefficients. One more than the degree of the polynomial. - * @return the number of polynomial coefficients. - */ - public int size() - { - return a.size() ; - } /* size */ - - /** Polynomial degree. - * @return the polynomial degree. - */ - public int degree() - { - return a.size()-1 ; - } /* degree */ - - /** Lower Polynomial degree. - * @return The smallest exponent n such that [x^n] of the polynomial is nonzero. - * If the polynmial is identical zero, the result is (still) 0. - * @since 2010-08-27 - */ - public int ldegree() - { - for(int n=0 ; n < a.size() ; n++) - if ( a.elementAt(n).compareTo(BigInteger.ZERO) != 0 ) - return n; - return 0 ; - } /* ldegree */ - - /** Multiply by a constant factor. - * @param val the factor - * @return the product of this with the factor. - * All coefficients of this have been multiplied individually by the factor. - */ - public RatPoly multiply(final Rational val) - { - RatPoly resul = new RatPoly() ; - if ( val.compareTo(BigInteger.ZERO) != 0 ) - for(int n=0; n < a.size() ; n++) - resul.set(n,a.elementAt(n).multiply(val) ) ; - return resul ; - } /* multiply */ - - /** Multiply by a constant factor. - * @param val the factor - * @return the product of this with the factor. - * All coefficients of this have been multiplied individually by the factor. - * @since 2010-08-27 - */ - public RatPoly multiply(final BigInteger val) - { - RatPoly resul = new RatPoly() ; - if ( val.compareTo(BigInteger.ZERO) != 0 ) - for(int n=0; n < a.size() ; n++) - resul.set(n,a.elementAt(n).multiply(val) ) ; - return resul ; - } /* multiply */ - - /** Multiply by another polynomial - * @param val the other polynomial - * @return the product of this with the other polynomial - */ - public RatPoly multiply(final RatPoly val) - { - RatPoly resul = new RatPoly() ; - /* the degree of the result is the sum of the two degrees. - */ - final int nmax = degree()+val.degree() ; - for(int n=0; n <= nmax ; n++) - { - Rational coef = new Rational(0,1) ; - for(int nleft=0; nleft <= n ; nleft++) - { - coef = coef.add(at(nleft).multiply(val.at(n-nleft))) ; - } - resul.set(n,coef) ; - } - resul.simplify() ; - return resul ; - } /* multiply */ - - /** Raise to a positive power. - * @param n The non-negative exponent of the power - * @return The n-th power of this. - */ - public RatPoly pow(final int n) throws ArithmeticException - { - RatPoly resul = new RatPoly("1") ; - if ( n < 0 ) - throw new ArithmeticException("negative polynomial power "+n) ; - else - { - /* this ought probably be done with some binary representation - * of the power and a smaller number of multiplications. - */ - for(int i=1 ; i <= n ; i++) - resul = resul.multiply(this) ; - resul.simplify() ; - return resul ; - } - } /* pow */ - - /** Raise to a rational power. - * The result is the taylor expansion of this, truncated at the first - * term that remains undetermined based on the current number of coefficients. - * @param r the exponent of the power - * @return This^r . - * @throws Errore - * @since 2009-05-18 - */ - public RatPoly pow(final Rational r) throws ArithmeticException, Errore - { - /* split (a0+a1*x+a2*x^2+...)^r = a0^r*(1+a1/a0*r+a2/a0*r^2+..)^r - */ - Rational f = at(0) ; - f = f.pow(r) ; - - /* scale the polynomial by division through the expansion coefficient of the absolute term - */ - RatPoly red = divide(a.elementAt(0)) ; - - /* and remove the leading term (now equal to 1) - */ - red.set(0,0) ; - - /* Binomial expansion of the rest. sum_{l=0..infinity} binomial(r,l)*red^l - */ - RatPoly resul = new RatPoly("1") ; - - final int d = degree() ; - for(int l=1 ; l <= d ; l++) - { - final Rational b = Rational.binomial(r,l) ; - resul = resul.add( red.pow(l).multiply(b) ) ; - } - return resul.multiply(f) ; - } /* pow */ - - /** Add another polynomial - * @param val The other polynomial - * @return The sum of this and the other polynomial - * @since 2008-10-25 - */ - public RatPoly add(final RatPoly val) - { - RatPoly resul = new RatPoly() ; - /* the degree of the result is the larger of the two degrees (before simplify() at least). - */ - final int nmax = (degree()>val.degree()) ? degree() : val.degree() ; - for(int n=0; n <= nmax ; n++) - { - Rational coef = at(n).add(val.at(n)) ; - resul.set(n,coef) ; - } - resul.simplify() ; - return resul ; - } /* add */ - - /** Subtract another polynomial - * @param val The other polynomial - * @return The difference between this and the other polynomial - * @since 2008-10-25 - */ - public RatPoly subtract(final RatPoly val) - { - RatPoly resul = new RatPoly() ; - /* the degree of the result is the larger of the two degrees (before simplify() at least). - */ - final int nmax = (degree()>val.degree()) ? degree() : val.degree() ; - for(int n=0; n <= nmax ; n++) - { - Rational coef = at(n).subtract(val.at(n)) ; - resul.set(n,coef) ; - } - resul.simplify() ; - return resul ; - } /* subtract */ - - /** Divide by a constant. - * @param val the constant through which the coefficients will be divided. - * @return the Taylor expansion of this/val . - * @throws Errore - * @since 2009-05-18 - */ - public RatPoly divide(final Rational val) throws Errore - { - if ( val.compareTo(Rational.ZERO) != 0 ) - { - RatPoly resul = new RatPoly() ; - for(int n=0; n < a.size() ; n++) - resul.set(n,a.elementAt(n).divide(val) ) ; - return resul ; - } - else - throw new ArithmeticException("Cannot divide " + toPString() +" through zero.") ; - } /* divide */ - - /** Divide by another polynomial. - * @param val the other polynomial - * @param nmax the maximum degree of the Taylor expansion of the result. - * @return the Taylor expansion of this/val up to degree nmax. - * @throws Errore - */ - public RatPoly divide(final RatPoly val,int nmax) throws Errore - { - RatPoly num = this ; - RatPoly denom = val ; - - /* divide by a common smallest power/degree - */ - while( num.at(0).compareTo(BigInteger.ZERO) == 0 && denom.at(0).compareTo(BigInteger.ZERO) == 0) - { - num.a.remove(0) ; - denom.a.remove(0) ; - if( num.size() <= 1 || denom.size() <= 1) - break ; - } - - RatPoly resul = new RatPoly() ; - /* todo: If the polynomial division is exact, we could leave - * the loop earlier, indeed - */ - for(int n=0; n <= nmax ; n++) - { - Rational coef = num.at(n) ; - for(int nres=0; nres < n ; nres++) - { - coef = coef.subtract(resul.at(nres).multiply(denom.at(n-nres))) ; - } - coef = coef.divide(denom.at(0)) ; - resul.set(n,coef) ; - } - resul.simplify() ; - return(resul) ; - } /* divide */ - - /** Divide by another polynomial. - * @param val the other polynomial - * @return A vector with [0] containg the polynomial of degree which is the - * difference of thisdegree and the degree of val. [1] the remainder polynomial. - * This = returnvalue[0] + returnvalue[1]/val . - * @throws Errore - * @since 2012-03-01 - */ - public RatPoly[] divideAndRemainder(final RatPoly val) throws Errore - { - RatPoly[] ret = new RatPoly[2] ; - /* remove any high-order zeros - */ - RatPoly valSimpl = val.clone() ; - valSimpl.simplify() ; - RatPoly thisSimpl = clone() ; - thisSimpl.simplify() ; - - /* catch the case with val equal to zero - */ - if ( valSimpl.degree() == 0 && valSimpl.a.firstElement().compareTo(Rational.ZERO) == 0) - throw new ArithmeticException("Division through zero polynomial") ; - /* degree of this smaller than degree of val: remainder is this - */ - if ( thisSimpl.degree() < valSimpl.degree() ) - { - /* leading polynomial equals zero - */ - ret[0] = new RatPoly() ; - ret[1] = thisSimpl ; - } - else - { - /* long division. Highest degree by dividing the highest degree - * of this thru val. - */ - ret[0] = new RatPoly() ; - ret[0].set(thisSimpl.degree()-valSimpl.degree(), - thisSimpl.a.lastElement().divide(valSimpl.a.lastElement()) ) ; - - /* recurrences: build this - val*(1-termresult) and feed this - * into another round of division. Have intermediate ret[0]+ret[1]/val. - */ - ret[1] = thisSimpl.subtract( ret[0].multiply( valSimpl) ); - - /* any remainder left ? - */ - if ( ret[1].degree() < valSimpl.degree() ) - ; - else - { - RatPoly rem[] = ret[1].divideAndRemainder(val) ; - ret[0] = ret[0].add(rem[0]) ; - ret[1] = rem[1] ; - } - } - return ret ; - } /* divideAndRemainder */ - - /** Print as a comma-separated list of coefficients. - * @return The representation a0,a1,a2,a3,... - * This is a sort of opposite of the ctor that takes a string as an argument. - * @since 2008-10-25 - */ - public String toString() - { - String str = new String(); - for(int n=0; n < a.size() ; n++) - { - if ( n == 0 ) - str += a.elementAt(n).toString() ; - else - str += ","+a.elementAt(n).toString() ; - } - /* print at least a sole zero - */ - if (str.length() == 0) - str = "0" ; - return str ; - } /* toString */ - - /** Print as a polyomial in x. - * @return To representation a0+a1*x+a2*x^2+... - * This does not print the terms with coefficients equal to zero. - * @since 2008-10-26 - */ - public String toPString() - { - String str = new String(); - for(int n=0; n < a.size() ; n++) - { - final BigInteger num = a.elementAt(n).a ; - if ( num.compareTo(BigInteger.ZERO) != 0 ) - { - str += " " ; - if ( num.compareTo(BigInteger.ZERO) > 0 ) - str += "+" ; - str += a.elementAt(n).toString() ; - if ( n > 0 ) - { - str += "*x" ; - if ( n > 1 ) - str += "^"+n ; - } - } - } - /* print at least a sole zero - */ - if (str.length() == 0) - str = "0" ; - return str ; - } /* toPString */ - - /** Simplify the representation. - * Trailing values with zero coefficients (at high powers) are deleted. - * This modifies the polynomial on the stop (does not return another instance) - */ - private void simplify() - { - int n = a.size()-1 ; - if ( n >= 0) - while( a.elementAt(n).compareTo(BigInteger.ZERO) == 0 ) - { - a.remove(n) ; - if( --n <0) - break ; - } - } /* simplify */ - - /** First derivative. - * @return The first derivative with respect to the indeterminate variable. - * @since 2008-10-26 - */ - public RatPoly derive() - { - if ( a.size() <= 1) - /* derivative of the constant is just zero - */ - return new RatPoly() ; - else - { - RatPoly d = new RatPoly() ; - for(int i=1 ; i <= degree() ; i++) - { - final Rational c = a.elementAt(i).multiply(i) ; - d.set(i-1,c) ; - } - return d ; - } - } /* derive */ - - /** Scale coefficients such that the coefficient in front of the maximum degree is unity. - * @return The scaled polynomial - * @throws Errore - * @since 2008-10-26 - */ - public RatPoly monic() throws Errore - { - RatPoly m = new RatPoly() ; - final int d = degree() ; - for(int i=0 ; i <= d ; i++) - { - final Rational c = a.elementAt(i).divide(a.elementAt(d) ) ; - m.set(i,c) ; - } - return m ; - } /* monic */ - - - /** Mobius transform. - * @param maxdeg the maximum polynomial degree of the result - * @return the sequence of coefficients is the Mobius transform of the original sequence. - * @since 2008-12-02 - */ - public RatPoly mobiusT(int maxdeg) - { - /* Start with the polynomial 0 - */ - RatPoly r = new RatPoly() ; - for(int i=1; i <= maxdeg; i++) - { - Rational c = new Rational() ; - for(int d=1; d <= i && d < a.size(); d++) - { - if (i % d == 0) - { - final Ifactor m = new Ifactor(i/d) ; - c = c.add( a.elementAt(d).multiply( m.moebius() ) ) ; - } - } - r.set(i,c) ; - } - r.simplify() ; - return r ; - } /* mobiusT */ - - /** Inverse Mobius transform. - * @param maxdeg the maximum polynomial degree of the result - * @return the sequence of coefficients is the inverse Mobius transform of the original sequence. - * @since 2008-12-02 - */ - public RatPoly mobiusTInv(int maxdeg) - { - /* Start with the polynomial 0 - */ - RatPoly r = new RatPoly() ; - for(int i=1; i <= maxdeg; i++) - { - Rational c = new Rational() ; - for(int d=1; d <= i && d < a.size(); d++) - { - if (i % d == 0) - c = c.add( a.elementAt(d) ) ; - } - r.set(i,c) ; - } - r.simplify() ; - return r ; - } /* mobiusTInv */ - - /** Binomial transform. - * @param maxdeg the maximum polynomial degree of the result - * @return the sequence of coefficients is the binomial transform of the original sequence. - * @since 2008-10-26 - */ - public RatPoly binomialT(int maxdeg) - { - RatPoly r = new RatPoly() ; - for(int i=0; i <= maxdeg; i++) - { - Rational c = new Rational(0,1) ; - for(int j=0; j <= i && j < a.size(); j++) - c = c.add( a.elementAt(j).multiply(BigIntegerMath.binomial(i,j)) ) ; - r.set(i,c) ; - } - r.simplify() ; - return r ; - } /* binomialT */ - - /** Inverse Binomial transform. - * @param maxdeg the maximum polynomial degree of the result - * @return the sequence of coefficients is the inverse binomial transform of the original sequence. - * @since 2008-10-26 - */ - public RatPoly binomialTInv(int maxdeg) - { - RatPoly r = new RatPoly() ; - for(int i=0; i <= maxdeg; i++) - { - Rational c = new Rational(0,1) ; - for(int j=0; j <= i && j < a.size(); j++) - if ( (j+i) % 2 != 0 ) - c = c.subtract( a.elementAt(j).multiply(BigIntegerMath.binomial(i,j)) ) ; - else - c = c.add( a.elementAt(j).multiply(BigIntegerMath.binomial(i,j)) ) ; - r.set(i,c) ; - } - r.simplify() ; - return r ; - } /* binomialTInv */ - - - /** Truncate polynomial degree. - * @param newdeg The maximum degree of the result. - * @return The polynomial with all coefficients beyond deg set to zero. - * If newdeg =3, for example the polynomial returned has at most degree 3. - * If newdeg is larger than the degree of this, zeros (at the higher orders of x) - * are appended. That polynomial would have formal degree larger than this. - * @since 2008-10-26 - */ - public RatPoly trunc(int newdeg) - { - RatPoly t = new RatPoly() ; - for(int i=0; i <= newdeg; i++) - t.set(i,at(i)) ; - t.simplify() ; - return t ; - } /* trunc */ - - /** Generate the roots of the polynomial in floating point arithmetic. - * @see Durand Kerner method - * @param the number of floating point digits - * @throws Errore - * @since 2008-10-26 - */ - public Vector roots(int digits) throws Errore - { - RatPoly mon = monic() ; - - Random rand = new Random() ; - MathContext mc = new MathContext(digits+3,RoundingMode.DOWN) ; - - Vector res =new Vector() ; - - final int d = mon.degree() ; - double randRad =0. ; - for(int i=0 ; i <= d ; i++) - { - /* scale coefficient at maximum degree */ - double absi = Math.abs( mon.at(i).doubleValue() ) ; - if ( absi > randRad) - randRad = absi ; - } - randRad += 1.0 ; - - /* initial values randomly in radius 1+randRad - */ - for(int i=0 ; i < d ; i++) - { - double rad = randRad*rand.nextDouble() ; - double phi = 2.0*3.14159*rand.nextDouble() ; - res.add(i, new BigComplex(rad*Math.cos(phi),rad*Math.sin(phi)) ) ; - } - - /* iterate until convr indicates that all values changed by less than the digits - * precision indicates. - */ - boolean convr = false ; - for(;! convr;)//ORIGINAL LINE: for(int itr =0 ; ! convr ; itr++) - { - convr = true ; - Vector resPlus =new Vector() ; - for(int v=0 ; v < d; v++) - { - /* evaluate f(x)/(x-root1)/(x-root2)/... (x-rootdegr), Newton method - */ - BigComplex thisx = res.elementAt(v) ; - BigComplex nv = mon.valueOf(thisx,mc) ; - for(int j=0; j < d ; j++) - { - if ( j != v ) - nv = nv.divide(thisx.subtract(res.elementAt(j)),mc) ; - } - - /* is this value converged ? */ - if ( nv.abs(mc).doubleValue() > thisx.abs(mc).doubleValue()*Math.pow(10.0,-digits) ) - convr =false; - - thisx = thisx.subtract(nv) ; - - /* If unstable, start over */ - if ( thisx.abs(MathContext.DECIMAL32).doubleValue() > randRad ) - return roots(digits) ; - - resPlus.add(thisx) ; - } - res = resPlus ; - - } - return res; - } /* roots */ - - /** Generate the integer roots of the polynomial. - * @return The vector of integer roots, with multiplicity. - * The shows alternatingly first a root then its multiplicty, then another root and multiplicty etc. - * @since 2008-10-26 - */ - public Vector iroots() - { - /* The vector of the roots */ - Vector res =new Vector() ; - - int lowd = ldegree() ; - if( lowd == 0 && a.elementAt(0).compareTo(BigInteger.ZERO) == 0) - { - /* Case of polynomial identical to zero: - * reported as a simple root of value 0. - */ - res.add(BigInteger.ZERO) ; - res.add(BigInteger.ONE) ; - return res ; - } - - /* multiply all coefs with the lcm() to get an integer polynomial - * start with denominator of first non-zero coefficient. - */ - BigInteger lcmDeno = a.elementAt(lowd).b ; - for(int i=lowd+1; i < degree() ; i++) - lcmDeno = BigIntegerMath.lcm(lcmDeno, a.elementAt(i).b ) ; - - /* and eventually get the integer polynomial by ignoring the denominators - */ - Vector ipo = new Vector() ; - for(int i=0 ; i < a.size() ; i++) - { - BigInteger d = a.elementAt(i).a.multiply( lcmDeno).divide( a.elementAt(i).b) ; - ipo.add(d) ; - } - - BigIntegerPoly p = new BigIntegerPoly(ipo) ; - /* collect the integer roots (multiple roots only once). Since we - * removed the zero already above, cand does not contain zeros. - */ - Vector cand = p.iroots() ; - for( int i =0 ; i < cand.size() ; i++) - { - final BigInteger r = cand.elementAt(i) ; - int deg = p.rootDeg( r) ; - res.add(r) ; - res.add(new BigInteger(""+deg)) ; - } - - return res; - } /* iroots */ - +import org.warp.picalculator.Errore; + +/** + * A one-parameter polynomial with rational coefficients. + * Alternatively to be interpreted as a sequence which has the polynomial as an + * (approximate) + * generating function. + * + * @since 2006-06-25 + * @author Richard J. Mathar + */ +class RatPoly { + /** + * The list of all coefficients, ascending exponents. Starting with a0, then + * a1, representing + * a value a0+a1*x+a2*x^2+a3*x^3+... + */ + protected Vector a; + + /** + * Default ctor. + * Initializes the zero-valued polynomial x=0. + */ + public RatPoly() { + a = new Vector(); + } /* ctor */ + + /** + * Constructor with an explicit list of coefficients. + * + * @param L + * the coefficients a0, a1, a2, a3,.., A deep copy of the these + * is created. + */ + public RatPoly(final Vector L) { + a = new Vector(); + for (int i = 0; i < L.size(); i++) + a.add(L.elementAt(i).clone()); + simplify(); + } /* ctor */ + + /** + * Constructor with a comma-separated list as the list of coefficients. + * + * @param L + * the string of the form a0,a1,a2,a3 with the coefficients + */ + public RatPoly(final String L) throws NumberFormatException { + a = new Vector(); + Scanner sc = new Scanner(L); + sc.useDelimiter(","); + while (sc.hasNext()) { + String tok = sc.next(); + a.add(new Rational(tok)); + } + simplify(); + sc.close(); + } /* ctor */ + + /** + * Constructor from a hypergeometric series. + * + * @param A + * the list of values in the numerator of AFB + * @param B + * the list of values in the denominator of AFB + * @param nmax + * the order of the truncated polynomial representation + * @throws Errore + * @since 2008-11-13 + */ + public RatPoly(final Vector A, final Vector B, int nmax) throws Errore { + /* + * To allow common initialization with the signature below, + * the main body is assembled in a separate function. + */ + init(A, B, nmax); + } + + /** + * Constructor from a hypergeometric series. + * + * @param A + * the list of values in the numerator of AFB. + * At least one of these values must be a negative integer, which + * implicitly determines + * the order of the new polynomial. + * @param B + * the list of values in the denominator of AFB + * @throws Errore + * @since 2009-08-05 + */ + public RatPoly(final Vector A, final Vector B) throws Errore { + BigInteger Nmax = BigInteger.ONE.negate(); + for (int j = 0; j < A.size(); j++) { + if (A.elementAt(j).compareTo(BigInteger.ZERO) <= 0) { + if (Nmax.compareTo(BigInteger.ZERO) < 0) + Nmax = A.elementAt(j).negate(); + else + Nmax = Nmax.min(A.elementAt(j).negate()); + } + } + if (Nmax.compareTo(BigInteger.ZERO) < 0) + throw new ArithmeticException("Infinite Number of Terms in Series " + Nmax.toString()); + + int nmax = Nmax.intValue() - 1; + init(A, B, nmax); + } /* ctor */ + + /** + * Constructor from a hypergeometric series. + * + * @param A + * the list of values in the numerator of AFB + * @param B + * the list of values in the denominator of AFB + * @param nmax + * the order of the truncated polynomial representation + * @throws Errore + * @since 2008-11-13 + */ + protected void init(final Vector A, final Vector B, int nmax) throws Errore { + a = new Vector(); + Factorial f = new Factorial(); + for (int n = 0; n <= nmax; n++) { + Rational c = new Rational(1, 1); + for (int j = 0; j < A.size(); j++) { + Rational aEl = new Rational(A.elementAt(j)); + c = c.multiply(aEl.Pochhammer(n)); + } + for (int j = 0; j < B.size(); j++) { + Rational bEl = new Rational(B.elementAt(j)); + c = c.divide(bEl.Pochhammer(n)); + } + c = c.divide(f.at(n)); + a.add(c); + } + simplify(); + } /* init */ + + /** + * Create a copy of this. + * + * @since 2008-11-07 + */ + @Override + @SuppressWarnings("unchecked") + public RatPoly clone() { + RatPoly clo = new RatPoly(); + clo.a = (Vector) a.clone(); + return clo; + } /* clone */ + + /** + * Retrieve a polynomial coefficient. + * + * @param n + * the zero-based index of the coefficient. n=0 for the constant + * term. + * @return the polynomial coefficient in front of x^n. + */ + public Rational at(final int n) { + if (n < a.size()) + return (a.elementAt(n)); + else + return (new Rational(0, 1)); + } /* at */ + + /** + * Horner scheme to find the function value at the argument x + * + * @param x + * The argument of the polynomial + * @param mc + * The context determining the precision of the value returned. + * @since 2008-10-26 + */ + public BigComplex valueOf(BigComplex x, MathContext mc) { + /* result is initialized to zero */ + BigComplex f = new BigComplex(); + for (int i = degree(); i >= 0; i--) + f = f.multiply(x, mc).add(a.elementAt(i).BigDecimalValue(mc)); + return f; + } /* valueOf */ + + /** + * Horner scheme to find the function value at the argument x + * + * @param x + * The argument of the polynomial + * @since 2008-11-13 + */ + public Rational valueOf(Rational x) { + /* result is initialized to zero */ + Rational f = new Rational(0, 1); + for (int i = degree(); i >= 0; i--) + f = f.multiply(x).add(a.elementAt(i)); + return f; + } /* valueOf */ + + /** + * Horner scheme to find the function value at the argument x + * + * @param x + * The argument of the polynomial + * @since 2008-11-13 + */ + public Rational valueOf(int x) { + return valueOf(new Rational(x, 1)); + } /* valueOf */ + + /** + * Horner scheme to evaluate the function at the argument x + * + * @param x + * The argument of the polynomial + * @since 2010-08-27 + */ + public Rational valueOf(BigInteger x) { + return valueOf(new Rational(x)); + } /* valueOf */ + + /* + * Set a polynomial coefficient. + * + * @param n the zero-based index of the coefficient. n=0 for the constant + * term. + * If the polynomial has not yet the degree to need this coefficient, + * the intermediate coefficients are implicitly set to zero. + * + * @param value the new value of the coefficient. + */ + public void set(final int n, final Rational value) { + if (n < a.size()) + a.set(n, value); + else { + /* + * fill intermediate powers with coefficients of zero + */ + while (a.size() < n) + a.add(new Rational(0, 1)); + a.add(value); + } + } /* set */ + + /** + * Set a polynomial coefficient. + * + * @param n + * the zero-based index of the coefficient. n=0 for the constant + * term. + * If the polynomial has not yet the degree to need this + * coefficient, + * the intermediate coefficients are implicitly set to zero. + * @param value + * the new value of the coefficient. + */ + public void set(final int n, final BigInteger value) { + Rational val2 = new Rational(value, BigInteger.ONE); + set(n, val2); + } /* set */ + + /** + * Set a polynomial coefficient. + * + * @param n + * the zero-based index of the coefficient. n=0 for the constant + * term. + * If the polynomial has not yet the degree to need this + * coefficient, + * the intermediate coefficients are implicitly set to zero. + * @param value + * the new value of the coefficient. + */ + public void set(final int n, final int value) { + Rational val2 = new Rational(value, 1); + set(n, val2); + } /* set */ + + /* + * Set to the taylor series of exp(x) up to degree nmax. + * + * @param nmax the maximum polynomial degree + */ + public void setExp(final int nmax) { + a.clear(); + Factorial factorial = new Factorial(); + for (int n = 0; n <= nmax; n++) + set(n, new Rational(BigInteger.ONE, factorial.at(n))); + } /* setExp */ + + /** + * Set to the taylor series representing 0+x. + */ + public void setx() { + a.clear(); + /* coefficient 0/1=0 */ + a.add(new Rational(0, 1)); + /* coefficient 1/1=1 */ + a.add(new Rational(1, 1)); + } /* setx */ + + /** + * Count of coefficients. One more than the degree of the polynomial. + * + * @return the number of polynomial coefficients. + */ + public int size() { + return a.size(); + } /* size */ + + /** + * Polynomial degree. + * + * @return the polynomial degree. + */ + public int degree() { + return a.size() - 1; + } /* degree */ + + /** + * Lower Polynomial degree. + * + * @return The smallest exponent n such that [x^n] of the polynomial is + * nonzero. + * If the polynmial is identical zero, the result is (still) 0. + * @since 2010-08-27 + */ + public int ldegree() { + for (int n = 0; n < a.size(); n++) + if (a.elementAt(n).compareTo(BigInteger.ZERO) != 0) + return n; + return 0; + } /* ldegree */ + + /** + * Multiply by a constant factor. + * + * @param val + * the factor + * @return the product of this with the factor. + * All coefficients of this have been multiplied individually by the + * factor. + */ + public RatPoly multiply(final Rational val) { + RatPoly resul = new RatPoly(); + if (val.compareTo(BigInteger.ZERO) != 0) + for (int n = 0; n < a.size(); n++) + resul.set(n, a.elementAt(n).multiply(val)); + return resul; + } /* multiply */ + + /** + * Multiply by a constant factor. + * + * @param val + * the factor + * @return the product of this with the factor. + * All coefficients of this have been multiplied individually by the + * factor. + * @since 2010-08-27 + */ + public RatPoly multiply(final BigInteger val) { + RatPoly resul = new RatPoly(); + if (val.compareTo(BigInteger.ZERO) != 0) + for (int n = 0; n < a.size(); n++) + resul.set(n, a.elementAt(n).multiply(val)); + return resul; + } /* multiply */ + + /** + * Multiply by another polynomial + * + * @param val + * the other polynomial + * @return the product of this with the other polynomial + */ + public RatPoly multiply(final RatPoly val) { + RatPoly resul = new RatPoly(); + /* + * the degree of the result is the sum of the two degrees. + */ + final int nmax = degree() + val.degree(); + for (int n = 0; n <= nmax; n++) { + Rational coef = new Rational(0, 1); + for (int nleft = 0; nleft <= n; nleft++) { + coef = coef.add(at(nleft).multiply(val.at(n - nleft))); + } + resul.set(n, coef); + } + resul.simplify(); + return resul; + } /* multiply */ + + /** + * Raise to a positive power. + * + * @param n + * The non-negative exponent of the power + * @return The n-th power of this. + */ + public RatPoly pow(final int n) throws ArithmeticException { + RatPoly resul = new RatPoly("1"); + if (n < 0) + throw new ArithmeticException("negative polynomial power " + n); + else { + /* + * this ought probably be done with some binary representation + * of the power and a smaller number of multiplications. + */ + for (int i = 1; i <= n; i++) + resul = resul.multiply(this); + resul.simplify(); + return resul; + } + } /* pow */ + + /** + * Raise to a rational power. + * The result is the taylor expansion of this, truncated at the first + * term that remains undetermined based on the current number of + * coefficients. + * + * @param r + * the exponent of the power + * @return This^r . + * @throws Errore + * @since 2009-05-18 + */ + public RatPoly pow(final Rational r) throws ArithmeticException, Errore { + /* + * split (a0+a1*x+a2*x^2+...)^r = a0^r*(1+a1/a0*r+a2/a0*r^2+..)^r + */ + Rational f = at(0); + f = f.pow(r); + + /* + * scale the polynomial by division through the expansion coefficient of + * the absolute term + */ + RatPoly red = divide(a.elementAt(0)); + + /* + * and remove the leading term (now equal to 1) + */ + red.set(0, 0); + + /* + * Binomial expansion of the rest. sum_{l=0..infinity} + * binomial(r,l)*red^l + */ + RatPoly resul = new RatPoly("1"); + + final int d = degree(); + for (int l = 1; l <= d; l++) { + final Rational b = Rational.binomial(r, l); + resul = resul.add(red.pow(l).multiply(b)); + } + return resul.multiply(f); + } /* pow */ + + /** + * Add another polynomial + * + * @param val + * The other polynomial + * @return The sum of this and the other polynomial + * @since 2008-10-25 + */ + public RatPoly add(final RatPoly val) { + RatPoly resul = new RatPoly(); + /* + * the degree of the result is the larger of the two degrees (before + * simplify() at least). + */ + final int nmax = (degree() > val.degree()) ? degree() : val.degree(); + for (int n = 0; n <= nmax; n++) { + Rational coef = at(n).add(val.at(n)); + resul.set(n, coef); + } + resul.simplify(); + return resul; + } /* add */ + + /** + * Subtract another polynomial + * + * @param val + * The other polynomial + * @return The difference between this and the other polynomial + * @since 2008-10-25 + */ + public RatPoly subtract(final RatPoly val) { + RatPoly resul = new RatPoly(); + /* + * the degree of the result is the larger of the two degrees (before + * simplify() at least). + */ + final int nmax = (degree() > val.degree()) ? degree() : val.degree(); + for (int n = 0; n <= nmax; n++) { + Rational coef = at(n).subtract(val.at(n)); + resul.set(n, coef); + } + resul.simplify(); + return resul; + } /* subtract */ + + /** + * Divide by a constant. + * + * @param val + * the constant through which the coefficients will be divided. + * @return the Taylor expansion of this/val . + * @throws Errore + * @since 2009-05-18 + */ + public RatPoly divide(final Rational val) throws Errore { + if (val.compareTo(Rational.ZERO) != 0) { + RatPoly resul = new RatPoly(); + for (int n = 0; n < a.size(); n++) + resul.set(n, a.elementAt(n).divide(val)); + return resul; + } else + throw new ArithmeticException("Cannot divide " + toPString() + " through zero."); + } /* divide */ + + /** + * Divide by another polynomial. + * + * @param val + * the other polynomial + * @param nmax + * the maximum degree of the Taylor expansion of the result. + * @return the Taylor expansion of this/val up to degree nmax. + * @throws Errore + */ + public RatPoly divide(final RatPoly val, int nmax) throws Errore { + RatPoly num = this; + RatPoly denom = val; + + /* + * divide by a common smallest power/degree + */ + while (num.at(0).compareTo(BigInteger.ZERO) == 0 && denom.at(0).compareTo(BigInteger.ZERO) == 0) { + num.a.remove(0); + denom.a.remove(0); + if (num.size() <= 1 || denom.size() <= 1) + break; + } + + RatPoly resul = new RatPoly(); + /* + * todo: If the polynomial division is exact, we could leave + * the loop earlier, indeed + */ + for (int n = 0; n <= nmax; n++) { + Rational coef = num.at(n); + for (int nres = 0; nres < n; nres++) { + coef = coef.subtract(resul.at(nres).multiply(denom.at(n - nres))); + } + coef = coef.divide(denom.at(0)); + resul.set(n, coef); + } + resul.simplify(); + return (resul); + } /* divide */ + + /** + * Divide by another polynomial. + * + * @param val + * the other polynomial + * @return A vector with [0] containg the polynomial of degree which is the + * difference of thisdegree and the degree of val. [1] the remainder + * polynomial. + * This = returnvalue[0] + returnvalue[1]/val . + * @throws Errore + * @since 2012-03-01 + */ + public RatPoly[] divideAndRemainder(final RatPoly val) throws Errore { + RatPoly[] ret = new RatPoly[2]; + /* + * remove any high-order zeros + */ + RatPoly valSimpl = val.clone(); + valSimpl.simplify(); + RatPoly thisSimpl = clone(); + thisSimpl.simplify(); + + /* + * catch the case with val equal to zero + */ + if (valSimpl.degree() == 0 && valSimpl.a.firstElement().compareTo(Rational.ZERO) == 0) + throw new ArithmeticException("Division through zero polynomial"); + /* + * degree of this smaller than degree of val: remainder is this + */ + if (thisSimpl.degree() < valSimpl.degree()) { + /* + * leading polynomial equals zero + */ + ret[0] = new RatPoly(); + ret[1] = thisSimpl; + } else { + /* + * long division. Highest degree by dividing the highest degree + * of this thru val. + */ + ret[0] = new RatPoly(); + ret[0].set(thisSimpl.degree() - valSimpl.degree(), thisSimpl.a.lastElement().divide(valSimpl.a.lastElement())); + + /* + * recurrences: build this - val*(1-termresult) and feed this + * into another round of division. Have intermediate + * ret[0]+ret[1]/val. + */ + ret[1] = thisSimpl.subtract(ret[0].multiply(valSimpl)); + + /* + * any remainder left ? + */ + if (ret[1].degree() < valSimpl.degree()) + ; + else { + RatPoly rem[] = ret[1].divideAndRemainder(val); + ret[0] = ret[0].add(rem[0]); + ret[1] = rem[1]; + } + } + return ret; + } /* divideAndRemainder */ + + /** + * Print as a comma-separated list of coefficients. + * + * @return The representation a0,a1,a2,a3,... + * This is a sort of opposite of the ctor that takes a string as an + * argument. + * @since 2008-10-25 + */ + @Override + public String toString() { + String str = new String(); + for (int n = 0; n < a.size(); n++) { + if (n == 0) + str += a.elementAt(n).toString(); + else + str += "," + a.elementAt(n).toString(); + } + /* + * print at least a sole zero + */ + if (str.length() == 0) + str = "0"; + return str; + } /* toString */ + + /** + * Print as a polyomial in x. + * + * @return To representation a0+a1*x+a2*x^2+... + * This does not print the terms with coefficients equal to zero. + * @since 2008-10-26 + */ + public String toPString() { + String str = new String(); + for (int n = 0; n < a.size(); n++) { + final BigInteger num = a.elementAt(n).a; + if (num.compareTo(BigInteger.ZERO) != 0) { + str += " "; + if (num.compareTo(BigInteger.ZERO) > 0) + str += "+"; + str += a.elementAt(n).toString(); + if (n > 0) { + str += "*x"; + if (n > 1) + str += "^" + n; + } + } + } + /* + * print at least a sole zero + */ + if (str.length() == 0) + str = "0"; + return str; + } /* toPString */ + + /** + * Simplify the representation. + * Trailing values with zero coefficients (at high powers) are deleted. + * This modifies the polynomial on the stop (does not return another + * instance) + */ + private void simplify() { + int n = a.size() - 1; + if (n >= 0) + while (a.elementAt(n).compareTo(BigInteger.ZERO) == 0) { + a.remove(n); + if (--n < 0) + break; + } + } /* simplify */ + + /** + * First derivative. + * + * @return The first derivative with respect to the indeterminate variable. + * @since 2008-10-26 + */ + public RatPoly derive() { + if (a.size() <= 1) + /* + * derivative of the constant is just zero + */ + return new RatPoly(); + else { + RatPoly d = new RatPoly(); + for (int i = 1; i <= degree(); i++) { + final Rational c = a.elementAt(i).multiply(i); + d.set(i - 1, c); + } + return d; + } + } /* derive */ + + /** + * Scale coefficients such that the coefficient in front of the maximum + * degree is unity. + * + * @return The scaled polynomial + * @throws Errore + * @since 2008-10-26 + */ + public RatPoly monic() throws Errore { + RatPoly m = new RatPoly(); + final int d = degree(); + for (int i = 0; i <= d; i++) { + final Rational c = a.elementAt(i).divide(a.elementAt(d)); + m.set(i, c); + } + return m; + } /* monic */ + + /** + * Mobius transform. + * + * @param maxdeg + * the maximum polynomial degree of the result + * @return the sequence of coefficients is the Mobius transform of the + * original sequence. + * @since 2008-12-02 + */ + public RatPoly mobiusT(int maxdeg) { + /* + * Start with the polynomial 0 + */ + RatPoly r = new RatPoly(); + for (int i = 1; i <= maxdeg; i++) { + Rational c = new Rational(); + for (int d = 1; d <= i && d < a.size(); d++) { + if (i % d == 0) { + final Ifactor m = new Ifactor(i / d); + c = c.add(a.elementAt(d).multiply(m.moebius())); + } + } + r.set(i, c); + } + r.simplify(); + return r; + } /* mobiusT */ + + /** + * Inverse Mobius transform. + * + * @param maxdeg + * the maximum polynomial degree of the result + * @return the sequence of coefficients is the inverse Mobius transform of + * the original sequence. + * @since 2008-12-02 + */ + public RatPoly mobiusTInv(int maxdeg) { + /* + * Start with the polynomial 0 + */ + RatPoly r = new RatPoly(); + for (int i = 1; i <= maxdeg; i++) { + Rational c = new Rational(); + for (int d = 1; d <= i && d < a.size(); d++) { + if (i % d == 0) + c = c.add(a.elementAt(d)); + } + r.set(i, c); + } + r.simplify(); + return r; + } /* mobiusTInv */ + + /** + * Binomial transform. + * + * @param maxdeg + * the maximum polynomial degree of the result + * @return the sequence of coefficients is the binomial transform of the + * original sequence. + * @since 2008-10-26 + */ + public RatPoly binomialT(int maxdeg) { + RatPoly r = new RatPoly(); + for (int i = 0; i <= maxdeg; i++) { + Rational c = new Rational(0, 1); + for (int j = 0; j <= i && j < a.size(); j++) + c = c.add(a.elementAt(j).multiply(BigIntegerMath.binomial(i, j))); + r.set(i, c); + } + r.simplify(); + return r; + } /* binomialT */ + + /** + * Inverse Binomial transform. + * + * @param maxdeg + * the maximum polynomial degree of the result + * @return the sequence of coefficients is the inverse binomial transform of + * the original sequence. + * @since 2008-10-26 + */ + public RatPoly binomialTInv(int maxdeg) { + RatPoly r = new RatPoly(); + for (int i = 0; i <= maxdeg; i++) { + Rational c = new Rational(0, 1); + for (int j = 0; j <= i && j < a.size(); j++) + if ((j + i) % 2 != 0) + c = c.subtract(a.elementAt(j).multiply(BigIntegerMath.binomial(i, j))); + else + c = c.add(a.elementAt(j).multiply(BigIntegerMath.binomial(i, j))); + r.set(i, c); + } + r.simplify(); + return r; + } /* binomialTInv */ + + /** + * Truncate polynomial degree. + * + * @param newdeg + * The maximum degree of the result. + * @return The polynomial with all coefficients beyond deg set to zero. + * If newdeg =3, for example the polynomial returned has at most + * degree 3. + * If newdeg is larger than the degree of this, zeros (at the higher + * orders of x) + * are appended. That polynomial would have formal degree larger + * than this. + * @since 2008-10-26 + */ + public RatPoly trunc(int newdeg) { + RatPoly t = new RatPoly(); + for (int i = 0; i <= newdeg; i++) + t.set(i, at(i)); + t.simplify(); + return t; + } /* trunc */ + + /** + * Generate the roots of the polynomial in floating point arithmetic. + * + * @see Durand + * Kerner method + * @param the + * number of floating point digits + * @throws Errore + * @since 2008-10-26 + */ + public Vector roots(int digits) throws Errore { + RatPoly mon = monic(); + + Random rand = new Random(); + MathContext mc = new MathContext(digits + 3, RoundingMode.DOWN); + + Vector res = new Vector(); + + final int d = mon.degree(); + double randRad = 0.; + for (int i = 0; i <= d; i++) { + /* scale coefficient at maximum degree */ + double absi = Math.abs(mon.at(i).doubleValue()); + if (absi > randRad) + randRad = absi; + } + randRad += 1.0; + + /* + * initial values randomly in radius 1+randRad + */ + for (int i = 0; i < d; i++) { + double rad = randRad * rand.nextDouble(); + double phi = 2.0 * 3.14159 * rand.nextDouble(); + res.add(i, new BigComplex(rad * Math.cos(phi), rad * Math.sin(phi))); + } + + /* + * iterate until convr indicates that all values changed by less than + * the digits + * precision indicates. + */ + boolean convr = false; + for (; !convr;)// ORIGINAL LINE: for(int itr =0 ; ! convr ; itr++) + { + convr = true; + Vector resPlus = new Vector(); + for (int v = 0; v < d; v++) { + /* + * evaluate f(x)/(x-root1)/(x-root2)/... (x-rootdegr), Newton + * method + */ + BigComplex thisx = res.elementAt(v); + BigComplex nv = mon.valueOf(thisx, mc); + for (int j = 0; j < d; j++) { + if (j != v) + nv = nv.divide(thisx.subtract(res.elementAt(j)), mc); + } + + /* is this value converged ? */ + if (nv.abs(mc).doubleValue() > thisx.abs(mc).doubleValue() * Math.pow(10.0, -digits)) + convr = false; + + thisx = thisx.subtract(nv); + + /* If unstable, start over */ + if (thisx.abs(MathContext.DECIMAL32).doubleValue() > randRad) + return roots(digits); + + resPlus.add(thisx); + } + res = resPlus; + + } + return res; + } /* roots */ + + /** + * Generate the integer roots of the polynomial. + * + * @return The vector of integer roots, with multiplicity. + * The shows alternatingly first a root then its multiplicty, then + * another root and multiplicty etc. + * @since 2008-10-26 + */ + public Vector iroots() { + /* The vector of the roots */ + Vector res = new Vector(); + + int lowd = ldegree(); + if (lowd == 0 && a.elementAt(0).compareTo(BigInteger.ZERO) == 0) { + /* + * Case of polynomial identical to zero: + * reported as a simple root of value 0. + */ + res.add(BigInteger.ZERO); + res.add(BigInteger.ONE); + return res; + } + + /* + * multiply all coefs with the lcm() to get an integer polynomial + * start with denominator of first non-zero coefficient. + */ + BigInteger lcmDeno = a.elementAt(lowd).b; + for (int i = lowd + 1; i < degree(); i++) + lcmDeno = BigIntegerMath.lcm(lcmDeno, a.elementAt(i).b); + + /* + * and eventually get the integer polynomial by ignoring the + * denominators + */ + Vector ipo = new Vector(); + for (int i = 0; i < a.size(); i++) { + BigInteger d = a.elementAt(i).a.multiply(lcmDeno).divide(a.elementAt(i).b); + ipo.add(d); + } + + BigIntegerPoly p = new BigIntegerPoly(ipo); + /* + * collect the integer roots (multiple roots only once). Since we + * removed the zero already above, cand does not contain zeros. + */ + Vector cand = p.iroots(); + for (int i = 0; i < cand.size(); i++) { + final BigInteger r = cand.elementAt(i); + int deg = p.rootDeg(r); + res.add(r); + res.add(new BigInteger("" + deg)); + } + + return res; + } /* iroots */ } /* RatPoly */ diff --git a/src/org/nevec/rjm/Rational.java b/src/org/nevec/rjm/Rational.java index 78034164..0ddc0a86 100644 --- a/src/org/nevec/rjm/Rational.java +++ b/src/org/nevec/rjm/Rational.java @@ -5,8 +5,8 @@ import java.math.BigInteger; import java.math.MathContext; import java.math.RoundingMode; -import org.warpgate.pi.calculator.Errore; -import org.warpgate.pi.calculator.Errori; +import org.warp.picalculator.Errore; +import org.warp.picalculator.Errori; /** * Fractions (rational numbers). They are divisions of two BigInteger numbers, @@ -153,6 +153,7 @@ public class Rational implements Cloneable, Comparable { * * @since 2008-11-07 */ + @Override public Rational clone() { /* * protected access means this does not work return new @@ -231,7 +232,7 @@ public class Rational implements Cloneable, Comparable { * the exponent. * @return this value raised to the power given by the exponent. If the * exponent is 0, the value 1 is returned. - * @throws Errore + * @throws Errore * @since 2009-05-18 */ public Rational pow(BigInteger exponent) throws Errore { @@ -253,7 +254,7 @@ public class Rational implements Cloneable, Comparable { * third root etc * @return this value raised to the inverse power given by the root * argument, this^(1/r). - * @throws Errore + * @throws Errore * @since 2009-05-18 */ public Rational root(BigInteger r) throws Errore { @@ -293,7 +294,7 @@ public class Rational implements Cloneable, Comparable { * The exponent. * @return This value raised to the power given by the exponent. If the * exponent is 0, the value 1 is returned. - * @throws Errore + * @throws Errore * @since 2009-05-18 */ public Rational pow(Rational exponent) throws Errore { @@ -314,7 +315,7 @@ public class Rational implements Cloneable, Comparable { * @param val * A second rational number. * @return The value of this/val - * @throws Errore + * @throws Errore */ public Rational divide(final Rational val) throws Errore { if (val.compareTo(Rational.ZERO) == 0) @@ -334,7 +335,7 @@ public class Rational implements Cloneable, Comparable { * @param val * a second number. * @return the value of this/val - * @throws Errore + * @throws Errore */ public Rational divide(BigInteger val) throws Errore { if (val.compareTo(BigInteger.ZERO) == 0) @@ -349,7 +350,7 @@ public class Rational implements Cloneable, Comparable { * @param val * A second number. * @return The value of this/val - * @throws Errore + * @throws Errore */ public Rational divide(int val) throws Errore { if (val == 0) @@ -451,7 +452,7 @@ public class Rational implements Cloneable, Comparable { * @return the binomial coefficient. * @since 2006-06-27 * @author Richard J. Mathar - * @throws Errore + * @throws Errore */ public static Rational binomial(Rational n, BigInteger m) throws Errore { if (m.compareTo(BigInteger.ZERO) == 0) @@ -473,7 +474,7 @@ public class Rational implements Cloneable, Comparable { * @return the binomial coefficient. * @since 2009-05-19 * @author Richard J. Mathar - * @throws Errore + * @throws Errore */ public static Rational binomial(Rational n, int m) throws Errore { if (m == 0) @@ -495,7 +496,7 @@ public class Rational implements Cloneable, Comparable { * @return Gamma(n+k+1/2)/k!/GAMMA(n-k+1/2) * @since 2010-07-18 * @author Richard J. Mathar - * @throws Errore + * @throws Errore */ public static Rational hankelSymb(Rational n, int k) throws Errore { if (k == 0) @@ -593,6 +594,7 @@ public class Rational implements Cloneable, Comparable { * @return -1, 0 or 1 if this number is numerically less than, equal to, or * greater than val. */ + @Override public int compareTo(final Rational val) { /* * Since we have always kept the denominators positive, simple @@ -622,6 +624,7 @@ public class Rational implements Cloneable, Comparable { * * @return the human-readable version in base 10 */ + @Override public String toString() { if (b.compareTo(BigInteger.ONE) != 0) return (a.toString() + "/" + b.toString()); @@ -788,7 +791,8 @@ public class Rational implements Cloneable, Comparable { /** * Conversion to an integer value, if this can be done exactly. - * @throws Errore + * + * @throws Errore * * @since 2011-02-13 */ @@ -800,7 +804,8 @@ public class Rational implements Cloneable, Comparable { /** * Conversion to a BigInteger value, if this can be done exactly. - * @throws Errore + * + * @throws Errore * * @since 2012-03-02 */ @@ -817,8 +822,7 @@ public class Rational implements Cloneable, Comparable { * @since 2010-05-26 */ public boolean isIntegerFrac() { - return (a.compareTo(MAX_INT) <= 0 && a.compareTo(MIN_INT) >= 0 && b.compareTo(MAX_INT) <= 0 - && b.compareTo(MIN_INT) >= 0); + return (a.compareTo(MAX_INT) <= 0 && a.compareTo(MIN_INT) >= 0 && b.compareTo(MAX_INT) <= 0 && b.compareTo(MIN_INT) >= 0); } /* Rational.isIntegerFrac */ /** diff --git a/src/org/nevec/rjm/Wigner3j.java b/src/org/nevec/rjm/Wigner3j.java index 3c55a874..17c18dac 100644 --- a/src/org/nevec/rjm/Wigner3j.java +++ b/src/org/nevec/rjm/Wigner3j.java @@ -3,7 +3,7 @@ package org.nevec.rjm; import java.math.BigInteger; import java.util.Scanner; -import org.warpgate.pi.calculator.Errore; +import org.warp.picalculator.Errore; /** * Exact representations of Wigner 3jm and 3nj values of half-integer arguments. @@ -32,7 +32,7 @@ public class Wigner3j { * * @since 2011-02-15 * @author Richard J. Mathar - * @throws Errore + * @throws Errore */ static public void main(String args[]) throws Errore { if (args[0].compareTo("6j") == 0) { @@ -105,7 +105,7 @@ public class Wigner3j { * inequalities is violated or some parameters are out of range. * @since 2011-02-13 * @author Richard J. Mathar - * @throws Errore + * @throws Errore */ static public BigSurd wigner3jm(int j1, int j2, int j3, int m1, int m2, int m3) throws Errore { Rational J1 = new Rational(j1, 2); @@ -140,7 +140,7 @@ public class Wigner3j { * @since 2011-02-13 * @since 2012-02-15 Upgraded return value to BigSurdVec * @author Richard J. Mathar - * @throws Errore + * @throws Errore */ static public BigSurdVec wigner3j(String m1, String t1, String t2, String j) throws Errore { /* @@ -270,9 +270,10 @@ public class Wigner3j { * @since 2011-02-13 * @since 2012-02-15 Upgraded to return BigSurdVec * @author Richard J. Mathar - * @throws Errore + * @throws Errore */ - static private BigSurdVec wigner3j(final int[] tvec, final Rational[] J, final Rational[] M, final int[] triadidx) throws Errore { + static private BigSurdVec wigner3j(final int[] tvec, final Rational[] J, final Rational[] M, final int[] triadidx) + throws Errore { /* * The result of the computation. The sum over all m-combinations of the * triads. @@ -479,9 +480,10 @@ public class Wigner3j { * inequalities is violated or some parameters are out of range. * @since 2011-02-13 * @author Richard J. Mathar - * @throws Errore + * @throws Errore */ - static protected BigSurd wigner3jm(Rational j1, Rational j2, Rational j3, Rational m1, Rational m2, Rational m3) throws Errore { + static protected BigSurd wigner3jm(Rational j1, Rational j2, Rational j3, Rational m1, Rational m2, Rational m3) + throws Errore { /* * Check that m1+m2+m3 = 0 */ @@ -549,8 +551,7 @@ public class Wigner3j { Factorial f = new Factorial(); Rational sumk = new Rational(); while (true) { - BigInteger d = f.at(k).multiply(f.at(j1j2jk)).multiply(f.at(j1m1k)).multiply(f.at(j2m2k)) - .multiply(f.at(jj2m1k)).multiply(f.at(jj1m2k)); + BigInteger d = f.at(k).multiply(f.at(j1j2jk)).multiply(f.at(j1m1k)).multiply(f.at(j2m2k)).multiply(f.at(jj2m1k)).multiply(f.at(jj1m2k)); if (k % 2 == 0) sumk = sumk.add(new Rational(BigInteger.ONE, d)); else diff --git a/src/org/nevec/rjm/Wigner3jGUI.java b/src/org/nevec/rjm/Wigner3jGUI.java index 6c9e245d..e6ffea8e 100644 --- a/src/org/nevec/rjm/Wigner3jGUI.java +++ b/src/org/nevec/rjm/Wigner3jGUI.java @@ -16,7 +16,7 @@ import javax.swing.JList; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; -import org.warpgate.pi.calculator.Errore; +import org.warp.picalculator.Errore; /** * An interactive interface to the Wigner3j class. The GUI allows to preselect @@ -41,8 +41,7 @@ public class Wigner3jGUI implements ActionListener, ListSelectionListener { JButton sear; JList searJ; - String[] searOpt = { "6j", "9j", "12j 1st", "12j 2nd (not symm)", "15j 1st", "15j 2nd", "15j 3rd", "15j 4th", - "15j 5th" }; + String[] searOpt = { "6j", "9j", "12j 1st", "12j 2nd (not symm)", "15j 1st", "15j 2nd", "15j 3rd", "15j 4th", "15j 5th" }; /** * Field with the triads inputs @@ -135,7 +134,7 @@ public class Wigner3jGUI implements ActionListener, ListSelectionListener { } /* init */ /** - * @throws Errore + * @throws Errore * @since 2010-08-27 */ public void compute() throws Errore { @@ -201,6 +200,7 @@ public class Wigner3jGUI implements ActionListener, ListSelectionListener { * the information on which button had been pressed in the GUI * @since 2011-02-15 */ + @Override public void actionPerformed(ActionEvent e) { String lin = e.getActionCommand(); /* @@ -226,99 +226,96 @@ public class Wigner3jGUI implements ActionListener, ListSelectionListener { * selected in the Menu * @since 2011-02-18 */ + @Override public void valueChanged(ListSelectionEvent e) { switch (searJ.getMinSelectionIndex()) { - case 0: - inpGtria.setText("6\n"); - inpGtria.append("1 2 -3 -1 5 6\n"); - inpGtria.append("4 -5 3 -4 -2 -6"); - outG.setText(""); - break; - case 1: - /* - * Yutsis Figure 18.1 index map. j1=1, j2=2, j3=3 k1=4, k2=5, k3=6 - * l1=7, l2=8, l3=9 - */ - inpGtria.setText("9\n"); - inpGtria.append("1 3 2 4 6 5 7 9 8 # (j1 j3 j2) (k1 k3 k2) (l1 l3 l2)\n"); - inpGtria.append("-2 -8 -5 -6 -3 -9 -7 -4 -1 # (j2 l2 k2) (k3 j3 l3) (l1 k1 j1)"); - outG.setText(""); - break; - case 2: - /* - * Yutsis Figure 19.1 and 19.2, index map, including the sign - * reveral of the l. Assume input order j1..j4, l1..l4, k1..k4. - * j1=1, j2=2, j3=3, j4=4 l1=5, l2=6, l3=7, l4=8 k1=9, k2=10, k3=11, - * k4=12 - */ - inpGtria.setText("12\n"); - inpGtria.append("1 12 -8 -1 5 -2 2 6 -3 3 7 -4 # (j1 k4 l4) (j1 l1 j2) (j2 l2 j3) (j3 l3 j4)\n"); - inpGtria.append("4 8 -9 9 -5 -10 10 -6 -11 11 -7 -12 # (j4 l4 k1) (k1 l1 k2) (k2 l2 k3) (k3 l3 k4)"); - outG.setText(""); - break; - case 3: - inpGtria.setText("12\n"); - inpGtria.append("1 5 9 -9 -2 -7 2 11 8 -8 -12 -4 # (j1 l1 k1) (k1 j2 l3 ) (j2 k3 l4) (l4 k4 j4)\n"); - inpGtria.append("4 7 10 -10 -3 -5 3 6 12 -6 -11 -1 # (j4 l3 k2) (k2 j3 l1) (j3 l2 k4) (l2 k3 j1)"); - outG.setText(""); - break; - case 4: - /* - * Yutsis Figure 20.2 to 20.3, index map. j1=1, j2=2, j3=3, j4=4, - * j5=5 l1=6, l2=7, l3=8, l4=9, l5=10 k1=11, k2=12, k3=13, k4=14, - * k5=15 - */ - inpGtria.setText("15\n"); - inpGtria.append( - "1 -6 2 -2 -7 3 -3 -8 4 -4 -9 5 -5 -10 11 # (j1 l1 j2)(j2 l2 j3)(j3 l3 j4)(j4 l4 j5)(j5 l5 k1)\n"); - inpGtria.append( - "-11 6 12 -12 7 13 -13 8 14 -14 9 15 -15 10 -1 # (k1 l1 k2)(k2 l2 k3)(k3 l3 k4)(k4 l4 k5)(k5 l5 j1)"); - outG.setText(""); - break; - case 5: - inpGtria.setText("15\n"); - inpGtria.append( - "-1 -6 2 -2 -7 3 -3 -8 4 -4 -9 5 1 -5 -10 # (j1 l1 j2)(j2 l2 j3)(j3 l3 j4)(j4 l4 j5)(j1 j5 l5)\n"); - inpGtria.append( - "11 -15 10 9 15 -14 8 14 -13 7 13 -12 6 12 -11 # (k1 k5 l5)(l4 k5 k4)(l3 k4 k3)(l2 k3 k2)(l1 k2 k1)"); - outG.setText(""); - break; - case 6: - /* - * Yutsis Figure 20.4a, index map. k1=1, k1'=2, k=3, k'=4, k2=5, - * k2'=6 p1=7, p=8, p2=9, j1=10, j1'=11 j=12 j'=13 j2=14 j2'=15 - */ - inpGtria.setText("15\n"); - inpGtria.append( - "-13 -12 -8 12 14 10 -10 -1 7 -7 -11 -2 2 4 6 # (j' j p)(j j2 j1)(j1 k1 p1)(p1 j1' k1')(k1' k' k2')\n"); - inpGtria.append( - "-4 -3 8 1 3 5 -14 -5 9 -15 -6 -9 15 11 13 # (k' k p)(k1 k k2)(j2 k2 p2)(j2' k2' p2)(j2' j1' j')"); - outG.setText(""); - break; - case 7: - /* - * Yutsis Figure 20.5a, index map. j1=1, k1=2 s1=3 k1'=4 j1'=5 p=6 - * l=7 s=8 l'=9 p'=10 j2=11 k2=12 s2=13 k2'=14 j2'=15 - */ - inpGtria.setText("15\n"); - inpGtria.append( - "-14 -12 -8 12 11 -10 -11 13 -7 7 -1 3 2 1 6 # (k2' k2 s)(k2 j2 p')(j2 s2 l)(l j1 s1)(k1 j1 p)\n"); - inpGtria.append( - "-4 -2 8 10 4 5 9 -5 -3 -13 -9 -15 15 -6 14 # (k1' k1 s)(p' k1' j1')(l' j1' s1)(s2 l' j2')(j2' p k2')"); - outG.setText(""); - break; - case 8: - /* - * Yutsis Figure 20.6, index map. k1=1 k1'=2 j1=3 l1=4 l1'=5 k2=6 - * k2'=7 j2=8 l2=9 l2'=10 k3=11 k3'=12 j3=13 l3=14 l3'=15 - */ - inpGtria.setText("15\n"); - inpGtria.append( - "-15 1 -7 -4 -11 7 5 4 -3 -12 -5 6 12 -9 -1 # (l3' k1 k2')(l1 k3 k2')(l1' l1 j1)(k3' l1' k2)(k3' l2 k1)\n"); - inpGtria.append( - "9 -8 10 -10 11 -2 -14 -6 2 14 -13 15 3 8 13 # (l2 j2 l2')(l2' k3 k1')(l3 k2 k1')(l3 j3 l3')(j1 j2 j3)"); - outG.setText(""); - break; + case 0: + inpGtria.setText("6\n"); + inpGtria.append("1 2 -3 -1 5 6\n"); + inpGtria.append("4 -5 3 -4 -2 -6"); + outG.setText(""); + break; + case 1: + /* + * Yutsis Figure 18.1 index map. j1=1, j2=2, j3=3 k1=4, k2=5, + * k3=6 + * l1=7, l2=8, l3=9 + */ + inpGtria.setText("9\n"); + inpGtria.append("1 3 2 4 6 5 7 9 8 # (j1 j3 j2) (k1 k3 k2) (l1 l3 l2)\n"); + inpGtria.append("-2 -8 -5 -6 -3 -9 -7 -4 -1 # (j2 l2 k2) (k3 j3 l3) (l1 k1 j1)"); + outG.setText(""); + break; + case 2: + /* + * Yutsis Figure 19.1 and 19.2, index map, including the sign + * reveral of the l. Assume input order j1..j4, l1..l4, k1..k4. + * j1=1, j2=2, j3=3, j4=4 l1=5, l2=6, l3=7, l4=8 k1=9, k2=10, + * k3=11, + * k4=12 + */ + inpGtria.setText("12\n"); + inpGtria.append("1 12 -8 -1 5 -2 2 6 -3 3 7 -4 # (j1 k4 l4) (j1 l1 j2) (j2 l2 j3) (j3 l3 j4)\n"); + inpGtria.append("4 8 -9 9 -5 -10 10 -6 -11 11 -7 -12 # (j4 l4 k1) (k1 l1 k2) (k2 l2 k3) (k3 l3 k4)"); + outG.setText(""); + break; + case 3: + inpGtria.setText("12\n"); + inpGtria.append("1 5 9 -9 -2 -7 2 11 8 -8 -12 -4 # (j1 l1 k1) (k1 j2 l3 ) (j2 k3 l4) (l4 k4 j4)\n"); + inpGtria.append("4 7 10 -10 -3 -5 3 6 12 -6 -11 -1 # (j4 l3 k2) (k2 j3 l1) (j3 l2 k4) (l2 k3 j1)"); + outG.setText(""); + break; + case 4: + /* + * Yutsis Figure 20.2 to 20.3, index map. j1=1, j2=2, j3=3, + * j4=4, + * j5=5 l1=6, l2=7, l3=8, l4=9, l5=10 k1=11, k2=12, k3=13, + * k4=14, + * k5=15 + */ + inpGtria.setText("15\n"); + inpGtria.append("1 -6 2 -2 -7 3 -3 -8 4 -4 -9 5 -5 -10 11 # (j1 l1 j2)(j2 l2 j3)(j3 l3 j4)(j4 l4 j5)(j5 l5 k1)\n"); + inpGtria.append("-11 6 12 -12 7 13 -13 8 14 -14 9 15 -15 10 -1 # (k1 l1 k2)(k2 l2 k3)(k3 l3 k4)(k4 l4 k5)(k5 l5 j1)"); + outG.setText(""); + break; + case 5: + inpGtria.setText("15\n"); + inpGtria.append("-1 -6 2 -2 -7 3 -3 -8 4 -4 -9 5 1 -5 -10 # (j1 l1 j2)(j2 l2 j3)(j3 l3 j4)(j4 l4 j5)(j1 j5 l5)\n"); + inpGtria.append("11 -15 10 9 15 -14 8 14 -13 7 13 -12 6 12 -11 # (k1 k5 l5)(l4 k5 k4)(l3 k4 k3)(l2 k3 k2)(l1 k2 k1)"); + outG.setText(""); + break; + case 6: + /* + * Yutsis Figure 20.4a, index map. k1=1, k1'=2, k=3, k'=4, k2=5, + * k2'=6 p1=7, p=8, p2=9, j1=10, j1'=11 j=12 j'=13 j2=14 j2'=15 + */ + inpGtria.setText("15\n"); + inpGtria.append("-13 -12 -8 12 14 10 -10 -1 7 -7 -11 -2 2 4 6 # (j' j p)(j j2 j1)(j1 k1 p1)(p1 j1' k1')(k1' k' k2')\n"); + inpGtria.append("-4 -3 8 1 3 5 -14 -5 9 -15 -6 -9 15 11 13 # (k' k p)(k1 k k2)(j2 k2 p2)(j2' k2' p2)(j2' j1' j')"); + outG.setText(""); + break; + case 7: + /* + * Yutsis Figure 20.5a, index map. j1=1, k1=2 s1=3 k1'=4 j1'=5 + * p=6 + * l=7 s=8 l'=9 p'=10 j2=11 k2=12 s2=13 k2'=14 j2'=15 + */ + inpGtria.setText("15\n"); + inpGtria.append("-14 -12 -8 12 11 -10 -11 13 -7 7 -1 3 2 1 6 # (k2' k2 s)(k2 j2 p')(j2 s2 l)(l j1 s1)(k1 j1 p)\n"); + inpGtria.append("-4 -2 8 10 4 5 9 -5 -3 -13 -9 -15 15 -6 14 # (k1' k1 s)(p' k1' j1')(l' j1' s1)(s2 l' j2')(j2' p k2')"); + outG.setText(""); + break; + case 8: + /* + * Yutsis Figure 20.6, index map. k1=1 k1'=2 j1=3 l1=4 l1'=5 + * k2=6 + * k2'=7 j2=8 l2=9 l2'=10 k3=11 k3'=12 j3=13 l3=14 l3'=15 + */ + inpGtria.setText("15\n"); + inpGtria.append("-15 1 -7 -4 -11 7 5 4 -3 -12 -5 6 12 -9 -1 # (l3' k1 k2')(l1 k3 k2')(l1' l1 j1)(k3' l1' k2)(k3' l2 k1)\n"); + inpGtria.append("9 -8 10 -10 11 -2 -14 -6 2 14 -13 15 3 8 13 # (l2 j2 l2')(l2' k3 k1')(l3 k2 k1')(l3 j3 l3')(j1 j2 j3)"); + outG.setText(""); + break; } } /* valueChanged */ diff --git a/src/org/warp/device/Keyboard.java b/src/org/warp/device/Keyboard.java new file mode 100644 index 00000000..e75a3b63 --- /dev/null +++ b/src/org/warp/device/Keyboard.java @@ -0,0 +1,529 @@ +package org.warp.device; + +import org.warp.engine.Display; +import org.warp.engine.Screen; +import org.warp.picalculator.Calculator; +import org.warp.picalculator.Main; +import org.warp.picalculator.Utils; +import org.warp.picalculator.device.chip.ParallelToSerial; +import org.warp.picalculator.device.chip.SerialToParallel; +import org.warp.picalculator.screens.MarioScreen; + +import com.pi4j.wiringpi.Gpio; + +public class Keyboard { + public static volatile boolean alpha = false; + public static volatile boolean shift = false; + + //From Serial + private static final int RCK_pin = 35; + private static final int SCK_and_CLK_pin = 38; + private static final int SER_pin = 36; + + //To Serial + private static final int SH_LD_pin = 37; + private static final int QH_pin = 40; + private static final int CLK_INH_pin = 33; + + private static boolean[][] precedentStates = new boolean[8][8]; + + public static void startKeyboard() { + if (Utils.debugOn == false) { + Gpio.pinMode(CLK_INH_pin, Gpio.OUTPUT); + Gpio.pinMode(RCK_pin, Gpio.OUTPUT); + Gpio.pinMode(SER_pin, Gpio.OUTPUT); + Gpio.pinMode(SH_LD_pin, Gpio.OUTPUT); + Gpio.pinMode(SCK_and_CLK_pin, Gpio.OUTPUT); + Gpio.pinMode(QH_pin, Gpio.INPUT); + + Gpio.digitalWrite(CLK_INH_pin, false); + Gpio.digitalWrite(RCK_pin, false); + Gpio.digitalWrite(SER_pin, false); + Gpio.digitalWrite(SH_LD_pin, false); + Gpio.digitalWrite(SCK_and_CLK_pin, false); + Gpio.digitalWrite(QH_pin, false); + Thread kt = new Thread(()->{ + SerialToParallel chip1 = new SerialToParallel(RCK_pin, SCK_and_CLK_pin /*SCK*/, SER_pin); + ParallelToSerial chip2 = new ParallelToSerial(SH_LD_pin, CLK_INH_pin, QH_pin, SCK_and_CLK_pin/*CLK*/); + while(true) { + boolean[] data; + for (int col = 0; col < 8; col++) { + data = new boolean[8]; + data[col] = true; + chip1.write(data); + + data = new boolean[8]; + data = chip2.read(); + + for (int row = 0; row < 8; row++) { + if (data[row] == true && precedentStates[row][col] == false) { + System.out.println("Pressed button at "+(row+1) +", "+(col+1)); + keyPressedRaw(row+1, col+1); + } else if (data[row] == false && precedentStates[row][col] == true) { + keyReleasedRaw(row+1, col+1); + } + precedentStates[row][col] = data[row]; + } + } + } + }); + kt.setName("Keyboard thread"); + kt.setPriority(Thread.MIN_PRIORITY); + kt.setDaemon(true); + kt.start(); + } + } + + public static boolean isKeyDown(int row, int col) { + return precedentStates[row][col]; + } + + private static void keyReleasedRaw(int row, int col) { + if (row == 1 && col == 1) { + keyReleased(Key.BRIGHTNESS_CYCLE); + } + } + + private static void keyPressedRaw(int row, int col) { + if (row == 1 && col == 1) { + keyPressed(Key.SHIFT); + } else if (row == 1 && col == 2) { + keyPressed(Key.ALPHA); + } else if (row == 1 && col == 7) { + if (shift) { + keyPressed(Key.BRIGHTNESS_CYCLE_REVERSE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.BRIGHTNESS_CYCLE); + } + } else if (row == 1 && col == 8) { + if (shift) { + keyPressed(Key.SIMPLIFY); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.SOLVE); + } + } else if (row == 2 && col == 8) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.DRG_CYCLE); + } else { + keyPressed(Key.NONE); + } + } else if (row == 3 && col == 2) { + if (shift) { + keyPressed(Key.ROOT); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.SQRT); + } + } else if (row == 4 && col == 8) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.DOT); + } + } else if (row == 5 && col == 8) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.NUM0); + } + } else if (row == 8 && col == 1) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.NUM1); + } + } else if (row == 8 && col == 2) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.NUM2); + } + } else if (row == 8 && col == 3) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.NUM3); + } + } else if (row == 7 && col == 1) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.NUM4); + } + } else if (row == 7 && col == 2) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.NUM5); + } + } else if (row == 7 && col == 3) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.NUM6); + } + } else if (row == 6 && col == 1) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.NUM7); + } + } else if (row == 6 && col == 2) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.NUM8); + } + } else if (row == 6 && col == 3) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.NUM9); + } + } else if (row == 8 && col == 4) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.PLUS); + } + } else if (row == 8 && col == 5) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.MINUS); + } + } else if (row == 7 && col == 4) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.MULTIPLY); + } + } else if (row == 7 && col == 5) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.DIVIDE); + } + } else if (row == 6 && col == 4) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.DELETE); + } + } else if (row == 6 && col == 5) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.RESET); + } + } else if (row == 1 && col == 4) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.UP); + } + } else if (row == 2 && col == 3) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.LEFT); + } + } else if (row ==2 && col == 5) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.RIGHT); + } + } else if (row == 3 && col == 4) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.DOWN); + } + } else if (row == 4 && col == 3) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.POWER_OF_2); + } + } else if (row == 4 && col == 4) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.POWER_OF_x); + } + } else if (row == 5 && col == 3) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.PARENTHESIS_OPEN); + } + } else if (row == 5 && col == 4) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.PARENTHESIS_CLOSE); + } + } else if (row == 2 && col == 1) { + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + keyPressed(Key.EQUAL); + } + } else if (row == 2 && col == 6) { + System.out.println("PREMUTO <"); + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + System.out.println("PREMUTO <"); + keyPressed(Key.HISTORY_BACK); + } + } else if (row == 2 && col == 7) { + System.out.println("PREMUTO >"); + if (shift) { + keyPressed(Key.NONE); + } else if (alpha) { + keyPressed(Key.NONE); + } else { + System.out.println("PREMUTO >"); + keyPressed(Key.HISTORY_FORWARD); + } + } else { + } + } + + public static void stopKeyboard() { + if (Utils.debugOn == false) { + Gpio.digitalWrite(33, false); + Gpio.digitalWrite(35, false); + Gpio.digitalWrite(36, false); + Gpio.digitalWrite(37, false); + Gpio.digitalWrite(38, false); + Gpio.digitalWrite(40, false); + } + } + + public static void keyPressed(Key k) { + if (Main.d != null) { + Screen scr = Main.d.getScreen(); + boolean refresh = false; + if(scr != null && scr.initialized && scr.keyPressed(k)) { + refresh = true; + } else { + switch (k) { + case POWER: + Display.destroy(); + break; + case NONE: + break; + case debug_DEG: + if (Calculator.angleMode.equals("deg") == false) { + refresh = true; + } + Calculator.angleMode = "deg"; + break; + case debug_RAD: + if (Calculator.angleMode.equals("rad") == false) { + refresh = true; + } + Calculator.angleMode = "rad"; + break; + case debug_GRA: + if (Calculator.angleMode.equals("gra") == false) { + refresh = true; + } + Calculator.angleMode = "gra"; + break; + case DRG_CYCLE: + if (Calculator.angleMode.equals("deg") == true) { + Calculator.angleMode = "rad"; + } else if (Calculator.angleMode.equals("rad") == true) { + Calculator.angleMode = "gra"; + } else { + Calculator.angleMode = "deg"; + } + refresh = true; + break; + case LETTER_X: + letterPressed('X'); + break; + case BRIGHTNESS_CYCLE: + PIDisplay.cycleBrightness(false); + refresh = true; + break; + case BRIGHTNESS_CYCLE_REVERSE: + PIDisplay.INSTANCE.setScreen(new MarioScreen()); //TODO: rimuovere: prova + PIDisplay.cycleBrightness(true); + refresh = true; + break; + case HISTORY_BACK: + PIDisplay.INSTANCE.goBack(); + refresh = true; + break; + case HISTORY_FORWARD: + PIDisplay.INSTANCE.goForward(); + refresh = true; + break; + default: + break; + } + } + switch (k) { + case SHIFT: + Keyboard.shift = !Keyboard.shift; + refresh = true; + break; + case ALPHA: + Keyboard.alpha = !Keyboard.alpha; + refresh = true; + break; + default: + break; + } + if (k != Key.SHIFT && Keyboard.shift) { + Keyboard.shift = false; + refresh = true; + } else if (k != Key.ALPHA && Keyboard.alpha) { + Keyboard.alpha = false; + refresh = true; + } + if (refresh) { + Display.refresh(true); + } + } + } + + private static void letterPressed(char L) { + + } + + public static void keyReleased(Key k) { + boolean refresh = false; + if (Main.d != null) { + Screen scr = Main.d.getScreen(); + if(scr != null && scr.initialized && scr.keyReleased(k)) { + refresh = true; + } else { + switch (k) { + case NONE: + break; + default: + break; + } + } + if (refresh) { + Display.refresh(true); + } + } + } + + public static enum Key { + POWER, debug_DEG, debug_RAD, debug_GRA, SHIFT, ALPHA, NONE, HISTORY_BACK, HISTORY_FORWARD, DRG_CYCLE, LETTER_X, SIMPLIFY, SOLVE, BRIGHTNESS_CYCLE, BRIGHTNESS_CYCLE_REVERSE, DOT, NUM0, NUM1, NUM2, NUM3, NUM4, NUM5, NUM6, NUM7, NUM8, NUM9, PARENTHESIS_OPEN, PARENTHESIS_CLOSE, PLUS, MINUS, MULTIPLY, DIVIDE, EQUAL, DELETE, RESET, LEFT, RIGHT, UP, DOWN, OK, debug1, debug2, debug3, debug4, debug5, SQRT, ROOT, POWER_OF_2, POWER_OF_x + } +} + + + +/* + +|1,1---|1,2---|------|1,4---|------|------|1,7---| +|SHIFT |ALPHA |------| ^ |------|------|+BRIGH| +|SHIFT |ALPHA |------| |------|------|-BRIGH| +|SHIFT |ALPHA |------| |------|------| | +|2,1---|2,2---|2,3---|2,4---|2,5---|2,6---|2,7---| +| = | | < | OK | > | Back | Fwd | +| | | | | | | | +| | | | | | | | +|3,1---|3,2---|------|3,4---|------|3,6---|3,7---| +| | SQRT |------| v |------| | | +| | ROOT |------| |------| | | +| | |------| |------| | | +|4,1---|4,2---|4,3---|4,4---|4,5---|4,6---|4,7---| +| | | POW 2| POW x| | | | +| | | | | | | | +| | | | | | | | +|5,1---|5,2---|5,3---|5,4---|5,5---|5,6---|5,7---| +| | | | | | | | +| | | | | | | | +| | | | | | | | +|6,1---|6,2---|6,3---|6,4---|6,5---|6,6---|6,7---| +| 7 | 8 | 9 | DEL | RESET | +| | | | | | +| | | | | | +|7,1---|7,2---|7,3---|7,4---|7,5-----------------| +| 4 | 5 | 6 | * | / | +| | | | | | +| | | | | | +|8,1---|8,2---|8,3---|8,4---|8,5-----------------| +| 1 | 2 | 3 | + | - | +| | | | | | +| | | | | | +|5,8---|4,8---|3,8---|2,8---|1,8-----------------| +| 0 | . | | | SOLVE | +| | | | | SIMPLIFY | +| | | |DRGCYCL| | +|------|------|------|------|--------------------| + + +*/ \ No newline at end of file diff --git a/src/org/warp/device/PIDisplay.java b/src/org/warp/device/PIDisplay.java new file mode 100644 index 00000000..f647878f --- /dev/null +++ b/src/org/warp/device/PIDisplay.java @@ -0,0 +1,472 @@ +package org.warp.device; + +import static org.warp.engine.Display.Render.getMatrixOfImage; +import static org.warp.engine.Display.Render.glClear; +import static org.warp.engine.Display.Render.glColor3f; +import static org.warp.engine.Display.Render.glColor4f; +import static org.warp.engine.Display.Render.glDrawLine; +import static org.warp.engine.Display.Render.glDrawSkin; +import static org.warp.engine.Display.Render.glDrawStringCenter; +import static org.warp.engine.Display.Render.glDrawStringLeft; +import static org.warp.engine.Display.Render.glDrawStringRight; +import static org.warp.engine.Display.Render.setFont; + +import java.awt.image.BufferedImage; +import java.io.IOException; + +import javax.imageio.ImageIO; + +import org.warp.engine.Display; +import org.warp.engine.Display.Startable; +import org.warp.engine.RAWFont; +import org.warp.engine.Screen; +import org.warp.picalculator.Calculator; +import org.warp.picalculator.Main; +import org.warp.picalculator.Utils; + +import com.pi4j.wiringpi.Gpio; + +/** + * STB Truetype oversampling demo. + * + *

+ * This is a Java port of https:/ + * /github + * .com/nothings/stb/blob/master/tests/oversample/main.c. + *

+ */ +public final class PIDisplay { + public static PIDisplay INSTANCE; + private static float brightness; + + private int[] skin; + private int[] skinSize; + public static RAWFont[] fonts = new RAWFont[2]; + + public static boolean loading = true; + public static String error = null; + public String[] errorStackTrace = null; + public final int[] glyphsHeight = new int[] { 9, 6 }; + public float loadingTextTranslation = 0.0f; + public boolean loadingTextTranslationTopToBottom = true; + + public static Screen screen; + public static String displayDebugString = ""; + + public PIDisplay(Screen screen) { + setScreen(screen); + INSTANCE = this; + } + /* + * private void load_skin() { + * try { + * skin_tex = glGenTextures(); + * glBindTexture(GL_TEXTURE_2D, skin_tex); + * glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + * + * InputStream in = new FileInputStream("skin.png"); + * PNGDecoder decoder = new PNGDecoder(in); + * + * System.out.println("width="+decoder.getWidth()); + * System.out.println("height="+decoder.getHeight()); + * + * ByteBuffer buf = + * ByteBuffer.allocateDirect(4*decoder.getWidth()*decoder.getHeight()); + * decoder.decode(buf, decoder.getWidth()*4, Format.RGBA); + * buf.flip(); + * + * skin = buf; + * skin_w = decoder.getWidth(); + * skin_h = decoder.getHeight(); + * glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, skin_w, + * skin_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, skin); + * } catch (IOException ex) { + * ex.printStackTrace(); + * } + * } + */ + + public void setScreen(Screen screen) { + if (screen.initialized == false) { + if (screen.canBeInHistory) { + Calculator.currentSession = 0; + for (int i = Calculator.sessions.length - 1; i >= 1; i--) { + Calculator.sessions[i] = Calculator.sessions[i - 1]; + } + Calculator.sessions[0] = screen; + } else { + Calculator.currentSession = -1; + } + } + screen.d = this; + try { + screen.create(); + PIDisplay.screen = screen; + if (initialized == true && screen.initialized == false) { + screen.initialize(); + } + } catch (Exception e) { + e.printStackTrace(); + System.exit(0); + } + } + + public boolean canGoBack() { + if (Calculator.currentSession == -1) { + return Calculator.sessions[0] != null; + } + if (PIDisplay.screen != Calculator.sessions[Calculator.currentSession]) { + + } else if (Calculator.currentSession + 1 < Calculator.sessions.length) { + if (Calculator.sessions[Calculator.currentSession + 1] != null) { + + } else { + return false; + } + } else { + return false; + } + if (Calculator.sessions[Calculator.currentSession] != null) { + return true; + } + return false; + } + + public void goBack() { + if (canGoBack()) { + if (Calculator.currentSession >= 0 && PIDisplay.screen != Calculator.sessions[Calculator.currentSession]) { + } else { + Calculator.currentSession += 1; + } + PIDisplay.screen = Calculator.sessions[Calculator.currentSession]; + Utils.debug.println("Current session: " + Calculator.currentSession); + } + } + + public boolean canGoForward() { + if (Calculator.currentSession <= 0) { // -1 e 0 + return false; + } + if (PIDisplay.screen != Calculator.sessions[Calculator.currentSession]) { + + } else if (Calculator.currentSession > 0) { + if (Calculator.sessions[Calculator.currentSession - 1] != null) { + + } else { + return false; + } + } else { + return false; + } + if (Calculator.sessions[Calculator.currentSession] != null) { + return true; + } + return false; + } + + public void goForward() { + if (canGoForward()) { + if (PIDisplay.screen != Calculator.sessions[Calculator.currentSession]) { + + } else { + Calculator.currentSession -= 1; + } + PIDisplay.screen = Calculator.sessions[Calculator.currentSession]; + } + } + + public Screen getScreen() { + return PIDisplay.screen; + } + + private void load_skin() throws IOException { + BufferedImage img = ImageIO.read(Main.instance.getClass().getResource("/skin.png")); + skin = getMatrixOfImage(img); + skinSize = new int[] { img.getWidth(), img.getHeight() }; + } + + private void load_fonts() { + fonts[0] = new RAWFont(); + fonts[0].create("big"); + fonts[1] = new RAWFont(); + fonts[1].create("small"); + setFont(fonts[0]); + } + + private void draw_init() { + glClear(); + } + + public void drawSkinPart(int x, int y, int sx1, int sy1, int sx2, int sy2) { + glDrawSkin(skinSize[0], skin, x, y, sx1, sy1, sx2, sy2, false); + } + + private void draw_status() { + glColor3f(0, 0, 0); + glDrawLine(0, 20, Main.screenSize[0]-1, 20); + glColor3f(0, 0, 0); + if (Keyboard.shift) { + drawSkinPart(2 + 18 * 0, 2, 16 * 2, 16 * 0, 16 + 16 * 2, 16 + 16 * 0); + } else { + drawSkinPart(2 + 18 * 0, 2, 16 * 3, 16 * 0, 16 + 16 * 3, 16 + 16 * 0); + } + if (Keyboard.alpha) { + drawSkinPart(2 + 18 * 1, 2, 16 * 0, 16 * 0, 16 + 16 * 0, 16 + 16 * 0); + } else { + drawSkinPart(2 + 18 * 1, 2, 16 * 1, 16 * 0, 16 + 16 * 1, 16 + 16 * 0); + } + if (Calculator.angleMode == "deg") { + drawSkinPart(8 + 18 * 2, 2, 16 * 4, 16 * 0, 16 + 16 * 4, 16 + 16 * 0); + drawSkinPart(8 + 18 * 3, 2, 16 * 7, 16 * 0, 16 + 16 * 7, 16 + 16 * 0); + drawSkinPart(8 + 18 * 4, 2, 16 * 9, 16 * 0, 16 + 16 * 9, 16 + 16 * 0); + } else if (Calculator.angleMode == "rad") { + drawSkinPart(8 + 18 * 2, 2, 16 * 5, 16 * 0, 16 + 16 * 5, 16 + 16 * 0); + drawSkinPart(8 + 18 * 3, 2, 16 * 6, 16 * 0, 16 + 16 * 6, 16 + 16 * 0); + drawSkinPart(8 + 18 * 4, 2, 16 * 9, 16 * 0, 16 + 16 * 9, 16 + 16 * 0); + } else if (Calculator.angleMode == "gra") { + drawSkinPart(8 + 18 * 2, 2, 16 * 5, 16 * 0, 16 + 16 * 5, 16 + 16 * 0); + drawSkinPart(8 + 18 * 3, 2, 16 * 7, 16 * 0, 16 + 16 * 7, 16 + 16 * 0); + drawSkinPart(8 + 18 * 4, 2, 16 * 8, 16 * 0, 16 + 16 * 8, 16 + 16 * 0); + } else { + drawSkinPart(8 + 18 * 2, 2, 16 * 5, 16 * 0, 16 + 16 * 5, 16 + 16 * 0); + drawSkinPart(8 + 18 * 3, 2, 16 * 7, 16 * 0, 16 + 16 * 7, 16 + 16 * 0); + drawSkinPart(8 + 18 * 4, 2, 16 * 9, 16 * 0, 16 + 16 * 9, 16 + 16 * 0); + } + + int padding = 2; + + int brightness = (int) (Math.ceil(PIDisplay.brightness * 4)); + if (brightness <= 1) { + drawSkinPart(Main.screenSize[0] - (padding + 16), 2, 16 * 10, 16 * 0, 16 + 16 * 10, 16 + 16 * 0); + } else if (brightness == 2) { + drawSkinPart(Main.screenSize[0] - (padding + 16), 2, 16 * 11, 16 * 0, 16 + 16 * 11, 16 + 16 * 0); + } else if (brightness == 3) { + drawSkinPart(Main.screenSize[0] - (padding + 16), 2, 16 * 12, 16 * 0, 16 + 16 * 12, 16 + 16 * 0); + } else if (brightness >= 4) { + drawSkinPart(Main.screenSize[0] - (padding + 16), 2, 16 * 13, 16 * 0, 16 + 16 * 13, 16 + 16 * 0); + } else { + Utils.debug.println("Brightness error"); + } + + padding += 18 + 6; + + boolean canGoBack = canGoBack(); + boolean canGoForward = canGoForward(); + + if (Calculator.haxMode) { + drawSkinPart(Main.screenSize[0] - (padding + 16), 2, 16 * 18, 16 * 0, 16 + 16 * 18, 16 + 16 * 0); + padding += 18 + 6; + } + + if (canGoBack && canGoForward) { + drawSkinPart(Main.screenSize[0] - (padding + 16), 2, 16 * 14, 16 * 0, 16 + 16 * 14, 16 + 16 * 0); + } else if (canGoBack) { + drawSkinPart(Main.screenSize[0] - (padding + 16), 2, 16 * 15, 16 * 0, 16 + 16 * 15, 16 + 16 * 0); + } else if (canGoForward) { + drawSkinPart(Main.screenSize[0] - (padding + 16), 2, 16 * 16, 16 * 0, 16 + 16 * 16, 16 + 16 * 0); + } else { + drawSkinPart(Main.screenSize[0] - (padding + 16), 2, 16 * 17, 16 * 0, 16 + 16 * 17, 16 + 16 * 0); + } + + padding += 18; + + } + + private void draw_screen() { + screen.render(); + } + + private void draw_bottom() { + glDrawStringLeft(2, 90, displayDebugString); + } + + private void draw_world() { + glColor3f(255, 255, 255); + + if (error != null) { + setFont(fonts[1]); + glColor3f(129, 28, 22); + glDrawStringRight(Main.screenSize[0] - 2, Main.screenSize[1]- this.glyphsHeight[1] - 2, "ANDREA CAVALLI'S CALCULATOR"); + glColor3f(149, 32, 26); + glDrawStringCenter((Main.screenSize[0] / 2), 22, error); + glColor3f(164, 34, 28); + int i = 22; + for (String stackPart : errorStackTrace) { + glDrawStringLeft(2, 22 + i, stackPart); + i += 11; + } + setFont(fonts[0]); + glColor3f(129, 28, 22); + glDrawStringCenter((Main.screenSize[0] / 2), 11, "UNEXPECTED EXCEPTION"); + } else if (loading) { + setFont(fonts[0]); + colore(1.0f, 1.0f, 1.0f, 1.0f); + glDrawStringCenter((Main.screenSize[0] / 2) - 1,(int) ((Main.screenSize[1]/ 2) - 25 + loadingTextTranslation), "ANDREA CAVALLI'S CALCULATOR"); + colore(1.0f, 1.0f, 1.0f, 1.0f); + glDrawStringCenter((Main.screenSize[0] / 2) + 1,(int) ((Main.screenSize[1]/ 2) - 25 + loadingTextTranslation), "ANDREA CAVALLI'S CALCULATOR"); + colore(1.0f, 1.0f, 1.0f, 1.0f); + glDrawStringCenter((Main.screenSize[0] / 2), (int) ((Main.screenSize[1]/ 2) - 25 - 1 + loadingTextTranslation), "ANDREA CAVALLI'S CALCULATOR"); + colore(1.0f, 1.0f, 1.0f, 1.0f); + glDrawStringCenter((Main.screenSize[0] / 2), (int) ((Main.screenSize[1]/ 2) - 25 + 1 + loadingTextTranslation), "ANDREA CAVALLI'S CALCULATOR"); + colore(1.0f, 0.5f, 0.0f, 1.0f); + glDrawStringCenter((Main.screenSize[0] / 2), (int) ((Main.screenSize[1]/ 2) - 25 + loadingTextTranslation), "ANDREA CAVALLI'S CALCULATOR"); + colore(0.0f, 0.0f, 0.0f, 0.75f); + glDrawStringCenter((Main.screenSize[0] / 2), (Main.screenSize[1]/ 2) + 11, "LOADING"); + setFont(fonts[1]); + colore(0.0f, 0.0f, 0.0f, 0.5f); + glDrawStringCenter((Main.screenSize[0] / 2), (Main.screenSize[1]/ 2) + 22, "PLEASE WAIT..."); + } else { + draw_status(); + draw_screen(); + draw_bottom(); + } + } + + private void draw() { + draw_init(); + draw_world(); + } + + private long precTime = -1; + + private void refresh(boolean forced) { + float dt = 0; + long newtime = System.nanoTime(); + if (precTime == -1) { + dt = 0; + } else { + dt = (float) ((newtime - precTime) / 1000000000d); + if (dt < 0.03 && !forced) { + return; + } + } + precTime = newtime; + /* + * Calcoli + */ + checkDisplayResized(); + + if (loading) { + if (loadingTextTranslation >= 10.0f) { + loadingTextTranslation = 10.0f; + loadingTextTranslationTopToBottom = false; + } else if (loadingTextTranslation <= -10.0f) { + loadingTextTranslation = -10.0f; + loadingTextTranslationTopToBottom = true; + } + + if (loadingTextTranslationTopToBottom) { + loadingTextTranslation += dt * 15; + } else { + loadingTextTranslation -= dt * 15; + } + } + + screen.beforeRender(dt); + + if(forced==true || screen.mustBeRefreshed() || loading) { + draw(); + } + + } + + private volatile Startable refresh = new Startable() { + @Override + public void run() { + PIDisplay.this.refresh(this.force); + } + }; + + private void checkDisplayResized() { + if (Display.wasResized()) { + Main.screenSize[0] = Display.getWidth(); + Main.screenSize[1]= Display.getHeight(); + } + }; + + private void createWindow(String title) { + Display.setTitle(title); + Display.setResizable(false); + Display.setDisplayMode(Main.screenSize[0], Main.screenSize[1]); + Display.create(); + } + + private boolean initialized = false; + + public void run(String title) { + try { + createWindow(title); + load_skin(); + load_fonts(); + + initialized = true; + + try { + screen.initialize(); + } catch (Exception e) { + e.printStackTrace(); + System.exit(0); + } + + Display.start(this.refresh); + + Main.instance.afterStart(); + + double extratime = 0; + while (Display.initialized()) { + long start = System.nanoTime(); + Display.refresh(false); + long end = System.nanoTime(); + double delta = (end - start) / 1000000000; + int deltaInt = (int) Math.floor(delta); + int extraTimeInt = (int) Math.floor(extratime); + if (extraTimeInt + deltaInt < 50) { + Thread.sleep(50 - (extraTimeInt + deltaInt)); + extratime = 0; + } else { + extratime += delta - 50d; + } + } + } catch (Exception ex) { + ex.printStackTrace(); + } finally { + } + } + + public static void changeBrightness(float change) { + setBrightness(brightness + change); + } + + public static void setBrightness(float newval) { + if (newval >= 0.1 && newval <= 1) { + brightness = newval; + if (Utils.debugOn == false) { + Gpio.pwmWrite(12, (int) Math.ceil(brightness*1024)); +// SoftPwm.softPwmWrite(12, (int)(Math.ceil(brightness*10))); + } + } + } + + public static void cycleBrightness(boolean reverse) { + final float step = reverse?-0.1f:0.1f; + if (brightness + step > 1f) { + setBrightness(0.1f); + } else if (brightness + step < 0.1f) { + setBrightness(1.0f); + } else { + changeBrightness(step); + } + } + + public static float getBrightness() { + return brightness; + } + + public float[] colore = new float[] { 0.0f, 0.0f, 0.0f, 1.0f }; + + public void colore(float f1, float f2, float f3, float f4) { + colore = new float[] { f1, f2, f3, f4 }; + glColor4f((int) (f1 * 255), (int) (f2 * 255), (int) (f3 * 255), (int) (f4 * 255)); + } +} \ No newline at end of file diff --git a/src/org/warp/device/PIFrame.java b/src/org/warp/device/PIFrame.java new file mode 100644 index 00000000..f4575533 --- /dev/null +++ b/src/org/warp/device/PIFrame.java @@ -0,0 +1,402 @@ +package org.warp.device; + +import java.awt.Cursor; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.Toolkit; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.image.BufferedImage; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +import org.warp.device.Keyboard.Key; +import org.warp.engine.Display; + +public class PIFrame extends JFrame { + private static final long serialVersionUID = 2945898937634075491L; + public CustomCanvas c; + public boolean wasResized = false; + + public PIFrame() { + c = new CustomCanvas(); + c.setDoubleBuffered(true); + this.add(c); + this.setExtendedState(Frame.MAXIMIZED_BOTH); + Toolkit.getDefaultToolkit().setDynamicLayout(false); + // Transparent 16 x 16 pixel cursor image. + BufferedImage cursorImg = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); + + // Create a new blank cursor. + Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImg, new Point(0, 0), "blank cursor"); + + // Set the blank cursor to the JFrame. + getContentPane().setCursor(blankCursor); + this.addComponentListener(new ComponentListener() { + @Override + public void componentHidden(ComponentEvent e) { + Display.destroy(); + } + + @Override + public void componentMoved(ComponentEvent e) {} + + @Override + public void componentResized(ComponentEvent e) { + wasResized = true; + } + + @Override + public void componentShown(ComponentEvent e) {} + }); + this.addKeyListener(new KeyListener() { + @Override + public void keyPressed(KeyEvent arg0) { + switch (arg0.getKeyCode()) { + case KeyEvent.VK_ESCAPE: + Keyboard.keyPressed(Key.POWER); + break; + case KeyEvent.VK_D: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.debug_DEG); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_R: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.debug_RAD); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_G: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.debug_GRA); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_X: + if (Keyboard.alpha) { + Keyboard.keyPressed(Key.LETTER_X); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_B: + if (Keyboard.shift) { + Keyboard.keyPressed(Key.BRIGHTNESS_CYCLE_REVERSE); + } else if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.BRIGHTNESS_CYCLE); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_ENTER: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.SOLVE); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_1: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.NUM1); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_2: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.NUM2); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_3: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.NUM3); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_4: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.NUM4); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_5: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.NUM5); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_6: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.NUM6); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_7: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.NUM7); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_8: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.NUM8); + } else if (Keyboard.shift) { + Keyboard.keyPressed(Key.PARENTHESIS_OPEN); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_9: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.NUM9); + } else if (Keyboard.shift) { + Keyboard.keyPressed(Key.PARENTHESIS_CLOSE); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_0: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.NUM0); + } else if (Keyboard.shift) { + Keyboard.keyPressed(Key.EQUAL); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_ADD: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.PLUS); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_SUBTRACT: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.MINUS); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_MULTIPLY: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.MULTIPLY); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_DIVIDE: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.DIVIDE); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_BACK_SPACE: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.DELETE); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_DELETE: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.RESET); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_LEFT: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.LEFT); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_RIGHT: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.RIGHT); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_NUMPAD4: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.HISTORY_BACK); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_NUMPAD6: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.HISTORY_FORWARD); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_PERIOD: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyPressed(Key.DOT); + } else { + Keyboard.keyPressed(Key.NONE); + } + break; + case KeyEvent.VK_SHIFT: + Keyboard.keyPressed(Key.SHIFT); + break; + case KeyEvent.VK_A: + Keyboard.keyPressed(Key.ALPHA); + break; + case KeyEvent.VK_NUMPAD1: + Keyboard.keyPressed(Key.SQRT); + break; + case KeyEvent.VK_NUMPAD2: + Keyboard.keyPressed(Key.ROOT); + break; + case KeyEvent.VK_NUMPAD3: + Keyboard.keyPressed(Key.POWER_OF_2); + break; + case KeyEvent.VK_NUMPAD5: + Keyboard.keyPressed(Key.POWER_OF_x); + break; + } + } + + @Override + public void keyReleased(KeyEvent arg0) { + switch (arg0.getKeyCode()) { + case KeyEvent.VK_ESCAPE: + Keyboard.keyReleased(Key.POWER); + break; + case KeyEvent.VK_D: + Keyboard.keyReleased(Key.debug_DEG); + break; + case KeyEvent.VK_R: + Keyboard.keyReleased(Key.debug_RAD); + break; + case KeyEvent.VK_G: + Keyboard.keyReleased(Key.debug_GRA); + break; + case KeyEvent.VK_X: + if (Keyboard.alpha) { + Keyboard.keyReleased(Key.LETTER_X); + } else { + Keyboard.keyReleased(Key.NONE); + } + break; + case KeyEvent.VK_B: + if (Keyboard.shift) { + Keyboard.keyReleased(Key.BRIGHTNESS_CYCLE_REVERSE); + } else if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyReleased(Key.BRIGHTNESS_CYCLE); + } else { + Keyboard.keyReleased(Key.NONE); + } + break; + case KeyEvent.VK_ENTER: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyReleased(Key.SOLVE); + } else { + Keyboard.keyReleased(Key.NONE); + } + break; + case KeyEvent.VK_1: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyReleased(Key.debug1); + } else { + Keyboard.keyReleased(Key.NONE); + } + break; + case KeyEvent.VK_2: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyReleased(Key.debug2); + } else { + Keyboard.keyReleased(Key.NONE); + } + break; + case KeyEvent.VK_3: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyReleased(Key.debug3); + } else { + Keyboard.keyReleased(Key.NONE); + } + break; + case KeyEvent.VK_4: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyReleased(Key.debug4); + } else { + Keyboard.keyReleased(Key.NONE); + } + break; + case KeyEvent.VK_5: + if (!Keyboard.shift && !Keyboard.alpha) { + Keyboard.keyReleased(Key.debug5); + } else { + Keyboard.keyReleased(Key.NONE); + } + break; + case KeyEvent.VK_SHIFT: + Keyboard.keyReleased(Key.SHIFT); + break; + case KeyEvent.VK_A: + Keyboard.keyReleased(Key.ALPHA); + break; + } + } + + @Override + public void keyTyped(KeyEvent arg0) { + // TODO Auto-generated method stub + + } + }); + } + + @Override + public void setSize(int width, int height) { + super.setSize(width, height); + c.setSize(width, height); + } + + public static class CustomCanvas extends JPanel { + + /** + * + */ + private static final long serialVersionUID = 605243927485370885L; + + @Override + public void paintComponent(Graphics graphics) { + Display.update(graphics, forcerefresh); + } + + @Override + public void repaint() { + forcerefresh = false; + super.repaint(); + } + + private boolean forcerefresh = false; + + public void repaint(boolean force) { + forcerefresh = force; + super.repaint(); + } + } +} diff --git a/src/org/warp/engine/Display.java b/src/org/warp/engine/Display.java new file mode 100644 index 00000000..e0afd8b8 --- /dev/null +++ b/src/org/warp/engine/Display.java @@ -0,0 +1,275 @@ +package org.warp.engine; + +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; + +import org.warp.device.PIDisplay; +import org.warp.device.PIFrame; +import org.warp.picalculator.Main; + +public class Display { + + private static PIFrame INSTANCE = new PIFrame(); + public static int[] size = new int[] { 1, 1 }; + public static BufferedImage g = new BufferedImage(size[0], size[1], BufferedImage.TYPE_INT_ARGB); + public static int[] canvas2d = new int[1]; + public static int color = 0xFF000000; + private static volatile Startable refresh; + private static boolean initialized = false; + + public static void setTitle(String title) { + INSTANCE.setTitle(title); + } + + public static void setResizable(boolean r) { + INSTANCE.setResizable(r); + if (!r) + INSTANCE.setUndecorated(true); + } + + public static void setDisplayMode(final int ww, final int wh) { + INSTANCE.setSize(ww, wh); + size = new int[] { ww, wh }; + canvas2d = new int[ww * wh]; + g = new BufferedImage(ww, wh, BufferedImage.TYPE_INT_ARGB); + INSTANCE.wasResized = false; + } + + public static void create() { + INSTANCE.setVisible(true); + initialized = true; + } + + public static boolean initialized() { + return initialized; + } + + public static boolean wasResized() { + if (INSTANCE.wasResized) { + size = new int[] { INSTANCE.getWidth(), INSTANCE.getHeight() }; + canvas2d = new int[size[0] * size[1]]; + g = new BufferedImage(size[0], size[1], BufferedImage.TYPE_INT_ARGB); + INSTANCE.wasResized = false; + return true; + } + return false; + } + + public static int getWidth() { + return INSTANCE.getWidth()-Main.screenPos[0]; + } + + public static int getHeight() { + return INSTANCE.getHeight()-Main.screenPos[1]; + } + + public static void destroy() { + initialized = false; + INSTANCE.setVisible(false); + INSTANCE.dispose(); + } + + public static void start(Startable refresh) { + Display.refresh = refresh; + } + + @Deprecated() + public static void refresh() { + if (PIDisplay.screen == null || PIDisplay.loading || (PIDisplay.error != null && PIDisplay.error.length() > 0) || PIDisplay.screen == null || PIDisplay.screen.mustBeRefreshed()) { + Display.INSTANCE.c.repaint(false); + } + } + + public static void refresh(boolean force) { + Display.INSTANCE.c.repaint(force); + } + + public static void update(Graphics g, boolean forcerefresh) { + if (refresh != null) { + refresh.force = forcerefresh; + refresh.run(); + + final int[] a = ((DataBufferInt) Display.g.getRaster().getDataBuffer()).getData(); + System.arraycopy(canvas2d, 0, a, 0, canvas2d.length); + g.clearRect(0, 0, size[0], size[1]); + g.drawImage(Display.g, 0, 0, null); + } + } + + public static abstract class Startable { + public Startable() { + this.force = false; + } + + public Startable(boolean force) { + this.force = force; + } + + public boolean force = false; + + public abstract void run(); + } + + public static class Render { + public static int clearcolor = 0xFFCCE7D4; + public static RAWFont currentFont; + + public static void glColor3f(int r, int gg, int b) { + glColor4f(r, gg, b, 255); + } + + public static void glColor(int c) { + color = c & 0xFFFFFFFF; + } + + public static void glClearColor(int c) { + clearcolor = c & 0xFFFFFFFF; + } + + public static void glColor4f(int red, int green, int blue, int alpha) { + color = (alpha << 24) + (red << 16) + (green << 8) + (blue); + } + + public static void glClearColor(int red, int green, int blue, int alpha) { + clearcolor = (alpha << 24) + (red << 16) + (green << 8) + (blue); + } + + public static void glClear() { + for (int x = 0; x < size[0]; x++) { + for (int y = 0; y < size[1]; y++) { + canvas2d[x + y * size[0]] = clearcolor; + } + } + } + + public static void glDrawSkin(int skinwidth, int[] skin, int x0, int y0, int s0, int t0, int s1, int t1, boolean transparent) { + x0+=Main.screenPos[0]; + y0+=Main.screenPos[1]; + if (x0 >= size[0] || y0 >= size[0]) { + return; + } + if (x0 + (s1-s0) >= size[0]) { + s1 = size[0] - x0 + s0; + } + if (y0 + (t1-t0) >= size[1]) { + t1 = size[1] - y0 + t0; + } + int oldColor; + int newColor; + for (int texx = 0; texx < s1 - s0; texx++) { + for (int texy = 0; texy < t1 - t0; texy++) { + newColor = skin[(s0 + texx) + (t0 + texy) * skinwidth]; + if (transparent) { + oldColor = canvas2d[(x0 + texx) + (y0 + texy) * size[0]]; + float a2 = ((float)(newColor >> 24 & 0xFF)) / 255f; + float a1 = 1f-a2; + int r = (int) ((oldColor >> 16 & 0xFF) * a1 + (newColor >> 16 & 0xFF) * a2); + int g = (int) ((oldColor >> 8 & 0xFF) * a1 + (newColor >> 8 & 0xFF) * a2); + int b = (int) ((oldColor & 0xFF) * a1 + (newColor & 0xFF) * a2); + newColor = 0xFF000000 | r << 16 | g << 8 | b; + } + canvas2d[(x0 + texx) + (y0 + texy) * size[0]] = newColor; + } + } + } + + public static void glDrawLine(int x0, int y0, int x1, int y1) { + x0+=Main.screenPos[0]; + x1+=Main.screenPos[0]; + y0+=Main.screenPos[1]; + y1+=Main.screenPos[1]; + if (x0 >= size[0] || y0 >= size[0]) { + return; + } + if (y0 == y1) { + for (int x = 0; x <= x1 - x0; x++) { + canvas2d[x0 + x + y0 * size[0]] = color; + } + } else if (x0 == x1) { + for (int y = 0; y <= y1 - y0; y++) { + canvas2d[x0 + (y0 + y) * size[0]] = color; + } + } else { + int m = (y1 - y0) / (x1 - x0); + for (int texx = 0; texx <= x1 - x0; texx++) { + if (x0 + texx < size[0] && y0 + (m * texx) < size[1]) { + canvas2d[(x0 + texx) + (y0 + (m * texx)) * size[0]] = color; + } + } + } + } + + public static void glFillRect(int x0, int y0, int w1, int h1) { + x0+=Main.screenPos[0]; + y0+=Main.screenPos[1]; + int x1 = x0+w1; + int y1 = y0+h1; + if (x0 >= size[0] || y0 >= size[0]) { + return; + } + if (x1 >= size[0]) { + x1 = size[0]; + } + if (y1 >= size[1]) { + y1 = size[1]; + } + final int sizeW = size[0]; + for (int x = x0; x < x1; x++) { + for (int y = y0; y < y1; y++) { + canvas2d[(x) + (y) * sizeW] = color; + } + } + } + + public static int[] getMatrixOfImage(BufferedImage bufferedImage) { + int width = bufferedImage.getWidth(null); + int height = bufferedImage.getHeight(null); + int[] pixels = new int[width * height]; + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + pixels[i + j * width] = bufferedImage.getRGB(i, j); + } + } + + return pixels; + } + + public static void glDrawStringLeft(int x, int y, String text) { + x+=Main.screenPos[0]; + y+=Main.screenPos[1]; + final int[] chars = currentFont.getCharIndexes(text); + currentFont.drawText(canvas2d, size, x, y, chars, color); + } + + public static void glDrawStringCenter(int x, int y, String text) { + glDrawStringLeft(x - (getStringWidth(text) / 2), y, text); + } + + public static void glDrawStringRight(int x, int y, String text) { + glDrawStringLeft(x - getStringWidth(text), y, text); + } + + public static void setFont(RAWFont font) { + if (currentFont != font) { + currentFont = font; + } + } + + public static int getStringWidth(String text) { + int w =(currentFont.charW+1)*text.length(); + if (text.length() > 0) { + return w-1; + } else { + return 0; + } + // return text.length()*6; + } + + public static int getWidth(FontMetrics fm, String text) { + return fm.stringWidth(text); + } + + } +} diff --git a/src/org/warp/engine/RAWFont.java b/src/org/warp/engine/RAWFont.java new file mode 100644 index 00000000..de8e2263 --- /dev/null +++ b/src/org/warp/engine/RAWFont.java @@ -0,0 +1,195 @@ +package org.warp.engine; + +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; +import java.io.File; +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.net.URL; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.imageio.ImageIO; + +import org.warp.picalculator.Main; +import org.warp.picalculator.Utils; + +/** + * + * @author andreacv + */ +public class RAWFont { + + public boolean[][] rawchars; + public int[] chars32; + public long[] chars64; + public static final boolean is64 = true; + public int minBound = 10; + public int maxBound = 9599; + public int charW; + public int charH; + public int charS; + + public void create(String name) { + try { + loadFont("/font_"+name+".rft"); + } catch (IOException e) { + e.printStackTrace(); + System.exit(1); + } + if (is64) { + chars64 = new long[maxBound-minBound]; + } else { + chars32 = new int[(maxBound-minBound)*2]; + } + for (int charIndex = 0; charIndex < maxBound-minBound; charIndex++) { + if (is64) { + boolean[] currentChar = rawchars[charIndex]; + if (currentChar == null) { + chars64[charIndex] = 0x1FFFFFFFFFFFL; + } else { + long result = 0, l = charS; + for (int i = 0; i < l; ++i) { + result = (result << 1) | (currentChar[i] ? 1L : 0L); + } + chars64[charIndex] = result; + } + } else { + boolean[] currentChar = rawchars[charIndex]; + if (currentChar == null) { + chars32[charIndex*2] = 0x1FFFFFFF; + chars32[(charIndex*2)+1] = 0xFFFF; + } else { + int result1 = 0, result2 = 0, l1 = 29, l2 = currentChar.length; + for (int i = 0; i < l1; ++i) { + result1 = (result1 << 1) + (currentChar[i] ? 1 : 0); + } + for (int i = l1; i < l2; ++i) { + result2 = (result2 << 1) + (currentChar[i] ? 1 : 0); + } + chars32[charIndex*2] = result1; + chars32[(charIndex*2)+1] = result2; + } + } + } + + Object obj = new Object(); + WeakReference ref = new WeakReference<>(obj); + obj = null; + while (ref.get() != null) { + System.gc(); + } + } + + private void loadFont(String string) throws IOException { + URL res = Main.instance.getClass().getResource(string); + int[] file = Utils.realBytes(Utils.convertStreamToByteArray(res.openStream(), res.getFile().length())); + int filelength = file.length; + if (filelength >= 16) { + if (file[0x0] == 114 && file[0x1] == 97 && file[0x2] == 119 && file[0x3] == 0xFF && file[0x6] == 0xFF && file[0xB] == 0xFF) { + charW = file[0x4]; + charH = file[0x5]; + charS = charW*charH; + minBound = file[0x7] << 24 | file[0x8] << 16 | file[0x9] << 8 | file[0xA]; + maxBound = file[0xC] << 24 | file[0xD] << 16 | file[0xE] << 8 | file[0xF]; + if (maxBound <= minBound) { + maxBound = 9599; //TODO remove it: temp fix + } + rawchars = new boolean[maxBound-minBound][]; + int index = 0x10; + while (index < filelength) { + int charIndex = file[index] << 8 | file[index+1]; + boolean[] rawchar = new boolean[charS]; + int charbytescount = (int) Math.ceil(charS/8)+1; + int currentBit = 0; + for (int i = 0; i <= charbytescount; i++) { + for (int bit = 0; bit < 8; bit++) { + if (currentBit >= charS) { + break; + } + rawchar[i*8+bit] = (((file[index + 2 + i] >> (7-bit)) & 0x1)>=1)?true:false; + currentBit++; + } + } + rawchars[charIndex - minBound] = rawchar; + index += 2 + charbytescount; + } + } else { + throw new IOException(); + } + } else { + throw new IOException(); + } + } + + public int[] getCharIndexes(String txt) { + final int l = txt.length(); + int[] indexes = new int[l]; + char[] chars = txt.toCharArray(); + for (int i = 0; i < l; i++) { + indexes[i] = (chars[i] & 0xFFFF)-minBound; + } + return indexes; + } + + @SuppressWarnings("unused") + private void saveArray(int[] screen, String coutputpng) { + BufferedImage bi = new BufferedImage(300, 200, BufferedImage.TYPE_INT_RGB); + final int[] a = ((DataBufferInt) bi.getRaster().getDataBuffer()).getData(); + System.arraycopy(screen, 0, a, 0, screen.length); + try { + ImageIO.write(bi, "PNG", new File(coutputpng)); + } catch (IOException ex) { + Logger.getLogger(RAWFont.class.getName()).log(Level.SEVERE, null, ex); + } + } + + public void drawText(int[] screen, int[] screenSize, int x, int y, int[] text, int color) { + final int screenLength = screen.length; + int screenPos = 0; + if (is64) { + long res = 0; + final int l = text.length; + for (int i = 0; i < l; i++) { + long chr = chars64[text[i]]; + int dx = 0; + int dy = 0; + for (int j = charS-1; j >= 0; j--) { + res = (chr >> j) & 1; + screenPos = (x + dx) + (y + dy) * screenSize[0]; + if (res == 1 & screenLength > screenPos) { + screen[screenPos] = color; + } + dx++; + if (dx >= charW) { + dx = 0; + dy++; + } + } + x+=charW+1; + } + } else { + int res = 0; + final int l = text.length; + for (int i = 0; i < l; i++) { + final int charIndex = text[i]*2; + int chrP1 = chars32[charIndex]; + int chrP2 = chars32[charIndex+1]; + for (int dx = 0; dx < charW; dx++) { + for (int dy = 0; dy < charH; dy++) { + int bit = dx + dy * charW; + if (bit < 29) { + res = chrP1 >> (28-bit) & 1; + } else { + res = chrP2 >> (12-bit) & 1; + } + screenPos = x + (i * (charW + 1)) + dx + (y + dy) * screenSize[0]; + if (res == 1 & screenLength > res) { + screen[screenPos] = color; + } + } + } + } + } + } +} diff --git a/src/org/warp/engine/Screen.java b/src/org/warp/engine/Screen.java new file mode 100644 index 00000000..c4c999a3 --- /dev/null +++ b/src/org/warp/engine/Screen.java @@ -0,0 +1,41 @@ +package org.warp.engine; + +import org.warp.device.Keyboard.Key; +import org.warp.device.PIDisplay; + +public abstract class Screen { + public PIDisplay d; + public boolean created = false; + public boolean initialized = false; + public boolean canBeInHistory = false; + + public Screen() {} + + public void initialize() throws InterruptedException { + if (!initialized) { + initialized = true; + init(); + } + } + + public void create() throws InterruptedException { + if (!created) { + created = true; + created(); + } + } + + public abstract void created() throws InterruptedException; + + public abstract void init() throws InterruptedException; + + public abstract void render(); + + public abstract void beforeRender(float dt); + + public abstract boolean mustBeRefreshed(); + + public abstract boolean keyPressed(Key k); + + public abstract boolean keyReleased(Key k); +} diff --git a/src/org/warp/engine/lwjgl/Display.java b/src/org/warp/engine/lwjgl/Display.java deleted file mode 100644 index 41fb926f..00000000 --- a/src/org/warp/engine/lwjgl/Display.java +++ /dev/null @@ -1,696 +0,0 @@ -package org.warp.engine.lwjgl; -import org.lwjgl.BufferUtils; -import org.lwjgl.glfw.GLFWErrorCallback; -import org.lwjgl.glfw.GLFWFramebufferSizeCallback; -import org.lwjgl.glfw.GLFWKeyCallback; -import org.lwjgl.glfw.GLFWVidMode; -import org.lwjgl.glfw.GLFWWindowSizeCallback; -import org.lwjgl.opengl.GL; -import org.lwjgl.opengl.GLCapabilities; -import org.lwjgl.opengl.GLUtil; -import org.lwjgl.stb.STBTTAlignedQuad; -import org.lwjgl.stb.STBTTPackContext; -import org.lwjgl.stb.STBTTPackedchar; -import org.warpgate.pi.calculator.Calculator; -import org.warpgate.pi.calculator.Keyboard; -import org.warpgate.pi.calculator.Main; -import org.warpgate.pi.calculator.Utils; - -import de.matthiasmann.twl.utils.PNGDecoder; -import de.matthiasmann.twl.utils.PNGDecoder.Format; - -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; -import java.nio.FloatBuffer; -import java.nio.IntBuffer; - -import static org.warp.engine.lwjgl.GLFWUtil.*; -import static org.warp.engine.lwjgl.IOUtil.*; -import static org.lwjgl.glfw.Callbacks.*; -import static org.lwjgl.glfw.GLFW.*; -import static org.lwjgl.opengl.GL11.*; -import static org.lwjgl.opengl.GL13.*; -import static org.lwjgl.opengl.GL30.*; -import static org.lwjgl.stb.STBTruetype.*; -import static org.lwjgl.system.MemoryUtil.*; -import static org.lwjgl.stb.STBImage.*; - -/** - * STB Truetype oversampling demo. - * - *

This is a Java port of https://github - * .com/nothings/stb/blob/master/tests/oversample/main.c.

- */ -public final class Display { - - private static final int BITMAP_W = 5*128; - private static final int BITMAP_H = 9*256; - - private static final float[] scale = { - 30.0f, - 15.0f - }; - - // ---- - - private final STBTTAlignedQuad q = STBTTAlignedQuad.malloc(); - private final FloatBuffer xb = memAllocFloat(1); - private final FloatBuffer yb = memAllocFloat(1); - - private long window; - - // ---- - - private int ww = 480; - private int wh = 320; - - private int fbw = ww; - private int fbh = wh; - - private int font_tex; - private int skin_tex; - private int skin_w; - private int skin_h; - private int skin_comp; - private ByteBuffer skin; - - private STBTTPackedchar.Buffer chardata; - - private boolean black_on_white; - private boolean integer_align; - private boolean translating; - private boolean rotating; - - private boolean supportsSRGB; - private boolean srgb; - - private float rotate_t, translate_t; - - private boolean show_tex; - - private int font = 0; - - private final int maxCharIndex = 9500; - private float[] background = new float[]{0f,0f,0f}; - public boolean loading = true; - public String error = null; - public String[] errorStackTrace = null; - public final int[] glyphsHeight = new int[]{9, 6}; - public float translation = 0.0f; - public boolean translation_top_to_bottom = true; - public static float brightness = 1.0f; - - private Screen screen; - - public Display(Screen screen, int ww, int wh) { - this.ww = ww; - this.wh = wh; - setScreen(screen); - } -/* - private void load_skin() { - try { - skin_tex = glGenTextures(); - glBindTexture(GL_TEXTURE_2D, skin_tex); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - InputStream in = new FileInputStream("res/skin.png"); - PNGDecoder decoder = new PNGDecoder(in); - - System.out.println("width="+decoder.getWidth()); - System.out.println("height="+decoder.getHeight()); - - ByteBuffer buf = ByteBuffer.allocateDirect(4*decoder.getWidth()*decoder.getHeight()); - decoder.decode(buf, decoder.getWidth()*4, Format.RGBA); - buf.flip(); - - skin = buf; - skin_w = decoder.getWidth(); - skin_h = decoder.getHeight(); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, skin_w, - skin_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, skin); - } catch (IOException ex) { - ex.printStackTrace(); - } - }*/ - - - private void setScreen(Screen screen) { - if (screen.initialized == false) { - if (screen.canBeInHistory) { - Calculator.currentSession = 0; - for (int i = 1; i < Calculator.sessions.length; i++) { - Calculator.sessions[i] = Calculator.sessions[i-1]; - } - Calculator.sessions[0] = this.screen; - } - } - screen.d = this; - try { - screen.initialize(); - this.screen = screen; - } catch (Exception e) { - e.printStackTrace(); - } - } - - private boolean canGoBack() { - if (this.screen != Calculator.sessions[Calculator.currentSession]) { - - } else if (Calculator.currentSession+1 < Calculator.sessions.length) { - if (Calculator.sessions[Calculator.currentSession + 1] != null) { - - } else { - return false; - } - } else { - return false; - } - if (Calculator.sessions[Calculator.currentSession] != null) { - return true; - } - return false; - } - - private void goBack() { - if (canGoBack()) { - if (this.screen != Calculator.sessions[Calculator.currentSession]) { - - } else { - Calculator.currentSession += 1; - } - this.screen = Calculator.sessions[Calculator.currentSession]; - } - } - - - private boolean canGoForward() { - if (this.screen != Calculator.sessions[Calculator.currentSession]) { - - } else if (Calculator.currentSession > 0) { - if (Calculator.sessions[Calculator.currentSession - 1] != null) { - - } else { - return false; - } - } else { - return false; - } - if (Calculator.sessions[Calculator.currentSession] != null) { - return true; - } - return false; - } - - private void goForward() { - if (canGoForward()) { - if (this.screen != Calculator.sessions[Calculator.currentSession]) { - - } else { - Calculator.currentSession -= 1; - } - this.screen = Calculator.sessions[Calculator.currentSession]; - } - } - - private Screen getScreen() { - return this.screen; - } - - - private void load_skin() { - ByteBuffer imageBuffer; - try { - imageBuffer = ioResourceToByteBuffer("skin.png", 8 * 1024); - } catch (IOException e) { - throw new RuntimeException(e); - } - skin_tex = glGenTextures(); - glBindTexture(GL_TEXTURE_2D, skin_tex); - IntBuffer w = BufferUtils.createIntBuffer(1); - IntBuffer h = BufferUtils.createIntBuffer(1); - IntBuffer comp = BufferUtils.createIntBuffer(1); - - skin = stbi_load_from_memory(imageBuffer, w, h, comp, 0); - - if ( skin == null ) - throw new RuntimeException("Failed to load image: " + stbi_failure_reason()); - skin_w = w.get(0); - skin_h = h.get(0); - skin_comp = comp.get(0); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - if ( skin_comp == 3 ) { - if ( (skin_w & 3) != 0 ) - glPixelStorei(GL_UNPACK_ALIGNMENT, 2 - (skin_w & 1)); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, skin_w, skin_h, 0, GL_RGB, GL_UNSIGNED_BYTE, skin); - } else { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, skin_w, skin_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, skin); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - - glBindTexture(GL_TEXTURE_2D, 0); - - stbi_image_free(skin); - } - - private void load_fonts() { - font_tex = glGenTextures(); - chardata = STBTTPackedchar.mallocBuffer(6*maxCharIndex); - glBindTexture(GL_TEXTURE_2D, font_tex); - - - try { - STBTTPackContext pc = STBTTPackContext.malloc(); - ByteBuffer ttfBig = ioResourceToByteBuffer("font_big.ttf", 18000); - ByteBuffer ttfSmall = ioResourceToByteBuffer("font_small.ttf", 18000); - - ByteBuffer bitmap = BufferUtils.createByteBuffer(BITMAP_W * BITMAP_H); - - stbtt_PackBegin(pc, bitmap, BITMAP_W, BITMAP_H, 0, 1, null); - chardata.limit(maxCharIndex); - chardata.position(0); - stbtt_PackSetOversampling(pc, 1, 1); - stbtt_PackFontRange(pc, ttfBig, 0, 15 /* Font size */, 0, chardata); - chardata.clear(); - chardata.limit(maxCharIndex*2); - chardata.position(maxCharIndex); - stbtt_PackSetOversampling(pc, 1, 1); - stbtt_PackFontRange(pc, ttfSmall, 0, 15 /* Font size */, 0, chardata); - chardata.clear(); - stbtt_PackEnd(pc); - glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, BITMAP_W, BITMAP_H, 0, GL_ALPHA, GL_UNSIGNED_BYTE, bitmap); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private void draw_init() { - glDisable(GL_CULL_FACE); - glDisable(GL_TEXTURE_2D); - glDisable(GL_LIGHTING); - glDisable(GL_DEPTH_TEST); - - glViewport(0, 0, fbw, fbh); - clearColor(background[0], background[1], background[2], 0.0f); - glClear(GL_COLOR_BUFFER_BIT); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0.0, ww, wh, 0.0, -1.0, 1.0); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - } - - private static void drawBoxTC(float x0, float y0, float x1, float y1, float s0, float t0, float s1, float t1) { - glTexCoord2f(s0, t0); - glVertex2f(x0, y0); - glTexCoord2f(s1, t0); - glVertex2f(x1, y0); - glTexCoord2f(s1, t1); - glVertex2f(x1, y1); - glTexCoord2f(s0, t1); - glVertex2f(x0, y1); - } - - private void print(float x, float y, int font, String text) { - xb.put(0, x); - yb.put(0, y+glyphsHeight[font]); - - chardata.position(font * maxCharIndex); - - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, font_tex); - - glBegin(GL_QUADS); - for ( int i = 0; i < text.length(); i++ ) { - stbtt_GetPackedQuad(chardata, BITMAP_W, BITMAP_H, text.charAt(i), xb, yb, q, (font == 0 && integer_align)?1:0); - drawBoxTC( - q.x0(), q.y0(), q.x1(), q.y1(), - q.s0(), q.t0(), q.s1(), q.t1() - ); - } - glEnd(); - glDisable( GL_TEXTURE_2D ); - } - - private int[] textSize(int font, String text) { - float[] size = new float[]{0,0}; - xb.put(0, 0); - yb.put(0, 0); - - chardata.position(font * maxCharIndex); - - for ( int i = 0; i < text.length(); i++ ) { - stbtt_GetPackedQuad(chardata, BITMAP_W, BITMAP_H, text.charAt(i), xb, yb, q, (font == 0 && integer_align)?1:0); - size[0]=q.x1(); - size[1]=q.y1(); - } - return new int[]{(int) size[0], (int) size[1]}; - } - - private void drawSkinPart(int[] posOnScreen, float[] posOnSkin) { - glEnable(GL_TEXTURE_2D); - colore(1, 1, 1, 1); - glBindTexture(GL_TEXTURE_2D, skin_tex); - - glBegin(GL_QUADS); - drawBoxTC(posOnScreen[0],posOnScreen[1],posOnScreen[2], posOnScreen[3], posOnSkin[0]/1000, posOnSkin[1]/100, posOnSkin[2]/1000, posOnSkin[3]/100); - glEnd(); - glDisable( GL_TEXTURE_2D ); - } - - private void drawLine(int[]... pos) { - glLineWidth(1.0f); - glBegin(GL_LINE_STRIP); - - for (int[] single_pos : pos) { - glVertex2d(single_pos[0], single_pos[1]); - glVertex2d(single_pos[2], single_pos[3]); - } - glEnd(); - } - - private void drawRect(int[]... pos) { - glLineWidth(1.0f); - glBegin(GL_QUADS); - - for (int[] single_pos : pos) { - glVertex2d(single_pos[0], single_pos[1]); - glVertex2d(single_pos[2], single_pos[1]); - glVertex2d(single_pos[2], single_pos[3]); - glVertex2d(single_pos[0], single_pos[3]); - } - glEnd(); - } - - - private void draw_status() { - colore(background[0],background[1],background[2]); - drawRect(new int[]{0,0,ww,20}); - colore(0,0,0); - drawLine(new int[]{0,20,ww,20}); - colore(0,0,0); - if (Keyboard.shift) { - drawSkinPart(new int[]{2+18*0,2,2+16+18*0,2+16}, new float[]{16*2,16*0,16+16*2,16+16*0}); - } else { - drawSkinPart(new int[]{2+18*0,2,2+16+18*0,2+16}, new float[]{16*3,16*0,16+16*3,16+16*0}); - } - if (Keyboard.alpha) { - drawSkinPart(new int[]{2+18*1,2,2+16+18*1,2+16}, new float[]{16*0,16*0,16+16*0,16+16*0}); - } else { - drawSkinPart(new int[]{2+18*1,2,2+16+18*1,2+16}, new float[]{16*1,16*0,16+16*1,16+16*0}); - } - if (Calculator.angleMode == "deg") { - drawSkinPart(new int[]{8+18*2,2,8+16+18*2,2+16}, new float[]{16*4,16*0,16+16*4,16+16*0}); - drawSkinPart(new int[]{8+18*3,2,8+16+18*3,2+16}, new float[]{16*7,16*0,16+16*7,16+16*0}); - drawSkinPart(new int[]{8+18*4,2,8+16+18*4,2+16}, new float[]{16*9,16*0,16+16*9,16+16*0}); - } else if (Calculator.angleMode == "rad") { - drawSkinPart(new int[]{8+18*2,2,8+16+18*2,2+16}, new float[]{16*5,16*0,16+16*5,16+16*0}); - drawSkinPart(new int[]{8+18*3,2,8+16+18*3,2+16}, new float[]{16*6,16*0,16+16*6,16+16*0}); - drawSkinPart(new int[]{8+18*4,2,8+16+18*4,2+16}, new float[]{16*9,16*0,16+16*9,16+16*0}); - } else if (Calculator.angleMode == "gra") { - drawSkinPart(new int[]{8+18*2,2,8+16+18*2,2+16}, new float[]{16*5,16*0,16+16*5,16+16*0}); - drawSkinPart(new int[]{8+18*3,2,8+16+18*3,2+16}, new float[]{16*7,16*0,16+16*7,16+16*0}); - drawSkinPart(new int[]{8+18*4,2,8+16+18*4,2+16}, new float[]{16*8,16*0,16+16*8,16+16*0}); - } else { - drawSkinPart(new int[]{8+18*2,2,8+16+18*2,2+16}, new float[]{16*5,16*0,16+16*5,16+16*0}); - drawSkinPart(new int[]{8+18*3,2,8+16+18*3,2+16}, new float[]{16*7,16*0,16+16*7,16+16*0}); - drawSkinPart(new int[]{8+18*4,2,8+16+18*4,2+16}, new float[]{16*9,16*0,16+16*9,16+16*0}); - } - - int padding = 2; - - int brightness = (int)(Display.brightness * 4); - if (brightness == 1) { - drawSkinPart(new int[]{ww-(padding+18*0),2,ww-(padding+16+18*0),2+16}, new float[]{16*10,16*0,16+16*10,16+16*0}); - } else if (brightness == 2) { - drawSkinPart(new int[]{ww-(padding+18*0),2,ww-(padding+16+18*0),2+16}, new float[]{16*11,16*0,16+16*11,16+16*0}); - } else if (brightness == 3) { - drawSkinPart(new int[]{ww-(padding+18*0),2,ww-(padding+16+18*0),2+16}, new float[]{16*12,16*0,16+16*12,16+16*0}); - } else if (brightness == 4) { - drawSkinPart(new int[]{ww-(padding+18*0),2,ww-(padding+16+18*0),2+16}, new float[]{16*13,16*0,16+16*13,16+16*0}); - } - - padding += 18+6; - - boolean canGoBack = canGoBack(); - boolean canGoForward = canGoForward(); - - - if (Calculator.haxMode) { - drawSkinPart(new int[]{ww-(padding+16),2,ww-padding,2+16}, new float[]{16*18,16*0,16+16*18,16+16*0}); - padding += 18+6; - } - - if (canGoBack && canGoForward) { - drawSkinPart(new int[]{ww-(padding+16),2,ww-padding,2+16}, new float[]{16*14,16*0,16+16*14,16+16*0}); - } else if (canGoBack) { - drawSkinPart(new int[]{ww-(padding+16),2,ww-padding,2+16}, new float[]{16*15,16*0,16+16*15,16+16*0}); - } else if (canGoForward) { - drawSkinPart(new int[]{ww-(padding+16),2,ww-padding,2+16}, new float[]{16*16,16*0,16+16*16,16+16*0}); - } else { - drawSkinPart(new int[]{ww-(padding+16),2,ww-padding,2+16}, new float[]{16*17,16*0,16+16*17,16+16*0}); - } - - padding += 18; - - } - - - private void draw_screen() { - screen.render(); - } - - - private void draw_bottom() { - - } - - private void draw_world() { - float x = 20; - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - colore(1.0f, 1.0f, 1.0f); - - if (error != null) { - colore(0.0f, 0.0f, 0.0f, 0.75f); - print(ww-textSize(1, "ANDREA CAVALLI'S CALCULATOR")[0]-2, wh-this.glyphsHeight[1]-2, 1, "ANDREA CAVALLI'S CALCULATOR"); - colore(0.0f, 0.0f, 0.0f, 0.75f); - print((ww/2)-(textSize(0, "UNEXPECTED EXCEPTION")[0]/2), 11, 0, "UNEXPECTED EXCEPTION"); - colore(0.0f, 0.0f, 0.0f, 0.5f); - print((ww/2)-(textSize(1, error)[0]/2), 22, 1, error); - colore(0.0f, 0.0f, 0.0f, 0.4f); - int i = 22; - for (String stackPart : errorStackTrace) { - print(2, 22+i, 1, stackPart); - i+=11; - } - } else if (loading) { - colore(1.0f, 1.0f, 1.0f, 1.0f); - int titlew = textSize(0, "ANDREA CAVALLI'S CALCULATOR")[0]; - print((ww/2)-(titlew/2)-1, (wh/2)-25+translation, 0, "ANDREA CAVALLI'S CALCULATOR"); - colore(1.0f, 1.0f, 1.0f, 1.0f); - print((ww/2)-(titlew/2)+1, (wh/2)-25+translation, 0, "ANDREA CAVALLI'S CALCULATOR"); - colore(1.0f, 1.0f, 1.0f, 1.0f); - print((ww/2)-(titlew/2), (wh/2)-25-1+translation, 0, "ANDREA CAVALLI'S CALCULATOR"); - colore(1.0f, 1.0f, 1.0f, 1.0f); - print((ww/2)-(titlew/2), (wh/2)-25+1+translation, 0, "ANDREA CAVALLI'S CALCULATOR"); - colore(1.0f, 0.5f, 0.0f, 1.0f); - print((ww/2)-(titlew/2), (wh/2)-25+translation, 0, "ANDREA CAVALLI'S CALCULATOR"); - colore(0.0f, 0.0f, 0.0f, 0.75f); - print((ww/2)-(textSize(0, "LOADING")[0]/2), (wh/2)+11, 0, "LOADING"); - colore(0.0f, 0.0f, 0.0f, 0.5f); - print((ww/2)-(textSize(1, "PLEASE WAIT...")[0]/2), (wh/2)+22, 1, "PLEASE WAIT..."); - } else { - draw_status(); - draw_screen(); - draw_bottom(); - } - } - - private void draw() { - draw_init(); - draw_world(); - glfwSwapBuffers(window); - } - - private void loopmode(float dt) { - - rotate_t += dt; - translate_t += dt; - - /* - * Calcoli - */ - - if (translation >= 10.0f) { - translation = 10.0f; - translation_top_to_bottom = false; - } else if (translation <= -10.0f) { - translation = -10.0f; - translation_top_to_bottom = true; - } - - if (translation_top_to_bottom) { - translation += dt*15; - } else { - translation -= dt*15; - } - - screen.beforeRender(dt); - - draw(); - } - - private GLFWKeyCallback keyCallback = new GLFWKeyCallback() { - @Override - public void invoke(long window, int key, int scancode, int action, int mods) { - if ( action == GLFW_RELEASE ) - return; - switch ( key ) { - case GLFW_KEY_ESCAPE: - glfwSetWindowShouldClose(window, GLFW_TRUE); - break; - case GLFW_KEY_F: - font = 1; - break; - case GLFW_KEY_R: - rotating = !rotating; - rotate_t = 0.0f; - break; - case GLFW_KEY_P: - integer_align = !integer_align; - break; - case GLFW_KEY_G: - font = 0; - break; - case GLFW_KEY_V: - show_tex = !show_tex; - break; - case GLFW_KEY_B: - black_on_white = !black_on_white; - break; - } - } - }; - - private GLFWWindowSizeCallback sizecallback = new GLFWWindowSizeCallback() { - @Override - public void invoke(long window, int width, int height) { - Display.this.ww = width; - Display.this.wh = height; - } - }; - - private GLFWFramebufferSizeCallback fbsizecallback = new GLFWFramebufferSizeCallback() { - @Override - public void invoke(long window, int width, int height) { - Display.this.fbw = width; - Display.this.fbh = height; - } - }; - - private void createWindow(String title) { - GLFWErrorCallback.createPrint().set(); - if ( glfwInit() == GLFW_FALSE ) - throw new IllegalStateException("Unable to initialize GLFW"); - - glfwDefaultWindowHints(); - glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); - glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); - - this.window = glfwCreateWindow(ww, wh, title, NULL, NULL); - if ( window == NULL ) - throw new RuntimeException("Failed to create the GLFW window"); - - glfwSetWindowSizeCallback(window, sizecallback); - glfwSetFramebufferSizeCallback(window, fbsizecallback); - glfwSetKeyCallback(window, keyCallback); - - // Center window - GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); - - glfwSetWindowPos( - window, - (vidmode.width() - ww) / 2, - (vidmode.height() - wh) / 2 - ); - - // Create context - glfwMakeContextCurrent(window); - GL.createCapabilities(); - - glfwSwapInterval(1); - glfwShowWindow(window); - - glfwInvoke(window, sizecallback, fbsizecallback); - - // Detect sRGB support - GLCapabilities caps = GL.getCapabilities(); - supportsSRGB = caps.OpenGL30 || caps.GL_ARB_framebuffer_sRGB || caps.GL_EXT_framebuffer_sRGB; - } - - public void run(String title) { - try { - createWindow(title); - load_skin(); - load_fonts(); - - long time = System.nanoTime(); - while ( glfwWindowShouldClose(window) == GLFW_FALSE ) { - glfwPollEvents(); - - long t = System.nanoTime(); - float dt = (float)((t - time) / 1000000000.0); - time = t; - - loopmode(dt); - } - } finally { - try { - destroy(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - private void destroy() { - chardata.clear(); - - glfwTerminate(); - glfwSetErrorCallback(null); - - memFree(yb); - memFree(xb); - - q.free(); - } - - public void setBackground(float d, float e, float f) { - background = new float[]{d,e,f}; - } - - public void colore(float f1, float f2, float f3) { - glColor3f(f1*brightness, f2*brightness, f3*brightness); - } - - public void colore(float f1, float f2, float f3, float f4) { - glColor4f(f1*brightness, f2*brightness, f3*brightness, f4); - } - - public void clearColor(float f1, float f2, float f3, float f4) { - glClearColor(f1*brightness,f2*brightness,f3*brightness,f4); - } -} \ No newline at end of file diff --git a/src/org/warp/engine/lwjgl/GLFWUtil.java b/src/org/warp/engine/lwjgl/GLFWUtil.java deleted file mode 100644 index f40da149..00000000 --- a/src/org/warp/engine/lwjgl/GLFWUtil.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.warp.engine.lwjgl; - -import org.lwjgl.glfw.GLFWFramebufferSizeCallbackI; -import org.lwjgl.glfw.GLFWWindowSizeCallbackI; -import org.lwjgl.system.MemoryStack; - -import java.nio.IntBuffer; - -import static org.lwjgl.glfw.GLFW.*; -import static org.lwjgl.system.MemoryStack.*; - -/** GLFW demo utilities. */ -public final class GLFWUtil { - - private GLFWUtil() { - } - - /** - * Invokes the specified callbacks using the current window and framebuffer sizes of the specified GLFW window. - * - * @param window the GLFW window - * @param windowSizeCB the window size callback, may be null - * @param framebufferSizeCB the framebuffer size callback, may be null - */ - public static void glfwInvoke( - long window, - GLFWWindowSizeCallbackI windowSizeCB, - GLFWFramebufferSizeCallbackI framebufferSizeCB - ) { - try ( MemoryStack stack = stackPush() ) { - IntBuffer w = stack.mallocInt(1); - IntBuffer h = stack.mallocInt(1); - - if ( windowSizeCB != null ) { - glfwGetWindowSize(window, w, h); - windowSizeCB.invoke(window, w.get(0), h.get(0)); - } - - if ( framebufferSizeCB != null ) { - glfwGetFramebufferSize(window, w, h); - framebufferSizeCB.invoke(window, w.get(0), h.get(0)); - } - } - - } - -} diff --git a/src/org/warp/engine/lwjgl/IOUtil.java b/src/org/warp/engine/lwjgl/IOUtil.java deleted file mode 100644 index 630d262d..00000000 --- a/src/org/warp/engine/lwjgl/IOUtil.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.warp.engine.lwjgl; - -import org.lwjgl.BufferUtils; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; -import java.nio.channels.Channels; -import java.nio.channels.ReadableByteChannel; -import java.nio.channels.SeekableByteChannel; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; - -import static org.lwjgl.BufferUtils.*; - -public final class IOUtil { - - private IOUtil() { - } - - private static ByteBuffer resizeBuffer(ByteBuffer buffer, int newCapacity) { - ByteBuffer newBuffer = BufferUtils.createByteBuffer(newCapacity); - buffer.flip(); - newBuffer.put(buffer); - return newBuffer; - } - - /** - * Reads the specified resource and returns the raw data as a ByteBuffer. - * - * @param resource the resource to read - * @param bufferSize the initial buffer size - * - * @return the resource data - * - * @throws IOException if an IO error occurs - */ - public static ByteBuffer ioResourceToByteBuffer(String resource, int bufferSize) throws IOException { - ByteBuffer buffer; - - Path path = Paths.get(resource); - if ( Files.isReadable(path) ) { - try (SeekableByteChannel fc = Files.newByteChannel(path)) { - buffer = BufferUtils.createByteBuffer((int)fc.size() + 1); - while ( fc.read(buffer) != -1 ) ; - } - } else { - try ( - InputStream source = IOUtil.class.getClassLoader().getResourceAsStream(resource); - ReadableByteChannel rbc = Channels.newChannel(source) - ) { - buffer = createByteBuffer(bufferSize); - - while ( true ) { - int bytes = rbc.read(buffer); - if ( bytes == -1 ) - break; - if ( buffer.remaining() == 0 ) - buffer = resizeBuffer(buffer, buffer.capacity() * 2); - } - } - } - - buffer.flip(); - return buffer; - } - -} \ No newline at end of file diff --git a/src/org/warp/engine/lwjgl/Screen.java b/src/org/warp/engine/lwjgl/Screen.java deleted file mode 100644 index 9316d88d..00000000 --- a/src/org/warp/engine/lwjgl/Screen.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.warp.engine.lwjgl; - -public abstract class Screen { - public Display d; - public boolean initialized = false; - public boolean canBeInHistory = false; - - public Screen() { - } - - public void initialize() throws InterruptedException { - if (!initialized) { - initialized = true; - init(); - } - } - - public abstract void init() throws InterruptedException; - - public abstract void render(); - - public abstract void beforeRender(float dt); -} diff --git a/src/org/warp/picalculator/BMPFile.java b/src/org/warp/picalculator/BMPFile.java new file mode 100644 index 00000000..bbb986c2 --- /dev/null +++ b/src/org/warp/picalculator/BMPFile.java @@ -0,0 +1,219 @@ +package org.warp.picalculator; + +import java.awt.Component; +import java.awt.Image; +import java.awt.image.PixelGrabber; +import java.io.FileOutputStream; + +public class BMPFile extends Component { + /** + * + */ + private static final long serialVersionUID = 9182927946568629682L; + // --- Private constants + private final static int BITMAPFILEHEADER_SIZE = 14; + private final static int BITMAPINFOHEADER_SIZE = 40; + // --- Private variable declaration + // --- Bitmap file header + @SuppressWarnings("unused") + private byte bitmapFileHeader[] = new byte[14]; + private byte bfType[] = { 'B', 'M' }; + private int bfSize = 0; + private int bfReserved1 = 0; + private int bfReserved2 = 0; + private int bfOffBits = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; + // --- Bitmap info header + @SuppressWarnings("unused") + private byte bitmapInfoHeader[] = new byte[40]; + private int biSize = BITMAPINFOHEADER_SIZE; + private int biWidth = 0; + private int biHeight = 0; + private int biPlanes = 1; + private int biBitCount = 24; + private int biCompression = 0; + private int biSizeImage = 0x030000; + private int biXPelsPerMeter = 0x0; + private int biYPelsPerMeter = 0x0; + private int biClrUsed = 0; + private int biClrImportant = 0; + // --- Bitmap raw data + private int bitmap[]; + // --- File section + private FileOutputStream fo; + + // --- Default constructor + public BMPFile() {} + + public void saveBitmap(String parFilename, Image parImage, int parWidth, int parHeight) { + try { + fo = new FileOutputStream(parFilename); + save(parImage, parWidth, parHeight); + fo.close(); + } catch (Exception saveEx) { + saveEx.printStackTrace(); + } + } + + /* + * The saveMethod is the main method of the process. This method + * will call the convertImage method to convert the memory image to + * a byte array; method writeBitmapFileHeader creates and writes + * the bitmap file header; writeBitmapInfoHeader creates the + * information header; and writeBitmap writes the image. + * + */ + private void save(Image parImage, int parWidth, int parHeight) { + try { + convertImage(parImage, parWidth, parHeight); + writeBitmapFileHeader(); + writeBitmapInfoHeader(); + writeBitmap(); + } catch (Exception saveEx) { + saveEx.printStackTrace(); + } + } + + /* + * convertImage converts the memory image to the bitmap format (BRG). + * It also computes some information for the bitmap info header. + * + */ + private boolean convertImage(Image parImage, int parWidth, int parHeight) { + int pad; + bitmap = new int[parWidth * parHeight]; + PixelGrabber pg = new PixelGrabber(parImage, 0, 0, parWidth, parHeight, bitmap, 0, parWidth); + try { + pg.grabPixels(); + } catch (InterruptedException e) { + e.printStackTrace(); + return (false); + } + pad = (4 - ((parWidth * 3) % 4)) * parHeight; + biSizeImage = ((parWidth * parHeight) * 3) + pad; + bfSize = biSizeImage + BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; + biWidth = parWidth; + biHeight = parHeight; + return (true); + } + + /* + * writeBitmap converts the image returned from the pixel grabber to + * the format required. Remember: scan lines are inverted in + * a bitmap file! + * + * Each scan line must be padded to an even 4-byte boundary. + */ + private void writeBitmap() { + int size; + int value; + int j; + int i; + int rowCount; + int rowIndex; + int lastRowIndex; + int pad; + int padCount; + byte rgb[] = new byte[3]; + size = (biWidth * biHeight) - 1; + pad = 4 - ((biWidth * 3) % 4); + if (pad == 4) // <==== Bug correction + pad = 0; // <==== Bug correction + rowCount = 1; + padCount = 0; + rowIndex = size - biWidth; + lastRowIndex = rowIndex; + try { + for (j = 0; j < size; j++) { + value = bitmap[rowIndex]; + rgb[0] = (byte) (value & 0xFF); + rgb[1] = (byte) ((value >> 8) & 0xFF); + rgb[2] = (byte) ((value >> 16) & 0xFF); + fo.write(rgb); + if (rowCount == biWidth) { + padCount += pad; + for (i = 1; i <= pad; i++) { + fo.write(0x00); + } + rowCount = 1; + rowIndex = lastRowIndex - biWidth; + lastRowIndex = rowIndex; + } else + rowCount++; + rowIndex++; + } + // --- Update the size of the file + bfSize += padCount - pad; + biSizeImage += padCount - pad; + } catch (Exception wb) { + wb.printStackTrace(); + } + } + + /* + * writeBitmapFileHeader writes the bitmap file header to the file. + * + */ + private void writeBitmapFileHeader() { + try { + fo.write(bfType); + fo.write(intToDWord(bfSize)); + fo.write(intToWord(bfReserved1)); + fo.write(intToWord(bfReserved2)); + fo.write(intToDWord(bfOffBits)); + } catch (Exception wbfh) { + wbfh.printStackTrace(); + } + } + + /* + * + * writeBitmapInfoHeader writes the bitmap information header + * to the file. + * + */ + private void writeBitmapInfoHeader() { + try { + fo.write(intToDWord(biSize)); + fo.write(intToDWord(biWidth)); + fo.write(intToDWord(biHeight)); + fo.write(intToWord(biPlanes)); + fo.write(intToWord(biBitCount)); + fo.write(intToDWord(biCompression)); + fo.write(intToDWord(biSizeImage)); + fo.write(intToDWord(biXPelsPerMeter)); + fo.write(intToDWord(biYPelsPerMeter)); + fo.write(intToDWord(biClrUsed)); + fo.write(intToDWord(biClrImportant)); + } catch (Exception wbih) { + wbih.printStackTrace(); + } + } + + /* + * + * intToWord converts an int to a word, where the return + * value is stored in a 2-byte array. + * + */ + private byte[] intToWord(int parValue) { + byte retValue[] = new byte[2]; + retValue[0] = (byte) (parValue & 0x00FF); + retValue[1] = (byte) ((parValue >> 8) & 0x00FF); + return (retValue); + } + + /* + * + * intToDWord converts an int to a double word, where the return + * value is stored in a 4-byte array. + * + */ + private byte[] intToDWord(int parValue) { + byte retValue[] = new byte[4]; + retValue[0] = (byte) (parValue & 0x00FF); + retValue[1] = (byte) ((parValue >> 8) & 0x000000FF); + retValue[2] = (byte) ((parValue >> 16) & 0x000000FF); + retValue[3] = (byte) ((parValue >> 24) & 0x000000FF); + return (retValue); + } +} \ No newline at end of file diff --git a/src/org/warp/picalculator/Calculator.java b/src/org/warp/picalculator/Calculator.java new file mode 100644 index 00000000..e3b34952 --- /dev/null +++ b/src/org/warp/picalculator/Calculator.java @@ -0,0 +1,79 @@ +package org.warp.picalculator; + +import org.nevec.rjm.NumeroAvanzato; +import org.warp.device.PIDisplay; +import org.warp.engine.Screen; +import org.warp.picalculator.screens.EquationScreen; +import org.warp.picalculator.screens.SolveEquationScreen; + +public class Calculator { + + public static String angleMode = "deg"; + public static Screen[] sessions = new Screen[5]; + public static int currentSession = 0; + public static boolean haxMode = true; + + public static Termine calcolarisultato(String string) throws Errore { + System.out.println("INPUT: " + string); + Espressione espressione = new Espressione(string); + return espressione.calcola(); + } + + public static Funzione interpreta(String string) throws Errore { + if (string.contains("{")) { + if (!string.startsWith("{")) { + throw new Errore(Errori.SYNTAX_ERROR); + } + String[] parts = string.substring(1).split("\\{"); + Sistema s = new Sistema(); + for (String part : parts) { + s.addVariableToEnd(interpretaEquazione(part)); + } + return s; + } else if (string.contains("=")) { + return interpretaEquazione(string); + } else { + return new Espressione(string); + } + } + + public static Funzione interpretaEquazione(String string) throws Errore { + String[] parts = string.split("="); + if (parts.length == 1) { + return new Equazione(new Espressione(parts[0]), new Termine(NumeroAvanzato.ZERO)); + } else if (parts.length == 2) { + return new Equazione(new Espressione(parts[0]), new Espressione(parts[1])); + } else { + throw new Errore(Errori.SYNTAX_ERROR); + } + } + + public static void solve() throws Errore { + if (Calculator.currentSession == 0 && Calculator.sessions[0] instanceof EquationScreen) { + EquationScreen es = (EquationScreen) Calculator.sessions[0]; + Funzione f = es.f; + if (f instanceof Equazione) { + PIDisplay.INSTANCE.setScreen(new SolveEquationScreen(es)); + } else { + es.f2 = es.f.calcola(); + es.f2.calcolaGrafica(); + } + } + } + + public static void solve(char letter) throws Errore { + if (Calculator.currentSession == 0 && Calculator.sessions[0] instanceof EquationScreen) { + EquationScreen es = (EquationScreen) Calculator.sessions[0]; + Funzione f = es.f; + if (f instanceof Equazione) { + es.f2 = ((Equazione)f).calcola(letter); + es.f2.calcolaGrafica(); + } + } + } + + public static void simplify() { + + } + +} diff --git a/src/org/warp/picalculator/Divisione.java b/src/org/warp/picalculator/Divisione.java new file mode 100644 index 00000000..8a90e98f --- /dev/null +++ b/src/org/warp/picalculator/Divisione.java @@ -0,0 +1,175 @@ +package org.warp.picalculator; + +import static org.warp.engine.Display.Render.getStringWidth; +import static org.warp.engine.Display.Render.glColor3f; +import static org.warp.engine.Display.Render.glDrawStringLeft; +import static org.warp.engine.Display.Render.glFillRect; + +import org.nevec.rjm.NumeroAvanzatoVec; +import org.warp.device.PIDisplay; +import org.warp.engine.Display; + +public class Divisione extends FunzioneDueValoriBase { + + public Divisione(FunzioneBase value1, FunzioneBase value2) { + super(value1, value2); + } + + @Override + public String simbolo() { + return Simboli.DIVISION; + } + + @Override + public Termine calcola() throws Errore { + if (variable2 == null || variable1 == null) { + return new Termine("0"); + } + if (variable2.calcola().getTerm().compareTo(NumeroAvanzatoVec.ZERO) == 0) { + throw new Errore(Errori.DIVISION_BY_ZERO); + } + return variable1.calcola().divide(variable2.calcola()); + } + + public boolean hasMinus() { + String numerator = variable1.toString(); + if (numerator.startsWith("-")) { + return true; + } + return false; + } + + public void draw(int x, int y, boolean small, boolean drawMinus) { + boolean beforedrawminus = this.drawMinus; + this.drawMinus = drawMinus; + draw(x, y); + this.drawMinus = beforedrawminus; + } + + private boolean drawMinus = true; + + @Override + public void calcolaGrafica() { + variable1.setSmall(true); + variable1.calcolaGrafica(); + + variable2.setSmall(true); + variable2.calcolaGrafica(); + + width = calcWidth(); + height = calcHeight(); + line = variable1.getHeight() + 1; + } + + @Override + public void draw(int x, int y) { +// glColor3f(255, 127-50+new Random().nextInt(50), 0); +// glFillRect(x,y,width,height); +// glColor3f(0, 0, 0); + + Object var1 = variable1; + Object var2 = variable2; + boolean minus = false; + int minusw = 0; + int minush = 0; + String numerator = ((Funzione) var1).toString(); + if (numerator.startsWith("-") && ((Funzione) var1) instanceof Termine && ((Termine) var1).term.isBigInteger(true)) { + minus = true; + numerator = numerator.substring(1); + } + int w1 = 0; + int h1 = 0; + if (minus) { + w1 = getStringWidth(numerator); + h1 = Utils.getFontHeight(small); + } else { + w1 = ((Funzione) var1).getWidth(); + h1 = ((Funzione) var1).getHeight(); + } + int w2 = ((Funzione) var2).getWidth(); + int maxw; + if (w1 > w2) { + maxw = 1 + w1; + } else { + maxw = 1 + w2; + } + if (minus && drawMinus) { + minusw = getStringWidth("-") + 1; + minush = Utils.getFontHeight(small); + if (small) { + Display.Render.setFont(PIDisplay.fonts[1]); + } else { + Display.Render.setFont(PIDisplay.fonts[0]); + } + glDrawStringLeft(x+1, y + h1 + 1 + 1 - (minush / 2), "-"); + glDrawStringLeft((int) (x+1 + minusw + 1 + (maxw - w1) / 2d), y, numerator); + } else { + ((Funzione) var1).draw((int) (x+1 + minusw + (maxw - w1) / 2d), y); + } + ((Funzione) var2).draw((int) (x+1 + minusw + (maxw - w2) / 2d), y + h1 + 1 + 1 + 1); + glColor3f(0, 0, 0); + glFillRect(x+1+ minusw, y + h1 + 1, maxw, 1); + } + + @Override + public int getHeight() { + return height; + } + + @Override + protected int calcHeight() { + + boolean minus = false; + String numerator = variable1.toString(); + if (numerator.startsWith("-") && variable1 instanceof Termine && ((Termine) variable1).term.isBigInteger(true)) { + minus = true; + numerator = numerator.substring(1); + } + int h1 = 0; + if (minus) { + h1 = Utils.getFontHeight(small); + } else { + h1 = variable1.getHeight(); + } + int h2 = variable2.getHeight(); + return h1 + 3 + h2; + } + + @Override + public int getLine() { + return line; + } + + @Override + public int getWidth() { + return width; + } + + @Override + protected int calcWidth() { + boolean minus = false; + String numerator = variable1.toString(); + if (numerator.startsWith("-") && variable1 instanceof Termine && ((Termine) variable1).term.isBigInteger(true)) { + minus = true; + numerator = numerator.substring(1); + } + int w1 = 0; + if (minus) { + w1 = getStringWidth(numerator); + } else { + w1 = variable1.getWidth(); + } + int w2 = variable2.getWidth(); + int maxw = 0; + if (w1 > w2) { + maxw = w1+1; + } else { + maxw = w2+1; + } + if (minus && drawMinus) { + return 1 + getStringWidth("-") + 1 + maxw; + } else { + return 1 + maxw; + } + } +} \ No newline at end of file diff --git a/src/org/warp/picalculator/Equazione.java b/src/org/warp/picalculator/Equazione.java new file mode 100644 index 00000000..be5ac025 --- /dev/null +++ b/src/org/warp/picalculator/Equazione.java @@ -0,0 +1,57 @@ +package org.warp.picalculator; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +import com.rits.cloning.Cloner; + +public class Equazione extends FunzioneDueValori { + + public Equazione(Funzione value1, Funzione value2) { + super(value1,value2); + } + + @Override + public String simbolo() { + return Simboli.EQUATION; + } + + @Override + public Funzione calcola() throws Errore { + return new Equazione(new Sottrazione((FunzioneBase)variable1.calcola(), (FunzioneBase)variable2.calcola()).calcola(), new Termine("0")); + } + + public Funzione calcola(char charIncognita) { + @SuppressWarnings("unused") + ArrayList e; + //TODO: Finire. Fare in modo che risolva i passaggi fino a che non ce ne sono più + return null; + } + + public ArrayList risolviPassaggio(char charIncognita) { + ArrayList result = new ArrayList(); + result.add(this.clone()); + for (Tecnica t : Tecnica.tecniche) { + ArrayList newResults = new ArrayList(); + final int sz = result.size(); + for (int n = 0; n < sz; n++) { + newResults.addAll(t.risolvi(result.get(n))); + } + Set hs = new HashSet<>(); + hs.addAll(newResults); + newResults.clear(); + newResults.addAll(hs); + result = newResults; + } + // TODO: controllare se è a posto + return result; + } + + @Override + public Equazione clone() { + Cloner cloner = new Cloner(); + return cloner.deepClone(this); + } + +} \ No newline at end of file diff --git a/src/org/warpgate/pi/calculator/Errore.java b/src/org/warp/picalculator/Errore.java similarity index 82% rename from src/org/warpgate/pi/calculator/Errore.java rename to src/org/warp/picalculator/Errore.java index b9106b3b..46a85a8e 100644 --- a/src/org/warpgate/pi/calculator/Errore.java +++ b/src/org/warp/picalculator/Errore.java @@ -1,15 +1,15 @@ -package org.warpgate.pi.calculator; - -public class Errore extends java.lang.Throwable { - - /** - * - */ - private static final long serialVersionUID = -1014947815755694651L; - - public Errore(Errori IDErrore) { - id = IDErrore; - } - - public Errori id = Errori.ERROR; -} +package org.warp.picalculator; + +public class Errore extends java.lang.Throwable { + + /** + * + */ + private static final long serialVersionUID = -1014947815755694651L; + + public Errore(Errori IDErrore) { + id = IDErrore; + } + + public Errori id = Errori.ERROR; +} diff --git a/src/org/warp/picalculator/Errori.java b/src/org/warp/picalculator/Errori.java new file mode 100644 index 00000000..eef61c6b --- /dev/null +++ b/src/org/warp/picalculator/Errori.java @@ -0,0 +1,5 @@ +package org.warp.picalculator; + +public enum Errori { + ERROR, DIVISION_BY_ZERO, UNBALANCED_BRACKETS, NOT_IMPLEMENTED, NEGATIVE_PARAMETER, NUMBER_TOO_LARGE, NUMBER_TOO_SMALL, CONVERSION_ERROR, SYNTAX_ERROR, NOT_AN_EQUATION +} diff --git a/src/org/warp/picalculator/Espressione.java b/src/org/warp/picalculator/Espressione.java new file mode 100644 index 00000000..c069cc6a --- /dev/null +++ b/src/org/warp/picalculator/Espressione.java @@ -0,0 +1,673 @@ +package org.warp.picalculator; + +import static org.warp.engine.Display.Render.glColor3f; +import static org.warp.engine.Display.Render.glDrawLine; +import static org.warp.picalculator.Utils.ArrayToRegex; +import static org.warp.picalculator.Utils.concat; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.nevec.rjm.NumeroAvanzato; +import org.nevec.rjm.NumeroAvanzatoVec; + +public class Espressione extends FunzioneMultiplaBase { + + public Espressione() { + super(); + } + + public Espressione(FunzioneBase[] values) { + super(values); + } + + private boolean parentesiIniziale = false; + + public Espressione(String string) throws Errore { + this(string, "", true); + } + + public Espressione(String string, String debugSpaces, boolean parentesiIniziale) throws Errore { + super(); + this.parentesiIniziale = parentesiIniziale; + boolean isNumber = false; + + // Determina se l'espressione è già un numero: + try { + new Termine(string); + isNumber = true; + } catch (NumberFormatException ex) { + isNumber = false; + } + + String processExpression = string; + Utils.debug.println(debugSpaces + "•Analyzing expression:" + processExpression); + + if (isNumber){ + // Se l'espressione è già un numero: + Termine t = new Termine(string); + setVariables(new FunzioneBase[] { t }); + Utils.debug.println(debugSpaces + "•Result:" + t.toString()); + } else { + // Altrimenti prepara l'espressione: + debugSpaces += " "; + + // Se l'espressione non è già un numero: + + // Controlla se ci sono più di un uguale + int equationsFound = 0; + int systemsFound = 0; + for (char c : processExpression.toCharArray()) { + if (("" + c).equals(Simboli.EQUATION)) { + equationsFound += 1; + } + if (("" + c).equals(Simboli.SYSTEM)) { + equationsFound += 1; + } + } + if (equationsFound == 1 && systemsFound == 0) { + processExpression = Simboli.SYSTEM + processExpression; + systemsFound += 1; + } + if (equationsFound != systemsFound) { + throw new Errore(Errori.SYNTAX_ERROR); + } + + // Correggi i segni ++ e -- in eccesso + Pattern pattern = Pattern.compile("\\+\\++?|\\-\\-+?"); + Matcher matcher = pattern.matcher(processExpression); + boolean cambiati = false; + while (matcher.find()) { + cambiati = true; + String correzione = "+"; + processExpression = processExpression.substring(0, matcher.start(0) + 1) + correzione + processExpression.substring(matcher.start(0) + matcher.group(0).length(), processExpression.length()); + matcher = pattern.matcher(processExpression); + } + + // Correggi i segni +- e -+ in eccesso + pattern = Pattern.compile("\\+\\-|\\-\\+"); + matcher = pattern.matcher(processExpression); + while (matcher.find()) { + cambiati = true; + String correzione = "-"; + processExpression = processExpression.substring(0, matcher.start(0)) + correzione + processExpression.substring(matcher.start(0) + matcher.group(0).length(), processExpression.length()); + matcher = pattern.matcher(processExpression); + } + + // Rimuovi i segni appena dopo le parentesi + if (processExpression.contains("(+")) { + cambiati = true; + processExpression = processExpression.replace("(+", "("); + } + + // Cambia i segni appena prima le parentesi + if (processExpression.contains("-(")) { + cambiati = true; + processExpression = processExpression.replace("-(", "-1*("); + } + // Rimuovi i segni appena dopo l'inizio + if (processExpression.startsWith("+")) { + cambiati = true; + processExpression = processExpression.substring(1, processExpression.length()); + } + + // Rimuovi i + in eccesso + pattern = Pattern.compile("[" + ArrayToRegex(Utils.add(concat(Simboli.segni(true, true), Simboli.funzioni()), "(")) + "]\\+[^" + ArrayToRegex(concat(concat(Simboli.segni(true, true), Simboli.funzioni()), new String[] { "(", ")" })) + "]+?[" + ArrayToRegex(concat(Simboli.segni(true, true), Simboli.funzioni())) + "]|[" + ArrayToRegex(concat(Simboli.segni(true, true), Simboli.funzioni())) + "]+?\\+[^" + ArrayToRegex(concat(concat(Simboli.segni(true, true), Simboli.funzioni()), new String[] { "(", ")" })) + "]"); + matcher = pattern.matcher(processExpression); + cambiati = false; + while (matcher.find()) { + cambiati = true; + String correzione = matcher.group(0).replaceFirst(Matcher.quoteReplacement("+"), ""); + processExpression = processExpression.substring(0, matcher.start(0) + 1) + correzione + processExpression.substring(matcher.start(0) + matcher.group(0).length(), processExpression.length()); + matcher = pattern.matcher(processExpression); + } + + // Correggi i segni - in +- + pattern = Pattern.compile("[^" + Utils.ArrayToRegex(concat(concat(Simboli.funzioni(), new String[] { Simboli.PARENTHESIS_OPEN }), Simboli.segni(true, true))) + "]-"); + matcher = pattern.matcher(processExpression); + while (matcher.find()) { + cambiati = true; + String correzione = "+-"; + processExpression = processExpression.substring(0, matcher.start(0) + 1) + correzione + processExpression.substring(matcher.start(0) + matcher.group(0).length(), processExpression.length()); + matcher = pattern.matcher(processExpression); + } + + if (cambiati) { + Utils.debug.println(debugSpaces + "•Resolved signs:" + processExpression); + } + + // Aggiungi i segni * accanto alle parentesi + pattern = Pattern.compile("\\([^\\(]+?\\)"); + matcher = pattern.matcher(processExpression); + cambiati = false; + while (matcher.find()) { + cambiati = true; + // sistema i segni * impliciti prima e dopo l'espressione. + String beforeexp = processExpression.substring(0, matcher.start(0)); + String newexp = matcher.group(0).substring(1, matcher.group(0).length() - 1); + String afterexp = processExpression.substring(matcher.start(0) + matcher.group(0).length(), processExpression.length()); + if (Pattern.compile("[^\\-" + Utils.ArrayToRegex(Utils.add(concat(Simboli.funzioni(), concat(Simboli.segni(true, true), Simboli.sintassiGenerale())), "(")) + "]$").matcher(beforeexp).find()) { + // Se la stringa precedente finisce con un numero + beforeexp += Simboli.MULTIPLICATION; + } + if (Pattern.compile("^[^\\-" + Utils.ArrayToRegex(Utils.add(concat(Simboli.funzioni(), concat(Simboli.segni(true, true), Simboli.sintassiGenerale())), ")")) + "]").matcher(afterexp).find()) { + // Se la stringa successiva inizia con un numero + afterexp = Simboli.MULTIPLICATION + afterexp; + } + processExpression = beforeexp + "â‘´" + newexp + "⑵" + afterexp; + matcher = pattern.matcher(processExpression); + } + + processExpression = processExpression.replace("â‘´", "(").replace("⑵", ")"); + + if (cambiati) { + Utils.debug.println(debugSpaces + "•Added implicit multiplications:" + processExpression); + } + + Utils.debug.println(debugSpaces + "•Subdivision in classes:"); + + debugSpaces += " "; + + // Suddividi tutto + Espressione parentesiNonSuddivisaCorrettamente = new Espressione(); + parentesiNonSuddivisaCorrettamente.setVariables(new FunzioneBase[] {}); + String tmp = ""; + final String[] funzioni = concat(concat(concat(concat(Simboli.funzioni(), Simboli.parentesi()), Simboli.segni(true, true)), Simboli.incognite()), Simboli.sintassiGenerale()); + for (int i = 0; i < processExpression.length(); i++) { + // Per ogni carattere cerca se è un numero o una funzione: + String charI = processExpression.charAt(i) + ""; + if (Utils.isInArray(charI, funzioni)) { + + // Cerca il tipo di funzione tra le esistenti + FunzioneBase f = null; + switch (charI) { + case Simboli.SUM: + f = new Somma(null, null); + break; + case Simboli.MULTIPLICATION: + f = new Moltiplicazione(null, null); + break; + case Simboli.PRIORITARY_MULTIPLICATION: + f = new MoltiplicazionePrioritaria(null, null); + break; + case Simboli.DIVISION: + f = new Divisione(null, null); + break; + case Simboli.NTH_ROOT: + f = new Radice(null, null); + break; + case Simboli.SQUARE_ROOT: + f = new RadiceQuadrata(null); + break; + case Simboli.POTENZA: + f = new Potenza(null, null); + break; + case Simboli.PARENTHESIS_OPEN: + // cerca l'ultima parentesi chiusa + int startIndex = i; + int endIndex = -1; + int jumps = -1; + for (int i2 = startIndex; i2 < processExpression.length(); i2++) { + if ((processExpression.charAt(i2) + "").equals(Simboli.PARENTHESIS_CLOSE)) { + if (jumps == 0) { + endIndex = i2; + break; + } else if (jumps > 0) { + jumps -= 1; + } else if (jumps < 0) { + throw new Errore(Errori.UNBALANCED_BRACKETS); + } + } else if ((processExpression.charAt(i2) + "").equals(Simboli.PARENTHESIS_OPEN)) { + jumps += 1; + } + } + if (endIndex == -1 || endIndex < startIndex) { + throw new Errore(Errori.UNBALANCED_BRACKETS); + } + startIndex += 1; + i = startIndex; + + String tmpExpr = ""; + while (i < endIndex) { + tmpExpr += processExpression.charAt(i); + i++; + } + f = new Espressione(tmpExpr, debugSpaces, false); + break; + default: + if (Utils.isInArray(charI, Simboli.incognite())) { + // Fallback + NumeroAvanzato na = NumeroAvanzato.ONE; + Incognite iy = na.getIncognitey(); + iy.incognite.add(new Incognita(charI.charAt(0), 1, 1)); + na = na.setIncognitey(iy); + f = new Termine(na); + } else { + throw new java.lang.RuntimeException("Il carattere " + charI + " non è tra le funzioni designate!\nAggiungerlo ad esse o rimuovere il carattere dall'espressione!"); + } + } + if (f instanceof Espressione) { + tmp = ""; + } else if (f instanceof Termine) { + if (parentesiNonSuddivisaCorrettamente.getVariablesLength() == 0) { + if (tmp.length() > 0) { + parentesiNonSuddivisaCorrettamente.addVariableToEnd(new Termine(tmp)); + Utils.debug.println(debugSpaces + "•Added value to expression:" + tmp); + parentesiNonSuddivisaCorrettamente.addVariableToEnd(new MoltiplicazionePrioritaria(null, null)); + Utils.debug.println(debugSpaces + "•Added variable to expression:" + new MoltiplicazionePrioritaria(null, null).simbolo()); + } + } else { + if (tmp.length() > 0) { + if (parentesiNonSuddivisaCorrettamente.getVariable(parentesiNonSuddivisaCorrettamente.getVariablesLength() - 1) instanceof Termine) { + parentesiNonSuddivisaCorrettamente.addVariableToEnd(new MoltiplicazionePrioritaria(null, null)); + Utils.debug.println(debugSpaces + "•Added variable to expression:" + new MoltiplicazionePrioritaria(null, null).simbolo()); + } + if (tmp.equals("-")) { + tmp = "-1"; + } + parentesiNonSuddivisaCorrettamente.addVariableToEnd(new Termine(tmp)); + Utils.debug.println(debugSpaces + "•Added value to expression:" + tmp); + } + if (tmp.length() > 0 || parentesiNonSuddivisaCorrettamente.getVariable(parentesiNonSuddivisaCorrettamente.getVariablesLength() - 1) instanceof Termine) { + parentesiNonSuddivisaCorrettamente.addVariableToEnd(new MoltiplicazionePrioritaria(null, null)); + Utils.debug.println(debugSpaces + "•Added variable to expression:" + new MoltiplicazionePrioritaria(null, null).simbolo()); + } + } + } else { + if (tmp.length() != 0) { + parentesiNonSuddivisaCorrettamente.addVariableToEnd(new Termine(tmp)); + Utils.debug.println(debugSpaces + "•Added variable to expression:" + tmp); + } + } + parentesiNonSuddivisaCorrettamente.addVariableToEnd(f); + Utils.debug.println(debugSpaces + "•Added variable to expression:" + f.simbolo()); + tmp = ""; + } else { + try { + if (charI.equals("-") == false && charI.equals(".") == false) { + new BigDecimal(tmp + charI); + } + // Se il carattere è un numero intero, un segno + // negativo, o un punto + tmp += charI; + } catch (Exception exc) { + throw new java.lang.RuntimeException("Il carattere " + tmp + charI + " non è nè un numero nè un espressione presente nella lista completa!\nAggiungerlo ad essa o rimuovere il carattere dall'espressione!"); + } + } + } + if (tmp.length() > 0) { + Utils.debug.println(debugSpaces + "•Added variable to expression:" + tmp); + try { + parentesiNonSuddivisaCorrettamente.addVariableToEnd(new Termine(tmp)); + } catch (NumberFormatException ex) { + throw new Errore(Errori.SYNTAX_ERROR); + } + tmp = ""; + } + + int dsl = debugSpaces.length(); + debugSpaces = ""; + for (int i = 0; i < dsl - 2; i++) { + debugSpaces += " "; + } + Utils.debug.println(debugSpaces + "•Finished the subdivision in classes."); + // Fine suddivisione di insieme + + Utils.debug.println(debugSpaces + "•Removing useless parentheses"); + for (int i = 0; i < parentesiNonSuddivisaCorrettamente.variables.length; i++) { + if (parentesiNonSuddivisaCorrettamente.variables[i] instanceof Espressione) { + Espressione par = (Espressione) parentesiNonSuddivisaCorrettamente.variables[i]; + if (par.variables.length == 1) { + FunzioneBase subFunz = par.variables[0]; + if (subFunz instanceof Espressione || subFunz instanceof Termine) { + parentesiNonSuddivisaCorrettamente.variables[i] = subFunz; + Utils.debug.println(debugSpaces + " •Useless parentheses removed"); + } + } + } + } + + // Inizia l'affinazione dell'espressione + Utils.debug.println(debugSpaces + "•Pushing classes..."); + + FunzioneBase[] funzioniOLDArray = parentesiNonSuddivisaCorrettamente.getVariables(); + ArrayList funzioniOLD = new ArrayList(); + for (int i = 0; i < funzioniOLDArray.length; i++) { + FunzioneBase funzione = funzioniOLDArray[i]; + if (funzione != null) { + //Affinazione + if (funzione instanceof Radice) { + if ((i - 1) >= 0 && funzioniOLDArray[i-1] instanceof Termine && ((Termine)funzioniOLDArray[i-1]).getTerm().compareTo(new NumeroAvanzatoVec(new NumeroAvanzato(new BigInteger("2")))) == 0) { + funzioniOLDArray[i] = null; + funzioniOLDArray[i-1] = null; + funzioniOLD.remove(funzioniOLD.size()-1); + i -= 1; + funzione = new RadiceQuadrata(null); + } + } + //Aggiunta della funzione alla lista grezza + funzioniOLD.add(funzione); + } + } + + if (funzioniOLD.size() > 1) { + Utils.debug.println(debugSpaces + " •Correcting classes:"); + + int before = 0; + String fase = "funzioniSN"; + int n = 0; + do { + before = funzioniOLD.size(); + int i = 0; + boolean change = false; + if (Utils.ciSonoMoltiplicazioniPrioritarieNonImpostate(funzioniOLD)) { + fase = "moltiplicazioni prioritarie"; // PRIMA FASE + } else if (Utils.ciSonoFunzioniSNnonImpostate(funzioniOLD)) { + fase = "funzioniSN"; // SECONDA FASE + } else if (Utils.ciSonoFunzioniNSNnonImpostate(funzioniOLD)) { + fase = "funzioniNSN"; // TERZA FASE + } else if (Utils.ciSonoMoltiplicazioniNonImpostate(funzioniOLD)) { + fase = "moltiplicazioni"; // QUARTA FASE + } else if (Utils.ciSonoSommeNonImpostate(funzioniOLD)) { + fase = "somme"; // QUINTA FASE + } else { +// fase = "errore"; + System.out.println("WARN: ---> POSSIBILE ERRORE????? <---");// BOH +// throw new Errore(Errori.SYNTAX_ERROR); + while (funzioniOLD.size() > 1) { + funzioniOLD.set(0, new Moltiplicazione(funzioniOLD.get(0), funzioniOLD.remove(1))); + } + } + Utils.debug.println(debugSpaces + " •Phase: "+fase); + while (i < funzioniOLD.size() && change == false && funzioniOLD.size() > 1) { + FunzioneBase funzioneTMP = funzioniOLD.get(i); + if (funzioneTMP instanceof FunzioneDueValoriBase) { + if (fase != "funzioniSN") { + if ( + (fase == "somme" && (funzioneTMP instanceof Somma) == true && ((funzioneTMP instanceof FunzioneAnterioreBase && ((FunzioneAnterioreBase) funzioneTMP).variable == null) || (funzioneTMP instanceof FunzioneDueValoriBase && ((FunzioneDueValoriBase) funzioneTMP).variable1 == null && ((FunzioneDueValoriBase) funzioneTMP).variable2 == null) || (!(funzioneTMP instanceof FunzioneAnterioreBase) && !(funzioneTMP instanceof FunzioneDueValoriBase)))) + || + ( + fase.equals("moltiplicazioni prioritarie") + && + (funzioneTMP instanceof MoltiplicazionePrioritaria) + && + ((FunzioneDueValoriBase) funzioneTMP).variable1 == null + && + ((FunzioneDueValoriBase) funzioneTMP).variable2 == null + ) + || + ( + fase.equals("moltiplicazioni") + && + ( + (funzioneTMP instanceof Moltiplicazione) + || + (funzioneTMP instanceof Divisione) + ) + && + ((FunzioneDueValoriBase) funzioneTMP).variable1 == null + && + ((FunzioneDueValoriBase) funzioneTMP).variable2 == null + ) + || + ( + fase == "funzioniNSN" + && + (funzioneTMP instanceof Somma) == false + && + (funzioneTMP instanceof Moltiplicazione) == false + && + (funzioneTMP instanceof MoltiplicazionePrioritaria) == false + && + (funzioneTMP instanceof Divisione) == false + && + ( + ( + funzioneTMP instanceof FunzioneAnterioreBase + && + ((FunzioneAnterioreBase) funzioneTMP).variable == null + ) + || + ( + funzioneTMP instanceof FunzioneDueValoriBase + && + ((FunzioneDueValoriBase) funzioneTMP).variable1 == null + && + ((FunzioneDueValoriBase) funzioneTMP).variable2 == null + ) + || + ( + !(funzioneTMP instanceof FunzioneAnterioreBase) + && + !(funzioneTMP instanceof FunzioneDueValoriBase) + ) + ) + ) + ) { + change = true; + + if (i + 1 < funzioniOLD.size() && i - 1 >= 0) { + ((FunzioneDueValoriBase) funzioneTMP).setVariable1((FunzioneBase) funzioniOLD.get(i - 1)); + ((FunzioneDueValoriBase) funzioneTMP).setVariable2((FunzioneBase) funzioniOLD.get(i + 1)); + funzioniOLD.set(i, funzioneTMP); + + // è importante togliere prima gli elementi + // in fondo e poi quelli davanti, perché gli + // indici scalano da destra a sinistra. + funzioniOLD.remove(i + 1); + funzioniOLD.remove(i - 1); + + Utils.debug.println(debugSpaces + " •Set variable to expression:" + funzioneTMP.simbolo()); + try { + Utils.debug.println(debugSpaces + " " + "var1=" + ((FunzioneDueValoriBase) funzioneTMP).getVariable1().calcola()); + } catch (NullPointerException ex2) {} + try { + Utils.debug.println(debugSpaces + " " + "var2=" + ((FunzioneDueValoriBase) funzioneTMP).getVariable2().calcola()); + } catch (NullPointerException ex2) {} + try { + Utils.debug.println(debugSpaces + " " + "(result)=" + ((FunzioneDueValoriBase) funzioneTMP).calcola()); + } catch (NullPointerException ex2) {} + + } else { + throw new java.lang.RuntimeException("Argomenti mancanti! Sistemare l'equazione!"); + } + } + } + } else if (funzioneTMP instanceof FunzioneAnterioreBase) { + if ((fase == "funzioniSN" && ((FunzioneAnterioreBase) funzioneTMP).variable == null)) { + if (i + 1 < funzioniOLD.size()) { + FunzioneBase nextFunc = funzioniOLD.get(i + 1); + if (nextFunc instanceof FunzioneAnterioreBase && ((FunzioneAnterioreBase)nextFunc).variable == null) { + + } else { + change = true; + ((FunzioneAnterioreBase) funzioneTMP).setVariable((FunzioneBase) nextFunc); + funzioniOLD.set(i, funzioneTMP); + + // è importante togliere prima gli elementi in + // fondo e poi quelli davanti, perché gli indici + // scalano da destra a sinistra. + funzioniOLD.remove(i + 1); + + Utils.debug.println(debugSpaces + " •Set variable to expression:" + funzioneTMP.simbolo()); + FunzioneBase var = ((FunzioneAnterioreBase) funzioneTMP).getVariable().calcola(); + if (var == null) { + Utils.debug.println(debugSpaces + " " + "var=null"); + } else { + Utils.debug.println(debugSpaces + " " + "var=" + var.toString()); + } + } + } else { + throw new java.lang.RuntimeException("Argomenti mancanti! Sistemare l'equazione!"); + } + } + } else if (funzioneTMP instanceof Termine || funzioneTMP instanceof Espressione) { + if (n < 300) { + // Utils.debug.println(debugSpaces+" •Set variable + // to number:"+funzioneTMP.calcola()); + } + } else { + throw new java.lang.RuntimeException("Tipo sconosciuto"); + } + i++; + n++; + } + } while (((funzioniOLD.size() != before || fase != "somme") && funzioniOLD.size() > 1)); + } + setVariables(funzioniOLD); + + dsl = debugSpaces.length(); + debugSpaces = ""; + for (int i = 0; i < dsl - 2; i++) { + debugSpaces += " "; + } + Utils.debug.println(debugSpaces + "•Finished correcting classes."); + + Termine result = calcola(); + Utils.debug.println(debugSpaces + "•Result:" + result); + } + } + + @Override + public String simbolo() { + return "Parentesi"; + } + + @Override + public Termine calcola() throws Errore { + if (variables.length == 0) { + return new Termine("0"); + } else if (variables.length == 1) { + return (Termine) variables[0].calcola(); + } else { + Termine result = new Termine("0"); + for (Funzione f : variables) { + result = result.add((Termine) f.calcola()); + } + return result; + } + } + + @Override + public void calcolaGrafica() { + for (Funzione var : variables) { + var.setSmall(small); + var.calcolaGrafica(); + } + + width = calcWidth(); + height = calcHeight(); + line = calcLine(); + } + + public boolean parenthesesNeeded() { + boolean parenthesesneeded = true; + if (parentesiIniziale) { + parenthesesneeded = false; + } else { + if (variables.length == 1) { + if (variables[0] instanceof Divisione) { + parenthesesneeded = false; + } else { + parenthesesneeded = true; + } + } + } + return parenthesesneeded; + } + + @Override + public void draw(int x, int y) { + if (parenthesesNeeded() == false) { + this.variables[0].draw(x, y); + } else { + float miny = y; + float maxy = y + getHeight(); + int h = getHeight(); + x += 1; + glColor3f(0, 0, 0); + glDrawLine(x, y + 2, x + 2, y); + glDrawLine(x, y + 2, x, y + h - 3); + glDrawLine(x, y + h - 3, x + 2, y + h - 1); + x += 4; + for (Funzione f : variables) { + float fheight = f.getHeight(); + float y2 = miny + ((maxy - miny) / 2 - fheight / 2); + f.draw(x, (int) y2); + x += f.getWidth(); + } + x += 2; + glDrawLine(x, y, x + 2, y + 2); + glDrawLine(x + 2, y + 2, x + 2, y + h - 3); + glDrawLine(x, y + h - 1, x + 2, y + h - 3); + x += 4; + } + } + + @Override + public int getWidth() { + return width; + } + + private int calcWidth() { + if (parenthesesNeeded() == false) { + return this.variables[0].getWidth(); + } else { + int w = 0; + for (Funzione f : variables) { + w += f.getWidth(); + } + return 1 + 4 + w + 2 + 4; + } + } + + @Override + public int getHeight() { + return height; + } + + private int calcHeight() { + if (parentesiIniziale || variables.length == 1) { + return this.variables[0].getHeight(); + } else { + Funzione tmin = null; + Funzione tmax = null; + for (Funzione t : variables) { + if (tmin == null || t.getLine() >= tmin.getLine()) { + tmin = t; + } + if (tmax == null || t.getHeight() - t.getLine() >= tmax.getHeight() - tmax.getLine()) { + tmax = t; + } + } + if (tmin == null) + return Utils.getFontHeight(small); + return tmin.getLine() + tmax.getHeight() - tmax.getLine(); + } + } + + @Override + public int getLine() { + return line; + } + + private int calcLine() { + if (parentesiIniziale || variables.length == 1) { + return this.variables[0].getLine(); + } else { + Funzione tl = null; + for (Funzione t : variables) { + if (tl == null || t.getLine() >= tl.getLine()) { + tl = t; + } + } + if (tl == null) + return Utils.getFontHeight(small) / 2; + return tl.getLine(); + } + } + +} diff --git a/src/org/warp/picalculator/Funzione.java b/src/org/warp/picalculator/Funzione.java new file mode 100644 index 00000000..90d1d914 --- /dev/null +++ b/src/org/warp/picalculator/Funzione.java @@ -0,0 +1,19 @@ +package org.warp.picalculator; + +public interface Funzione { + public String simbolo(); + + public Funzione calcola() throws Errore; + + public void calcolaGrafica(); + + public void draw(int x, int y); + + public int getWidth(); + + public int getHeight(); + + public int getLine(); + + public void setSmall(boolean small); +} diff --git a/src/org/warp/picalculator/FunzioneAnteriore.java b/src/org/warp/picalculator/FunzioneAnteriore.java new file mode 100644 index 00000000..6171a20d --- /dev/null +++ b/src/org/warp/picalculator/FunzioneAnteriore.java @@ -0,0 +1,97 @@ +package org.warp.picalculator; + +import static org.warp.engine.Display.Render.getStringWidth; +import static org.warp.engine.Display.Render.glDrawStringLeft; + +import org.nevec.rjm.NumeroAvanzatoVec; +import org.warp.device.PIDisplay; +import org.warp.engine.Display; + +import com.rits.cloning.Cloner; + +public abstract class FunzioneAnteriore implements Funzione { + public FunzioneAnteriore(Funzione value) { + setVariable(value); + } + + protected Funzione variable = new Termine(NumeroAvanzatoVec.ZERO); + protected int width; + protected int height; + protected int line; + protected boolean small; + + public Funzione getVariable() { + return variable; + } + + public void setVariable(Funzione value) { + variable = value; + } + + @Override + public abstract String simbolo(); + + @Override + public abstract Funzione calcola() throws Errore; + + @Override + public void calcolaGrafica() { + variable.setSmall(small); + variable.calcolaGrafica(); + + width = getStringWidth(simbolo()) + 1 + getVariable().getWidth(); + height = variable.getHeight(); + line = variable.getLine(); + } + + @Override + public void draw(int x, int y) { + float h1 = getVariable().getHeight(); + int wsegno = getStringWidth(simbolo()); + float hsegno = Utils.getFontHeight(small); + float maxh = getHeight(); + if (small) { + Display.Render.setFont(PIDisplay.fonts[1]); + } else { + Display.Render.setFont(PIDisplay.fonts[0]); + } + + glDrawStringLeft(x, (int) Math.floor(y + (maxh - hsegno) / 2), simbolo()); + getVariable().draw(x + wsegno + 1, (int) Math.floor(y + (maxh - h1) / 2)); + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public int getLine() { + return line; + } + + @Override + public String toString() { + try { + return calcola().toString(); + } catch (Errore e) { + return e.id.toString(); + } + } + + @Override + public FunzioneAnteriore clone() { + Cloner cloner = new Cloner(); + return cloner.deepClone(this); + } + + @Override + public void setSmall(boolean small) { + this.small = small; + } +} diff --git a/src/org/warp/picalculator/FunzioneAnterioreBase.java b/src/org/warp/picalculator/FunzioneAnterioreBase.java new file mode 100644 index 00000000..44076f0a --- /dev/null +++ b/src/org/warp/picalculator/FunzioneAnterioreBase.java @@ -0,0 +1,97 @@ +package org.warp.picalculator; + +import static org.warp.engine.Display.Render.getStringWidth; +import static org.warp.engine.Display.Render.glDrawStringLeft; + +import org.nevec.rjm.NumeroAvanzatoVec; +import org.warp.device.PIDisplay; +import org.warp.engine.Display; + +import com.rits.cloning.Cloner; + +public abstract class FunzioneAnterioreBase extends FunzioneBase { + public FunzioneAnterioreBase(FunzioneBase value) { + setVariable(value); + } + + protected FunzioneBase variable = new Termine(NumeroAvanzatoVec.ZERO); + protected int width; + protected int height; + protected int line; + protected boolean small; + + public FunzioneBase getVariable() { + return variable; + } + + public void setVariable(FunzioneBase value) { + variable = value; + } + + @Override + public abstract String simbolo(); + + @Override + public abstract Termine calcola() throws Errore; + + @Override + public void calcolaGrafica() { + variable.setSmall(small); + variable.calcolaGrafica(); + + width = getStringWidth(simbolo()) + 1 + getVariable().getWidth(); + height = variable.getHeight(); + line = variable.getLine(); + } + + @Override + public void draw(int x, int y) { + float h1 = getVariable().getHeight(); + int wsegno = getStringWidth(simbolo()); + float hsegno = Utils.getFontHeight(small); + float maxh = getHeight(); + if (small) { + Display.Render.setFont(PIDisplay.fonts[1]); + } else { + Display.Render.setFont(PIDisplay.fonts[0]); + } + + glDrawStringLeft(x, (int) Math.floor(y + (maxh - hsegno) / 2), simbolo()); + getVariable().draw(x + wsegno + 1, (int) Math.floor(y + (maxh - h1) / 2)); + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public int getLine() { + return line; + } + + @Override + public String toString() { + try { + return calcola().toString(); + } catch (Errore e) { + return e.id.toString(); + } + } + + @Override + public FunzioneAnterioreBase clone() { + Cloner cloner = new Cloner(); + return cloner.deepClone(this); + } + + @Override + public void setSmall(boolean small) { + this.small = small; + } +} diff --git a/src/org/warp/picalculator/FunzioneBase.java b/src/org/warp/picalculator/FunzioneBase.java new file mode 100644 index 00000000..2a74ab06 --- /dev/null +++ b/src/org/warp/picalculator/FunzioneBase.java @@ -0,0 +1,29 @@ +package org.warp.picalculator; + +public abstract class FunzioneBase implements Funzione { + + @Override + public abstract String simbolo(); + + @Override + public abstract Termine calcola() throws Errore; + + @Override + public abstract void calcolaGrafica(); + + @Override + public abstract void draw(int x, int y); + + @Override + public abstract int getWidth(); + + @Override + public abstract int getHeight(); + + @Override + public abstract int getLine(); + + @Override + public abstract void setSmall(boolean small); + +} diff --git a/src/org/warp/picalculator/FunzioneDueValori.java b/src/org/warp/picalculator/FunzioneDueValori.java new file mode 100644 index 00000000..25ba467f --- /dev/null +++ b/src/org/warp/picalculator/FunzioneDueValori.java @@ -0,0 +1,142 @@ +package org.warp.picalculator; + +import static org.warp.engine.Display.Render.getStringWidth; +import static org.warp.engine.Display.Render.glDrawStringLeft; + +import org.nevec.rjm.Rational; +import org.warp.device.PIDisplay; +import org.warp.engine.Display; + +import com.rits.cloning.Cloner; + +public abstract class FunzioneDueValori implements Funzione { + public FunzioneDueValori(Funzione value1, Funzione value2) { + setVariable1(value1); + setVariable2(value2); + } + + protected Funzione variable1 = new Termine(Rational.ZERO); + protected int width; + protected int height; + protected int line; + protected boolean small; + + public Funzione getVariable1() { + return variable1; + } + + public void setVariable1(Funzione value) { + variable1 = value; + } + + protected Funzione variable2 = new Termine(Rational.ZERO); + + public Funzione getVariable2() { + return variable2; + } + + public void setVariable2(Funzione value) { + variable2 = value; + } + + @Override + public abstract String simbolo(); + + @Override + public abstract Funzione calcola() throws Errore; + + @Override + public void calcolaGrafica() { + variable1.setSmall(small); + variable1.calcolaGrafica(); + + variable2.setSmall(small); + variable2.calcolaGrafica(); + + width = calcWidth(); + height = calcHeight(); + line = calcLine(); + } + + @Override + public void draw(int x, int y) { + int ln = getLine(); + int dx = 0; + variable1.draw(dx + x, ln - variable1.getLine() + y); + dx += 1+variable1.getWidth(); + if (drawSignum()) { + if (small) { + Display.Render.setFont(PIDisplay.fonts[1]); + } else { + Display.Render.setFont(PIDisplay.fonts[1]); + } + glDrawStringLeft(dx + x, ln - Utils.getFontHeight(small) / 2 + y, simbolo()); + dx += getStringWidth(simbolo()); + } + variable2.draw(dx + x, ln - variable2.getLine() + y); + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public int getLine() { + return line; + } + + @Override + public String toString() { + try { + return calcola().toString(); + } catch (Errore e) { + return e.id.toString(); + } + } + + @Override + public FunzioneDueValori clone() { + Cloner cloner = new Cloner(); + return cloner.deepClone(this); + } + + public boolean drawSignum() { + return true; + } + + @Override + public void setSmall(boolean small) { + this.small = small; + } + + protected int calcWidth() { + return variable1.getWidth() + 1 + (drawSignum() ? getStringWidth(simbolo()) : 0) + variable2.getWidth(); + } + + protected int calcHeight() { + + Funzione tmin = variable1; + Funzione tmax = variable1; + if (tmin == null || variable2.getLine() >= tmin.getLine()) { + tmin = variable2; + } + if (tmax == null || variable2.getHeight() - variable2.getLine() >= tmax.getHeight() - tmax.getLine()) { + tmax = variable2; + } + return tmin.getLine() + tmax.getHeight() - tmax.getLine(); + } + + protected int calcLine() { + Funzione tl = variable1; + if (tl == null || variable2.getLine() >= tl.getLine()) { + tl = variable2; + } + return tl.getLine(); + } +} diff --git a/src/org/warp/picalculator/FunzioneDueValoriBase.java b/src/org/warp/picalculator/FunzioneDueValoriBase.java new file mode 100644 index 00000000..73c72aea --- /dev/null +++ b/src/org/warp/picalculator/FunzioneDueValoriBase.java @@ -0,0 +1,142 @@ +package org.warp.picalculator; + +import static org.warp.engine.Display.Render.getStringWidth; +import static org.warp.engine.Display.Render.glDrawStringLeft; + +import org.nevec.rjm.Rational; +import org.warp.device.PIDisplay; +import org.warp.engine.Display; + +import com.rits.cloning.Cloner; + +public abstract class FunzioneDueValoriBase extends FunzioneBase { + public FunzioneDueValoriBase(FunzioneBase value1, FunzioneBase value2) { + setVariable1(value1); + setVariable2(value2); + } + + protected FunzioneBase variable1 = new Termine(Rational.ZERO); + protected int width; + protected int height; + protected int line; + protected boolean small; + + public FunzioneBase getVariable1() { + return variable1; + } + + public void setVariable1(FunzioneBase value) { + variable1 = value; + } + + protected FunzioneBase variable2 = new Termine(Rational.ZERO); + + public FunzioneBase getVariable2() { + return variable2; + } + + public void setVariable2(FunzioneBase value) { + variable2 = value; + } + + @Override + public abstract String simbolo(); + + @Override + public abstract Termine calcola() throws Errore; + + @Override + public void calcolaGrafica() { + variable1.setSmall(small); + variable1.calcolaGrafica(); + + variable2.setSmall(small); + variable2.calcolaGrafica(); + + width = calcWidth(); + height = calcHeight(); + line = calcLine(); + } + + @Override + public void draw(int x, int y) { + int ln = getLine(); + int dx = 0; + variable1.draw(dx + x, ln - variable1.getLine() + y); + dx += 1+variable1.getWidth(); + if (drawSignum()) { + if (small) { + Display.Render.setFont(PIDisplay.fonts[1]); + } else { + Display.Render.setFont(PIDisplay.fonts[0]); + } + glDrawStringLeft(dx + x, ln - Utils.getFontHeight(small) / 2 + y, simbolo()); + dx += getStringWidth(simbolo()); + } + variable2.draw(dx + x, ln - variable2.getLine() + y); + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public int getLine() { + return line; + } + + @Override + public String toString() { + try { + return calcola().toString(); + } catch (Errore e) { + return e.id.toString(); + } + } + + @Override + public FunzioneDueValoriBase clone() { + Cloner cloner = new Cloner(); + return cloner.deepClone(this); + } + + public boolean drawSignum() { + return true; + } + + @Override + public void setSmall(boolean small) { + this.small = small; + } + + protected int calcWidth() { + return variable1.getWidth() + 1 + (drawSignum() ? getStringWidth(simbolo()) : 0) + variable2.getWidth(); + } + + protected int calcHeight() { + + FunzioneBase tmin = variable1; + FunzioneBase tmax = variable1; + if (tmin == null || variable2.getLine() >= tmin.getLine()) { + tmin = variable2; + } + if (tmax == null || variable2.getHeight() - variable2.getLine() >= tmax.getHeight() - tmax.getLine()) { + tmax = variable2; + } + return tmin.getLine() + tmax.getHeight() - tmax.getLine(); + } + + protected int calcLine() { + FunzioneBase tl = variable1; + if (tl == null || variable2.getLine() >= tl.getLine()) { + tl = variable2; + } + return tl.getLine(); + } +} diff --git a/src/org/warpgate/pi/calculator/FunzioneMultipla.java b/src/org/warp/picalculator/FunzioneMultipla.java similarity index 74% rename from src/org/warpgate/pi/calculator/FunzioneMultipla.java rename to src/org/warp/picalculator/FunzioneMultipla.java index 20292b74..42d4778b 100644 --- a/src/org/warpgate/pi/calculator/FunzioneMultipla.java +++ b/src/org/warp/picalculator/FunzioneMultipla.java @@ -1,4 +1,4 @@ -package org.warpgate.pi.calculator; +package org.warp.picalculator; import java.util.Arrays; import java.util.List; @@ -7,18 +7,27 @@ import com.rits.cloning.Cloner; public abstract class FunzioneMultipla implements Funzione { public FunzioneMultipla() { - setVariables(new Funzione[]{}); + setVariables(new Funzione[] {}); } + public FunzioneMultipla(Funzione[] values) { setVariables(values); } + protected Funzione[] variables; + protected int width; + protected int height; + protected int line; + protected boolean small; + public Funzione[] getVariables() { return variables; } + public void setVariables(Funzione[] value) { variables = value; } + public void setVariables(final List value) { int vsize = value.size(); Funzione[] tmp = new Funzione[vsize]; @@ -27,32 +36,37 @@ public abstract class FunzioneMultipla implements Funzione { } variables = tmp; } - + public Funzione getVariable(int index) { return variables[index]; } + public void setVariable(int index, Funzione value) { variables[index] = value; } - + public void addVariableToEnd(Funzione value) { int index = variables.length; - setVariablesLength(index+1); + setVariablesLength(index + 1); variables[index] = value; } public int getVariablesLength() { return variables.length; } - + public void setVariablesLength(int length) { variables = Arrays.copyOf(variables, length); } - + @Override public abstract String simbolo(); + @Override - public abstract Termine calcola() throws Errore; + public abstract Funzione calcola() throws Errore; + + @Override + public abstract void calcolaGrafica(); @Override public String toString() { @@ -68,4 +82,9 @@ public abstract class FunzioneMultipla implements Funzione { Cloner cloner = new Cloner(); return cloner.deepClone(this); } + + @Override + public void setSmall(boolean small) { + this.small = small; + } } diff --git a/src/org/warp/picalculator/FunzioneMultiplaBase.java b/src/org/warp/picalculator/FunzioneMultiplaBase.java new file mode 100644 index 00000000..87f5a128 --- /dev/null +++ b/src/org/warp/picalculator/FunzioneMultiplaBase.java @@ -0,0 +1,90 @@ +package org.warp.picalculator; + +import java.util.Arrays; +import java.util.List; + +import com.rits.cloning.Cloner; + +public abstract class FunzioneMultiplaBase extends FunzioneBase { + public FunzioneMultiplaBase() { + setVariables(new FunzioneBase[] {}); + } + + public FunzioneMultiplaBase(FunzioneBase[] values) { + setVariables(values); + } + + protected FunzioneBase[] variables; + protected int width; + protected int height; + protected int line; + protected boolean small; + + public FunzioneBase[] getVariables() { + return variables; + } + + public void setVariables(FunzioneBase[] value) { + variables = value; + } + + public void setVariables(final List value) { + int vsize = value.size(); + FunzioneBase[] tmp = new FunzioneBase[vsize]; + for (int i = 0; i < vsize; i++) { + tmp[i] = value.get(i); + } + variables = tmp; + } + + public FunzioneBase getVariable(int index) { + return variables[index]; + } + + public void setVariable(int index, FunzioneBase value) { + variables[index] = value; + } + + public void addVariableToEnd(FunzioneBase value) { + int index = variables.length; + setVariablesLength(index + 1); + variables[index] = value; + } + + public int getVariablesLength() { + return variables.length; + } + + public void setVariablesLength(int length) { + variables = Arrays.copyOf(variables, length); + } + + @Override + public abstract String simbolo(); + + @Override + public abstract Termine calcola() throws Errore; + + @Override + public abstract void calcolaGrafica(); + + @Override + public String toString() { + try { + return calcola().toString(); + } catch (Errore e) { + return e.id.toString(); + } + } + + @Override + public FunzioneMultiplaBase clone() { + Cloner cloner = new Cloner(); + return cloner.deepClone(this); + } + + @Override + public void setSmall(boolean small) { + this.small = small; + } +} diff --git a/src/org/warpgate/pi/calculator/Incognita.java b/src/org/warp/picalculator/Incognita.java similarity index 80% rename from src/org/warpgate/pi/calculator/Incognita.java rename to src/org/warp/picalculator/Incognita.java index 34da74c3..52296905 100644 --- a/src/org/warpgate/pi/calculator/Incognita.java +++ b/src/org/warp/picalculator/Incognita.java @@ -1,13 +1,11 @@ -package org.warpgate.pi.calculator; - -import java.math.BigInteger; +package org.warp.picalculator; import org.nevec.rjm.Rational; public class Incognita { public char simbolo = 'X'; public Rational esponente = Rational.ONE; - + public Incognita(char simbolo, Rational esponente) { this.simbolo = simbolo; this.esponente = esponente; @@ -17,7 +15,7 @@ public class Incognita { this.simbolo = simbolo; this.esponente = new Rational(a, b); } - + public Incognita(char simbolo) { this.simbolo = simbolo; this.esponente = new Rational(1, 1); @@ -34,9 +32,9 @@ public class Incognita { } return false; } - + @Override public int hashCode() { - return Character.getNumericValue(simbolo)*3+esponente.hashCode(); + return Character.getNumericValue(simbolo) * 3 + esponente.hashCode(); } } diff --git a/src/org/warpgate/pi/calculator/Incognite.java b/src/org/warp/picalculator/Incognite.java similarity index 78% rename from src/org/warpgate/pi/calculator/Incognite.java rename to src/org/warp/picalculator/Incognite.java index 4085d2ec..ddebda89 100644 --- a/src/org/warpgate/pi/calculator/Incognite.java +++ b/src/org/warp/picalculator/Incognite.java @@ -1,22 +1,20 @@ -package org.warpgate.pi.calculator; +package org.warp.picalculator; import java.math.BigInteger; -import java.text.Collator; import java.util.Comparator; -import java.util.List; import java.util.Vector; import org.nevec.rjm.BigIntegerMath; import org.nevec.rjm.Rational; public class Incognite { - + Vector incognite; public Incognite() { incognite = new Vector(); } - + public Incognite(Incognita[] value) { this(); for (Incognita i : value) { @@ -28,16 +26,16 @@ public class Incognite { this(); incognite = value; } - + public Incognite(Incognita value) { this(); incognite.add(value); } - + public int count() { return incognite.size(); } - + public boolean contieneSimbolo(char simbolo) { for (Incognita i : incognite) { if (i.simbolo == simbolo) { @@ -55,7 +53,7 @@ public class Incognite { } return new Rational(0, 1); } - + public void impostaEsponenteSimbolo(char simbolo, Rational esponente) { for (Incognita i : incognite) { if (i.simbolo == simbolo) { @@ -63,7 +61,7 @@ public class Incognite { } } } - + @SuppressWarnings("unchecked") @Override public Incognite clone() { @@ -72,19 +70,21 @@ public class Incognite { public Incognite multiply(Incognite val) { Incognite result = new Incognite(); - //Passaggio 1: test vari - //Se il primo gruppo di incognite è nullo allora ritorna il secondo gruppo + // Passaggio 1: test vari + // Se il primo gruppo di incognite à nullo allora ritorna il secondo + // gruppo if (this.count() == 0) { result = val.clone(); return result; } - //Se il secondo gruppo di incognite è nullo allora ritorna il primo gruppo + // Se il secondo gruppo di incognite à nullo allora ritorna il primo + // gruppo if (val.count() == 0) { result = this.clone(); return result; } - - //Passaggio 2: le incognite doppie vengono raggruppate. + + // Passaggio 2: le incognite doppie vengono raggruppate. for (Incognita i1 : incognite) { for (Incognita i2 : val.incognite) { if (i1.simbolo == i2.simbolo) { @@ -95,7 +95,7 @@ public class Incognite { } } } - //Passaggio 3: le incognite non ancora presenti vengono aggiunte. + // Passaggio 3: le incognite non ancora presenti vengono aggiunte. for (Incognita i : incognite) { if (!result.contieneSimbolo(i.simbolo)) { result.incognite.add(i); @@ -111,8 +111,8 @@ public class Incognite { public Incognite divide(Incognite val) { Incognite result = new Incognite(); - - //Passaggio 2: le incognite doppie vengono raggruppate. + + // Passaggio 2: le incognite doppie vengono raggruppate. for (Incognita i1 : incognite) { for (Incognita i2 : val.incognite) { if (i1.simbolo == i2.simbolo) { @@ -123,7 +123,7 @@ public class Incognite { } } } - //Passaggio 3: le incognite non ancora presenti vengono aggiunte. + // Passaggio 3: le incognite non ancora presenti vengono aggiunte. for (Incognita i : incognite) { if (!result.contieneSimbolo(i.simbolo)) { result.incognite.add(i); @@ -150,7 +150,7 @@ public class Incognite { } return result.normalize(); } - + public Incognite normalize() { Incognite result = new Incognite(); for (Incognita i1 : incognite) { @@ -159,42 +159,45 @@ public class Incognite { } } result.incognite.sort(new Comparator() { - @Override - public int compare(Incognita o1, Incognita o2) { - int index1 = letterIndex(o1.simbolo); - int index2 = letterIndex(o2.simbolo); - return index1-index2; - } - }); + @Override + public int compare(Incognita o1, Incognita o2) { + int index1 = letterIndex(o1.simbolo); + int index2 = letterIndex(o2.simbolo); + return index1 - index2; + } + }); return result; } public byte letterIndex(char l) { return letterIndex(l, false); } + public static byte letterIndex(char l, boolean reverse) { - int total = Simboli.incognite().length-1; + int total = Simboli.incognite().length - 1; for (byte x = 0; x < Simboli.incognite().length; x++) { - if (Simboli.incognite()[x].equals(""+l)) { + if (Simboli.incognite()[x].equals("" + l)) { if (reverse) { - return (byte) (total-x); + return (byte) (total - x); } else { return x; } } } - + return -1; } - + public boolean compareTo(Incognite val) { - if (this.equals(val)) return true; + if (this.equals(val)) + return true; return false; } @Override public boolean equals(Object val) { - if (val == null) return false; + if (val == null) + return false; if (val instanceof Incognite) { Incognite ii2 = (Incognite) val; for (Incognita i1 : incognite) { @@ -227,14 +230,14 @@ public class Incognite { } return false; } - + @Override public String toString() { String result = ""; if (incognite.size() != 1) { for (Incognita i : incognite) { if (i.esponente.compareTo(Rational.ONE) != 0) { - result += "("+i.simbolo+"^"+i.esponente+")"; + result += "(" + i.simbolo + "^" + i.esponente + ")"; } else { result += i.simbolo; } @@ -242,7 +245,7 @@ public class Incognite { } else if (incognite.size() == 1) { Incognita i = incognite.get(0); if (i.esponente.compareTo(Rational.ONE) != 0) { - result += ""+i.simbolo+"^"+i.esponente+""; + result += "" + i.simbolo + "^" + i.esponente + ""; } else if (i.esponente.compareTo(Rational.ONE) == 0) { result += i.simbolo; } @@ -252,19 +255,21 @@ public class Incognite { public static Incognite lcm(Incognite val1, Incognite val2) { Incognite result = new Incognite(); - //Passaggio 1: test vari - //Se il primo gruppo di incognite è nullo allora ritorna il secondo gruppo + // Passaggio 1: test vari + // Se il primo gruppo di incognite à nullo allora ritorna il secondo + // gruppo if (val1.count() == 0) { result = val2.clone(); return result; } - //Se il secondo gruppo di incognite è nullo allora ritorna il primo gruppo + // Se il secondo gruppo di incognite à nullo allora ritorna il primo + // gruppo if (val2.count() == 0) { result = val1.clone(); return result; } - - //Passaggio 2: le incognite doppie vengono raggruppate. + + // Passaggio 2: le incognite doppie vengono raggruppate. for (Incognita i1 : val1.incognite) { for (Incognita i2 : val2.incognite) { if (i1.simbolo == i2.simbolo) { @@ -280,7 +285,7 @@ public class Incognite { } } } - //Passaggio 3: le incognite non ancora presenti vengono aggiunte. + // Passaggio 3: le incognite non ancora presenti vengono aggiunte. for (Incognita i : val1.incognite) { if (!result.contieneSimbolo(i.simbolo)) { result.incognite.add(i); @@ -314,7 +319,6 @@ public class Incognite { } } incognitex = newincognitex; - for (Incognita i : incognitey.incognite) { if (i.esponente.signum() < 0) { @@ -329,12 +333,11 @@ public class Incognite { } } - //TODO: SPOSTARE LE Y NEGATIVE SOTTO LA FRAZIONE, DALLA Y ALLA Z - + // TODO: SPOSTARE LE Y NEGATIVE SOTTO LA FRAZIONE, DALLA Y ALLA Z Incognite incogniteyresult = new Incognite(); Incognite incognitezresult = new Incognite(); - //Le incognite doppie vengono tolte + // Le incognite doppie vengono tolte for (Incognita i1 : incognitey.incognite) { for (Incognita i2 : incognitez.incognite) { if (i1.simbolo == i2.simbolo) { @@ -347,7 +350,7 @@ public class Incognite { } } - //Le altre incognite vengono ri-messe + // Le altre incognite vengono ri-messe for (Incognita i : incognitey.incognite) { if (!incogniteyresult.contieneSimbolo(i.simbolo)) { incogniteyresult = incogniteyresult.multiply(new Incognite(i)); @@ -358,31 +361,31 @@ public class Incognite { incognitezresult = incognitezresult.multiply(new Incognite(i)); } } - + incognitey = incogniteyresult; incognitez = incognitezresult; - - return new Incognite[]{incognitex, incognitey, incognitez}; + + return new Incognite[] { incognitex, incognitey, incognitez }; } - public static int priorità(Incognite ii) { - double priorità = 0; + public static int priorità(Incognite ii) { + double priorità = 0; double letterMax = 0; - for(Incognita i : ii.incognite) { + for (Incognita i : ii.incognite) { int lettIndex = letterIndex(i.simbolo, true); if (lettIndex > letterMax) { letterMax = lettIndex; } } - priorità+=letterMax*100000; - - for(Incognita i : ii.incognite) { + priorità += letterMax * 100000; + + for (Incognita i : ii.incognite) { int lettIndex = letterIndex(i.simbolo, true); if (letterMax == lettIndex) { - priorità+=i.esponente.doubleValue()*100000; + priorità += i.esponente.doubleValue() * 100000; } - priorità+=+i.esponente.doubleValue(); + priorità += +i.esponente.doubleValue(); } - return (int) priorità; + return (int) priorità; } } diff --git a/src/org/warp/picalculator/Main.java b/src/org/warp/picalculator/Main.java new file mode 100644 index 00000000..5cad9001 --- /dev/null +++ b/src/org/warp/picalculator/Main.java @@ -0,0 +1,61 @@ +package org.warp.picalculator; + +import org.warp.device.Keyboard; +import org.warp.device.PIDisplay; +import org.warp.picalculator.screens.EquationScreen; + +import com.pi4j.wiringpi.Gpio; + +public class Main { + public static int[] screenPos = new int[] { 55, 0 }; + public static final int[] screenSize = new int[] { 480, 320 }; + public static final int screenScale = 1; + public static PIDisplay d; + public static Main instance; + + public Main() throws InterruptedException { + instance = this; + Thread.currentThread().setPriority(Thread.MAX_PRIORITY); + beforeStart(); + d = new PIDisplay(new EquationScreen()); + d.run("Calculator"); + Utils.debug.println("Shutdown..."); + beforeShutdown(); + Utils.debug.println(""); + Utils.debug.println("Closed"); + System.exit(0); + } + + public void beforeStart() { + if (System.getProperty("os.name").equals("Linux")) { + Gpio.wiringPiSetupPhys(); + Gpio.pinMode(12, Gpio.PWM_OUTPUT); + } else { + screenPos = new int[]{0,0}; + Utils.debugOn = true; + } + PIDisplay.setBrightness(0.5f); + } + + + public void afterStart() { + Keyboard.startKeyboard(); + } + + public void beforeShutdown() { + Keyboard.stopKeyboard(); + } + + public static void main(String[] args) throws InterruptedException { + try { + Termine t = new Termine("9999.9"); + Termine r = t.calcola(); + System.out.println(t.toString()); + System.out.println(r.toString()); + } catch (Errore e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + new Main(); + } +} diff --git a/src/org/warpgate/pi/calculator/Moltiplicazione.java b/src/org/warp/picalculator/Moltiplicazione.java similarity index 52% rename from src/org/warpgate/pi/calculator/Moltiplicazione.java rename to src/org/warp/picalculator/Moltiplicazione.java index 773ddaa6..acb12074 100644 --- a/src/org/warpgate/pi/calculator/Moltiplicazione.java +++ b/src/org/warp/picalculator/Moltiplicazione.java @@ -1,13 +1,10 @@ -package org.warpgate.pi.calculator; +package org.warp.picalculator; -import java.awt.Graphics; import java.math.BigInteger; -import org.nevec.rjm.NumeroAvanzatoVec; +public class Moltiplicazione extends FunzioneDueValoriBase { -public class Moltiplicazione extends FunzioneDueValori { - - public Moltiplicazione(Funzione value1, Funzione value2) { + public Moltiplicazione(FunzioneBase value1, FunzioneBase value2) { super(value1, value2); } @@ -20,11 +17,11 @@ public class Moltiplicazione extends FunzioneDueValori { public Termine calcola() throws Errore { return getVariable1().calcola().multiply(getVariable2().calcola()); } - + @Override public boolean drawSignum() { - Funzione[] tmpVar = new Funzione[]{variable1, variable2}; - boolean[] ok = new boolean[]{false, false}; + Funzione[] tmpVar = new Funzione[] { variable1, variable2 }; + boolean[] ok = new boolean[] { false, false }; for (int val = 0; val < 2; val++) { while (!ok[val]) { if (tmpVar[val] instanceof Divisione) { @@ -39,9 +36,9 @@ public class Moltiplicazione extends FunzioneDueValori { if (!(tmpVar[0] instanceof Termine)) { ok[val] = true; } else { - if (((Termine)tmpVar[0]).term.isBigInteger(false)) { - if (((Termine)tmpVar[val]).term.toBigInteger(true).compareTo(new BigInteger("1")) == 0) { - if (((Termine)tmpVar[val]).term.toNumeroAvanzato().getIncognitey().count() > 0) { + if (((Termine) tmpVar[val]).term.isBigInteger(false)) { // TODO: prima era tmpVar[0], ma crashava. RICONTROLLARE! La logica potrebbe essere sbagliata + if (((Termine) tmpVar[val]).term.toBigInteger(true).compareTo(new BigInteger("1")) == 0) { + if (((Termine) tmpVar[val]).term.toNumeroAvanzato().getIncognitey().count() > 0) { ok[val] = true; } else { break; @@ -55,26 +52,26 @@ public class Moltiplicazione extends FunzioneDueValori { } } } else if (tmpVar[val] instanceof Potenza) { - tmpVar[val] = ((Potenza)tmpVar[val]).variable1; + tmpVar[val] = ((Potenza) tmpVar[val]).variable1; } else if (tmpVar[val] instanceof Radice) { ok[val] = true; } else if (tmpVar[val] instanceof RadiceQuadrata) { ok[val] = true; - } else if (tmpVar[val] instanceof Parentesi) { + } else if (tmpVar[val] instanceof Espressione) { ok[0] = true; ok[1] = true; - } else if (tmpVar[val] instanceof FunzioneDueValori) { + } else if (tmpVar[val] instanceof FunzioneDueValoriBase) { if (val == 0) { - tmpVar[val] = ((FunzioneDueValori)tmpVar[val]).variable2; + tmpVar[val] = ((FunzioneDueValoriBase) tmpVar[val]).variable2; } else { - tmpVar[val] = ((FunzioneDueValori)tmpVar[val]).variable1; + tmpVar[val] = ((FunzioneDueValoriBase) tmpVar[val]).variable1; } - } else if (tmpVar[val] instanceof FunzioneAnteriore) { - tmpVar[val] = ((FunzioneAnteriore)tmpVar[val]).variable; + } else if (tmpVar[val] instanceof FunzioneAnterioreBase) { + tmpVar[val] = ((FunzioneAnterioreBase) tmpVar[val]).variable; } } } - + if (ok[0] == true && ok[1] == true) { return false; } else { diff --git a/src/org/warp/picalculator/MoltiplicazionePrioritaria.java b/src/org/warp/picalculator/MoltiplicazionePrioritaria.java new file mode 100644 index 00000000..e24f50b4 --- /dev/null +++ b/src/org/warp/picalculator/MoltiplicazionePrioritaria.java @@ -0,0 +1,23 @@ +package org.warp.picalculator; + +public class MoltiplicazionePrioritaria extends FunzioneDueValoriBase { + + public MoltiplicazionePrioritaria(FunzioneBase value1, FunzioneBase value2) { + super(value1, value2); + } + + @Override + public String simbolo() { + return Simboli.PRIORITARY_MULTIPLICATION; + } + + @Override + public Termine calcola() throws Errore { + return getVariable1().calcola().multiply(getVariable2().calcola()); + } + + @Override + public boolean drawSignum() { + return false; + } +} \ No newline at end of file diff --git a/src/org/warp/picalculator/ParteSistema.java b/src/org/warp/picalculator/ParteSistema.java new file mode 100644 index 00000000..bd6b2d58 --- /dev/null +++ b/src/org/warp/picalculator/ParteSistema.java @@ -0,0 +1,64 @@ +package org.warp.picalculator; + +import static org.warp.engine.Display.Render.glColor3f; +import static org.warp.engine.Display.Render.glDrawLine; + +public class ParteSistema extends FunzioneAnteriore { + + public ParteSistema(Equazione equazione) { + super(equazione); + } + + @Override + public String simbolo() { + return Simboli.SYSTEM; + } + + @Override + public Equazione calcola() throws NumberFormatException, Errore { + // TODO implementare il calcolo dei sistemi + return (Equazione) variable.calcola(); + } + + @Override + public void calcolaGrafica() { + variable.setSmall(false); + variable.calcolaGrafica(); + + width = 5 + getVariable().getWidth(); + height = 3 + getVariable().getHeight() + 2; + line = 3 + getVariable().getLine(); + } + + @Override + public void draw(int x, int y) { + final int h = this.getHeight() - 1; + final int paddingTop = 3; + final int spazioSotto = (h - 3 - 2) / 2 + paddingTop; + final int spazioSopra = h - spazioSotto; + variable.draw(x + 5, y + paddingTop); + glColor3f(0, 0, 0); + glDrawLine(x + 2, y + 0, x + 3, y + 0); + glDrawLine(x + 1, y + 1, x + 1, y + spazioSotto / 2); + glDrawLine(x + 2, y + spazioSotto / 2 + 1, x + 2, y + spazioSotto - 1); + glDrawLine(x + 0, y + spazioSotto, x + 1, y + spazioSotto); + glDrawLine(x + 2, y + spazioSotto + 1, x + 2, y + spazioSotto + spazioSopra / 2 - 1); + glDrawLine(x + 1, y + spazioSotto + spazioSopra / 2, x + 1, y + h - 1); + glDrawLine(x + 2, y + h, x + 3, y + h); + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public int getLine() { + return line; + } +} diff --git a/src/org/warp/picalculator/Potenza.java b/src/org/warp/picalculator/Potenza.java new file mode 100644 index 00000000..8b57dd18 --- /dev/null +++ b/src/org/warp/picalculator/Potenza.java @@ -0,0 +1,58 @@ +package org.warp.picalculator; + +public class Potenza extends FunzioneDueValoriBase { + + public Potenza(FunzioneBase value1, FunzioneBase value2) { + super(value1, value2); + } + + @Override + public String simbolo() { + return Simboli.POTENZA; + } + + @Override + public void calcolaGrafica() { + variable1.setSmall(small); + variable1.calcolaGrafica(); + + variable2.setSmall(true); + variable2.calcolaGrafica(); + + height = variable1.getHeight() + variable2.getHeight() - 4; + line = variable2.getHeight() - 4 + variable1.getLine(); + width = getVariable1().getWidth() + getVariable2().getWidth()+1; + } + + @Override + public Termine calcola() throws NumberFormatException, Errore { + return getVariable1().calcola().pow(getVariable2().calcola()); + } + + @Override + public void draw(int x, int y) { +// glColor3f(0, 127-50+new Random().nextInt(50), 0); +// glFillRect(x,y,width,height); +// glColor3f(0, 0, 0); + + int dx = 0; + variable1.draw(dx + x, getHeight() - variable1.getHeight() + y); + dx += variable1.getWidth(); + variable2.draw(dx + x, y); + } + + @Override + public int getHeight() { + return height; + } + + @Override + public int getLine() { + return line; + } + + @Override + public int getWidth() { + return width; + } +} diff --git a/src/org/warp/picalculator/Radice.java b/src/org/warp/picalculator/Radice.java new file mode 100644 index 00000000..f114e2d0 --- /dev/null +++ b/src/org/warp/picalculator/Radice.java @@ -0,0 +1,74 @@ +package org.warp.picalculator; + +import static org.warp.engine.Display.Render.glDrawLine; + +import org.nevec.rjm.NumeroAvanzatoVec; + +public class Radice extends FunzioneDueValoriBase { + + public Radice(FunzioneBase value1, FunzioneBase value2) { + super(value1, value2); + } + + @Override + public String simbolo() { + return Simboli.NTH_ROOT; + } + + @Override + public void calcolaGrafica() { + variable1.setSmall(true); + variable1.calcolaGrafica(); + + variable2.setSmall(small); + variable2.calcolaGrafica(); + + width = 1 + variable1.getWidth() + 2 + variable2.getWidth() + 2; + height = variable1.getHeight() + variable2.getHeight() - 2; + line = variable1.getHeight() + variable2.getLine() - 2; + } + + @Override + public Termine calcola() throws NumberFormatException, Errore { + Termine exponent = new Termine(NumeroAvanzatoVec.ONE); + exponent = exponent.divide(getVariable1().calcola()); + return getVariable2().calcola().pow(exponent); + } + + @Override + public void draw(int x, int y) { +// glColor3f(0, 255, 0); +// glFillRect(x,y,width,height); +// glColor3f(0, 0, 0); + + int w1 = getVariable2().getWidth(); + int h1 = getVariable2().getHeight(); + int w2 = getVariable1().getWidth(); + int h2 = getVariable1().getHeight(); + int height = getHeight(); + int hh = (int) Math.ceil((double) h1 / 2); + + getVariable1().draw(x + 1, y); + getVariable2().draw(x + 1 + w2 + 2, y + h2 - 2); + + glDrawLine(x + 1 + w2 - 2, y + height - 3, x + 1 + w2, y + height); + glDrawLine(x + 1 + w2, y + height - 1 - hh, x + 1 + w2, y + height - 1); + glDrawLine(x + 1 + w2 + 1, y + height - 2 - h1, x + 1 + w2 + 1, y + height - 1 - hh - 1); + glDrawLine(x + 1 + w2 + 1, y + height - h1 - 2, x + 1 + w2 + 2 + w1 + 1, y + height - h1 - 2); + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public int getLine() { + return line; + } +} diff --git a/src/org/warp/picalculator/RadiceQuadrata.java b/src/org/warp/picalculator/RadiceQuadrata.java new file mode 100644 index 00000000..dafeae9b --- /dev/null +++ b/src/org/warp/picalculator/RadiceQuadrata.java @@ -0,0 +1,64 @@ +package org.warp.picalculator; + +import org.nevec.rjm.Rational; + +public class RadiceQuadrata extends FunzioneAnterioreBase { + + public RadiceQuadrata(FunzioneBase value) { + super(value); + } + + @Override + public String simbolo() { + return Simboli.SQUARE_ROOT; + } + + @Override + public void calcolaGrafica() { + variable.setSmall(small); + variable.calcolaGrafica(); + + height = getVariable().getHeight() + 2; + width = 1 + 4 + getVariable().getWidth() + 1; + line = getVariable().getLine() + 2; + } + + @Override + public Termine calcola() throws Errore { + try { + Termine result = getVariable().calcola(); + result = result.pow(new Termine(new Rational(1, 2))); + return result; + } catch(NullPointerException ex) { + throw new Errore(Errori.ERROR); + } catch(NumberFormatException ex) { + throw new Errore(Errori.SYNTAX_ERROR); + } catch(ArithmeticException ex) { + throw new Errore(Errori.NUMBER_TOO_SMALL); + } + } + + @Override + public void draw(int x, int y) { +// glColor3f(0, 255, 0); +// glFillRect(x,y,width,height); +// glColor3f(0, 0, 0); + + Utils.writeSquareRoot(getVariable(), x, y, small); + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public int getLine() { + return line; + } +} diff --git a/src/org/warpgate/pi/calculator/RisultatoEquazione.java b/src/org/warp/picalculator/RisultatoEquazione.java similarity index 59% rename from src/org/warpgate/pi/calculator/RisultatoEquazione.java rename to src/org/warp/picalculator/RisultatoEquazione.java index b1f8b3ce..8093d263 100644 --- a/src/org/warpgate/pi/calculator/RisultatoEquazione.java +++ b/src/org/warp/picalculator/RisultatoEquazione.java @@ -1,11 +1,13 @@ -package org.warpgate.pi.calculator; - -public class RisultatoEquazione { - public boolean isAnEquation = false; - public Termine LR = new Termine("0"); - - public RisultatoEquazione(Termine LR, boolean isAnEquation) { - this.LR = LR; - this.isAnEquation = isAnEquation; - } -} +package org.warp.picalculator; + +import java.math.BigInteger; + +public class RisultatoEquazione { + public boolean isAnEquation = false; + public Termine LR = new Termine(new BigInteger("0")); + + public RisultatoEquazione(Termine LR, boolean isAnEquation) { + this.LR = LR; + this.isAnEquation = isAnEquation; + } +} diff --git a/src/org/warpgate/pi/calculator/Simboli.java b/src/org/warp/picalculator/Simboli.java similarity index 54% rename from src/org/warpgate/pi/calculator/Simboli.java rename to src/org/warp/picalculator/Simboli.java index 403b225d..2f34743a 100644 --- a/src/org/warpgate/pi/calculator/Simboli.java +++ b/src/org/warp/picalculator/Simboli.java @@ -1,11 +1,12 @@ -package org.warpgate.pi.calculator; +package org.warp.picalculator; -import static org.warpgate.pi.calculator.Utils.concat; +import static org.warp.picalculator.Utils.concat; public class Simboli { public static final String SUM = "+"; public static final String SUBTRACTION = "-"; public static final String MULTIPLICATION = "*"; + public static final String PRIORITARY_MULTIPLICATION = "â–ª"; public static final String DIVISION = "/"; public static final String NTH_ROOT = "√"; public static final String SQUARE_ROOT = "â’¶"; @@ -14,30 +15,39 @@ public class Simboli { public static final String POTENZA = "â’·"; public static final String EQUATION = "="; public static final String SYSTEM = "{"; - + public static final String[] funzioni() { return concat(funzioniNSN(), funzioniSN()); } + public static final String[] funzioniNSN() { - return new String[]{NTH_ROOT, POTENZA}; + return new String[] { NTH_ROOT, POTENZA }; } + public static final String[] funzioniSN() { - return new String[]{SQUARE_ROOT}; + return new String[] { SQUARE_ROOT }; } - public static final String[] segni(boolean withMultiplication) { - String[] ret = new String[]{SUM, DIVISION}; + + public static final String[] segni(boolean withMultiplication, boolean withPrioritaryMultiplication) { + String[] ret = new String[] { SUM, DIVISION }; if (withMultiplication) { ret = Utils.add(ret, MULTIPLICATION); } + if (withPrioritaryMultiplication) { + ret = Utils.add(ret, PRIORITARY_MULTIPLICATION); + } return ret; } + public static final String[] parentesi() { - return new String[]{PARENTHESIS_OPEN, PARENTHESIS_CLOSE}; + return new String[] { PARENTHESIS_OPEN, PARENTHESIS_CLOSE }; } + public static String[] incognite() { - return new String[]{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}; + return new String[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" }; } + public static String[] sintassiGenerale() { - return new String[]{SYSTEM, EQUATION}; + return new String[] { SYSTEM, EQUATION }; } } diff --git a/src/org/warp/picalculator/Sistema.java b/src/org/warp/picalculator/Sistema.java new file mode 100644 index 00000000..81529f4a --- /dev/null +++ b/src/org/warp/picalculator/Sistema.java @@ -0,0 +1,92 @@ +package org.warp.picalculator; + +import static org.warp.engine.Display.Render.glDrawLine; + +public class Sistema extends FunzioneMultipla { + static final int spacing = 2; + + public Sistema() { + super(); + } + + public Sistema(Funzione value) { + super(new Funzione[]{value}); + } + + public Sistema(Funzione[] value) { + super(value); + } + + @Override + public String simbolo() { + return null; + } + + @Override + public Funzione calcola() throws NumberFormatException, Errore { + // TODO implementare il calcolo dei sistemi + return variables[0].calcola(); + } + + @Override + public void calcolaGrafica() { + for (Funzione f : variables) { + f.setSmall(false); + f.calcolaGrafica(); + } + + width = 0; + for (Funzione f : variables) { + if (f.getWidth() > width) { + width = f.getWidth(); + } + } + width += 5; + + height = 3; + for (Funzione f : variables) { + height += f.getHeight()+spacing; + } + height = height - spacing + 2; + + line = height/2; + } + + @Override + public void draw(int x, int y) { + + final int h = this.getHeight() - 1; + final int paddingTop = 3; + final int spazioSotto = (h - 3 - 2) / 2 + paddingTop; + final int spazioSopra = h - spazioSotto; + int dy = paddingTop; + for (Funzione f : variables) { + f.draw(x + 5, y + dy); + dy+=f.getHeight()+spacing; + } + + + glDrawLine(x + 2, y + 0, x + 3, y + 0); + glDrawLine(x + 1, y + 1, x + 1, y + spazioSotto / 2); + glDrawLine(x + 2, y + spazioSotto / 2 + 1, x + 2, y + spazioSotto - 1); + glDrawLine(x + 0, y + spazioSotto, x + 1, y + spazioSotto); + glDrawLine(x + 2, y + spazioSotto + 1, x + 2, y + spazioSotto + spazioSopra / 2 - 1); + glDrawLine(x + 1, y + spazioSotto + spazioSopra / 2, x + 1, y + h - 1); + glDrawLine(x + 2, y + h, x + 3, y + h); + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public int getLine() { + return line; + } +} diff --git a/src/org/warp/picalculator/Somma.java b/src/org/warp/picalculator/Somma.java new file mode 100644 index 00000000..0c205f77 --- /dev/null +++ b/src/org/warp/picalculator/Somma.java @@ -0,0 +1,75 @@ +package org.warp.picalculator; + +import static org.warp.engine.Display.Render.getStringWidth; +import static org.warp.engine.Display.Render.glDrawStringLeft; + +import org.warp.device.PIDisplay; +import org.warp.engine.Display; + +public class Somma extends FunzioneDueValoriBase { + + public Somma(FunzioneBase value1, FunzioneBase value2) { + super(value1, value2); + } + + @Override + public String simbolo() { + return Simboli.SUM; + } + + @Override + public Termine calcola() throws Errore { + Termine val1 = getVariable1().calcola(); + Termine val2 = getVariable2().calcola(); + Termine result = val1.add(val2); + return result; + } + + @Override + public void calcolaGrafica() { + variable1.setSmall(small); + variable1.calcolaGrafica(); + + variable2.setSmall(small); + variable2.calcolaGrafica(); + + width = calcWidth(); + height = calcHeight(); + line = calcLine(); + } + + @Override + public void draw(int x, int y) { +// glColor3f(127, 127-50+new Random().nextInt(50), 255); +// glFillRect(x,y,width,height); +// glColor3f(0, 0, 0); + + int ln = getLine(); + int dx = 0; + variable1.draw(dx + x, ln - variable1.getLine() + y); + dx += variable1.getWidth(); + if (small) { + Display.Render.setFont(PIDisplay.fonts[1]); + } else { + Display.Render.setFont(PIDisplay.fonts[0]); + } + dx += 1; + glDrawStringLeft(dx + x, ln - Utils.getFontHeight(small) / 2 + y, simbolo()); + dx += getStringWidth(simbolo()); + variable2.draw(dx + x, ln - variable2.getLine() + y); + } + + @Override + public int getWidth() { + return width; + } + + @Override + protected int calcWidth() { + int dx = 0; + dx += variable1.getWidth(); + dx += 1; + dx += getStringWidth(simbolo()); + return dx += variable2.getWidth(); + } +} diff --git a/src/org/warpgate/pi/calculator/Sottrazione.java b/src/org/warp/picalculator/Sottrazione.java similarity index 53% rename from src/org/warpgate/pi/calculator/Sottrazione.java rename to src/org/warp/picalculator/Sottrazione.java index 60b1ff37..f35d92ab 100644 --- a/src/org/warpgate/pi/calculator/Sottrazione.java +++ b/src/org/warp/picalculator/Sottrazione.java @@ -1,11 +1,8 @@ -package org.warpgate.pi.calculator; +package org.warp.picalculator; -import java.awt.Color; -import java.awt.Graphics; +public class Sottrazione extends FunzioneDueValoriBase { -public class Sottrazione extends FunzioneDueValori { - - public Sottrazione(Funzione value1, Funzione value2) { + public Sottrazione(FunzioneBase value1, FunzioneBase value2) { super(value1, value2); } diff --git a/src/org/warp/picalculator/Tecnica.java b/src/org/warp/picalculator/Tecnica.java new file mode 100644 index 00000000..d166b40c --- /dev/null +++ b/src/org/warp/picalculator/Tecnica.java @@ -0,0 +1,9 @@ +package org.warp.picalculator; + +import java.util.ArrayList; + +public interface Tecnica { + public static final Tecnica[] tecniche = new Tecnica[] {}; + + public abstract ArrayList risolvi(Equazione equazione); +} diff --git a/src/org/warpgate/pi/calculator/Termine.java b/src/org/warp/picalculator/Termine.java similarity index 61% rename from src/org/warpgate/pi/calculator/Termine.java rename to src/org/warp/picalculator/Termine.java index 74754aeb..0c96c5a7 100644 --- a/src/org/warpgate/pi/calculator/Termine.java +++ b/src/org/warp/picalculator/Termine.java @@ -1,28 +1,36 @@ -package org.warpgate.pi.calculator; +package org.warp.picalculator; + +import static org.warp.engine.Display.Render.getStringWidth; +import static org.warp.engine.Display.Render.glColor3f; +import static org.warp.engine.Display.Render.glDrawStringLeft; +import static org.warp.engine.Display.Render.glFillRect; -import java.awt.Color; -import java.awt.Graphics; import java.math.BigDecimal; import java.math.BigInteger; import java.math.MathContext; -import java.util.ArrayList; import org.nevec.rjm.BigDecimalMath; import org.nevec.rjm.NumeroAvanzato; import org.nevec.rjm.NumeroAvanzatoVec; import org.nevec.rjm.Rational; +import org.warp.device.PIDisplay; +import org.warp.engine.Display; import com.rits.cloning.Cloner; -public class Termine implements Funzione { +public class Termine extends FunzioneBase { protected NumeroAvanzatoVec term = NumeroAvanzatoVec.ZERO; + protected int width; + protected int height; + protected int line; + protected boolean small; public Termine(NumeroAvanzatoVec val) { term = val; } - public Termine(String s) { + public Termine(String s) throws Errore { term = new NumeroAvanzatoVec(new NumeroAvanzato(Utils.getRational(s), Rational.ONE)); } @@ -50,6 +58,13 @@ public class Termine implements Funzione { term = val; } + @Override + public void calcolaGrafica() { + line = calcLine(); //TODO pp + height = calcHeight(); + width = calcWidth(); + } + @Override public Termine calcola() { return this; @@ -82,8 +97,9 @@ public class Termine implements Funzione { ret = ret.multiply(new Termine(getTerm())); } } else if (getTerm().isRational(true) && f.getTerm().isRational(false) && f.getTerm().toRational(false).compareTo(Rational.HALF) == 0) { - //Rational originalExponent = f.getTerm().toRational(); - //Rational rootExponent = new Rational(originalExponent.denom(), originalExponent.numer()); + // Rational originalExponent = f.getTerm().toRational(); + // Rational rootExponent = new Rational(originalExponent.denom(), + // originalExponent.numer()); Rational numberToRoot = getTerm().toRational(true); NumeroAvanzato na = new NumeroAvanzato(Rational.ONE, numberToRoot); na = na.setIncognitex(getTerm().toNumeroAvanzato().getIncognitey().multiply(getTerm().toNumeroAvanzato().getIncognitez())); @@ -101,34 +117,37 @@ public class Termine implements Funzione { return getTerm().toFancyString(); } - public void draw(int x, int y, Display g, boolean small, boolean drawMinus) { - boolean beforedrawminus = this.drawMinus; - this.drawMinus = drawMinus; - draw(x, y, g, small); - this.drawMinus = beforedrawminus; - } - +// public void draw(int x, int y, PIDisplay g, boolean small, boolean drawMinus) { +// boolean beforedrawminus = this.drawMinus; +// this.drawMinus = drawMinus; +// draw(x, y, small); +// this.drawMinus = beforedrawminus; +// } + private boolean drawMinus = true; - + @Override - public void draw(int x, int y, Display g, boolean small) { + public void draw(int x, int y) { + if (getTerm().isBigInteger(false)) { + if (small) { + Display.Render.setFont(PIDisplay.fonts[1]); + } else { + Display.Render.setFont(PIDisplay.fonts[0]); + } String t = toString(); - int w1 = Utils.getPlainTextWidth(t); - int h1 = Utils.getFontHeight(small); - + if (t.startsWith("-")) { if (drawMinus) { - + } else { - int minusw = Utils.getPlainTextWidth("-"); - int minush = Utils.getFontHeight(small); t = t.substring(1); } } - Utils.writeLetter(g, t, x, y, small); + glDrawStringLeft(x+1, y, t); } else if (getTerm().isRational(false)) { small = true; + Display.Render.setFont(PIDisplay.fonts[1]); Rational r = getTerm().toRational(false); boolean minus = false; int minusw = 0; @@ -138,94 +157,98 @@ public class Termine implements Funzione { minus = true; numerator = numerator.substring(1); } - int w1 = Utils.getPlainTextWidth(numerator); + int w1 = getStringWidth(numerator); int h1 = Utils.getFontHeight(small); - int w2 = Utils.getPlainTextWidth(r.denom().toString()); - int h2 = Utils.getFontHeight(small); + int w2 = getStringWidth(r.denom().toString()); int maxw; if (w1 > w2) { - maxw = 1+w1+1; + maxw = 1 + w1 + 1; } else { - maxw = 1+w2+1; + maxw = 1 + w2 + 1; } if (minus) { if (drawMinus) { - minusw = Utils.getPlainTextWidth("-"); + minusw = getStringWidth("-"); minush = Utils.getFontHeight(small); maxw += minusw; - Utils.writeLetter(g, "-", x, y+h1+1+1-(minush/2), small); + glDrawStringLeft(x, y + h1 + 1 + 1 - (minush / 2), "-"); } } - Utils.writeLetter(g, numerator, (int)(x+minusw+1+((double)(maxw-w1))/2d), y, small); - Utils.writeLetter(g, r.denom().toString(), (int)(x+minusw+1+((double)(maxw-w2))/2d), y+h1+1+1+1, small); - g.setColor(Color.BLACK); - g.fillRect(x+minusw+1, y+h1+1, maxw, 1); + glDrawStringLeft((int) (x + minusw + 1 + (maxw - w1) / 2d), y, numerator); + glDrawStringLeft((int) (x + minusw + 1 + (maxw - w2) / 2d), y + h1 + 1 + 1 + 1, r.denom().toString()); + glColor3f(0, 0, 0); + glFillRect(x + minusw + 1, y + h1 + 1, maxw, 1); } else if (getTerm().toFancyString().contains("/")) { small = true; + Display.Render.setFont(PIDisplay.fonts[1]); String r = getTerm().toFancyString(); String numer = r.substring(0, r.lastIndexOf("/")); - String denom = r.substring(numer.length()+1, r.length()); + String denom = r.substring(numer.length() + 1, r.length()); if (numer.startsWith("(") && numer.endsWith(")")) { - numer = numer.substring(1, numer.length()-1); + numer = numer.substring(1, numer.length() - 1); } boolean minus = false; if (numer.startsWith("-")) { minus = true; numer = numer.substring(1); } - int w1 = Utils.getPlainTextWidth(numer.toString()); + int w1 = getStringWidth(numer.toString()); int h1 = Utils.getFontHeight(small); - int w2 = Utils.getPlainTextWidth(denom.toString()); - int h2 = Utils.getFontHeight(small); + int w2 = getStringWidth(denom.toString()); int maxw; if (w1 > w2) { - maxw = w1+2; + maxw = w1 + 2; } else { - maxw = w2+2; + maxw = w2 + 2; } int minusw = 0; int minush = 0; if (minus) { if (drawMinus) { - minusw = Utils.getPlainTextWidth("-")+1; + minusw = getStringWidth("-") + 1; minush = Utils.getFontHeight(small); maxw += minusw; - Utils.writeLetter(g, "-", x, y+h1+1+1-(minush/2), small); + glDrawStringLeft(x, y + h1 + 1 + 1 - (minush / 2), "-"); } } - Utils.writeLetter(g, numer, (int)(x+minusw+1+((double)(maxw-w1))/2d), y, small); - Utils.writeLetter(g, denom, (int)(x+minusw+1+((double)(maxw-w2))/2d), y+h1+1+1+1, small); - g.setColor(Color.BLACK); - g.fillRect(x+minusw+1, y+h1+1, maxw, 1); + glDrawStringLeft((int) (x + minusw + 1 + (maxw - w1) / 2d), y, numer); + glDrawStringLeft((int) (x + minusw + 1 + (maxw - w2) / 2d), y + h1 + 1 + 1 + 1, denom); + glColor3f(0, 0, 0); + glFillRect(x + minusw + 1, y + h1 + 1, maxw, 1); } else { + if (small) { + Display.Render.setFont(PIDisplay.fonts[1]); + } else { + Display.Render.setFont(PIDisplay.fonts[0]); + } String r = getTerm().toFancyString(); - int w1 = Utils.getPlainTextWidth(r.toString()); - int h1 = Utils.getFontHeight(small); - + if (r.startsWith("-")) { if (drawMinus) { - + } else { - int minusw = Utils.getPlainTextWidth("-")+1; - int minush = Utils.getFontHeight(small); r = r.substring(1); } } - - Utils.writeLetter(g, r.toString(), x, y, small); + + glDrawStringLeft(x+1, y, r.toString()); } } - public int getHeight(boolean small, boolean drawMinus) { + public int getHeight(boolean drawMinus) { boolean beforedrawminus = this.drawMinus; this.drawMinus = drawMinus; - int h = getHeight(small); + int h = getHeight(); this.drawMinus = beforedrawminus; return h; } - + @Override - public int getHeight(boolean small) { + public int getHeight() { + return height; + } + + private int calcHeight() { if (getTerm().isBigInteger(false)) { int h1 = Utils.getFontHeight(small); return h1; @@ -233,38 +256,33 @@ public class Termine implements Funzione { small = true; int h1 = Utils.getFontHeight(small); int h2 = Utils.getFontHeight(small); - return h1+3+h2; + return h1 + 3 + h2; } else if (getTerm().toFancyString().contains("/")) { small = true; int h1 = Utils.getFontHeight(small); int h2 = Utils.getFontHeight(small); - return h1+3+h2; + return h1 + 3 + h2; } else { int h1 = Utils.getFontHeight(small); return h1; } } - - public int getWidth(boolean drawMinus) { - boolean beforedrawminus = this.drawMinus; - this.drawMinus = drawMinus; - int w = getWidth(); - this.drawMinus = beforedrawminus; - return w; - } - @Override public int getWidth() { + return width; + } + + public int calcWidth() { if (getTerm().isBigInteger(false)) { String t = toString(); if (t.startsWith("-")) { if (drawMinus) { - + } else { t = t.substring(1); } } - return Utils.getPlainTextWidth(t); + return getStringWidth(t)+1; } else if (getTerm().isRational(false)) { Rational r = getTerm().toRational(false); boolean minus = false; @@ -273,59 +291,59 @@ public class Termine implements Funzione { minus = true; numerator = numerator.substring(1); } - int w1 = Utils.getPlainTextWidth(numerator); - int w2 = Utils.getPlainTextWidth(r.denom().toString()); + int w1 = getStringWidth(numerator); + int w2 = getStringWidth(r.denom().toString()); int maxw; if (w1 > w2) { - maxw = 1+w1+1; + maxw = 1 + w1 + 1; } else { - maxw = 1+w2+1; + maxw = 1 + w2 + 1; } if (minus) { if (drawMinus) { - maxw += Utils.getPlainTextWidth("-"); + maxw += getStringWidth("-"); } } - return maxw+2; + return maxw + 1; } else if (getTerm().toFancyString().contains("/")) { String r = getTerm().toFancyString(); String numer = r.substring(0, r.lastIndexOf("/")); - String denom = r.substring(numer.length()+1, r.length()); + String denom = r.substring(numer.length() + 1, r.length()); if (numer.startsWith("(") && numer.endsWith(")")) { - numer = numer.substring(1, numer.length()-1); + numer = numer.substring(1, numer.length() - 1); } boolean minus = false; if (numer.startsWith("-")) { minus = true; numer = numer.substring(1); } - int w1 = Utils.getPlainTextWidth(numer.toString()); - int w2 = Utils.getPlainTextWidth(denom.toString()); + int w1 = getStringWidth(numer.toString()); + int w2 = getStringWidth(denom.toString()); int maxw; if (w1 > w2) { - maxw = w1+2; + maxw = w1 + 1; } else { - maxw = w2+2; + maxw = w2 + 1; } if (minus) { if (drawMinus) { - maxw += Utils.getPlainTextWidth("-"); + maxw += getStringWidth("-"); } } - return maxw+2; + return maxw + 2; } else { String r = getTerm().toFancyString(); if (r.startsWith("-")) { if (drawMinus) { - + } else { r = r.substring(1); } } - return Utils.getPlainTextWidth(r.toString()); + return getStringWidth(r.toString())+1; } } - + public boolean soloIncognitaSemplice() { if (this.getTerm().isBigInteger(true)) { if (this.getTerm().toBigInteger(true).compareTo(BigInteger.ONE) == 0 && this.getTerm().toNumeroAvanzato().getIncognitey().count() > 0) { @@ -336,42 +354,51 @@ public class Termine implements Funzione { } @Override - public int getLine(boolean small) { + public int getLine() { + return line; + } + + private int calcLine() { if (getTerm().isBigInteger(false)) { - return Utils.getFontHeight(small)/2; + return Utils.getFontHeight(small) / 2; } else if (getTerm().isRational(false)) { small = true; int h1 = Utils.getFontHeight(small); - return h1+1; + return h1 + 1; } else if (getTerm().toFancyString().contains("/")) { small = true; int h1 = Utils.getFontHeight(small); - return h1+1; + return h1 + 1; } else { int h1 = Utils.getFontHeight(small); - return h1/2; + return h1 / 2; } } - + @Override public Termine clone() { Cloner cloner = new Cloner(); return cloner.deepClone(this); } - /* @Override - public void draw(int x, int y, Graphics g) { + public void setSmall(boolean small) { + this.small = small; } - @Override - public int getHeight() { - return Utils.getFontHeight(); - } - - @Override - public int getWidth() { - return 6*toString().length()-1; - } - */ + /* + * @Override + * public void draw(int x, int y, Graphics g) { + * } + * + * @Override + * public int getHeight() { + * return Utils.getFontHeight(); + * } + * + * @Override + * public int getWidth() { + * return 6*toString().length()-1; + * } + */ } diff --git a/src/org/warp/picalculator/Utils.java b/src/org/warp/picalculator/Utils.java new file mode 100644 index 00000000..0a96e8bf --- /dev/null +++ b/src/org/warp/picalculator/Utils.java @@ -0,0 +1,385 @@ +package org.warp.picalculator; + +import static org.warp.engine.Display.Render.glDrawLine; + +import java.awt.Font; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.util.ArrayList; + +import org.nevec.rjm.BigDecimalMath; +import org.nevec.rjm.Rational; + +public class Utils { + + public static final int scale = 24; + public static final int resultScale = 8; + + public static final int scaleMode = BigDecimal.ROUND_HALF_UP; + public static final RoundingMode scaleMode2 = RoundingMode.HALF_UP; + + public static DebugStream debug = new DebugStream(); + + public static boolean debugOn; + + public static final class DebugStream extends StringWriter { + + public void println(String str) { + if (debugOn) { + System.err.println(str); + } + } + + int before = 0; + boolean due = false; + + } + + public static boolean isInArray(String ch, String[] a) { + boolean contains = false; + for (String c : a) { + if (c.equals(ch)) { + contains = true; + break; + } + } + return contains; + } + + public static String ArrayToRegex(String[] array) { + String regex = null; + for (String symbol : array) { + if (regex != null) { + regex += "|\\" + symbol; + } else { + regex = "\\" + symbol; + } + } + return regex; + } + + public static String[] concat(String[] a, String[] b) { + int aLen = a.length; + int bLen = b.length; + String[] c = new String[aLen + bLen]; + System.arraycopy(a, 0, c, 0, aLen); + System.arraycopy(b, 0, c, aLen, bLen); + return c; + } + + public static String[] add(String[] a, String b) { + int aLen = a.length; + String[] c = new String[aLen + 1]; + System.arraycopy(a, 0, c, 0, aLen); + c[aLen] = b; + return c; + } + + public static boolean ciSonoSoloFunzioniImpostateSommeEquazioniESistemi(ArrayList fl) { + for (int i = 0; i < fl.size(); i++) { + if (!(fl.get(i) instanceof Termine || fl.get(i) instanceof Somma || fl.get(i) instanceof Equazione || fl.get(i) instanceof ParteSistema || fl.get(i) instanceof Espressione)) { + if (fl.get(i) instanceof FunzioneAnterioreBase) { + if (((FunzioneAnterioreBase) fl.get(i)).variable == null) { + return false; + } + } else if (fl.get(i) instanceof FunzioneDueValoriBase) { + if (((FunzioneDueValoriBase) fl.get(i)).variable1 == null || ((FunzioneDueValoriBase) fl.get(i)).variable2 == null) { + return false; + } + } else { + return false; + } + } + } + return true; + } + + public static boolean ciSonoSoloFunzioniImpostateSommeMoltiplicazioniEquazioniESistemi(ArrayList fl) { + for (int i = 0; i < fl.size(); i++) { + if (!(fl.get(i) instanceof Termine || fl.get(i) instanceof Moltiplicazione || fl.get(i) instanceof MoltiplicazionePrioritaria || fl.get(i) instanceof Somma || fl.get(i) instanceof Equazione || fl.get(i) instanceof ParteSistema || fl.get(i) instanceof Espressione)) { + if (fl.get(i) instanceof FunzioneAnterioreBase) { + if (((FunzioneAnterioreBase) fl.get(i)).variable == null) { + return false; + } + } else if (fl.get(i) instanceof FunzioneDueValoriBase) { + if (((FunzioneDueValoriBase) fl.get(i)).variable1 == null || ((FunzioneDueValoriBase) fl.get(i)).variable2 == null) { + return false; + } + } else { + return false; + } + } + } + return true; + } + + public static boolean ciSonoSoloFunzioniImpostateEquazioniESistemi(ArrayList fl) { + for (int i = 0; i < fl.size(); i++) { + if (!(fl.get(i) instanceof Termine || fl.get(i) instanceof Equazione || fl.get(i) instanceof ParteSistema || fl.get(i) instanceof Espressione)) { + if (fl.get(i) instanceof FunzioneAnterioreBase) { + if (((FunzioneAnterioreBase) fl.get(i)).variable == null) { + return false; + } + } else if (fl.get(i) instanceof FunzioneDueValoriBase) { + if (((FunzioneDueValoriBase) fl.get(i)).variable1 == null || ((FunzioneDueValoriBase) fl.get(i)).variable2 == null) { + return false; + } + } else { + return false; + } + } + } + return true; + } + + public static boolean ciSonoSoloFunzioniImpostateESistemi(ArrayList fl) { + for (int i = 0; i < fl.size(); i++) { + if (!(fl.get(i) instanceof Termine || fl.get(i) instanceof Equazione || fl.get(i) instanceof ParteSistema || fl.get(i) instanceof Espressione)) { + if (fl.get(i) instanceof FunzioneAnterioreBase) { + if (((FunzioneAnterioreBase) fl.get(i)).variable == null) { + return false; + } + } else if (fl.get(i) instanceof FunzioneDueValoriBase) { + if (((FunzioneDueValoriBase) fl.get(i)).variable1 == null || ((FunzioneDueValoriBase) fl.get(i)).variable2 == null) { + return false; + } + } else { + return false; + } + } + } + return true; + } + + public static boolean ciSonoFunzioniSNnonImpostate(ArrayList fl) { + for (int i = 0; i < fl.size(); i++) { + if (fl.get(i) instanceof FunzioneAnterioreBase) { + if (((FunzioneAnterioreBase) fl.get(i)).variable == null) { + return true; + } + } + } + return false; + } + + public static boolean ciSonoFunzioniNSNnonImpostate(ArrayList fl) { + for (int i = 0; i < fl.size(); i++) { + if (fl.get(i) instanceof FunzioneDueValoriBase && !(fl.get(i) instanceof Somma) && !(fl.get(i) instanceof Sottrazione) && !(fl.get(i) instanceof Moltiplicazione) && !(fl.get(i) instanceof MoltiplicazionePrioritaria) && !(fl.get(i) instanceof Divisione)) { + if (((FunzioneDueValoriBase) fl.get(i)).variable1 == null && ((FunzioneDueValoriBase) fl.get(i)).variable2 == null) { + return true; + } + } + } + return false; + } + + + public static boolean ciSonoMoltiplicazioniPrioritarieNonImpostate(ArrayList funzioniOLD) { + for (int i = 0; i < funzioniOLD.size(); i++) { + if (funzioniOLD.get(i) instanceof MoltiplicazionePrioritaria) { + if (((FunzioneDueValoriBase) funzioniOLD.get(i)).variable1 == null && ((FunzioneDueValoriBase) funzioniOLD.get(i)).variable2 == null) { + return true; + } + } + } + return false; + } + + public static boolean ciSonoMoltiplicazioniNonImpostate(ArrayList fl) { + for (int i = 0; i < fl.size(); i++) { + if (fl.get(i) instanceof Moltiplicazione || fl.get(i) instanceof Divisione) { + if (((FunzioneDueValoriBase) fl.get(i)).variable1 == null && ((FunzioneDueValoriBase) fl.get(i)).variable2 == null) { + return true; + } + } + } + return false; + } + + public static boolean ciSonoSommeNonImpostate(ArrayList fl) { + for (int i = 0; i < fl.size(); i++) { + if (fl.get(i) instanceof Somma) { + if (((FunzioneDueValoriBase) fl.get(i)).variable1 == null && ((FunzioneDueValoriBase) fl.get(i)).variable2 == null) { + return true; + } + } + } + return false; + } + + public static boolean ciSonoSistemiNonImpostati(ArrayList fl) { + for (int i = 0; i < fl.size(); i++) { + if (fl.get(i) instanceof ParteSistema) { + if (((ParteSistema) fl.get(i)).variable == null) { + return true; + } + } + } + return false; + } + + public static boolean ciSonoAltreFunzioniImpostate(ArrayList fl) { + for (int i = 0; i < fl.size(); i++) { + if (!(fl.get(i) instanceof Termine || fl.get(i) instanceof Somma || fl.get(i) instanceof Espressione || fl.get(i) instanceof FunzioneAnterioreBase || fl.get(i) instanceof Moltiplicazione || fl.get(i) instanceof MoltiplicazionePrioritaria || fl.get(i) instanceof Divisione)) { + if (fl.get(i) instanceof FunzioneAnterioreBase) { + if (((FunzioneAnterioreBase) fl.get(i)).variable == null) { + return true; + } + } else if (fl.get(i) instanceof FunzioneDueValoriBase) { + if (((FunzioneDueValoriBase) fl.get(i)).variable1 == null || ((FunzioneDueValoriBase) fl.get(i)).variable2 == null) { + return true; + } + } else { + return true; + } + } + } + return false; + } + + public static Rational getRational(BigDecimal str) { + try { + return getRational(str.toString()); + } catch (Errore e) { + //E' IMPOSSIBILE CHE VENGA THROWATO UN ERRORE + return new Rational("0"); + } + } + + public static Rational getRational(String str) throws Errore { + try { + return new Rational(str); + } catch (NumberFormatException ex) { + if (new BigDecimal(str).compareTo(new BigDecimal(8000.0)) < 0 && new BigDecimal(str).compareTo(new BigDecimal(-8000.0)) > 0) { + if (str.equals("-")) { + str = "-1"; + } + long bits = Double.doubleToLongBits(Double.parseDouble(str)); + + long sign = bits >>> 63; + long exponent = ((bits >>> 52) ^ (sign << 11)) - 1023; + long fraction = bits << 12; // bits are "reversed" but that's + // not a problem + + long a = 1L; + long b = 1L; + + for (int i = 63; i >= 12; i--) { + a = a * 2 + ((fraction >>> i) & 1); + b *= 2; + } + + if (exponent > 0) + a *= 1 << exponent; + else + b *= 1 << -exponent; + + if (sign == 1) + a *= -1; + + if (b == 0) { + a = 0; + b = 1; + } + + return new Rational(new BigInteger(a + ""), new BigInteger(b + "")); + } else { + BigDecimal original = new BigDecimal(str); + + BigInteger numerator = original.unscaledValue(); + + BigInteger denominator = BigDecimalMath.pow(BigDecimal.TEN, new BigDecimal(original.scale())).toBigIntegerExact(); + + return new Rational(numerator, denominator); + } + } + } + + public static BigDecimal rationalToIrrationalString(Rational r) { + return BigDecimalMath.divideRound(new BigDecimal(r.numer()).setScale(Utils.scale, Utils.scaleMode), new BigDecimal(r.denom()).setScale(Utils.scale, Utils.scaleMode)); + } + + public static boolean variabiliUguali(ArrayList variables, ArrayList variables2) { + if (variables.size() != variables2.size()) { + return false; + } else { + for (Incognita v : variables) { + if (!variables2.contains(v)) { + return false; + } + } + return true; + } + } + + public static void writeSquareRoot(Funzione var, int x, int y, boolean small) { + var.setSmall(small); + int w1 = var.getWidth(); + int h1 = var.getHeight(); + int wsegno = 5; + int hsegno = h1 + 2; + + var.draw(x + wsegno, y + (hsegno - h1)); + + glDrawLine(x + 1, y + hsegno - 3, x + 3, y + hsegno - 1); + glDrawLine(x + 3, y + (hsegno - 1) / 2 + 1, x + 3, y + hsegno - 1); + glDrawLine(x + 4, y, x + 4, y + (hsegno - 1) / 2); + glDrawLine(x + 4, y, x + 4 + 1 + w1 + 1, y); + } + + public static final int getFontHeight() { + return getFontHeight(false); + } + + public static final int getFontHeight(boolean small) { + if (small) { + return 6; + } else { + return 9; + } + } + + public static int getFontHeight(Font font) { + if (font.getFontName().contains("Big")) { + return 9; + } else { + return 6; + } + } + + public static byte[] convertStreamToByteArray(InputStream stream, long size) throws IOException { + + // check to ensure that file size is not larger than Integer.MAX_VALUE. + if (size > Integer.MAX_VALUE) { + return new byte[0]; + } + + byte[] buffer = new byte[(int)size]; + ByteArrayOutputStream os = new ByteArrayOutputStream(); + + int line = 0; + // read bytes from stream, and store them in buffer + while ((line = stream.read(buffer)) != -1) { + // Writes bytes from byte array (buffer) into output stream. + os.write(buffer, 0, line); + } + stream.close(); + os.flush(); + os.close(); + return os.toByteArray(); + } + + public static int[] realBytes(byte[] bytes) { + int len = bytes.length; + int[] realbytes = new int[len]; + for (int i = 0; i < len; i++) { + realbytes[i] = Byte.toUnsignedInt(bytes[i]); + } + return realbytes; + } +} diff --git a/src/org/warp/picalculator/device/chip/ParallelToSerial.java b/src/org/warp/picalculator/device/chip/ParallelToSerial.java new file mode 100644 index 00000000..691162d3 --- /dev/null +++ b/src/org/warp/picalculator/device/chip/ParallelToSerial.java @@ -0,0 +1,35 @@ +package org.warp.picalculator.device.chip; + +import com.pi4j.wiringpi.Gpio; + +public class ParallelToSerial { + + private int SH_LD; + private int CLK_INH; + private int QH; + private int CLK; + + public ParallelToSerial(int SH_LD_pin, int CLK_INH_pin, int QH_pin, int CLK_pin) { + SH_LD = SH_LD_pin; + CLK_INH = CLK_INH_pin; + QH = QH_pin; + CLK = CLK_pin; + } + + public boolean[] read() { + boolean[] data = new boolean[8]; + Gpio.digitalWrite(CLK_INH, Gpio.HIGH); + Gpio.digitalWrite(SH_LD, Gpio.LOW); + Gpio.delay(1); + Gpio.digitalWrite(SH_LD, Gpio.HIGH); + Gpio.digitalWrite(CLK_INH, Gpio.LOW); + + for (int i = 7; i >= 0; i--) { + Gpio.digitalWrite(CLK, Gpio.HIGH); + Gpio.digitalWrite(CLK, Gpio.LOW); + data[i] = Gpio.digitalRead(QH)==Gpio.HIGH?true:false; + } + + return data; + } +} diff --git a/src/org/warp/picalculator/device/chip/SerialToParallel.java b/src/org/warp/picalculator/device/chip/SerialToParallel.java new file mode 100644 index 00000000..712549f9 --- /dev/null +++ b/src/org/warp/picalculator/device/chip/SerialToParallel.java @@ -0,0 +1,31 @@ +package org.warp.picalculator.device.chip; + +import com.pi4j.wiringpi.Gpio; + +public class SerialToParallel { + private int RCK; //Storage register clock pin (latch pin) + private int SCK; //Shift register clock pin + private int SER; //Serial data input + + public SerialToParallel(int RCK_pin, int SCK_pin, int SER_pin) { + RCK = RCK_pin; + SCK = SCK_pin; + SER = SER_pin; + } + + public void write(boolean[] data) { + if (data.length != 8) { + return; + } else { + Gpio.digitalWrite(RCK, Gpio.LOW); + + for (int i = 7; i >= 0; i--) { + Gpio.digitalWrite(SCK, Gpio.LOW); + Gpio.digitalWrite(SER, data[i]); + Gpio.digitalWrite(SCK, Gpio.HIGH); + } + + Gpio.digitalWrite(RCK, Gpio.HIGH); + } + } +} diff --git a/src/org/warp/picalculator/screens/EmptyScreen.java b/src/org/warp/picalculator/screens/EmptyScreen.java new file mode 100644 index 00000000..fd457931 --- /dev/null +++ b/src/org/warp/picalculator/screens/EmptyScreen.java @@ -0,0 +1,54 @@ +package org.warp.picalculator.screens; + +import org.warp.device.Keyboard.Key; +import org.warp.device.PIDisplay; +import org.warp.engine.Screen; + +public class EmptyScreen extends Screen { + + public float endLoading; + + public EmptyScreen() { + super(); + canBeInHistory = false; + } + + @Override + public void created() throws InterruptedException { + endLoading = 0; + } + + @Override + public void init() throws InterruptedException {} + + @Override + public void render() { + // TODO Auto-generated method stub + + } + + @Override + public void beforeRender(float dt) { + endLoading += dt; + if (PIDisplay.loading & endLoading >= 2.5) { + PIDisplay.loading = false; + } + } + + @Override + public boolean mustBeRefreshed() { + return true; + } + + @Override + public boolean keyPressed(Key k) { + + return false; + } + + @Override + public boolean keyReleased(Key k) { + return false; + } + +} diff --git a/src/org/warp/picalculator/screens/EquationScreen.java b/src/org/warp/picalculator/screens/EquationScreen.java new file mode 100644 index 00000000..ebd96b34 --- /dev/null +++ b/src/org/warp/picalculator/screens/EquationScreen.java @@ -0,0 +1,324 @@ +package org.warp.picalculator.screens; + +import static org.warp.engine.Display.Render.glClearColor; +import static org.warp.engine.Display.Render.glDrawStringLeft; +import static org.warp.engine.Display.Render.setFont; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import org.warp.device.Keyboard.Key; +import org.warp.device.PIDisplay; +import org.warp.engine.Display; +import org.warp.engine.Screen; +import org.warp.picalculator.Calculator; +import org.warp.picalculator.Errore; +import org.warp.picalculator.Errori; +import org.warp.picalculator.Funzione; +import org.warp.picalculator.Utils; + +public class EquationScreen extends Screen { + + public float endLoading; + public volatile String equazioneCorrente = ""; + public volatile String nuovaEquazione = ""; + public volatile int caretPos = 0; + public volatile boolean showCaret = true; + public volatile float showCaretDelta = 0f; + public Funzione f; + public Funzione f2; + public int ew1; + public int ew2; + public int eh2; + public int x1; + public int x2; + public boolean requiresleep1; + public boolean requiresleep2; + public boolean aftersleep; + public boolean autoscroll; + public int errorLevel = 0; // 0 = nessuno, 1 = risultato, 2 = tutto + public Errore err1; + public Errore err2; + boolean mustRefresh = true; + + public EquationScreen() { + super(); + canBeInHistory = true; + } + + @Override + public void created() throws InterruptedException { + endLoading = 0; + } + + @Override + public void init() throws InterruptedException { + try { + /* Fine caricamento */ + + // Parentesi f = new + // Parentesi("(â’¶(2X)*3Y)/(5Z^2)+(â’¶(11A)*13B)/(7CZ)=19XZ"); + // PARENTESI CON CALCOLI + // Funzione f = new Sottrazione(new Somma(new Parentesi("â’¶9/2+â’¶7/2", + // "").calcola(), new Termine("3.5")), new + // Parentesi("3*2√14","").calcola()); + // PARENTESI CON DUE NUMERI FRAZIONALI COMPLETI CON INCOGNITE: + // Funzione f = new + // Parentesi("(â’¶(2X)*3Y)/(5Z^2)+(â’¶(11A)*13B)/(7CZ)", ""); + // PARENTESI CON DUE NUMERI FRAZIONALI DISALLINEATI GRAFICAMENTE: + // Funzione f = new Parentesi("((5^2-1)/2)/5-5/(5/2)=2", ""); + // TERMINE DI PROVA COMPLETO: + // Funzione f = new Termine(new NumeroAvanzato(new Rational(3, 2), + // new Rational(7, 1), new Incognite(new Incognita('X', + // Rational.ONE)), new Incognite(new Incognita('Y', Rational.ONE)), + // new Incognite(new Incognita('z', Rational.ONE)))); + // PARENTESI REALISTICA CON INCOGNITE: + // Funzione f = new Equazione(new + // Parentesi("X^2+(MX-M+4)^2-4X-4(MX-M+4)^2+7", ""), new + // Termine("0")); + // POTENZA: + // Funzione f = new Parentesi("(MX-M+4)^2", ""); + // NUMERO SEMPLICE LUNGO: + // Funzione f = new Parentesi("-1219999799999996934.42229", ""); + // : + // Funzione f = new Parentesi("5Y+XY=2", "") + +// calcola("((5^2+3√(100/0.1))+â’¶(7)+9/15*2√(26/2))/21"); + interpreta("0"); +// interpreta("{(5X*(15X/3X))+(25X/(5X*(15X/3X)))=15{X=5"); //TODO RIMUOVERE + + // long start = System.nanoTime(); + // Termine result = + // Calculator.calcolarisultato("((5â’·2+3√(100/0.1))*â’¶7+9/15*2√(26/2))/21"); + // long end = System.nanoTime(); + // long timeElapsed = end-start; + // System.out.println("RESULT: " + result); + // System.out.println("DECIMAl RESULT: " + + // result.getTerm().toBigDecimal()); + // System.out.println("Time elapsed: " + (double) timeElapsed / + // 1000000 + " milliseconds\n"); + // + // + // start = System.nanoTime(); + // RisultatoEquazione eresult = + // Calculator.calcolaequazione("((5â’·2+3√(100/0.1))*â’¶7+9/15*2√(26/2))/21=(175*(2√7)+3*(2√13))/105"); + // end = System.nanoTime(); + // timeElapsed = end-start; + // System.out.println("Is an equation: " + eresult.isAnEquation); + // System.out.println("L-R: " + eresult.LR); + // System.out.println("Time elapsed: " + (double) timeElapsed / + // 1000000 + " milliseconds\n"); + + } catch (Errore e) { + glClearColor(0xFFDC3C32); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + d.errorStackTrace = sw.toString().toUpperCase().replace("\t", " ").replace("\r", "").split("\n"); + PIDisplay.error = e.id.toString(); + System.err.println(e.id); + } + } + + public void interpreta(String eqn) throws Errore { + equazioneCorrente = eqn; + f = Calculator.interpreta(equazioneCorrente.replace("sqrt", "â’¶").replace("^", "â’·")); + f.calcolaGrafica(); + } + + public void solve() throws Errore { + Calculator.solve(); + } + + @Override + public void beforeRender(float dt) { + endLoading += dt; + if (endLoading >= 1) { + PIDisplay.loading = false; + } + showCaretDelta += dt; + if (showCaretDelta >= 0.5f) { + mustRefresh = true; + showCaret = !showCaret; + showCaretDelta = 0f; + } + } + + @Override + public void render() { + setFont(PIDisplay.fonts[0]); + glClearColor(0xFFCCE7D4); + glDrawStringLeft(2, 22, nuovaEquazione.substring(0, caretPos)+(showCaret?"|":"")+nuovaEquazione.substring(((showCaret==false||nuovaEquazione.length()<=caretPos)?caretPos:caretPos+1), nuovaEquazione.length())); + if (f != null) + f.draw(2, 22+1+9+1); + if (f2 != null) + f2.draw(Display.getWidth() - 2 - f2.getWidth(), Display.getHeight() - 2 - f2.getHeight()); + } + + @Override + public boolean mustBeRefreshed() { + if (mustRefresh) { + mustRefresh = false; + return true; + } else { + return false; + } + } + + @Override + public boolean keyPressed(Key k) { + switch (k) { + case SIMPLIFY: + if (nuovaEquazione.length() > 0) { + Calculator.simplify(); + } + return true; + case SOLVE: + if (PIDisplay.error != null) { + Utils.debug.println("Resetting after error..."); + PIDisplay.error = null; + return true; + } else { + if (nuovaEquazione != equazioneCorrente && nuovaEquazione.length() > 0) { + try { + try { + interpreta(nuovaEquazione); + solve(); + } catch (Exception ex) { + throw new Errore(Errori.ERROR); + } + } catch (Errore e) { + glClearColor(0xFFDC3C32); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + d.errorStackTrace = sw.toString().toUpperCase().replace("\t", " ").replace("\r", "").split("\n"); + PIDisplay.error = e.id.toString(); + System.err.println(e.id); + } + } + return true; + } + case NUM0: + typeChar("0"); + return true; + case NUM1: + typeChar("1"); + return true; + case NUM2: + typeChar("2"); + return true; + case NUM3: + typeChar("3"); + return true; + case NUM4: + typeChar("4"); + return true; + case NUM5: + typeChar("5"); + return true; + case NUM6: + typeChar("6"); + return true; + case NUM7: + typeChar("7"); + return true; + case NUM8: + typeChar("8"); + return true; + case NUM9: + typeChar("9"); + return true; + case PLUS: + typeChar("+"); + return true; + case MINUS: + typeChar("-"); + return true; + case MULTIPLY: + typeChar("*"); + return true; + case DIVIDE: + typeChar("/"); + return true; + case PARENTHESIS_OPEN: + typeChar("("); + return true; + case PARENTHESIS_CLOSE: + typeChar(")"); + return true; + case DOT: + typeChar("."); + return true; + case EQUAL: + typeChar("="); + return true; + case SQRT: + typeChar("â’¶"); + return true; + case ROOT: + typeChar("√"); + return true; + case POWER_OF_2: + typeChar("^"); + typeChar("2"); + return true; + case POWER_OF_x: + typeChar("^"); + return true; + case DELETE: + if (nuovaEquazione.length() > 0) { + if (caretPos > 0) { + caretPos-=1; + nuovaEquazione=nuovaEquazione.substring(0, caretPos)+nuovaEquazione.substring(caretPos+1, nuovaEquazione.length()); + } else { + nuovaEquazione = nuovaEquazione.substring(1); + } + } + return true; + case LEFT: + if (caretPos > 0) { + caretPos -= 1; + showCaret = true; + showCaretDelta = 0L; + } + return true; + case RIGHT: + if (caretPos < nuovaEquazione.length()) { + caretPos += 1; + showCaret = true; + showCaretDelta = 0L; + } + return true; + case RESET: + if (PIDisplay.error != null) { + Utils.debug.println("Resetting after error..."); + PIDisplay.error = null; + return true; + } else { + caretPos = 0; + nuovaEquazione=""; + return true; + } + case debug1: + PIDisplay.INSTANCE.setScreen(new EmptyScreen()); + return true; + default: + return false; + } + } + + public void typeChar(String chr) { + nuovaEquazione=nuovaEquazione.substring(0, caretPos)+chr+nuovaEquazione.substring(caretPos, nuovaEquazione.length()); + caretPos+=1; + showCaret = true; + showCaretDelta = 0L; + } + + @Override + public boolean keyReleased(Key k) { + + return false; + } + +} diff --git a/src/org/warp/picalculator/screens/MarioScreen.java b/src/org/warp/picalculator/screens/MarioScreen.java new file mode 100644 index 00000000..fea27e62 --- /dev/null +++ b/src/org/warp/picalculator/screens/MarioScreen.java @@ -0,0 +1,87 @@ +package org.warp.picalculator.screens; + +import static org.warp.engine.Display.Render.getMatrixOfImage; +import static org.warp.engine.Display.Render.glClearColor; +import static org.warp.engine.Display.Render.glDrawSkin; +import static org.warp.engine.Display.Render.glDrawStringLeft; +import static org.warp.engine.Display.Render.setFont; + +import java.awt.image.BufferedImage; +import java.io.IOException; + +import javax.imageio.ImageIO; + +import org.warp.device.Keyboard; +import org.warp.device.Keyboard.Key; +import org.warp.device.PIDisplay; +import org.warp.engine.Screen; +import org.warp.picalculator.Main; + +public class MarioScreen extends Screen { + + private int[] skin; + private int[] skinSize; + private boolean errored; + public int posX = 0; + + public MarioScreen() { + super(); + canBeInHistory = true; + } + + @Override + public void init() { + BufferedImage img; + try { + img = ImageIO.read(Main.instance.getClass().getResource("/marioskin.png")); + skin = getMatrixOfImage(img); + skinSize = new int[] { img.getWidth(), img.getHeight() }; + } catch (IOException e) { + e.printStackTrace(); + errored = true; + } + } + + @Override + public void created() throws InterruptedException { + if (!errored) { + + } + } + + @Override + public void beforeRender(float dt) { + if (!errored) { + if (Keyboard.isKeyDown(2, 5)) { //RIGHT + posX+=1; + } + } + } + + @Override + public void render() { + if (errored) { + glDrawStringLeft(0, 20, "ERROR"); + } else { + setFont(PIDisplay.fonts[0]); + glClearColor(0xFFCCE7D4); + glDrawSkin(skinSize[0], skin, posX, 25, 36, 1, 70, 27, true); + } + } + + @Override + public boolean mustBeRefreshed() { + return true; + } + + @Override + public boolean keyReleased(Key k) { + return false; + } + + @Override + public boolean keyPressed(Key k) { + return false; + } + +} diff --git a/src/org/warp/picalculator/screens/SolveEquationScreen.java b/src/org/warp/picalculator/screens/SolveEquationScreen.java new file mode 100644 index 00000000..35caa3f7 --- /dev/null +++ b/src/org/warp/picalculator/screens/SolveEquationScreen.java @@ -0,0 +1,74 @@ +package org.warp.picalculator.screens; + +import static org.warp.engine.Display.Render.glDrawStringCenter; + +import org.warp.device.Keyboard.Key; +import org.warp.engine.Display; +import org.warp.engine.Screen; +import org.warp.picalculator.Calculator; +import org.warp.picalculator.Errore; +import org.warp.picalculator.Main; + +public class SolveEquationScreen extends Screen { + + @SuppressWarnings("unused") + private EquationScreen es; + + public SolveEquationScreen(EquationScreen es) { + super(); + canBeInHistory = false; + + this.es = es; + } + + @Override + public void created() throws InterruptedException { + } + + @Override + public void init() throws InterruptedException {} + + @Override + public void render() { + glDrawStringCenter(Display.getWidth()/2, 29, "ciaoooooooooooooooooooooooooooooo"); + } + + @Override + public void beforeRender(float dt) { + + } + + @Override + public boolean mustBeRefreshed() { + return true; + } + + @Override + public boolean keyPressed(Key k) { + switch (k) { + case LETTER_X: + Main.d.goBack(); + try { + Calculator.solve('X'); + } catch (Errore e) { + Screen scr = Main.d.getScreen(); + if (scr instanceof EquationScreen) { + EquationScreen escr = (EquationScreen) scr; + escr.errorLevel = 1; + escr.err2 = e; + } else { + e.printStackTrace(); + } + } + return true; + default: + return false; + } + } + + @Override + public boolean keyReleased(Key k) { + return false; + } + +} diff --git a/src/org/warpgate/pi/calculator/BMPFile.java b/src/org/warpgate/pi/calculator/BMPFile.java deleted file mode 100644 index fb24a7e7..00000000 --- a/src/org/warpgate/pi/calculator/BMPFile.java +++ /dev/null @@ -1,212 +0,0 @@ -package org.warpgate.pi.calculator; -import java.awt.*; -import java.io.*; -import java.awt.image.*; -public class BMPFile extends Component { - //--- Private constants - private final static int BITMAPFILEHEADER_SIZE = 14; - private final static int BITMAPINFOHEADER_SIZE = 40; - //--- Private variable declaration - //--- Bitmap file header - private byte bitmapFileHeader [] = new byte [14]; - private byte bfType [] = {'B', 'M'}; - private int bfSize = 0; - private int bfReserved1 = 0; - private int bfReserved2 = 0; - private int bfOffBits = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; - //--- Bitmap info header - private byte bitmapInfoHeader [] = new byte [40]; - private int biSize = BITMAPINFOHEADER_SIZE; - private int biWidth = 0; - private int biHeight = 0; - private int biPlanes = 1; - private int biBitCount = 24; - private int biCompression = 0; - private int biSizeImage = 0x030000; - private int biXPelsPerMeter = 0x0; - private int biYPelsPerMeter = 0x0; - private int biClrUsed = 0; - private int biClrImportant = 0; - //--- Bitmap raw data - private int bitmap []; - //--- File section - private FileOutputStream fo; - //--- Default constructor - public BMPFile() { - } - public void saveBitmap (String parFilename, Image parImage, int -parWidth, int parHeight) { - try { - fo = new FileOutputStream (parFilename); - save (parImage, parWidth, parHeight); - fo.close (); - } - catch (Exception saveEx) { - saveEx.printStackTrace (); - } - } - /* - * The saveMethod is the main method of the process. This method - * will call the convertImage method to convert the memory image to - * a byte array; method writeBitmapFileHeader creates and writes - * the bitmap file header; writeBitmapInfoHeader creates the - * information header; and writeBitmap writes the image. - * - */ - private void save (Image parImage, int parWidth, int parHeight) { - try { - convertImage (parImage, parWidth, parHeight); - writeBitmapFileHeader (); - writeBitmapInfoHeader (); - writeBitmap (); - } - catch (Exception saveEx) { - saveEx.printStackTrace (); - } - } - /* - * convertImage converts the memory image to the bitmap format (BRG). - * It also computes some information for the bitmap info header. - * - */ - private boolean convertImage (Image parImage, int parWidth, int parHeight) { - int pad; - bitmap = new int [parWidth * parHeight]; - PixelGrabber pg = new PixelGrabber (parImage, 0, 0, parWidth, parHeight, - bitmap, 0, parWidth); - try { - pg.grabPixels (); - } - catch (InterruptedException e) { - e.printStackTrace (); - return (false); - } - pad = (4 - ((parWidth * 3) % 4)) * parHeight; - biSizeImage = ((parWidth * parHeight) * 3) + pad; - bfSize = biSizeImage + BITMAPFILEHEADER_SIZE + -BITMAPINFOHEADER_SIZE; - biWidth = parWidth; - biHeight = parHeight; - return (true); - } - /* - * writeBitmap converts the image returned from the pixel grabber to - * the format required. Remember: scan lines are inverted in - * a bitmap file! - * - * Each scan line must be padded to an even 4-byte boundary. - */ - private void writeBitmap () { - int size; - int value; - int j; - int i; - int rowCount; - int rowIndex; - int lastRowIndex; - int pad; - int padCount; - byte rgb [] = new byte [3]; - size = (biWidth * biHeight) - 1; - pad = 4 - ((biWidth * 3) % 4); - if (pad == 4) // <==== Bug correction - pad = 0; // <==== Bug correction - rowCount = 1; - padCount = 0; - rowIndex = size - biWidth; - lastRowIndex = rowIndex; - try { - for (j = 0; j < size; j++) { - value = bitmap [rowIndex]; - rgb [0] = (byte) (value & 0xFF); - rgb [1] = (byte) ((value >> 8) & 0xFF); - rgb [2] = (byte) ((value >> 16) & 0xFF); - fo.write (rgb); - if (rowCount == biWidth) { - padCount += pad; - for (i = 1; i <= pad; i++) { - fo.write (0x00); - } - rowCount = 1; - rowIndex = lastRowIndex - biWidth; - lastRowIndex = rowIndex; - } - else - rowCount++; - rowIndex++; - } - //--- Update the size of the file - bfSize += padCount - pad; - biSizeImage += padCount - pad; - } - catch (Exception wb) { - wb.printStackTrace (); - } - } - /* - * writeBitmapFileHeader writes the bitmap file header to the file. - * - */ - private void writeBitmapFileHeader () { - try { - fo.write (bfType); - fo.write (intToDWord (bfSize)); - fo.write (intToWord (bfReserved1)); - fo.write (intToWord (bfReserved2)); - fo.write (intToDWord (bfOffBits)); - } - catch (Exception wbfh) { - wbfh.printStackTrace (); - } - } - /* - * - * writeBitmapInfoHeader writes the bitmap information header - * to the file. - * - */ - private void writeBitmapInfoHeader () { - try { - fo.write (intToDWord (biSize)); - fo.write (intToDWord (biWidth)); - fo.write (intToDWord (biHeight)); - fo.write (intToWord (biPlanes)); - fo.write (intToWord (biBitCount)); - fo.write (intToDWord (biCompression)); - fo.write (intToDWord (biSizeImage)); - fo.write (intToDWord (biXPelsPerMeter)); - fo.write (intToDWord (biYPelsPerMeter)); - fo.write (intToDWord (biClrUsed)); - fo.write (intToDWord (biClrImportant)); - } - catch (Exception wbih) { - wbih.printStackTrace (); - } - } - /* - * - * intToWord converts an int to a word, where the return - * value is stored in a 2-byte array. - * - */ - private byte [] intToWord (int parValue) { - byte retValue [] = new byte [2]; - retValue [0] = (byte) (parValue & 0x00FF); - retValue [1] = (byte) ((parValue >> 8) & 0x00FF); - return (retValue); - } - /* - * - * intToDWord converts an int to a double word, where the return - * value is stored in a 4-byte array. - * - */ - private byte [] intToDWord (int parValue) { - byte retValue [] = new byte [4]; - retValue [0] = (byte) (parValue & 0x00FF); - retValue [1] = (byte) ((parValue >> 8) & 0x000000FF); - retValue [2] = (byte) ((parValue >> 16) & 0x000000FF); - retValue [3] = (byte) ((parValue >> 24) & 0x000000FF); - return (retValue); - } -} \ No newline at end of file diff --git a/src/org/warpgate/pi/calculator/Calculator.java b/src/org/warpgate/pi/calculator/Calculator.java deleted file mode 100644 index 34abfd29..00000000 --- a/src/org/warpgate/pi/calculator/Calculator.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.warpgate.pi.calculator; - -import org.nevec.rjm.NumeroAvanzatoVec; -import org.warp.engine.lwjgl.Screen; - -public class Calculator { - - public static String angleMode = "deg"; - public static Screen[] sessions = new Screen[5]; - public static int currentSession = 0; - public static boolean haxMode = true; - - public static Termine calcolarisultato(String string) throws Errore { - System.out.println("INPUT: " + string); - Parentesi espressione = new Parentesi(string); - return espressione.calcola(); - } - - public static RisultatoEquazione calcolaequazione(String string) throws Errore { - if (string.split("=").length == 0) { - return new RisultatoEquazione(new Termine("0"), true); - } - if (string.split("=").length <= 2) { - if (string.split("=").length == 1) { - string = string + "=0"; - } - Termine res1 = calcolarisultato(string.split("=")[0]); - Termine res2 = calcolarisultato(string.split("=")[1]); - Termine res = res1.add(res2.multiply(new Termine("-1"))); - if (res.calcola().getTerm().toString().equals("0")) { - return new RisultatoEquazione(res.calcola(), true); - } - return new RisultatoEquazione(res.calcola(), false); - } - return new RisultatoEquazione(null, false); - } - -} diff --git a/src/org/warpgate/pi/calculator/Divisione.java b/src/org/warpgate/pi/calculator/Divisione.java deleted file mode 100644 index 5afd3d2f..00000000 --- a/src/org/warpgate/pi/calculator/Divisione.java +++ /dev/null @@ -1,168 +0,0 @@ -package org.warpgate.pi.calculator; - -import java.awt.Color; -import java.awt.Graphics; - -import org.nevec.rjm.NumeroAvanzatoVec; -import org.nevec.rjm.Rational; - -public class Divisione extends FunzioneDueValori { - - public Divisione(Funzione value1, Funzione value2) { - super(value1, value2); - } - - @Override - public String simbolo() { - return Simboli.DIVISION; - } - - @Override - public Termine calcola() throws Errore { - if (variable2 == null || variable1 == null) { - return new Termine("0"); - } - if (variable2.calcola().getTerm().compareTo(NumeroAvanzatoVec.ZERO) == 0) { - throw new Errore(Errori.DIVISION_BY_ZERO); - } - return variable1.calcola().divide(variable2.calcola()); - } - - public boolean hasMinus() { - String numerator = variable1.toString(); - if (numerator.startsWith("-")) { - return true; - } - return false; - } - - public void draw(int x, int y, boolean small, boolean drawMinus) { - boolean beforedrawminus = this.drawMinus; - this.drawMinus = drawMinus; - draw(x, y, small); - this.drawMinus = beforedrawminus; - } - - private boolean drawMinus = true; - - @Override - public void draw(int x, int y, boolean small) { - Object var1 = variable1; - Object var2 = variable2; - small = true; - boolean minus = false; - int minusw = 0; - int minush = 0; - String numerator = ((Funzione)var1).toString(); - if (numerator.startsWith("-") && ((Funzione)var1) instanceof Termine && ((Termine)var1).term.isBigInteger(true)) { - minus = true; - numerator = numerator.substring(1); - } - int w1 = 0; - int h1 = 0; - if (minus) { - w1 = Utils.getPlainTextWidth(numerator); - h1 = Utils.getFontHeight(small); - } else { - w1 = ((Funzione)var1).getWidth(); - h1 = ((Funzione)var1).getHeight(small); - } - int w2 = ((Funzione)var2).getWidth(); - int maxw; - if (w1 > w2) { - maxw = 1+w1+1; - } else { - maxw = 1+w2+1; - } - if (minus && drawMinus) { - minusw = Utils.getPlainTextWidth("-")+1; - minush = Utils.getFontHeight(small); - Utils.writeLetter(g, "-", x, y+h1+1+1-(minush/2), small); - Utils.writeLetter(g, numerator, (int)(x+minusw+1+((double)(maxw-w1))/2d), y, small); - } else { - ((Funzione)var1).draw((int)(x+minusw+1+((double)(maxw-w1))/2d), y, g, true); - } - ((Funzione)var2).draw((int)(x+minusw+1+((double)(maxw-w2))/2d), y+h1+1+1+1, g, true); - g.setColor(Color.BLACK); - g.fillRect(x+minusw+1, y+h1+1, maxw, 1); - } - - public int getHeight(boolean small, boolean drawMinus) { - boolean beforedrawminus = this.drawMinus; - this.drawMinus = drawMinus; - int h = getHeight(small); - this.drawMinus = beforedrawminus; - return h; - } - - @Override - public int getHeight(boolean small) { - boolean minus = false; - small = true; - String numerator = variable1.toString(); - if (numerator.startsWith("-") && variable1 instanceof Termine && ((Termine) variable1).term.isBigInteger(true)) { - minus = true; - numerator = numerator.substring(1); - } - int w1 = 0; - int h1 = 0; - if (minus) { - w1 = Utils.getPlainTextWidth(numerator); - h1 = Utils.getFontHeight(small); - } else { - w1 = variable1.getWidth(); - h1 = variable1.getHeight(small); - } - int w2 = variable2.getWidth(); - int h2 = variable2.getHeight(small); - int maxw; - if (w1 > w2) { - maxw = 1+w1+1; - } else { - maxw = 1+w2+1; - } - return h1+3+h2; - } - - @Override - public int getLine(boolean small) { - return variable1.getHeight(true)+1; - } - - public int getWidth(boolean drawMinus) { - boolean beforedrawminus = this.drawMinus; - this.drawMinus = drawMinus; - int w = getWidth(); - this.drawMinus = beforedrawminus; - return w; - } - - @Override - public int getWidth() { - boolean minus = false; - String numerator = variable1.toString(); - if (numerator.startsWith("-") && variable1 instanceof Termine && ((Termine) variable1).term.isBigInteger(true)) { - minus = true; - numerator = numerator.substring(1); - } - int w1 = 0; - int h1 = 0; - if (minus) { - w1 = Utils.getPlainTextWidth(numerator); - } else { - w1 = variable1.getWidth(); - } - int w2 = variable2.getWidth(); - int maxw; - if (w1 > w2) { - maxw = 1+w1+1; - } else { - maxw = 1+w2+1; - } - if (minus && drawMinus) { - return Utils.getPlainTextWidth("-")+1+maxw+1; - } else { - return maxw+2; - } - } -} \ No newline at end of file diff --git a/src/org/warpgate/pi/calculator/Equazione.java b/src/org/warpgate/pi/calculator/Equazione.java deleted file mode 100644 index c82b4253..00000000 --- a/src/org/warpgate/pi/calculator/Equazione.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.warpgate.pi.calculator; - -import java.util.Vector; - -import com.rits.cloning.Cloner; - -public class Equazione extends FunzioneDueValori { - - public Equazione(Funzione value1, Funzione value2) { - super(value1, value2); - } - - @Override - public String simbolo() { - return Simboli.EQUATION; - } - - @Override - public Termine calcola() throws Errore { - return new Sottrazione(getVariable1().calcola(),getVariable2().calcola()).calcola(); - } - - public Vector risolviPassaggio(char charIncognita) { - Vector result = new Vector(); - result.add(this.clone()); - for (Tecnica t : Tecnica.tecniche) { - Vector newResults = new Vector(); - final int sz = result.size(); - for (int n = 0; n < sz; n++) { - Vector singleResult = t.risolvi(result.get(n)); - final int sz2 = singleResult.size(); - for (int n2 = 0; n2 < sz2; n2++) { - - } - } - } - //TODO: finire - return result; - } - - public Equazione clone() { - Cloner cloner = new Cloner(); - return cloner.deepClone(this); - } - -} \ No newline at end of file diff --git a/src/org/warpgate/pi/calculator/Errori.java b/src/org/warpgate/pi/calculator/Errori.java deleted file mode 100644 index b9697ebf..00000000 --- a/src/org/warpgate/pi/calculator/Errori.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.warpgate.pi.calculator; - -public enum Errori { - ERROR, - DIVISION_BY_ZERO, - UNBALANCED_BRACKETS, - NOT_IMPLEMENTED, - NEGATIVE_PARAMETER, - NUMBER_TOO_LARGE, - NUMBER_TOO_SMALL, - CONVERSION_ERROR, - SYNTAX_ERROR -} diff --git a/src/org/warpgate/pi/calculator/Funzione.java b/src/org/warpgate/pi/calculator/Funzione.java deleted file mode 100644 index 519d8b51..00000000 --- a/src/org/warpgate/pi/calculator/Funzione.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.warpgate.pi.calculator; - -import com.rits.cloning.Cloner; - -public interface Funzione { - public String simbolo(); - public Termine calcola() throws Errore; - public void draw(int x, int y, boolean small); - public int getWidth(); - public int getHeight(boolean small); - public int getLine(boolean small); -} diff --git a/src/org/warpgate/pi/calculator/FunzioneAnteriore.java b/src/org/warpgate/pi/calculator/FunzioneAnteriore.java deleted file mode 100644 index de9ea80b..00000000 --- a/src/org/warpgate/pi/calculator/FunzioneAnteriore.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.warpgate.pi.calculator; - -import org.nevec.rjm.NumeroAvanzatoVec; - -import com.rits.cloning.Cloner; - -public abstract class FunzioneAnteriore implements Funzione { - public FunzioneAnteriore(Funzione value) { - setVariable(value); - } - protected Funzione variable = new Termine(NumeroAvanzatoVec.ZERO); - public Funzione getVariable() { - return variable; - } - public void setVariable(Funzione value) { - variable = value; - } - @Override - public abstract String simbolo(); - @Override - public abstract Termine calcola() throws Errore; - - - @Override - public void draw(int x, int y, Display g, boolean small) { - int w1 = getVariable().getWidth(); - float h1 = getVariable().getHeight(small); - int wsegno = Utils.getPlainTextWidth(simbolo()); - float hsegno = Utils.getFontHeight(small); - float maxh = getHeight(small); - - Utils.writeLetter(g, simbolo(), x, (int)Math.floor(y+(maxh-hsegno)/2), small); - getVariable().draw(x+wsegno+1, (int)Math.floor(y+(maxh-h1)/2), g, small); - } - - @Override - public int getWidth() { - return Utils.getPlainTextWidth(simbolo())+1+getVariable().getWidth(); - } - - @Override - public int getHeight(boolean small) { - return variable.getHeight(small); - } - - @Override - public int getLine(boolean small) { - return variable.getLine(small); - } - - @Override - public String toString() { - try { - return calcola().toString(); - } catch (Errore e) { - return e.id.toString(); - } - } - - @Override - public FunzioneAnteriore clone() { - Cloner cloner = new Cloner(); - return cloner.deepClone(this); - } -} diff --git a/src/org/warpgate/pi/calculator/FunzioneDueValori.java b/src/org/warpgate/pi/calculator/FunzioneDueValori.java deleted file mode 100644 index f4355e3e..00000000 --- a/src/org/warpgate/pi/calculator/FunzioneDueValori.java +++ /dev/null @@ -1,92 +0,0 @@ -package org.warpgate.pi.calculator; - -import org.nevec.rjm.Rational; -import org.warp.engine.Display; - -import com.rits.cloning.Cloner; - -public abstract class FunzioneDueValori implements Funzione { - public FunzioneDueValori(Funzione value1, Funzione value2) { - setVariable1(value1); - setVariable2(value2); - } - protected Funzione variable1 = new Termine(Rational.ZERO); - public Funzione getVariable1() { - return variable1; - } - public void setVariable1(Funzione value) { - variable1 = value; - } - protected Funzione variable2 = new Termine(Rational.ZERO); - public Funzione getVariable2() { - return variable2; - } - public void setVariable2(Funzione value) { - variable2 = value; - } - @Override - public abstract String simbolo(); - @Override - public abstract Termine calcola() throws Errore; - - - @Override - public void draw(int x, int y, Display g, boolean small) { - int ln = getLine(small); - int dx = 0; - variable1.draw(dx+x, ln-variable1.getLine(small)+y, g, small); - dx+=variable1.getWidth()+1; - if (drawSignum()) { - Utils.writeLetter(g, simbolo(), dx+x, ln-Utils.getFontHeight(small)/2+y, small); - dx+=1; - dx+=Utils.getPlainTextWidth(simbolo()); - } - variable2.draw(dx+x, ln-variable2.getLine(small)+y, g, small); - } - - @Override - public int getWidth() { - return variable1.getWidth()+1+ (drawSignum() ? Utils.getPlainTextWidth(simbolo())+1 : 0) +variable2.getWidth(); - } - - @Override - public int getHeight(boolean small) { - Funzione tmin = variable1; - Funzione tmax = variable1; - if (tmin == null || variable2.getLine(small) >= tmin.getLine(small)) { - tmin = variable2; - } - if (tmax == null || variable2.getHeight(small) - variable2.getLine(small) >= tmax.getHeight(small) - tmax.getLine(small)) { - tmax = variable2; - } - return tmin.getLine(small) + tmax.getHeight(small) - tmax.getLine(small); - } - - @Override - public int getLine(boolean small) { - Funzione tl = variable1; - if (tl == null || variable2.getLine(small) >= tl.getLine(small)) { - tl = variable2; - } - return tl.getLine(small); - } - - @Override - public String toString() { - try { - return calcola().toString(); - } catch (Errore e) { - return e.id.toString(); - } - } - - @Override - public FunzioneDueValori clone() { - Cloner cloner = new Cloner(); - return cloner.deepClone(this); - } - - public boolean drawSignum() { - return true; - } -} diff --git a/src/org/warpgate/pi/calculator/Keyboard.java b/src/org/warpgate/pi/calculator/Keyboard.java deleted file mode 100644 index f9f9da65..00000000 --- a/src/org/warpgate/pi/calculator/Keyboard.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.warpgate.pi.calculator; - -public class Keyboard { - public static boolean alpha = false; - public static boolean shift = false; -} diff --git a/src/org/warpgate/pi/calculator/Main.java b/src/org/warpgate/pi/calculator/Main.java deleted file mode 100644 index bd97da2b..00000000 --- a/src/org/warpgate/pi/calculator/Main.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.warpgate.pi.calculator; - -import java.awt.Color; -import java.io.PrintWriter; -import java.io.StringWriter; - -import org.warp.engine.lwjgl.Display; -import org.warpgate.pi.calculator.screens.EmptyScreen; -import org.warpgate.pi.calculator.screens.EquationScreen; - -import javafx.animation.KeyFrame; -import javafx.animation.Timeline; -import javafx.event.Event; -import javafx.event.EventHandler; -import javafx.util.Duration; - -public class Main { - public static final int[] screenSize = new int[]{480, 320}; - public static final int screenScale = 1; - public static Display d; - public static Main instance; - - public Main() throws InterruptedException { - instance = this; - d = new Display(new EmptyScreen(), screenSize[0], screenSize[1]); - d.setBackground(0.796875f, 0.90234375f, 0.828125f); - Thread t = new Thread("Graphic thread"){@Override public void run() {d.run("");}}; - t.start(); - } - - public static void main(String[] args) throws InterruptedException { - new Main(); - } -} diff --git a/src/org/warpgate/pi/calculator/Parentesi.java b/src/org/warpgate/pi/calculator/Parentesi.java deleted file mode 100644 index 7d5f89fa..00000000 --- a/src/org/warpgate/pi/calculator/Parentesi.java +++ /dev/null @@ -1,638 +0,0 @@ -package org.warpgate.pi.calculator; - -import static org.warpgate.pi.calculator.Utils.ArrayToRegex; -import static org.warpgate.pi.calculator.Utils.concat; - -import java.awt.Color; -import java.util.ArrayList; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.nevec.rjm.NumeroAvanzato; -import org.nevec.rjm.NumeroAvanzatoVec; - -public class Parentesi extends FunzioneMultipla { - - public Parentesi() { - super(); - } - - public Parentesi(Funzione[] values) { - super(values); - } - - private boolean parentesiIniziale = false; - - public Parentesi(String string) throws Errore { - this(string, "", true); - } - - public Parentesi(String string, String debugSpaces, boolean parentesiIniziale) throws Errore { - super(); - this.parentesiIniziale = parentesiIniziale; - try{ - //Se l'espressione è già un numero: - setVariables(new Funzione[]{new Termine(string)}); - Utils.debug.println(debugSpaces+"•(Value:"+string+")"); - } catch (NumberFormatException ex) { - String processExpression = string; - - Utils.debug.println(debugSpaces+"•Analyzing expression:"+processExpression); - debugSpaces+= " "; - - //Se l'espressione non è già un numero: - - //Controlla se ci sono più di un uguale - int equationsFound = 0; - int systemsFound = 0; - for (char c : processExpression.toCharArray()) { - if ((""+c).equals(Simboli.EQUATION)) { - equationsFound+=1; - } - if ((""+c).equals(Simboli.SYSTEM)) { - equationsFound+=1; - } - } - if (equationsFound == 1 && systemsFound == 0) { - processExpression = Simboli.SYSTEM+processExpression; - systemsFound+=1; - } - if (equationsFound != systemsFound) { - throw new Errore(Errori.SYNTAX_ERROR); - } - - //Correggi i segni ++ e -- in eccesso - Pattern pattern = Pattern.compile("\\+\\++?|\\-\\-+?"); - Matcher matcher = pattern.matcher(processExpression); - boolean cambiati = false; - while (matcher.find()) { - cambiati = true; - String correzione = "+"; - processExpression = processExpression.substring(0, matcher.start(0)+1)+correzione+processExpression.substring(matcher.start(0)+matcher.group(0).length(), processExpression.length()); - matcher = pattern.matcher(processExpression); - } - - //Correggi i segni +- e -+ in eccesso - pattern = Pattern.compile("\\+\\-|\\-\\+"); - matcher = pattern.matcher(processExpression); - while (matcher.find()) { - cambiati = true; - String correzione = "-"; - processExpression = processExpression.substring(0, matcher.start(0))+correzione+processExpression.substring(matcher.start(0)+matcher.group(0).length(), processExpression.length()); - matcher = pattern.matcher(processExpression); - } - - //Rimuovi i segni appena dopo le parentesi - if (processExpression.contains("(+")) { - cambiati = true; - processExpression = processExpression.replace("(+", "("); - } - - //Cambia i segni appena prima le parentesi - if (processExpression.contains("-(")) { - cambiati = true; - processExpression = processExpression.replace("-(", "-1*("); - } - //Rimuovi i segni appena dopo l'inizio - if (processExpression.startsWith("+")) { - cambiati = true; - processExpression = processExpression.substring(1, processExpression.length()); - } - - //Rimuovi i + in eccesso - pattern = Pattern.compile("["+ - ArrayToRegex(Utils.add(concat(Simboli.segni(true), Simboli.funzioni()), "(")) - +"]\\+[^"+ - ArrayToRegex(concat(concat(Simboli.segni(true), Simboli.funzioni()), new String[]{"(", ")"})) - +"]+?["+ - ArrayToRegex(concat(Simboli.segni(true), Simboli.funzioni())) - +"]|["+ - ArrayToRegex(concat(Simboli.segni(true), Simboli.funzioni())) - +"]+?\\+[^"+ - ArrayToRegex(concat(concat(Simboli.segni(true), Simboli.funzioni()), new String[]{"(", ")"})) - +"]"); - matcher = pattern.matcher(processExpression); - cambiati = false; - while (matcher.find()) { - cambiati = true; - String correzione = matcher.group(0).replaceFirst(Matcher.quoteReplacement("+"), ""); - processExpression = processExpression.substring(0, matcher.start(0)+1)+correzione+processExpression.substring(matcher.start(0)+matcher.group(0).length(), processExpression.length()); - matcher = pattern.matcher(processExpression); - } - - //Correggi i segni - in +- - pattern = Pattern.compile("[^"+Utils.ArrayToRegex(concat(concat(Simboli.funzioni(),new String[]{Simboli.PARENTHESIS_OPEN}), Simboli.segni(true)))+"]-"); - matcher = pattern.matcher(processExpression); - while (matcher.find()) { - cambiati = true; - String correzione = "+-"; - processExpression = processExpression.substring(0, matcher.start(0)+1)+correzione+processExpression.substring(matcher.start(0)+matcher.group(0).length(), processExpression.length()); - matcher = pattern.matcher(processExpression); - } - - if (cambiati) { - Utils.debug.println(debugSpaces+"•Resolved signs:"+processExpression); - } - - //Aggiungi i segni * accanto alle parentesi - pattern = Pattern.compile("\\([^\\(]+?\\)"); - matcher = pattern.matcher(processExpression); - cambiati = false; - while (matcher.find()) { - cambiati = true; - //sistema i segni * impliciti prima e dopo l'espressione. - String beforeexp = processExpression.substring(0, matcher.start(0)); - String newexp = matcher.group(0).substring(1, matcher.group(0).length()-1); - String afterexp = processExpression.substring(matcher.start(0)+matcher.group(0).length(), processExpression.length()); - if (Pattern.compile("[^\\-"+Utils.ArrayToRegex(Utils.add(concat(Simboli.funzioni(), concat(Simboli.segni(true), Simboli.sintassiGenerale())), "("))+"]$").matcher(beforeexp).find()) { - //Se la stringa precedente finisce con un numero - beforeexp += Simboli.MULTIPLICATION; - } - if (Pattern.compile("^[^\\-"+Utils.ArrayToRegex(Utils.add(concat(Simboli.funzioni(), concat(Simboli.segni(true), Simboli.sintassiGenerale())), ")"))+"]").matcher(afterexp).find()) { - //Se la stringa successiva inizia con un numero - afterexp = Simboli.MULTIPLICATION+afterexp; - } - processExpression = beforeexp+"â‘´"+newexp+"⑵"+afterexp; - matcher = pattern.matcher(processExpression); - } - - processExpression = processExpression.replace("â‘´", "(").replace("⑵", ")"); - - if (cambiati) { - Utils.debug.println(debugSpaces+"•Added implicit multiplications:"+processExpression); - } - - Utils.debug.println(debugSpaces+"•Subdivision in classes:"); - - debugSpaces += " "; - - - //Suddividi tutto - Parentesi parentesiNonSuddivisaCorrettamente = new Parentesi(); - parentesiNonSuddivisaCorrettamente.setVariables(new Funzione[]{}); - String tmp = ""; - Incognite tmpI = new Incognite(); - final String[] funzioni = concat(concat(concat(concat(Simboli.funzioni(), Simboli.parentesi()), Simboli.segni(true)), Simboli.incognite()), Simboli.sintassiGenerale()); - for (int i = 0; i < processExpression.length(); i++) { - //Per ogni carattere cerca se è un numero o una funzione: - String charI = processExpression.charAt(i)+""; - if (Utils.isInArray(charI, funzioni)) { - - //Cerca il tipo di funzione tra le esistenti - Funzione f = null; - switch (charI) { - case Simboli.SUM: - f = new Somma(null, null); - break; - case Simboli.MULTIPLICATION: - f = new Moltiplicazione(null, null); - break; - case Simboli.DIVISION: - f = new Divisione(null, null); - break; - case Simboli.NTH_ROOT: - f = new Radice(null, null); - break; - case Simboli.SQUARE_ROOT: - f = new RadiceQuadrata(null); - break; - case Simboli.POTENZA: - f = new Potenza(null, null); - break; - case Simboli.EQUATION: - f = new Equazione(null, null); - break; - case Simboli.SYSTEM: - f = new ParteSistema(null); - break; - case Simboli.PARENTHESIS_OPEN: - //cerca l'ultima parentesi chiusa - int startIndex = i; - int endIndex = -1; - int jumps = -1; - for (int i2 = startIndex; i2 < processExpression.length(); i2++) { - if ((processExpression.charAt(i2)+"").equals(Simboli.PARENTHESIS_CLOSE)) { - if (jumps == 0) { - endIndex = i2; - break; - } else if (jumps > 0) { - jumps -= 1; - } else if (jumps < 0) { - throw new Errore(Errori.UNBALANCED_BRACKETS); - } - } else if ((processExpression.charAt(i2)+"").equals(Simboli.PARENTHESIS_OPEN)) { - jumps +=1; - } - } - if (endIndex == -1 || endIndex < startIndex) { - throw new Errore(Errori.UNBALANCED_BRACKETS); - } - startIndex += 1; - i = startIndex; - - String tmpExpr = ""; - while (i < endIndex) { - tmpExpr += processExpression.charAt(i); - i++; - } - f = new Parentesi(tmpExpr, debugSpaces, false); - break; - default: - if (Utils.isInArray(charI, Simboli.incognite())) { - //Fallback - NumeroAvanzato na = NumeroAvanzato.ONE; - Incognite iy = na.getIncognitey(); - iy.incognite.add(new Incognita(charI.charAt(0), 1, 1)); - na = na.setIncognitey(iy); - f = new Termine(na); - } else { - throw new java.lang.RuntimeException("Il carattere "+charI+" non è tra le funzioni designate!\nAggiungerlo ad esse o rimuovere il carattere dall'espressione!"); - } - } - if (f instanceof Parentesi) { - tmp = ""; - } else if (f instanceof Termine) { - if (parentesiNonSuddivisaCorrettamente.getVariablesLength() == 0) { - if (tmp.length() > 0) { - parentesiNonSuddivisaCorrettamente.addVariableToEnd(new Termine(tmp)); - Utils.debug.println(debugSpaces+"•Added value to expression:"+tmp); - parentesiNonSuddivisaCorrettamente.addVariableToEnd(new Moltiplicazione(null, null)); - Utils.debug.println(debugSpaces+"•Added variable to expression:"+new Moltiplicazione(null, null).simbolo()); - } - } else { - if (tmp.length() > 0) { - if (parentesiNonSuddivisaCorrettamente.getVariable(parentesiNonSuddivisaCorrettamente.getVariablesLength()-1) instanceof Termine) { - parentesiNonSuddivisaCorrettamente.addVariableToEnd(new Moltiplicazione(null, null)); - Utils.debug.println(debugSpaces+"•Added variable to expression:"+new Moltiplicazione(null, null).simbolo()); - } - if (tmp.equals("-")) { - tmp = "-1"; - } - parentesiNonSuddivisaCorrettamente.addVariableToEnd(new Termine(tmp)); - Utils.debug.println(debugSpaces+"•Added value to expression:"+tmp); - } - if (tmp.length() > 0 || parentesiNonSuddivisaCorrettamente.getVariable(parentesiNonSuddivisaCorrettamente.getVariablesLength()-1) instanceof Termine) { - parentesiNonSuddivisaCorrettamente.addVariableToEnd(new Moltiplicazione(null, null)); - Utils.debug.println(debugSpaces+"•Added variable to expression:"+new Moltiplicazione(null, null).simbolo()); - } - } - } else { - if (tmp.length() != 0) { - parentesiNonSuddivisaCorrettamente.addVariableToEnd(new Termine(tmp)); - Utils.debug.println(debugSpaces+"•Added variable to expression:"+tmp); - } - } - parentesiNonSuddivisaCorrettamente.addVariableToEnd(f); - Utils.debug.println(debugSpaces+"•Added variable to expression:"+f.simbolo()); - tmp = ""; - } else { - try{ - if (charI.equals("-") == false && charI.equals(".") == false) { - Double.parseDouble(tmp + charI); - } - //Se il carattere è un numero intero, un segno negativo, o un punto - tmp += charI; - } catch (NumberFormatException exc) { - throw new java.lang.RuntimeException("Il carattere "+tmp+charI+" non è nè un numero nè un espressione presente nella lista completa!\nAggiungerlo ad essa o rimuovere il carattere dall'espressione!"); - } - } - } - if (tmp.length() > 0) { - Utils.debug.println(debugSpaces+"•Added variable to expression:"+tmp); - parentesiNonSuddivisaCorrettamente.addVariableToEnd(new Termine(tmp)); - tmp = ""; - } - - int dsl = debugSpaces.length();debugSpaces = "";for (int i = 0; i < dsl-2; i++) {debugSpaces += " ";} - Utils.debug.println(debugSpaces+"•Finished the subdivision in classes."); - //Fine suddivisione di insieme - - Utils.debug.println(debugSpaces+"•Removing useless parentheses"); - for (int i = 0; i < parentesiNonSuddivisaCorrettamente.variables.length; i++) { - if (parentesiNonSuddivisaCorrettamente.variables[i] instanceof Parentesi) { - Parentesi par = (Parentesi) parentesiNonSuddivisaCorrettamente.variables[i]; - if (par.variables.length == 1) { - Funzione subFunz = par.variables[0]; - if (subFunz instanceof Parentesi || subFunz instanceof Termine) { - parentesiNonSuddivisaCorrettamente.variables[i] = subFunz; - Utils.debug.println(debugSpaces+" •Useless parentheses removed"); - } - } - } - } - - //Inizia l'affinazione dell'espressione - Utils.debug.println(debugSpaces+"•Pushing classes..."); - - Funzione[] funzioniOLDArray = parentesiNonSuddivisaCorrettamente.getVariables(); - ArrayList funzioniOLD = new ArrayList(); - for (int i = 0; i < funzioniOLDArray.length; i++) { - if (funzioniOLDArray[i] != null) { - funzioniOLD.add(funzioniOLDArray[i]); - } - } - - Utils.debug.println(debugSpaces+" •Correcting classes:"); - - int before = 0; - String fase = "funzioniSN"; - int n = 0; - do { - before = funzioniOLD.size(); - int i = 0; - boolean change = false; - if (Utils.ciSonoFunzioniSNnonImpostate(funzioniOLD)) { - fase = "funzioniSN"; // PRIMA FASE - } else if (Utils.ciSonoFunzioniNSNnonImpostate(funzioniOLD)) { - fase = "funzioniNSN"; //SECONDA FASE - } else if (Utils.ciSonoMoltiplicazioniNonImpostate(funzioniOLD)) { - fase = "moltiplicazioni"; //TERZA FASE - } else if (Utils.ciSonoSommeNonImpostate(funzioniOLD)) { - fase = "somme"; //QUARTA FASE - } else if (Utils.ciSonoEquazioniNonImpostate(funzioniOLD)) { - fase = "equazioni"; //QUINTA FASE - } else if (Utils.ciSonoSistemiNonImpostati(funzioniOLD)) { - fase = "sistemi"; //SESTA FASE - } else { - System.out.println("errore?");//BOH - throw new Errore(Errori.SYNTAX_ERROR); - } - while (i < funzioniOLD.size() && change == false && funzioniOLD.size() > 1) { - Funzione funzioneTMP = funzioniOLD.get(i); - if (funzioneTMP instanceof FunzioneDueValori) { - if (fase != "funzioniSN") { - if ( - ( - fase == "somme" && (funzioneTMP instanceof Somma) == true - && - ( - ( - funzioneTMP instanceof FunzioneAnteriore - && - ((FunzioneAnteriore)funzioneTMP).variable == null - ) - || - ( - funzioneTMP instanceof FunzioneDueValori - && - ((FunzioneDueValori)funzioneTMP).variable1 == null - && - ((FunzioneDueValori)funzioneTMP).variable2 == null - ) - || - ( - !(funzioneTMP instanceof FunzioneAnteriore) - && - !(funzioneTMP instanceof FunzioneDueValori) - ) - ) - ) - || - ( - (fase == "moltiplicazioni" && (funzioneTMP instanceof Somma) == false) - && - ( - ( - funzioneTMP instanceof FunzioneAnteriore - && - ((FunzioneAnteriore)funzioneTMP).variable == null - ) - || - ( - funzioneTMP instanceof FunzioneDueValori - && - ((FunzioneDueValori)funzioneTMP).variable1 == null - && - ((FunzioneDueValori)funzioneTMP).variable2 == null - ) - || - ( - !(funzioneTMP instanceof FunzioneAnteriore) - && - !(funzioneTMP instanceof FunzioneDueValori) - ) - ) - ) - || - (fase == "equazioni" && funzioneTMP instanceof Equazione) - || - ( - fase == "funzioniNSN" - && - (funzioneTMP instanceof Somma) == false - && - (funzioneTMP instanceof Moltiplicazione) == false - && - (funzioneTMP instanceof Divisione) == false - && - ( - ( - funzioneTMP instanceof FunzioneAnteriore - && - ((FunzioneAnteriore)funzioneTMP).variable == null - ) - || - ( - funzioneTMP instanceof FunzioneDueValori - && - ((FunzioneDueValori)funzioneTMP).variable1 == null - && - ((FunzioneDueValori)funzioneTMP).variable2 == null - ) - || - ( - !(funzioneTMP instanceof FunzioneAnteriore) - && - !(funzioneTMP instanceof FunzioneDueValori) - ) - ) - ) - ) { - change = true; - - if (i+1 < funzioniOLD.size() && i-1 >= 0 ) { - ((FunzioneDueValori) funzioneTMP).setVariable1(funzioniOLD.get(i-1)); - ((FunzioneDueValori) funzioneTMP).setVariable2(funzioniOLD.get(i+1)); - funzioniOLD.set(i, funzioneTMP); - - //è importante togliere prima gli elementi in fondo e poi quelli davanti, perché gli indici scalano da destra a sinistra. - funzioniOLD.remove(i+1); - funzioniOLD.remove(i-1); - - Utils.debug.println(debugSpaces+" •Set variable to expression:"+funzioneTMP.simbolo()); - try {Utils.debug.println(debugSpaces+" "+"var1="+((FunzioneDueValori) funzioneTMP).getVariable1().calcola());} catch (NullPointerException ex2) {} - try {Utils.debug.println(debugSpaces+" "+"var2="+((FunzioneDueValori) funzioneTMP).getVariable2().calcola());} catch (NullPointerException ex2) {} - try {Utils.debug.println(debugSpaces+" "+"(result)="+((FunzioneDueValori) funzioneTMP).calcola());} catch (NullPointerException ex2) {} - - } else { - throw new java.lang.RuntimeException("Argomenti mancanti! Sistemare l'equazione!"); - } - } - } - } else if (funzioneTMP instanceof FunzioneAnteriore) { - if ( - ( - fase == "funzioniSN" - && - ((FunzioneAnteriore) funzioneTMP).variable == null - ) - || - ( - fase == "sistemi" - && - funzioneTMP instanceof ParteSistema - ) - ) { - change = true; - if (i+1 < funzioniOLD.size()) { - ((FunzioneAnteriore) funzioneTMP).setVariable(funzioniOLD.get(i+1)); - funzioniOLD.set(i, funzioneTMP); - - //è importante togliere prima gli elementi in fondo e poi quelli davanti, perché gli indici scalano da destra a sinistra. - funzioniOLD.remove(i+1); - - Utils.debug.println(debugSpaces+" •Set variable to expression:"+funzioneTMP.simbolo()); - Utils.debug.println(debugSpaces+" "+"var="+((FunzioneAnteriore) funzioneTMP).getVariable().calcola().toString()); - } else { - throw new java.lang.RuntimeException("Argomenti mancanti! Sistemare l'equazione!"); - } - } - } else if (funzioneTMP instanceof Termine || funzioneTMP instanceof Parentesi) { - if (n < 300) { - //Utils.debug.println(debugSpaces+" •Set variable to number:"+funzioneTMP.calcola()); - } - } else { - throw new java.lang.RuntimeException("Tipo sconosciuto"); - } - i++; - n++; - } - } while (((funzioniOLD.size() != before || fase != "sistemi") && funzioniOLD.size() > 1)); - setVariables(funzioniOLD); - - dsl = debugSpaces.length();debugSpaces = "";for (int i = 0; i < dsl-2; i++) {debugSpaces += " ";} - Utils.debug.println(debugSpaces+"•Finished correcting classes."); - - Termine result = calcola(); - Utils.debug.println(debugSpaces+"•Result:"+result); - } - } - - @Override - public String simbolo() { - return "Parentesi"; - } - - public ParteSistema calcolaSistema() throws Errore { - return new ParteSistema(calcolaEquazione()); - } - - public Equazione calcolaEquazione() throws Errore { - return new Equazione(calcola(), new Termine("0")); - } - - @Override - public Termine calcola() throws Errore { - if (variables.length == 0) { - return new Termine("0"); - } else if (variables.length == 1) { - return variables[0].calcola(); - } else { - Termine result = new Termine("0"); - for (Funzione f : variables) { - result = result.add(f.calcola()); - } - return result; - } - } - - @Override - public void draw(int x, int y, Display g, boolean small) { - if (parentesiIniziale && variables.length == 1) { - this.variables[0].draw(x, y, g, small); - } else { - float miny = y; - float maxy = y+getHeight(small); - int lw = Utils.getPlainTextWidth("(")+1; - int h = getHeight(small); - g.setColor(Color.BLACK); - if (small) { - g.draw45Line(x+3, y, x+2, y+1, true); - g.drawOrthoLine(x+2, y+1, x+2, y+h-2); - g.draw45Line(x+2, y+h-2, x+3, y+h-1, false); - } else { - g.draw45Line(x+3, y, x+1, y+2, true); - g.drawOrthoLine(x+1, y+2, x+1, y+h-3); - g.draw45Line(x+1, y+h-3, x+3, y+h-1, false); - } - x += lw; - for (Funzione f : variables) { - float fheight = f.getHeight(small); - float y2=miny+((maxy-miny)/2-fheight/2); - f.draw(x, (int) y2, g, small); - x+=f.getWidth(); - } - if (small) { - g.draw45Line(x+1, y, x+2, y+1, false); - g.drawOrthoLine(x+2, y+1, x+2, y+h-2); - g.draw45Line(x+2, y+h-2, x+1, y+h-1, true); - } else { - g.draw45Line(x+1, y, x+3, y+2, false); - g.drawOrthoLine(x+3, y+2, x+3, y+h-3); - g.draw45Line(x+3, y+h-3, x+1, y+h-1, true); - } - } - } - - @Override - public int getWidth() { - if (parentesiIniziale && variables.length == 1) { - return this.variables[0].getWidth(); - } else { - int w = 0; - int lw = Utils.getPlainTextWidth("(")+1; - for (Funzione f : variables) { - w+=f.getWidth(); - } - return w+lw*2; - } - } - - @Override - public int getHeight(boolean small) { - if (parentesiIniziale && variables.length == 1) { - return this.variables[0].getHeight(small); - } else { - Funzione tmin = null; - Funzione tmax = null; - for (Funzione t : variables) { - if (tmin == null || t.getLine(small) >= tmin.getLine(small)) { - tmin = t; - } - if (tmax == null || t.getHeight(small) - t.getLine(small) >= tmax.getHeight(small) - tmax.getLine(small)) { - tmax = t; - } - } - if (tmin == null) return Utils.getFontHeight(small); - return tmin.getLine(small) + tmax.getHeight(small) - tmax.getLine(small); - } - } - - - @Override - public int getLine(boolean small) { - if (parentesiIniziale && variables.length == 1) { - return this.variables[0].getLine(small); - } else { - Funzione tl = null; - for (Funzione t : variables) { - if (tl == null || t.getLine(small) >= tl.getLine(small)) { - tl = t; - } - } - if (tl == null) return Utils.getFontHeight(small)/2; - return tl.getLine(small); - } - } - -} diff --git a/src/org/warpgate/pi/calculator/ParteSistema.java b/src/org/warpgate/pi/calculator/ParteSistema.java deleted file mode 100644 index f49f568b..00000000 --- a/src/org/warpgate/pi/calculator/ParteSistema.java +++ /dev/null @@ -1,63 +0,0 @@ -package org.warpgate.pi.calculator; - -import java.awt.Color; - -import org.nevec.rjm.Rational; -import org.warp.engine.Display; - -public class ParteSistema extends FunzioneAnteriore { - - public ParteSistema(Funzione value) { - super(value); - } - - @Override - public String simbolo() { - return Simboli.SYSTEM; - } - - @Override - public Termine calcola() throws NumberFormatException, Errore { - //TODO implementare il calcolo dei sistemi - return variable.calcola(); - } - - - @Override - public void draw(int x, int y, Display g, boolean small) { - small = false; - final int h = this.getHeight(small)-1; - final int varLine = variable.getLine(small); - final int paddingTop = 3; - final int spazioSotto = (h-3-2)/2+paddingTop; - final int spazioSopra = h - spazioSotto; - variable.draw(x+5, y+paddingTop, g, small); - g.setColor(Color.black); - g.drawOrthoLine(x+2, y+0, x+3, y+0); - g.drawOrthoLine(x+1, y+1, x+1, y+spazioSotto/2); - g.drawOrthoLine(x+2, y+spazioSotto/2+1, x+2, y+spazioSotto-1); - g.drawOrthoLine(x+0, y+spazioSotto, x+1, y+spazioSotto); - g.drawOrthoLine(x+2, y+spazioSotto+1, x+2, y+spazioSotto+spazioSopra/2-1); - g.drawOrthoLine(x+1, y+spazioSotto+spazioSopra/2, x+1, y+h-1); - g.drawOrthoLine(x+2, y+h, x+3, y+h); - } - - @Override - public int getWidth() { - return 5+getVariable().getWidth(); - } - - @Override - public int getHeight(boolean small) { - small = false; - int h1 = 3+getVariable().getHeight(small)+2; - return h1; - } - - @Override - public int getLine(boolean small) { - small = false; - int h1 = 3+getVariable().getLine(small); - return h1; - } -} diff --git a/src/org/warpgate/pi/calculator/Potenza.java b/src/org/warpgate/pi/calculator/Potenza.java deleted file mode 100644 index 171bc0f8..00000000 --- a/src/org/warpgate/pi/calculator/Potenza.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.warpgate.pi.calculator; - -import org.warp.engine.Display; - -public class Potenza extends FunzioneDueValori { - - public Potenza(Funzione value1, Funzione value2) { - super(value1, value2); - } - - @Override - public String simbolo() { - return Simboli.POTENZA; - } - - @Override - public Termine calcola() throws NumberFormatException, Errore { - return getVariable1().calcola().pow(getVariable2().calcola()); - } - - @Override - public void draw(int x, int y, Display g, boolean small) { - int dx = 0; - variable1.draw(dx+x, getHeight(small)-variable1.getHeight(small)+y, g, small); - dx+=variable1.getWidth()+1; - variable2.draw(dx+x, y, g, small); - } - - @Override - public int getHeight(boolean small) { - return variable1.getHeight(small)+variable2.getHeight(true)-4; - } - - @Override - public int getLine(boolean small) { - return variable2.getHeight(true)-4+variable1.getLine(small); - } - - @Override - public int getWidth() { - int w1 = getVariable1().getWidth(); - int w2 = getVariable2().getWidth(); - - return w1+1+w2; - } -} diff --git a/src/org/warpgate/pi/calculator/Radice.java b/src/org/warpgate/pi/calculator/Radice.java deleted file mode 100644 index 8dd40e3d..00000000 --- a/src/org/warpgate/pi/calculator/Radice.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.warpgate.pi.calculator; - -import org.nevec.rjm.NumeroAvanzatoVec; -import org.warp.engine.Display; - -public class Radice extends FunzioneDueValori { - - public Radice(Funzione value1, Funzione value2) { - super(value1, value2); - } - - @Override - public String simbolo() { - return Simboli.NTH_ROOT; - } - - @Override - public Termine calcola() throws NumberFormatException, Errore { - Termine exponent = new Termine(NumeroAvanzatoVec.ONE); - exponent = exponent.divide(getVariable1().calcola()); - return getVariable2().calcola().pow(exponent); - } - - @Override - public void draw(int x, int y, Display g, boolean small) { - // TODO Auto-generated method stub - - } - - @Override - public int getWidth() { - // TODO Auto-generated method stub - return 0; - } - - @Override - public int getHeight(boolean small) { - // TODO Auto-generated method stub - return 0; - } -} diff --git a/src/org/warpgate/pi/calculator/RadiceQuadrata.java b/src/org/warpgate/pi/calculator/RadiceQuadrata.java deleted file mode 100644 index 5145e14e..00000000 --- a/src/org/warpgate/pi/calculator/RadiceQuadrata.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.warpgate.pi.calculator; - -import org.nevec.rjm.NumeroAvanzatoVec; -import org.nevec.rjm.Rational; -import org.warp.engine.Display; - -public class RadiceQuadrata extends FunzioneAnteriore { - - public RadiceQuadrata(Funzione value) { - super(value); - } - - @Override - public String simbolo() { - return Simboli.SQUARE_ROOT; - } - - @Override - public Termine calcola() throws NumberFormatException, Errore { - Termine result = getVariable().calcola().pow(new Termine(new Rational(1,2))); - return result; - } - - - @Override - public void draw(int x, int y, Display g, boolean small) { - Utils.writeSquareRoot(g, getVariable(), x, y, small); - } - - @Override - public int getWidth() { - return 5+getVariable().getWidth()+2; - } - - @Override - public int getHeight(boolean small) { - int h1 = getVariable().getHeight(small)+2; - return h1; - } - - @Override - public int getLine(boolean small) { - int h1 = getVariable().getLine(small)+2; - return h1; - } -} diff --git a/src/org/warpgate/pi/calculator/Somma.java b/src/org/warpgate/pi/calculator/Somma.java deleted file mode 100644 index 91841d27..00000000 --- a/src/org/warpgate/pi/calculator/Somma.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.warpgate.pi.calculator; - -import java.awt.Color; -import java.math.BigInteger; - -import org.nevec.rjm.NumeroAvanzatoVec; -import org.warp.engine.Display; - -public class Somma extends FunzioneDueValori { - - public Somma(Funzione value1, Funzione value2) { - super(value1, value2); - } - - @Override - public String simbolo() { - return Simboli.SUM; - } - - @Override - public Termine calcola() throws Errore { - Termine val1 = getVariable1().calcola(); - Termine val2 = getVariable2().calcola(); - Termine result = val1.add(val2); - return result; - } - - - @Override - public void draw(int x, int y, Display g, boolean small) { - int ln = getLine(small); - int dx = 0; - variable1.draw(dx+x, ln-variable1.getLine(small)+y, g, small); - dx+=variable1.getWidth()+1; - try { - NumeroAvanzatoVec tm = variable2.calcola().getTerm(); - - Utils.writeLetter(g, simbolo(), dx+x, ln-Utils.getFontHeight(small)/2+y, small); - dx+=Utils.getPlainTextWidth(simbolo()); - dx+=1; - - } catch (Errore e) { - e.printStackTrace(); - } - variable2.draw(dx+x, ln-variable2.getLine(small)+y, g, small); - } - - @Override - public int getWidth() { - int dx = 0; - dx+=variable1.getWidth()+1; - try { - NumeroAvanzatoVec tm = variable2.calcola().getTerm(); - dx+=Utils.getPlainTextWidth(simbolo()); - dx+=1; - } catch (Errore e) { - e.printStackTrace(); - } - return dx+=variable2.getWidth(); - } -} diff --git a/src/org/warpgate/pi/calculator/Tecnica.java b/src/org/warpgate/pi/calculator/Tecnica.java deleted file mode 100644 index aa70269c..00000000 --- a/src/org/warpgate/pi/calculator/Tecnica.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.warpgate.pi.calculator; - -import java.util.Vector; - -public interface Tecnica { - public static final Tecnica[] tecniche = new Tecnica[] {}; - - public abstract Vector risolvi(Equazione equazione); -} diff --git a/src/org/warpgate/pi/calculator/Utils.java b/src/org/warpgate/pi/calculator/Utils.java deleted file mode 100644 index f7928823..00000000 --- a/src/org/warpgate/pi/calculator/Utils.java +++ /dev/null @@ -1,347 +0,0 @@ -package org.warpgate.pi.calculator; - -import java.awt.Color; -import java.awt.FontMetrics; -import java.awt.Graphics; -import java.awt.image.BufferedImage; -import java.io.StringWriter; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.math.MathContext; -import java.math.RoundingMode; -import java.util.ArrayList; - -import org.nevec.rjm.BigDecimalMath; -import org.nevec.rjm.Rational; - -public class Utils { - - public static final int scale = 130; - public static final int resultScale = 8; - - public static final int scaleMode = BigDecimal.ROUND_HALF_UP; - public static final RoundingMode scaleMode2 = RoundingMode.HALF_UP; - - public static DebugStream debug = new DebugStream(); - - public static boolean debugOn; - - public static final class DebugStream extends StringWriter { - - public void println(String str) { - if (debugOn) { - System.err.println(str); - } - } - int before = 0; - boolean due = false; - - } - - public static boolean isInArray(String ch, String[] a) { - boolean contains = false; - for (String c : a) { - if (c.equals(ch)) { - contains = true; - break; - } - } - return contains; - } - public static String ArrayToRegex(String[] array) { - String regex = null; - for(String symbol : array) { - if (regex != null) { - regex+="|\\"+symbol; - } else { - regex = "\\"+symbol; - } - } - return regex; - } - public static String[] concat(String[] a, String[] b) { - int aLen = a.length; - int bLen = b.length; - String[] c= new String[aLen+bLen]; - System.arraycopy(a, 0, c, 0, aLen); - System.arraycopy(b, 0, c, aLen, bLen); - return c; - } - public static String[] add(String[] a, String b) { - int aLen = a.length; - String[] c= new String[aLen+1]; - System.arraycopy(a, 0, c, 0, aLen); - c[aLen] = b; - return c; - } - - public static boolean ciSonoSoloFunzioniImpostateSommeEquazioniESistemi(ArrayList fl) { - for (int i = 0; i < fl.size(); i++) { - if (!(fl.get(i) instanceof Termine || fl.get(i) instanceof Somma || fl.get(i) instanceof Equazione || fl.get(i) instanceof ParteSistema || fl.get(i) instanceof Parentesi)) { - if (fl.get(i) instanceof FunzioneAnteriore) { - if (((FunzioneAnteriore)fl.get(i)).variable == null) { - return false; - } - } else if (fl.get(i) instanceof FunzioneDueValori) { - if (((FunzioneDueValori)fl.get(i)).variable1 == null || ((FunzioneDueValori)fl.get(i)).variable2 == null) { - return false; - } - } else { - return false; - } - } - } - return true; - } - public static boolean ciSonoSoloFunzioniImpostateSommeMoltiplicazioniEquazioniESistemi(ArrayList fl) { - for (int i = 0; i < fl.size(); i++) { - if (!(fl.get(i) instanceof Termine || fl.get(i) instanceof Moltiplicazione || fl.get(i) instanceof Somma || fl.get(i) instanceof Equazione || fl.get(i) instanceof ParteSistema || fl.get(i) instanceof Parentesi)) { - if (fl.get(i) instanceof FunzioneAnteriore) { - if (((FunzioneAnteriore)fl.get(i)).variable == null) { - return false; - } - } else if (fl.get(i) instanceof FunzioneDueValori) { - if (((FunzioneDueValori)fl.get(i)).variable1 == null || ((FunzioneDueValori)fl.get(i)).variable2 == null) { - return false; - } - } else { - return false; - } - } - } - return true; - } - - public static boolean ciSonoSoloFunzioniImpostateEquazioniESistemi(ArrayList fl) { - for (int i = 0; i < fl.size(); i++) { - if (!(fl.get(i) instanceof Termine || fl.get(i) instanceof Equazione || fl.get(i) instanceof ParteSistema || fl.get(i) instanceof Parentesi)) { - if (fl.get(i) instanceof FunzioneAnteriore) { - if (((FunzioneAnteriore)fl.get(i)).variable == null) { - return false; - } - } else if (fl.get(i) instanceof FunzioneDueValori) { - if (((FunzioneDueValori)fl.get(i)).variable1 == null || ((FunzioneDueValori)fl.get(i)).variable2 == null) { - return false; - } - } else { - return false; - } - } - } - return true; - } - - public static boolean ciSonoSoloFunzioniImpostateESistemi(ArrayList fl) { - for (int i = 0; i < fl.size(); i++) { - if (!(fl.get(i) instanceof Termine || fl.get(i) instanceof Equazione || fl.get(i) instanceof ParteSistema || fl.get(i) instanceof Parentesi)) { - if (fl.get(i) instanceof FunzioneAnteriore) { - if (((FunzioneAnteriore)fl.get(i)).variable == null) { - return false; - } - } else if (fl.get(i) instanceof FunzioneDueValori) { - if (((FunzioneDueValori)fl.get(i)).variable1 == null || ((FunzioneDueValori)fl.get(i)).variable2 == null) { - return false; - } - } else { - return false; - } - } - } - return true; - } - - public static boolean ciSonoFunzioniSNnonImpostate(ArrayList fl) { - for (int i = 0; i < fl.size(); i++) { - if (fl.get(i) instanceof FunzioneAnteriore && !(fl.get(i) instanceof ParteSistema)) { - if (((FunzioneAnteriore)fl.get(i)).variable == null) { - return true; - } - } - } - return false; - } - - public static boolean ciSonoFunzioniNSNnonImpostate(ArrayList fl) { - for (int i = 0; i < fl.size(); i++) { - if (fl.get(i) instanceof FunzioneDueValori && !(fl.get(i) instanceof Equazione) && !(fl.get(i) instanceof Somma) && !(fl.get(i) instanceof Sottrazione) && !(fl.get(i) instanceof Moltiplicazione) && !(fl.get(i) instanceof Divisione)) { - if (((FunzioneDueValori)fl.get(i)).variable1 == null && ((FunzioneDueValori)fl.get(i)).variable2 == null) { - return true; - } - } - } - return false; - } - - public static boolean ciSonoMoltiplicazioniNonImpostate(ArrayList fl) { - for (int i = 0; i < fl.size(); i++) { - if (fl.get(i) instanceof Moltiplicazione || fl.get(i) instanceof Divisione) { - if (((FunzioneDueValori)fl.get(i)).variable1 == null && ((FunzioneDueValori)fl.get(i)).variable2 == null) { - return true; - } - } - } - return false; - } - - public static boolean ciSonoSommeNonImpostate(ArrayList fl) { - for (int i = 0; i < fl.size(); i++) { - if (fl.get(i) instanceof Somma) { - if (((FunzioneDueValori)fl.get(i)).variable1 == null && ((FunzioneDueValori)fl.get(i)).variable2 == null) { - return true; - } - } - } - return false; - } - - public static boolean ciSonoEquazioniNonImpostate(ArrayList fl) { - for (int i = 0; i < fl.size(); i++) { - if (fl.get(i) instanceof Equazione) { - if (((FunzioneDueValori)fl.get(i)).variable1 == null && ((FunzioneDueValori)fl.get(i)).variable2 == null) { - return true; - } - } - } - return false; - } - - public static boolean ciSonoSistemiNonImpostati(ArrayList fl) { - for (int i = 0; i < fl.size(); i++) { - if (fl.get(i) instanceof ParteSistema) { - if (((ParteSistema)fl.get(i)).variable == null) { - return true; - } - } - } - return false; - } - - public static boolean ciSonoAltreFunzioniImpostate(ArrayList fl) { - for (int i = 0; i < fl.size(); i++) { - if (!(fl.get(i) instanceof Termine || fl.get(i) instanceof Somma || fl.get(i) instanceof Parentesi || fl.get(i) instanceof FunzioneAnteriore || fl.get(i) instanceof Moltiplicazione || fl.get(i) instanceof Divisione)) { - if (fl.get(i) instanceof FunzioneAnteriore) { - if (((FunzioneAnteriore)fl.get(i)).variable == null) { - return true; - } - } else if (fl.get(i) instanceof FunzioneDueValori) { - if (((FunzioneDueValori)fl.get(i)).variable1 == null || ((FunzioneDueValori)fl.get(i)).variable2 == null) { - return true; - } - } else { - return true; - } - } - } - return false; - } - - public static Rational getRational(BigDecimal str) { - return getRational(str.toString()); - } - - public static Rational getRational(String str) { - try { - return new Rational(str); - } catch (NumberFormatException ex) { - if (new BigDecimal(str).compareTo(new BigDecimal(Double.MAX_VALUE)) < 0 && new BigDecimal(str).compareTo(new BigDecimal(Double.MIN_VALUE)) > 0) { - if (str.equals("-")) { - str = "-1"; - } - long bits = Double.doubleToLongBits(Double.parseDouble(str)); - - long sign = bits >>> 63; - long exponent = ((bits >>> 52) ^ (sign << 11)) - 1023; - long fraction = bits << 12; // bits are "reversed" but that's not a problem - - long a = 1L; - long b = 1L; - - for (int i = 63; i >= 12; i--) { - a = a * 2 + ((fraction >>> i) & 1); - b *= 2; - } - - if (exponent > 0) - a *= 1 << exponent; - else - b *= 1 << -exponent; - - if (sign == 1) - a *= -1; - - if (b == 0) { - a = 0; - b = 1; - } - - return new Rational(new BigInteger(a+""),new BigInteger(b+"")); - } else { - BigDecimal original = new BigDecimal(str); - - BigInteger numerator = original.unscaledValue(); - - BigInteger denominator = BigDecimalMath.pow(BigDecimal.TEN, new BigDecimal(original.scale())).toBigIntegerExact(); - - return new Rational(numerator, denominator); - } - } - } - - public static BigDecimal rationalToIrrationalString(Rational r) { - return BigDecimalMath.divideRound(new BigDecimal(r.numer()).setScale(Utils.scale, Utils.scaleMode), new BigDecimal(r.denom()).setScale(Utils.scale, Utils.scaleMode)); - } - public static boolean variabiliUguali(ArrayList variables, ArrayList variables2) { - if (variables.size() != variables2.size()) { - return false; - } else { - for (Incognita v : variables) { - if (!variables2.contains(v)) { - return false; - } - } - return true; - } - } - - public static void writeLetter(Display d, String text, int x, int y, boolean small) { - if (small) { - d.setFont(fontSmall); - } else { - d.setFont(fontBig); - } - d.setColor(Color.BLACK); - d.drawString(text, x, y+getFontHeight(small)); - } - - public static void writeSquareRoot(Display g, Funzione var, int x, int y, boolean small) { - if (small) { - g.setFont(fontSmall); - } else { - g.setFont(fontBig); - } - g.setColor(Color.BLACK); - int w1 = var.getWidth(); - int h1 = var.getHeight(small); - int wsegno = 5; - int hsegno = h1+2; - - var.draw(x+wsegno, y+(hsegno-h1), g, small); - - g.draw45Line(x, y+hsegno-3, x+2, y+hsegno-1, false); - g.drawOrthoLine(x+2, y+(hsegno-1)/2+1, x+2, y+hsegno-1); - g.drawOrthoLine(x+3, y, x+3, y+(hsegno-1)/2); - g.drawOrthoLine(x+3, y, x+3+1+w1+2, y); - } - - public static final int getFontHeight() { - return getFontHeight(false); - } - - public static final int getFontHeight(boolean small) { - if (small) { - return 6; - } else { - return 9; - } - } -} diff --git a/src/org/warpgate/pi/calculator/screens/EmptyScreen.java b/src/org/warpgate/pi/calculator/screens/EmptyScreen.java deleted file mode 100644 index db2c4a2b..00000000 --- a/src/org/warpgate/pi/calculator/screens/EmptyScreen.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.warpgate.pi.calculator.screens; - -import org.warp.engine.lwjgl.Screen; - -public class EmptyScreen extends Screen { - - public float endLoading; - - @Override - public void init() throws InterruptedException { - canBeInHistory = true; - endLoading = 0; - } - - @Override - public void render() { - // TODO Auto-generated method stub - - } - - @Override - public void beforeRender(float dt) { - endLoading += dt; - if (d.loading & endLoading >= 2.5) { - d.loading = false; - } - } - -} diff --git a/src/org/warpgate/pi/calculator/screens/EquationScreen.java b/src/org/warpgate/pi/calculator/screens/EquationScreen.java deleted file mode 100644 index 116d1a06..00000000 --- a/src/org/warpgate/pi/calculator/screens/EquationScreen.java +++ /dev/null @@ -1,203 +0,0 @@ -package org.warpgate.pi.calculator.screens; - -import java.awt.Color; -import java.io.PrintWriter; -import java.io.StringWriter; - -import org.warp.engine.lwjgl.Screen; -import org.warpgate.pi.calculator.Calculator; -import org.warpgate.pi.calculator.Errore; -import org.warpgate.pi.calculator.Errori; -import org.warpgate.pi.calculator.Funzione; -import org.warpgate.pi.calculator.Main; -import org.warpgate.pi.calculator.Parentesi; -import org.warpgate.pi.calculator.RisultatoEquazione; -import org.warpgate.pi.calculator.Termine; - -public class EquationScreen extends Screen { - - public float endLoading; - public static volatile String equazione = ""; - public static Parentesi f; - public static Funzione f2; - public static int ew1; - public static int ew2; - public static int eh2; - public static int x1; - public static int x2; - public static boolean requiresleep1; - public static boolean requiresleep2; - public static boolean aftersleep; - public static boolean autoscroll; - - @Override - public void init() throws InterruptedException { - canBeInHistory = true; - endLoading = 0; - try { - /* Fine caricamento */ - - //Parentesi f = new Parentesi("(â’¶(2X)*3Y)/(5Z^2)+(â’¶(11A)*13B)/(7CZ)=19XZ"); - //PARENTESI CON CALCOLI - //Funzione f = new Sottrazione(new Somma(new Parentesi("â’¶9/2+â’¶7/2", "").calcola(), new Termine("3.5")), new Parentesi("3*2√14","").calcola()); - //PARENTESI CON DUE NUMERI FRAZIONALI COMPLETI CON INCOGNITE: - //Funzione f = new Parentesi("(â’¶(2X)*3Y)/(5Z^2)+(â’¶(11A)*13B)/(7CZ)", ""); - //PARENTESI CON DUE NUMERI FRAZIONALI DISALLINEATI GRAFICAMENTE: - //Funzione f = new Parentesi("((5^2-1)/2)/5-5/(5/2)=2", ""); - //TERMINE DI PROVA COMPLETO: - //Funzione f = new Termine(new NumeroAvanzato(new Rational(3, 2), new Rational(7, 1), new Incognite(new Incognita('X', Rational.ONE)), new Incognite(new Incognita('Y', Rational.ONE)), new Incognite(new Incognita('z', Rational.ONE)))); - //PARENTESI REALISTICA CON INCOGNITE: - //Funzione f = new Equazione(new Parentesi("X^2+(MX-M+4)^2-4X-4(MX-M+4)^2+7", ""), new Termine("0")); - //POTENZA: - //Funzione f = new Parentesi("(MX-M+4)^2", ""); - //NUMERO SEMPLICE LUNGO: - //Funzione f = new Parentesi("-1219999799999996934.42229", ""); - //: - //Funzione f = new Parentesi("5Y+XY=2", "") - - equazione = "0"; - f = new Parentesi(equazione); - f2 = f.calcolaSistema(); - - - ew1 = f.getWidth(); - ew2 = f2.getWidth(); - eh2 = f2.getHeight(false); - x1 = 2; - x2 = 2; - requiresleep1 = false; - requiresleep2 = false; - aftersleep = false; - autoscroll = false; - - long start = System.nanoTime(); - Termine result = Calculator.calcolarisultato("((5â’·2+3√(100/0.1))*â’¶7+9/15*2√(26/2))/21"); - long end = System.nanoTime(); - long timeElapsed = end-start; - System.out.println("RESULT: " + result); - System.out.println("DECIMAl RESULT: " + result.getTerm().toBigDecimal()); - System.out.println("Time elapsed: " + (double) timeElapsed / 1000000 + " milliseconds\n"); - - - start = System.nanoTime(); - RisultatoEquazione eresult = Calculator.calcolaequazione("((5â’·2+3√(100/0.1))*â’¶7+9/15*2√(26/2))/21=(175*(2√7)+3*(2√13))/105"); - end = System.nanoTime(); - timeElapsed = end-start; - System.out.println("Is an equation: " + eresult.isAnEquation); - System.out.println("L-R: " + eresult.LR); - System.out.println("Time elapsed: " + (double) timeElapsed / 1000000 + " milliseconds\n"); - } catch (Errore e) { - d.setBackground(0.99609375f,102f/256f,34f/256f); - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - e.printStackTrace(pw); - d.errorStackTrace = sw.toString().toUpperCase().replace("\t", " ").replace("\r", "").split("\n"); - d.error = e.id.toString(); - System.err.println(e.id); - } - } - - public void calcola(String eqn) { - equazione = eqn.replace("sqrt", "â’¶").replace("^", "â’·"); - try { - f = new Parentesi(equazione); - } catch (Errore e) { - e.printStackTrace(); - } - try { - f2 = f.calcolaSistema(); - } catch (Errore e) { - e.printStackTrace(); - } - ew1 = f.getWidth(); - ew2 = f2.getWidth(); - eh2 = f2.getHeight(false); - x1 = 2; - x2 = 2; - requiresleep1 = false; - requiresleep2 = false; - aftersleep = false; - autoscroll = false; - } - - @Override - public void beforeRender(float dt) { - endLoading += dt; - if (endLoading >= 2.5) { - d.loading = false; - } - } - - @Override - public void render() { - d.setBackground(0.796875f, 0.90234375f, 0.828125f); - try { -// long delta = System.currentTimeMillis(); -// -// if (equazione == "") { -// throw new Errore(Errori.SYNTAX_ERROR); -// } else { -// if (ew1 + x1 + 5 > screenSize[0] && !requiresleep1 && autoscroll) { -// x1-=1; -// } else if(autoscroll) { -// requiresleep1 = true; -// } -// -// if (ew2 + x2 + 5 > screenSize[0] && !requiresleep2 && autoscroll) { -// x2-=1; -// } else if(autoscroll) { -// requiresleep2 = true; -// } -// -// if (requiresleep1 && requiresleep2 && autoscroll) { -// requiresleep1 = false; -// requiresleep2 = false; -// try { -// Thread.sleep(500); -// } catch (InterruptedException e) { -// // TODO Auto-generated catch block -// e.printStackTrace(); -// } -// x1 = 2; -// x2 = 2; -// aftersleep = true; -// } -// -// f.draw(x1,2,d, false); -// f2.draw(x2,screenSize[1]-2-eh2,d, false); -// } -// -// if (aftersleep && autoscroll) { -// aftersleep = false; -// try { -// Thread.sleep(500); -// } catch (InterruptedException e) { -// // TODO Auto-generated catch block -// e.printStackTrace(); -// } -// } -// -// try { -// delta = System.currentTimeMillis() - delta; -// if (50-delta > 0 && autoscroll) { -// Thread.sleep(50-delta); -// } -// } catch (InterruptedException e) { -// // TODO Auto-generated catch block -// e.printStackTrace(); -// } - if (false) { - throw new Errore(Errori.SYNTAX_ERROR); - } - } catch (Errore e) { - d.setBackground(0.99609375f,102f/256f,34f/256f); - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - e.printStackTrace(pw); - d.errorStackTrace = sw.toString().toUpperCase().replace("\t", " ").replace("\r", "").split("\n"); - d.error = e.id.toString(); - System.err.println(e.id); - } - } - -}