Saturday, April 23, 2011

Computus in scala and java

In the spirit of the holiday, I figured I would try my hand at writing computus in scala and java. For those who aren't aware, computus is the algorithm used to compute which day easter falls on.

First, the scala version:
object Computus {
  def main(args: Array[String]) {
    val start = System.currentTimeMillis()
    for (year <- 2000.until(1000000)) {
      println(pretty_computus(year))
    }
    println(System.currentTimeMillis()-start)
  }

  def golden(year:Long):Long = {
    year % 19 + 1
  }

  def century(year:Long):Long = {
    (year / 100)  +1
  }

  def  solar(year:Long):Long = {
     (3 * (century(year) /4) ) -12
  }

  def  lunar(year:Long):Long = {
     ((8 * century(year) +5) / 25) - 5
  }

  def  letter(year:Long):Long = {
    5 * year / 4 - solar(year) - 10
  }

  def  epact(year:Long):Long = {
    (11 * golden(year) + 20 +lunar(year) - solar(year)) %30
  }

  def  correct_9006(year:Long):Long = {
    val epact_val = epact(year)
    if (epact_val < 0) {
      epact_val + 30
    } else {
      epact_val
    }
  }

  def  correct_epact(year:Long):Long = {
    val epact_val = correct_9006(year)
    if (((epact_val ==25) && (golden(year) > 11)) || (epact_val ==24)) {
      epact_val + 1
    } else {
      epact_val
    }
  }

  def  n_whatever(year:Long):Long = {
    44- correct_epact(year)
  }

  def  fix_n(year:Long):Long = {
    val n=n_whatever(year)
    if (n<21){
      n + 30
    } else {
      n
    }
  }

  def  computus(year:Long):Long = {
    fix_n(year) + 7 -((letter(year)+fix_n(year))%7)
  }

  def pretty_computus(year:Long):String = {
    val day_of_march = computus(year)
    if (day_of_march>31) {
      "April " + (day_of_march -31).toString()
    } else {
      "March " + day_of_march.toString()
    }
  }
}

and now the java version

public class JavaComputus {
    
    
  public static void main(String[] args) {
    Long start = System.currentTimeMillis();
    
    for (long i = 2000; i < 1000000; i++) {
       System.out.println(pretty_computus(i));
        
    }
    System.out.println(System.currentTimeMillis()-start);
  }

  public static Long golden(Long year) {
    return year % 19 + 1;
  }

  public static Long century(Long year) {
    return (year / 100)  +1;
  }

  public static Long  solar(Long year) {
     return (3 * (century(year) /4) ) -12;
  }

  public static Long  lunar(Long year) {
     return ((8 * century(year) +5) / 25) - 5;
  }

  public static Long  letter(Long year) {
    return 5 * year / 4 - solar(year) - 10;
  }

  public static Long  epact(Long year) {
    return (11 * golden(year) + 20 +lunar(year) - solar(year)) %30;
  }

  public static Long  correct_9006(Long year) {
    Long  epact_val = epact(year);
    if (epact_val < 0) {
      return epact_val + 30;
    } else {
      return epact_val;
    }
  }

  public static Long  correct_epact(Long year) {
    Long epact_val = correct_9006(year);
    if (((epact_val ==25) && (golden(year) > 11)) || (epact_val ==24)) {
      return epact_val + 1;
    } else {
      return epact_val;
    }
  }

  public static Long  n_whatever(Long year) {
    return 44- correct_epact(year);
  }

  public static Long  fix_n(Long year) {
    Long n=n_whatever(year);
    if (n<21){
      return n + 30;
    } else {
      return n;
    }
  }

  public static Long  computus(Long year) {
    return fix_n(year) + 7 -((letter(year)+fix_n(year))%7);
  }

  public static String pretty_computus(Long year) {
    Long day_of_march = computus(year);
    if (day_of_march>31) {
      return "April " + ((Long)(day_of_march -31)).toString();
    } else {
      return "March " + day_of_march.toString();
    }
  }

}

I realize the scala implementation is basically "java in scala", but an interesting data point is that the the scala version is about 20% slower. From my perspective, it seems like the scala version should have almost identical performance characteristics to the java version.

More importantly, I must certainly be doing doing something wrong. Iterating and building the list in this manner cannot be the "right" way to do this, but I'm drawing a blank at how to improve things. Aside from the performance characteristics, I'm really interested in trying to improve the scala version to make it more idiomatic. I'm pretty sure I'm hampered by my lack of experience with functional languages.

No comments: