Archive for November, 2006

Mobile Java

Tuesday, November 28th, 2006

In the previous post I described what I call “Assembly Java”. The name itself, Assembly Java, is paradoxical (oxymoronic) through the opposition between a high-level programming language (Java) and a low-level programming style (’assembly language’).

The previous post was intended, I guess, as a (subtle) critique of Java used as a low-level programming language.

In the constrained mobile (MIDP) environment, the Java language becomes deprived (through the low-level programming style) of its advanced features, such as classes, virtual dispatch, polymorphism, packages, reflections, and, to some degree, garbage collection. Java turns into a C-like low level language, while still missing some of the C features that would have been useful in the restricted environment, such as: data structs, stack-allocated instances (’auto’ variables, object instances that aren’t allocated on the heap), arrays of structs (with the structs stored inside the array, not with pointers stored in the array), inlined functions, preprocessor (used for conditional compilation and macro expansion), enums (useful for the ’switch-dispatch’ pattern, among other things), first-class functions (’function pointers’), etc.

But don’t get my critique too harsh: I’m an enthusiast mobile Java developer myself, and I would recommend Java as the first choice (by far) for mobile application development.

There is also the time aspect: due to the quick evolution of the hardware, the mobile environment is becoming less and less constrained, and thus mobile Java programming is gradually getting closer to the standard Java programming, employing less low-level optimizations and more high-level design. Also the mobile VM (java virtual machine) should be expected to become smarter in time and, why not, perhaps the java compiler (javac) might employ some compile-time optimizations (such as method inlining, and a bunch of expression and loop optimizations) as well.

In the meanwhile, a smart obfuscator such as Proguard can help with the size-reduction of the compiled classes (through automatic class and method renaming, package renaming and elimination, class-merging, method inlining), thus allowing you to keep your design/code simple, clear and clean, while still taking advantage of these optimizations. (note: class-merging is still experimental in proguard, and method inlining is planned for the future).

For the developer, the first concern should be to (quickly) implement the application and get it to work correctly, and only afterwards does the performance (and optimizations) come into discussion. This is simply because the performance of an unfinished or failed project is of little importance.

Getting the application done is no easy task. You should take advantage of clean design, simple code, clear and small interfaces, hidden (private) implementations, because all these help you to develop your application, to modify and adapt it, and to reuse your code.

You should strieve to achive beautiful code, kind of an art-work. A measure for this is to imagine that your code is open-source, and all your friends look at it and discuss it. If you’d still be proud of your code were it public, it’s a good sign.

The so-called guidelines from my previous post are rather bad advice, and were intended for your entertainment (note that #0-#10 is in fact 11 items, not 10 as stated in the post). By negating (complementing?) them you might get some sound design advice.

Let’s try the negation trick:

#0. Don’t merge classes.

You should not merge un-related functionality together in a single class. You should keep the class design clear and neat because it makes the code easier to understand, to modify, and to reuse between projects. As classes do incur some space overhead, you should avoid the extreme of class inflation, the situation where you have a large number of very small classes (classes with just one or two methods inside). Perhaps a reasonable number of classes might be somewhere between 4 and 20 classes, depending on project size and design complexity.

Proguard the obfuscator offers an experimental merge-class feature. This is another reason to keep your design/code clean, with nice classes, and you can always get the merged-class benefit at the obfuscation step if you want it, with no negative impact on your class design.

#1. Don’t merge methods.

The space overhead of a function declaration is so small, that you shouldn’t worry about it. Do split big methods if it increases the clarity of the code. Strive for having each method do just one thing. Try to have methods with small and clear signatures (small number of parameters) and clear semantic (’what does this method do?’).

#2. Switch or polymorphic dispatch?

Using the switch() instead of the polymorphic dispatch is generally considered bad OOP practice. You may nevertheless want to do it sometimes to avoid having a large number of small classes. Ponder well when choosing between the two.

#3. Instantiation vs. reuse

While object instantiation economy is not bad, don’t sacrifice too much of your design quality for the sake of it, because at the same time object instantiation / garbage collection is not prohibitively expensive when done with measure. The places where you should keep an eye on object instantiation is inside tight loops (e.g. inside a for loop, it may be a bad idea to repeatedly instantiate / garbage collect a temporary object — you may want to instantiate it just once outside the loop, and reuse it during the loop).

I’ll stop here, saving you the remaining ‘guidelines’.

The 10 principles of Assembly Java

Monday, November 27th, 2006

Assembly Java is a particularly economical and efficient style of usage of the Java language. These are the principles of Assembly Java:

#0: Define as few classes as possible. Ideally, use a single class.

How: Pack as much as possible in each class. Merge conceptually-unrelated classes together, when possible.
Why: Every class incurs a space-overhead (of about 200 bytes) in the final jar. Having everything in one class also improves the class-locality (as all the methods/fields you use are likely to be from the same class).
Limit: The minimum number of classes you may use is given by the number of classes that you have to extend, as you have to define a new class for each inheritance.

#1: Define as few methods as possible.

How: pack as much of sequential logic as you can in a single big method. Don’t split methods solely for clarity’s sake; merge them.
Why: Performance gain by avoiding method call overhead. Space gain by avoiding the overhead of the method declaration.
Limit: You need a separate method for each method you overload, in particular for each abstract method you implement.

#2: Avoid polymorphism; use switch() instead.

How: Rather than declaring a base interface or abstract class, and extend it in order to polymorphically customize behavior,
use a single concrete class with switch statements for all variable behavior. Declare integer constants identifying the behavior cases.
Why: Size gain by reducing the number of classes (#0 above) (abstract classes and interfaces are as bad as any other class). Performance gain by avoiding the virtual dispatch.

Bad Assembly Java:


abstract class Animal {
  abstract String saySomething();
}

class Dog extends Animal {
  String saySomething() { return "how how"; }
}

class Cat extends Animal {
  String saySomething() { return "meow"; }
}

Good Assembly Java:


class Animal {
  static final int DOG=1, CAT=2;
  String saySomething(int kind) {
    switch (kind) {
      case DOG: return "how how";
      case CAT: return "meow";
      default: return null;
   }
}

#3: Avoid object instantiation (object creation).

How:
Reuse the same object time and again. All classes should have a init() or reset() or reinit() method (instead of a constructor) which would allow the re-use of the old object.
Don’t return objects from methods (because this likely requires the creation of the object to be returned), instead have the method take an out object parameter, which is passed in by the caller (who can reuse it) and is filled by the method with the result value.
Why: Performance gain by avoiding the object allocation overhead, and by reducing the garbage-collection overhead. Memory footprint reduction by decreasing the number of allocations.

#4: Don’t use packages. Put everything in the root package.

Why:
The package names incur some space overhead in the compiled classes.
Due to class-merging (#0), the package concept is anyway already irrelevant.
Having everything in a single package allows global access to package-visible methods and fields, thus enabling technique #5a below.

#5: Avoid method calls.

Why: Performance gain by avoiding the method call overhead. Also see #1, Define as few methods as possible.

#5a: Avoid accessor methods (getters, setters). Access directly the data members.

How: having everything in a single package (#4) allows access to all the members except the private ones. I.e., you don’t have to make a data member public
just because you want to access it, package-visibility is enough.

#5b: Inline small methods.

Why: avoids call overhead and reduces the number of methods.
How: use a preprocessor and preprocessor macros for the inlining. As the Java language doesn’t have a default preprocessor, you have the liberty to use any preprocessor you like. For example you may use CPP (’C Pre Processor’) from GCC, but there are plenty of other choices.
Note: don’t trust the compiler or the virtual machine to inline your small methods (#7).

#6: Don’t use String. Use char array instead.

How: have a large char array, and use a pair of indices (begin, end) inside the array to delimitate your string.
Why:
Reduces the number of object creations and method calls. A char array (with a pair of indices) allows you to extract substrings without object creation,
and to iterate over the characters without the overhead of a method call for each character (String.getAt()).
Also enables a method to return a string without requiring object instantiation (by passing the char array as an out parameter to the method).

#7: Don’t trust the compiler or VM with the optimizations.

Why: the only optimizations you can rely on are the ones that you explicitly code yourself. Conservatively consider the compiler and the VM as dummy as possible.
Bad:


doSomething(2*n, 2*n+1);

Good:


int doubleN = n << 1;
doSomething(doubleN, doubleN + 1);

#8: Avoid array of objects, use multiple elementary arrays instead.

This is best described by an example. Let’s say you need an array of points, where a point is a pair of two integer coordinates (x, y).
Instead of the first-shot solution:


static final int MAX_POINTS = 100;
class Point {
  int x, y;
}
Point myPoints[] = new Point[MAX_POINTS];
{
  for (int i = 0; i < MAX_POINTS; ++i) {
    myPoints[i] = new Point();
  }
}

Use the much-improved solution:


int Xs[] = new int[MAX_POINTS];
int Ys[] = new int[MAX_POINTS];

Why: you gain one less class, and a lot of memory footprint from avoiding the many object instantiations.

#9: Use the all-static singleton.

Every time you have a class that will have a single instance, you should take the opportunity to use the singleton pattern.
The preferred way of implementing the singleton is to declare all the fields (methods and data) static. This avoids the need to allocate an instance of the singleton class (saves memory), and allows for the class-merging of the all-static singleton with any other (non-singleton) class, thus saving one more class definition (#0).

#10: Avoid exceptions.

Why: The try/catch block incurs a space overhead (in the compiled class). There is also a performance penalty when an exception is thrown/caught.
How: Don’t throw. Don’t catch. Don’t define new exception classes (#0), use the standard Error or RuntimeException instead (these are unchecked exceptions, so you don’t have to catch them) if you really have to throw at times.

Conclusion

The name Assembly Java makes reference to Assembly Language, which too is renowned for its outstanding performance and ressource efficency.

So these are the 10 principles of Assembly Java (#0 - #10 above), use them well.

Update 2006-11-30: there is a follow-up to this article, called ‘Mobile Java’, that you might want to read as well.

The rise of the dot-org

Wednesday, November 22nd, 2006

Here I show why the .org TLD will grow in standing and reputation, eating from .com’s domain market share.

TLD means Top Level Domain, such as .com and .org.
CC-TLD means Country Code TLD, such as .de and .uk.

(source)
This graphic shows the evolution of the total number of domains, per TLD, in time. From the last column, which shows the situation in august 2006, we can see that the biggest (in number of domains) TLDs are, in this order: com, de, net, uk, org, info, eu.
If we consider only the unrestricted, global TLDs (and excluding .info), we are left with this order: .com (50 million domains), .net (6 million), .org (4 million).

So .com is the main TLD, this is well known. But how to choose between .net and .org? Suppose you want to buy a domain name, and it’s not available in .com, but it is available in both .net and .org, and you want to buy it only once. What should you choose? My take is: choose .org.
Why?

While both .com and .net are operated by Verisign, .org is operated by Public Interest Registry. Verisign is generally viewed as the big bad company who makes all the money from the domain business. Between the two registries, I’d choose PIR.

But this is not all: .org simply sounds better than .net, because of the more vowel-like, round sound. It generally composes better with names ending in a consonant. Compare: menstral.net and menstral.org, I find the .org variant nicer. On the other hand, .net has the side of the omonimous Microsoft platform .NET, which perhaps has some positive impact on the number of .net domains. At least all the sites related to MS .NET would perhaps choose a .net domain over a .org one.

.net is generally viewed as the shadow of .com, the second-class .com, the .com of the poor people. That’s why many people see it as the second-choice after .com. And as .com, .net has a rather commercial connotation. On the other hand, .org has a distinct sound, originating from its association with ‘organization’. It offers the connotations of ‘community’ and ’social’. There are many businesses who deliberately chose .org over .com, the premier example being OpenOffice.org. In general, the community-oriented and the open-oriented sites would favor .org over .net. Because of these reasons, .org is gradually accumulating a better reputation than .com and .net on the web, some king of a friendlier, welcoming sound: we’re here to help, not to rip you off. But above all, the main reason for the .org rise in importance will be this self-fulfilling prophecy (just joking).

The problem with .com is the domain-spammers, or domain-bandits. They grab large amounts of domains (in the range of millions of domains), and put on them pre-made advertising pages, with no real content, with the sole intent to trick the users into clicking the ads. The situation is so bad that perhaps as much as 80% of .com domains are such obnoxious ad garbage. This is why it’s so hard to find an available useful domain in .com. This domain-pollution affects all the TLDs, not only .com, but .com is the main target because it’s perceived as being the most valuable TLD. On the other hand, as the pollution gets worse, at some point same action will have to be taken to keep the whole domain name principle working, similarly to the way email-spam, threatening the email usability, is prompting active measures.

In the mean-while, what will happen is that .com will increasingly become the domain of spammers (who are good and quick at grabbing any name that’s possible to get), while the legitimate new web sites will increasingly choose either a CC-TLD or one of .org and .net.

What about .eu? Unfortunately, .eu combines the worst features described above: it is mainly a spammers’ domain too (because of the botched launch by eurid), and at the same time it is a minor domain, much smaller than .de or .org. What’s more, it is limited to EU registrations. On the positive side, the price of .eu will decrease following the price reduction from 10e to 5e by eurid.

What about .info, .name, .biz, etc? These are minor and will perhaps never take off. The four-letter TLDs are a particularilly bad because they add one letter to the length of the domain name, without offering any advantage in exchange.

What about .mobi? IMO we should boycott this TLD, not buy it at all, because it is based on the wrong idea of a TLD, as it goes against the one-web principle. And it’s overpriced. And it’s a 4-letter TLD. I’d keep out of it for now, thanks. I hope the failure of .mobi will be a lesson for those trying to artificially impose not-needed domains. Like, why buy just one domain, when you could buy ten?

In general, I think the importance of domain names will continue to diminish, as more and more traffic is directed by search engines as opposed to users typing the URL in the browser. And I’m looking forward for a solutions to the exploding domain-spam business.

And these are all the domains I own:
francuski.com
frumos.com
javia.org
javia.eu
menstral.net
menstral.org
procod.com
proverbe.org

MIDP over JavaSE

Monday, November 20th, 2006

This is a topic I posted on the Mobile and Embedded forum:

There is a need among midlet developers for a ‘phone emulator’ which runs in a (desktop) web browser. This would allow the developers to demo their midlets on the web, and allow the users to try/test the midlets before installing them on their mobiles.

A first choice is for the emulator to be a JavaSE applet, which runs in the browser.

Developing such a ‘phone applet’ is roughly equivalent to having a MIDP (and additional API JSRs) implementation over JavaSE.

Can phoneME-feature be used to develop such an applet?

And could JavaSE be considered as a possible new ‘target’ for phoneME compilation (in addition to Linux/arm, Windows/i386, etc)?

In my understanding, I would see JavaSE replacing the CLDC level in a phoneME/SE target. In this case, the porting (of phoneME) to JavaSE would be different from the typical porting process to a new platform which happens at a much lower (kvm, cldc) level. E.g., because JavaSE already has a java VM, the kvm wouldn’t be needed.

So what do you think about using phoneME to obtain a MIDP over JavaSE implementation? What about a JavaSE target for phoneME-feature?

JavaME open source

Monday, November 20th, 2006

It’s old news now, since a week (since 2006-11-14) a first piece of JavaME has been open-sourced by Sun under GPL, under the name PhoneME (Mobile and Embedded).

First of all, this is a great move, thanks Sun. The license (GPL) is ok, even though it could have been a more liberal one like Apache, BSD or LGPL.
I think it’s GPL because Sun wanted to somewhat protect its financial investment and commercial license revenews.

PhoneME is the same thing as Sun Java Wireless Client, which is a commercial product that Sun is licensing to mobile device manufacturers. The differences are the name (Sun avoided to use ‘Java’ in ‘PhomeME’) and some proprietary parts that will perhaps be open-sourced later.

The PhoneME-feature contains (from the lower level up):
- a low-level portability layer, PCSL
- the java small virtual machine implementation, KVM
- CLDC
- MIDP and additional API JSRs

PhoneME now supports as targets Linux/i386, Linux/ARM and Windows/x86.

Development as a series of Expansions and Contractions

Friday, November 10th, 2006

Software development can be seen as an iterative process where each iteration consists of an Expansion followed by a Contraction.

An Expansion consists in implementing new features as quickly as possible. The focus is exclusively on getting the new features to (somewhat) work. This may be seen as a prototyping of the new features, because they are implemented hastily and thus superficially, but quickly. During an expansion the elements that are not directly observable in the functionality being added may be ignored: elegance, orthogonality, consistency, performance, algorithmic complexity, economic data structures, exception condition handling, error reporting, logging, scalability. An expansion consists in getting it to work as fast as possible, but the quick solution may be a dirty one.

A Contraction conserves the existing features, but cleans up the mess. The code is refactored. The redundant parts are removed. The design is pondered and improved. Design orthogonality, consistency, simplicity, elegance are of paramount importance. The behavior in corner-cases (limit, exceptional situations) is carefully analized and tested. Many bugs are found and fixed. The performance (speed, memory usage, etc.) is measured and improved (profiling). More efficient algorithms replace the naive implementations of the Expansion. During a Contraction no new features are added, the externally observable functionality remains unchanged, but the internals are consolidated.

An Expansion shows the developers where the product can go, it gives them inspiration and trust that the difficulties can be overcome. But during an expansion, as the implementation becomes more and more disorganized, the developers get a sense of uncertainty on the code, of gradually losing the understanding of how the things work, losing grip on the code. Forward progress becomes gradually harder, because building on the now shaky foundation is difficult. These are indications that it’s time for a Contraction.

A Contraction makes the developer feel that the implementation is getting clean, elegant, and solid. The developer re-gains control and trust over the code. The code is becoming smaller, streamlined, beautiful, efficient. On the other hand, the observable functionality of the product doesn’t change, doesn’t progress during the Contraction. The result of the Contraction is a clean, solid and trusty base from which the next Expansion can be attempted.

During the Expansion the developer increases the size of the codebase. During the Contraction, he decreases the size of the code. Thus the work of the developer consists in both adding code (the positive, constructive aspect) and removing code (the destructive aspect). Destructive is meant here in a good sense, like burning the extra fat, cleaning the weeds, removing a tumor or sweeping-out the garbage. And I dare say that removing code, that is re-organising the code in a better way while keeping the functionality essentially unchanged, is harder to master than adding new code.

To somebody not involved in the internals of a project, only the Expansion has visible results. From the external viewpoint, during the Expansion the project grows as new functionality is added, while during the Contraction it looks like nothing is happening. It is tempting to direct the team like this: good, you just finished this Expansion, it’s time to start the next Expansion right ahead. Were developers to follow such advice, they would find it harder and harder to advance as the project becomes disorganized, anarchical, amorphic after a series of Expansions with no Contractions.

And a parallel with the bubble: during an economic expansion, there is an abundance of capital and exuberant growth. But the abundent capital drives both good and not so good enterprises, and the affluent ressources are not used efficiently. A contraction is needed to wipe out the lousy businesses, thus freeing the capital blocked by them, and to urge the remaining business to move to a new level of efficiency. The healthier result forms the base of the next expansion.

Implementing the lgamma() function in Java

Sunday, November 5th, 2006

The gamma function is the extension of the factorial to real numbers (n! == gamma(n+1)), and it’s a very important function in mathematics. Gamma(x) grows very quickly (similar to x**x), that’s why lgamma(), which is the logarithm of the gamma function, is often used instead. In the discussion below, I’m only considering the lgamma(x) on the positive axis (x>0).

I wanted to have an implementation of lgamma() in Java. I studied these four implementation alternatives:

1) The Lanczos approximation using g=7 and 9 terms, which is used in the GNU Scientific Library. Also the sample code in the wikipedia article uses the same parameters (g=7, 9 terms).


private static final double L9[] = {
    0.99999999999980993227684700473478,
    676.520368121885098567009190444019,
    -1259.13921672240287047156078755283,
    771.3234287776530788486528258894,
    -176.61502916214059906584551354,
    12.507343278686904814458936853,
    -0.13857109526572011689554707,
    9.984369578019570859563e-6,
    1.50563273514931155834e-7
};
private static final double LN_SQRT2PI = 0.9189385332046727418; //log(2*PI)/2

static final double lanczosLGamma9(double x) {
    if (x <= -1) return Double.NaN;
    double a = L9[0];
    for (int i = 1; i < 9; ++i) {
        a+= L9[i]/(x+i);
    }
    return (LN_SQRT2PI + Math.log(a) - 7.) + (x+.5)*Math.log((x+7.5)/Math.E);
}

2) The Lanczos approximation using g=607/128 and 15 terms, used in the Jakarta Mathematics Library and described here.


private static final double[] L15 = {
    0.99999999999999709182,
    57.156235665862923517,
    -59.597960355475491248,
    14.136097974741747174,
    -0.49191381609762019978,
    .33994649984811888699e-4,
    .46523628927048575665e-4,
    -.98374475304879564677e-4,
    .15808870322491248884e-3,
    -.21026444172410488319e-3,
    .21743961811521264320e-3,
    -.16431810653676389022e-3,
    .84418223983852743293e-4,
    -.26190838401581408670e-4,
    .36899182659531622704e-5,
};

static final double lanczosLGamma15(double x) {
    if (x <= -1) return Double.NaN;
    double a = L15[0];
    for (int i = 1; i < 15; ++i) {
        a += L15[i]/(x+i);
    }

    double tmp = x + (607/128. + .5);
    return (LN_SQRT2PI + Math.log(a)) + (x+.5)*Math.log(tmp) - tmp;
}

3) The Stirling approximation with 4 terms.


private static final double
    LC1 = 0.08333333333333333,
    LC2 = -0.002777777777777778,
    LC3 = 7.936507936507937E-4,
    LC4 = -5.952380952380953E-4;

static final double stirlingLGamma(double x) {
    final double
        r1 = 1./x,
        r2 = r1*r1,
        r3 = r1*r2,
        r5 = r2*r3,
        r7 = r3*r3*r1;
    return (x+.5)*Math.log(x) -x + LN_SQRT2PI + LC1*r1 + LC2*r3 + LC3*r5 + LC4*r7;
}

4) The lgamma implementation from SUN’s FDLIBM 5.3.
As this implementation is more complex, and notably uses 61 precomputed double constants, I don’t inline here, but you may find it in the full source code.

I was curious which one of these implementations is the most precise, and which one is the fastest.
I computed an “error table” which shows the error of these functions in ULPs over the integers from 0 to 170. This is the beginning of the error table:

Error in ULPs

x FDLIBM Lanczos 15 Lanczos 9 Stirling
1 0 0 32 -1000+
2 0 1 9 -1000+
3 0 -2 10 -1000+
4 0 -1 -1 -1000+
5 0 -1 3 -1000+
6 0 -2 1 -1000+
7 -1 0 2 -1000+
8 0 -1 1 -1000+
9 1 1 4 -1000+
10 1 -1 -1 -463
11 0 0 0 -98
12 1 1 0 -45
13 -1 0 0 -23
14 0 2 0 -12
15 0 2 0 -5
16 1 0 2 -4
17 0 0 0 -1
18 0 -1 2 -1
19 0 0 0 -1
20 0 0 0 1

You may also consult the full table of lgamma error.

The conclusion is that FDLIBM’s implementation is the most precise, as it always has an error of at most 1 ULP.
Lanczos with 15 terms is good, as it has an error of at most 2 ULPs. Lanczos with 9 terms is somewhat worse, especially for small values. And Stirling is bad for arguments under 16, but above 16 has an error similar to Lanczos 15.

So FDLIBM’s lgamma() is the clear winner on precision. What about speed?
Below is the time (ms) needed for computing lgamma for all the integers from 0 to 170, 20000 times.

method time (ms)
FDLIBM 1082
Lanczos 15 1905
Lanczos 9 1524
Stirling 618

(smaller time is better)

The fastest method is Stirling (but unfortunately it’s not accurate for small arguments), followed by FDLIM’s lgamma(), which is almost twice as fast as Lanczos 15.
So the winner considering both performance and speed is FDLIBM. The downside of FDLIBM’s lgamma() is that it is a bit more complex that the other methods, and also it uses a table of 61 precomputed double values, which is 4 times bigger than the table used by Lanczos 15.

The full source code, containing the four implementations of lgamma(), the error evaluation and speed benchmark is available here:
Gamma.java

Java horror

Sunday, November 5th, 2006

This is life without enums, macros or function pointers:


static final int
    SIN  = 1, COS  = 2, TAN  = 3,
    ASIN = 4, ACOS = 5, ATAN = 6,
    SINH = 7, COSH = 8, TANH = 9,
    ASINH = 10, ACOSH = 11, ATANH = 12,
    EXP   = 20, LOG  = 21, LOG10 = 22, LOG2  = 23,
    SQRT = 31, CBRT  = 32, POW   = 33,
    INT   = 40, FRAC = 41, ABS = 42,
    FLOOR = 43, CEIL = 44, SIGN = 45,
    MIN   = 46, MAX  = 47, GCD  = 48,
    COMB  = 49, PERM = 50, RND  = 51,
    FACT  = 52;

static final String names[] = {
    "sin",  "cos",  "tan",  "asin",  "acos",  "atan",
    "sinh", "cosh", "tanh", "asinh", "acosh", "atanh",
    "exp",  "log",  "ln",   "log10", "log2",  "pow",
    "sqrt", "cbrt",
    "\u221a", "\u221b",
    "int", "frac", "abs",
    "floor", "ceil", "sign",
    "min", "max", "gcd",
    "comb", "perm", "rnd",
};

/* keep 'codes' in sync with 'names' above */
static final int codes[] = {
    SIN,  COS,  TAN,  ASIN,  ACOS,  ATAN,
    SINH, COSH, TANH, ASINH, ACOSH, ATANH,
    EXP,  LOG,  LOG,  LOG10, LOG2,  POW,
    SQRT, CBRT,
    SQRT, CBRT,
    INT,  FRAC, ABS,
    FLOOR,CEIL, SIGN,
    MIN,  MAX,  GCD,
    COMB, PERM, RND,
};

double eval(int code, double params[]) {
    double x = params[0];
    switch (code) {
    case SIN:   return Math.sin(x);
    case COS:   return Math.cos(x);
    case TAN:   return Math.tan(x);

    case ASIN:  return Math.asin(x);
    case ACOS:  return Math.acos(x);
    case ATAN:  return Math.atan(x);

        //more follow
    }
}

Good old factorial

Saturday, November 4th, 2006

A simple routine for computing the factorial in java, with good speed and low footprint.


static final double factorial(int n) {
    if (n < 0) {
        return Double.NaN;
    }
    if (n > 170) {
        return Double.POSITIVE_INFINITY;
    }
    double x = n, tail = x;
    switch (n & 7) {
    case 7: tail *= --x;
    case 6: tail *= --x;
    case 5: tail *= --x;
    case 4: tail *= --x;
    case 3: tail *= --x;
    case 2: tail *= --x;
    case 1: return FACT[n >> 3] * tail;
    case 0: return FACT[n >> 3];
    }
    return 0;//not reached
}

private static final double FACT[] = {
    1.0,
    40320.0,
    2.0922789888E13,
    6.204484017332394E23,
    2.631308369336935E35,
    8.159152832478977E47,
    1.2413915592536073E61,
    7.109985878048635E74,
    1.2688693218588417E89,
    6.1234458376886085E103,
    7.156945704626381E118,
    1.8548264225739844E134,
    9.916779348709496E149,
    1.0299016745145628E166,
    1.974506857221074E182,
    6.689502913449127E198,
    3.856204823625804E215,
    3.659042881952549E232,
    5.5502938327393044E249,
    1.3113358856834524E267,
    4.7147236359920616E284,
    2.5260757449731984E302,
};