Page 2 of 2 FirstFirst 12
Results 11 to 20 of 20

Thread: Beginners programming challenge #29

  1. #11
    Join Date
    Mar 2006
    Beans
    393

    Re: Beginners programming challenge #29

    I'm assuming you mean the Gregorian Calendar in France, which was adopted in 1582, not England's (and America's) of 1752.
    Just my 0.00000002 million dollars worth,
    Shawn

    Programming is as much about organization and communication as it is about coding.

  2. #12

    Re: Beginners programming challenge #29

    Either way, there is no obvious way to extend the Republican calendar proleptically before 1792, so I think the best solution for earlier dates is probably to just throw an error. This is a beginner's challenge after all.

  3. #13
    Join Date
    Mar 2006
    Beans
    393

    Re: Beginners programming challenge #29

    Quote Originally Posted by trent.josephsen View Post
    Either way, there is no obvious way to extend the Republican calendar proleptically before 1792, so I think the best solution for earlier dates is probably to just throw an error. This is a beginner's challenge after all.
    It seems to me that the hardest part of this challenge is figuring out the policy the program should follow and not the code.
    Just my 0.00000002 million dollars worth,
    Shawn

    Programming is as much about organization and communication as it is about coding.

  4. #14

    Re: Beginners programming challenge #29

    So, a great example of real-world problem solving?

    There are a few wrinkles but I don't think it's a bad challenge overall.
    Last edited by trent.josephsen; May 19th, 2013 at 02:22 AM.

  5. #15
    Join Date
    Nov 2012
    Beans
    39
    Distro
    Ubuntu 13.04 Raring Ringtail

    Re: Beginners programming challenge #29

    I would say that there is no requirement to extend dates backwards past the first year of the revolutionary calendar as there was never any way of determining how this would apply
    Also i mean England's and America's Gregorian Calendar i.e the current one
    Do you know why snow is white? Because it forgot what colour it was.
    You really hate it when people die, yet you're in the army. Why is that?
    I'm in the army so they won't die.
    That contradiction might kill you someday.


  6. #16
    Join Date
    Apr 2013
    Location
    43.49°N 7.46°E
    Beans
    117
    Distro
    Ubuntu 12.04 Precise Pangolin

    Re: Beginners programming challenge #29

    Quote Originally Posted by trent.josephsen View Post
    Years divisible by 100 but not by 400 are not leap years in the Gregorian calendar; this confusion is the source of one somewhat notorious bug in Microsoft Excel. But I read it as meaning that every 4000th year is a common year. So I'd tweak alan9800's algorithm to be
    Code:
    def leap_year(y)
        (4 divides y) and (100 does not divide y) or (400 divides y) and (4000 does not divide y)
    You basically have to write out the whole truth table to verify it, though. Maybe there's a clearer way.
    if this is the meaning of the rule, then the function may be rewritten as follows:
    Code:
    def leap_year(yr)
        return (((yr.remainder(4)==0) and (yr.remainder(100)!=0)) or ((yr.remainder(400)==0) and (yr.remainder(4000)!=0)))
    end

  7. #17

    Re: Beginners programming challenge #29

    Was my pseudocode not good enough for you?

  8. #18
    Join Date
    Apr 2013
    Location
    43.49°N 7.46°E
    Beans
    117
    Distro
    Ubuntu 12.04 Precise Pangolin

    Re: Beginners programming challenge #29

    Quote Originally Posted by trent.josephsen View Post
    Was my pseudocode not good enough for you?
    your code was surely good, it's the interpretation of the rule which is quite unclear.
    I'm still waiting for knowing if it's only the year 4000 to not be leap according to the above rule or even its multiples.

  9. #19
    Join Date
    Nov 2012
    Beans
    39
    Distro
    Ubuntu 13.04 Raring Ringtail

    Re: Beginners programming challenge #29

    if its a multiple of 100 and not 400, or its a multiple of 4000 its not a leap year
    Do you know why snow is white? Because it forgot what colour it was.
    You really hate it when people die, yet you're in the army. Why is that?
    I'm in the army so they won't die.
    That contradiction might kill you someday.


  10. #20
    Join Date
    Jun 2011
    Location
    Paris
    Beans
    55

    Re: Beginners programming challenge #29

    Since it's been a month since this has been posted and there are no other entries, I'll go ahead and post mine.

    Code:
    import java.io.{FileWriter, BufferedWriter}
    import scala.io.Source
    
    
    object DateUtil {
      def isLeapYear(y: Int) = y % 4 == 0 && y % 100 != 0 || y % 400 == 0
    
    
      def gregorianMonthLengths(y: Int) = List(31, 28 + (if(isLeapYear(y)) 1 else 0), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
    }
    
    object FRDate {
      //for some reason, after 1992 and before the 1800s the new year is the 12th of nivose, not the 11th. Also, on the years 1804 and 1808, the gregorian new year was on the 10th of nivose.
      def gregorianNewYear(a: Int) = if(a == 1804 || a == 1808) FRDate(10,04,a-1792) else if(a > 1992 || a < 1800) FRDate(12,04,a-1792) else FRDate(11,04,a-1792)
    
    
      //converts unicode characters into non-unicode ones
      private def deUnicode(c: Char) = {
        c.toLower match {
          case 'é'|'ê' => 'e'
          case 'ô' => 'o'
          case x => x
        }
      }
    
    
    
    
      //An extension method class for ints, all ints now have a romanize function that turns them into a roman numeral
      implicit class RomanInts(val i: Int) extends AnyVal {
    
    
        private def romanize(level: Int, ones: String, fives: String, tens: String) = {
          val s = ((i / level) % 10) match {
            case 4 => ones + fives
            case x if x > 4 & x < 9 => fives
            case 9 => ones + tens
            case _ => ""
          }
          s + (ones*((i / level) % 10 % 5 % 4))
        }
    
    
        def toRomanNumeral = "M" * (i / 1000) + romanize(100, "C", "D", "M") + romanize(10, "X", "L", "C") + romanize(1, "I", "V", "X")
      }
    
    
      //used to match for input parsing, as well as pretty printing the date
      val monthMap = IndexedSeq("Vendémiaire", "Brumaire", "Frimaire",
        "Nivôse", "Pluviôse", "Ventôse",
        "Germinal", "Floréal", "Prairial",
        "Messidor", "Thermidor", "Fructidor", "Sans-culottide")
    
    
      //lazy values are only calculated when they are needed, these won't be calculated until the parser tries to parse ascii versions of the month
      lazy val nonUnicodeMonthMap = monthMap map (_ map (deUnicode))
    
    
      val dayMap = IndexedSeq("Primidi", "Duodi", "Tridi", "Quartidi", "Quintidi", "Sextidi", "Septidi", "Octidi", "Nonidi", "Décadi")
    
    
      lazy val nonUnicodeDayMap = dayMap map (_ map (deUnicode))
    
    
      //days for the "13th" month
      val sansCulottides = IndexedSeq("La Fête de la Vertu", "La Fête du Génie", "La Fête du Travail", "La Fête de l'Opinion",
        "La Fête des Récompenses", "La Fête de la Revolution")
    
    
      lazy val nonUnicodeSansCulottides = sansCulottides map (_ map (deUnicode))
    
    
      //checks both unicode and non-unicode versions of the months, followed by an attempt to parse a roman numeral
      // => IndexedSeq[String] means that it doesn't evaluate the argument until it's used in the function. This is to keep lazy vals such as nonUnicodeMonthMap from being calculated until absolutely necessary.
      private def getIndexOrConvertNumeral(s: String, nuL: => IndexedSeq[String], l: => IndexedSeq[String]) = {
        (l.map(_.toLowerCase).indexOf(s.toLowerCase) match {
          case -1 => nuL.indexOf(s.toLowerCase)
          case i => i
        }) match {
          case -1 => s match {
            case romanRegex(s) => fromRomanNumeral(s)
            case _ => throw new Exception("String doesn't match any form of valid input!")
          }
          case i => i + 1
        }
      }
    
    
      //takes a string matching the format of either regex.
      def parse(s: String) = {
        val regex = """D[eé]cade\s+([MCDXLIV]*),\s*(\p{L}+|[XVI]+)\s+de\s+(\p{L}+|[XVI]+)\s+de\s+l'Ann[ée]e\s+([MCDXLIV]+)\s+de\s+la\s+R[ée]volution\.?""".r
        val sCulottRegex = """(La F[êe]te .+)\s+de\s+l'Ann[ée]e\s+([MCDXLIV]+)\s+de\s+la\s+R[ée]volution""".r
        s match {
          case regex(dec, jour, mois, annee) => {
            val week = if(dec.isEmpty) 0 else fromRomanNumeral(dec)
            val day = getIndexOrConvertNumeral(jour, nonUnicodeDayMap, dayMap)
            val month = getIndexOrConvertNumeral(mois, nonUnicodeMonthMap, monthMap)
            val year = fromRomanNumeral(annee)
    
    
            FRDate(day+ (week - 1) * 10, month, year)
          }
          case sCulottRegex(jour, annee) => {
            val day = (sansCulottides map (_.toLowerCase) indexOf (jour.toLowerCase) match {
              case -1 => nonUnicodeSansCulottides map (_.toLowerCase) indexOf (jour.toLowerCase)
              case i => i
            }) match {
              case -1 => throw new Exception("String doesn't match any form of valid input!")
              case i => i + 1
            }
    
    
            val year = fromRomanNumeral(annee)
    
    
            FRDate(day, 13, year)
          }
          case _ => throw new Exception("Invalid date!!")
        }
    
    
      }
    
    
      //converts a roman numeral into an int
      private def fromRomanNumeral(s: String) = {
        val startCount = s.count(_ == 'M')*1000 + s.count(_ == 'C')*100 + s.count(_ == 'D')*500 +
          s.count(_ == 'X') * 10 + s.count(_ == 'L') * 50 + s.count(_ == 'V') * 5 + s.count(_ == 'I') * 1
    
    
        //turns a string like "abc" into a list like List(('a','b'),('b','c'))
        val pairs = s zip s.tail
    
    
        //removes the extra stuff added by startcount interpreting things like IV as 6
        startCount - pairs.count{case (a,b) => a == 'I' && (b == 'X' || b == 'V')} * 2 -
          pairs.count{case (a,b) => a == 'X' && (b == 'L' || b == 'C')} * 20 -
          pairs.count{case (a,b) => a == 'C' && (b == 'D' || b == 'M')} * 200
      }
    
    
      val romanRegex = """([MCDXLIV]+)""".r
    
    
      //takes a date of DD/MM/YYYY gregorian format and makes it into a FRDate.
      def fromGregorianDate(d: Int, m: Int, y: Int) = {
        val newYear = gregorianNewYear(y)
        //days since 1/4/y in the French Revolutionary Calendar
        val days = ((m-1,0) /: DateUtil.gregorianMonthLengths(y)) {case ((remMonths, totalDays), daysInMonth) => if(remMonths > 0) (remMonths-1, totalDays + daysInMonth) else (0, totalDays)}._2 + (d-1)
    
    
        val moisPt1 = 4 + ((days + newYear.jour - 1)/30)
    
    
        val frNewYearPassed = moisPt1 > 13 || moisPt1 == 13 && (days+newYear.jour) % 30 > (if(DateUtil.isLeapYear(y)) 6 else 5)
    
    
        //if the number of months reported by moisPt1 is 13 and the numbers of days in the month is greater than 6 or 5
        // (depending on whether or not it's a leap year), remove the gregorianNewYear - French revolutionary new year of
        // the next year days from days to make adjusted days, otherwise, adjusted days == days
        val adjustedDays = if(frNewYearPassed) days - FRDate(01,01, y+1-1792).daysBetween(newYear) else days + newYear.jour - 1
    
    
        // calculation of the day of the month in the french calendar
        val jour = adjustedDays % 30 + 1
    
    
        //calculation of the month of the year in the french calendar
        val mois = if(frNewYearPassed) adjustedDays / 30 + 1 else moisPt1
    
    
        //calculation of the year in the french calendar
        val année = y - 1792 + (if(frNewYearPassed) 1 else 0)
    
    
    
    
        FRDate(jour,mois,année)
      }
    }
    
    
    //A date on the French revolutionary calendar
    // jour, mois, and année are the french words for day, month, and year
    case class FRDate(jour: Int, mois: Int, année: Int) {
      require(jour <= 30 && jour >= 1, s"day out of range: $jour")
      require(mois <= 13 && mois >= 1, "month out of range")
      require(année >= 1, s"year before 1: $année")
    
    
      import FRDate.RomanInts
    
    
    
    
      // lazy values are not calculated until requested.
      lazy val leapYear = DateUtil.isLeapYear(année)
    
    
      lazy val leapDay: Option[FRDate] = if(leapYear) Some(FRDate(06, 13, année)) else None
    
    
      lazy val gregorianNewYear = FRDate.gregorianNewYear(année+1792)
    
    
      def avecAnnée(a: Int) = FRDate(jour, mois, a)
    
    
      def avecMois(m: Int) = FRDate(jour, m, année)
    
    
      def avecJour(j: Int) = FRDate(j, mois, année)
    
    
      //used to determine if one date is before another
      def before(other: FRDate) = if(année < other.année) true else if (année == other.année) {
        if(mois < other.mois) true else if (mois == other.mois) {
          if(jour < other.jour) true else false
        } else false
      } else false
    
    
      //calculates the number of days between two dates
      def daysBetween(other: FRDate) = {
        //calculates the number of days from years between these dates. if there is no difference in year, this returns the calculated day difference
        def dayHelper(m: FRDate, s: FRDate): Int = {
          if(m.année != s.année) 365 + (if(m.leapYear) 1 else 0) + dayHelper(m avecAnnée(m.année - 1), s)
          else _monthAdjuster(m,s) + _dayAdjuster(m avecMois s.mois, s)
        }
    
    
        //calculates the days from months
        def _monthAdjuster(m: FRDate, s: FRDate): Int = m.mois * 30 - s.mois * 30
    
    
        def _dayAdjuster(m: FRDate, s: FRDate): Int = m.jour - s.jour
    
    
        val (m,s) = if(this before other) (other, this) else (this, other)
    
    
        dayHelper(m,s) - (m.leapDay map (lD => if((m before lD) && s.année - m.année >= 1) 1 else 0) getOrElse(0))
      }
    
    
      //the year of this FRDate in the gregorian calendar
      lazy val gregorianYear = 1791 + année + (if(this before gregorianNewYear) 0 else 1)
    
    
    
    
      //the month of this FRDate in the gregorian calendar
      lazy val gregorianMonth = {
        val refPoint = if(this before gregorianNewYear) gregorianNewYear avecAnnée(année - 1) else gregorianNewYear
    
    
        val daysSince = (refPoint daysBetween this) + 1
    
    
        val m = DateUtil.gregorianMonthLengths(gregorianYear)
    
    
        (m foldLeft (0 -> daysSince)) {case (tup, days) => (tup._1 + (if(tup._2 > 0) 1 else 0), tup._2 - days) }._1
      }
    
    
      //the day of this FRDate in the gregorian calendar
      lazy val gregorianDay = {
        val refPoint = if(this before gregorianNewYear) gregorianNewYear avecAnnée(année - 1) else gregorianNewYear
    
    
        val daysSince = (refPoint daysBetween this) + 1
    
    
        (DateUtil.gregorianMonthLengths(gregorianYear).take(gregorianMonth - 1) foldLeft daysSince) (_-_)
      }
    
    
      def gregorianDateString = s"$gregorianDay/$gregorianMonth/$gregorianYear"
    
    
      override def toString = if(mois != 13)
        s"Décade ${((jour - 1)/ 10 + 1).toRomanNumeral}, ${FRDate.dayMap((jour - 1) % 10)} de ${FRDate.monthMap(mois-1)} de l'Année ${année.toRomanNumeral} de la Révolution"
      else
        s"${FRDate.sansCulottides(jour - 1)} de l'Année ${année.toRomanNumeral} de la Révolution"
    }
    
    object Cal {
    
    
      def main(args: Array[String]) {
    
    
        //converts either gregorian dates of the format DD/MM/YYYY or french republic dates of the format listed in the challenge into the corresponding French republic date or Gregorian date
        def interpreter(l: String) = {
          val r = """\s*([0-3]?\d)/([01]?\d)/(\d{4})""".r
          l match {
            case "exit" => System.exit(0); ???  //??? is there as a return type of Nothing. If it's accessed, an error will occur, which shouldn't happen because the program should have exited already
            case r(day,month,year) => try {
              FRDate.fromGregorianDate(day.toInt,month.toInt,year.toInt).toString
            } catch { case e: Exception => e.getMessage }
            case _ => try {
              FRDate.parse(l).gregorianDateString
            }
          }
        }
    
    
        args match {
          case Array("-i") => {
            Source.fromInputStream(System.in)(io.Codec.UTF8).getLines() foreach {l => println(interpreter(l))}
          }
          case Array(file) => Source.fromFile(file)(io.Codec.UTF8).getLines() foreach {l => println(interpreter(l))}
          case Array(file, "-o", outFile) => {
            val res = Source.fromFile(file)(io.Codec.UTF8).getLines() map interpreter
            val os = new BufferedWriter(new FileWriter(outFile))
    
    
            res.foreach {l => os.write(l + "\n")}
            os.flush()
          }
          case _ => println("no match" + args.fold("") (_ + _))
        }
      }
    }
    This code does not use standard lib calendars or any other kind of calendar. As far as I can tell so far, it's completely accurate, but calendars are tough so it may not be.

    This challenge was tough for many reasons, but the day of the gregorian new year in the french republican calendar changing 6 times in the past 200 years (and seemingly at random) didn't help.

    My project takes DD/MM/YYYY for gregorian dates and the same format as in the op for French Revolutionary dates (though it also matches on roman numerals for the day and month field, no decade field specified with a 1-30 roman numeral for the day, 1-infinite whitespace between all words, optional accent aigu and accent circonflexe, etc.)
    Last edited by Leuchten; June 9th, 2013 at 05:39 PM.

Page 2 of 2 FirstFirst 12

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •