PONOMAR

An Application Programming Interface for Liturgical Computations in the Perl Language

Created By Aleksandr Andreev Yuri Shardt Slavonic Computing Initiative

St. Petersburg 2018 © 2012–2018 Aleksandr Andreev and Yuri Shardt

This document is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. To view a copy of this license, visit the CreativeCom- mons website.

The software is provided “as is”, without warranty of any kind, express orim- plied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copy- right holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software.

1 Contents

1 Introduction to the Ponomar API 3

2 The Ponomar Class 5

3 Ponomar::Bible 8

4 Ponomar::Cu 9

5 Ponomar::I18n 10

6 Ponomar::JDate 11

7 Ponomar::Reading 17

8 Ponomar::Saint 19

9 Ponomar::Service 21

10 Ponomar::Sunrise 23

11 Ponomar::Util 25

2 1 Introduction to the Ponomar API

Ponomar is an Object Oriented API; the set of Ponomar classes is designed to eliminate the need for low-level interaction with XML (or YAML) data and CMDs (liturgical CoMmanDs). Basic implementations of calendar software can be writ- ten using this API with about 5 lines of code. More complex implementations will require array-based manipulation of Ponomar objects (as in, lots of grep, map and foreach); but it beats working with the XML directly. Note that the API is designed to be format independent. That is, data outputed by the API is strictly Unicode text; it lacks formatting or markup. (THIS IS NOT ACTUALLY TRUE: certain XML files in Ponomar contain markup, e.g., tags. This is problematic and should be considered a defect of theAPI). Note that Ponomar works with Julian Dates (actually, Julian days). Unless a method specifically contains the words ‘Gregorian’ or ‘Milankovich’, all calcu- lations and dates are according to the . The non-OOO Ponomar::Util class provides handy functions for generating common dates including Today and any year’s Pascha without having to deal with the Ponomar::JDate object. See the documentation for Ponomar::Util for details.

SYNOPSIS

usePonomar; usePonomar::JDate;

$ponomar=newPonomar(newPonomar::JDate(1,1,2001),’en’); printjoin(’;’,map{$_->getKey(’Name’)->{Nominative}} $ponomar->getSaints(’menaion’));

Workflow Upon initialization, Ponomar immediately loads the Ponomar::I18n helper class, which handles all Internationalization (I18n) support for Ponomar. The load() method of Ponomar::I18n loads into memory Ponomar locale data. Locale data is stored in YAML (YAML A’int a Markup Language) format in the file locales.yml. The user begins by creating a Ponomar object. The Ponomar object takes two initial parameters, the $date and the $locale. The $locale is an ISO 639- 2 language code (string). The $date is an instance of the Ponomar::JDate class (see documentation for Ponomar::JDate), which is in essence a glorified Integer (Julian Day object).

3 The constructor of Ponomar calls the init() method, which loads XML data for the given day and locale. The reading of XML DATA proceeds in the following FIFO (First In First Out) order:

Step 1. The relevant top-level saint files are read for / Trio- dion. This creates a set of Ponomar::Saint objects with two properties:

a. The CId (Commemoration Id) b. The associated JDate object c. The locale

Important: Ponomar Saints are not date-independent entities: saint data may contain CMDs which are date dependent. b. and c., above, are inher- ited from the underlying Ponomar object.

Step 2. At this stage, the Tone is set.

Step 3. Loading of Saint data for Pentecostarion / Triodion Immediately upon construction, the Ponomar::Saint object loads all top- down XML files associated with CId. This will load the NAMEs of theSaint as well as the LIFEs and the SERVICEs.

Step 4. Loading of service data for Pentecostarion / Triodion Presently, SERVICE tags are more or less wrappers. The only useful infor- mation obtained from the SERVICE tag is the Type (more properly, “rank”) of the service. This is used to set the Type property of the Saint object. SERVICE objects also handle Commands (see the documentation of Pono- mar::Service for details). Upon encountering individual services (VESPERS, MATINS, LITURGY, etc), Ponomar::Saint creates a new instance of Ponomar::Service. The Ser- vice object contains only two properties, the Type (which is type of ser- vice, more properly, the type of “office”, e.g., ‘vespers’, ‘matins’, ‘liturgy’, etc) and the dRank of the service (which is the rank and is inheretied from the Type property of the Ponomar::Saint parent). Note that dRank is not the same as the day’s dRank, computed below. The confusion in terminology is unfortunate. BUG NOTICE: As presently written, the API accepts only one SERVICE per Saint. That is, only one type of service may be read in, e.g., readinga

4 second VESPERS service into a Saint will create two Service objects that are completely indistinguishable. This means that subsequent Scripture readings, if any, will be assigned to both Service objects. This will have unforseen consequences, especially for the sorting algorithm of Scriptures. This is a bug.

Step 5. Loading of Menaion data Steps 1, 3 and 4 are repeated for the Menaion-based data.

Step 6. The dRank (rank of the day) is set.

Presently, dRank is the max of all ranks available for a given day. As cur- rently written, the API does not allow the user to select between service or commemoration alternatives.

Step 7. The day’s fasting information is computed.

Initialization stops at this point and your Ponomar object is ready to work. NOTE: The initial initialization does not handle suppression or transfer of readings. This is handled by calling the executeCommands method of relevant Service objects. See the documentation for Ponomar::Service for details.

2 The Ponomar Class

METHODS new($date, $language, [$GS] )

Creates a new Ponomar object for Julian date $date and language $language, using Gospel Selection algorithm $GS. Runs the initial initialization pro- cess, reading XML for this $date. Returns a reference to the new object. E.g.:

$ponomar = new Ponomar(Ponomar::Util::getToday(), ’en’)

The deprecated paramater $GS determines if the Lucan Jump is used in the selection of scriptures. As of August 2018, non-Lucan Jump implementa- tions are no longer supported ($GS is always treated as 1 regardless of user input).

5 getSaints( [$src] ) Returns the Array of Saint objects associated with the Ponomar object Op- tional parameter $src can take on one of two values: ’pentecostarion’ or ’menaion’ and conditions the returned array on the Source of the com- memoration. Note that for the purposes of Source, Triodion-based commemorations are called ’pentecostarion’, i.e., ’pentecostarion’ refers to everything based off the . E.g.:

$ponomar->getSaints( ’pentecostarion’ );

Returns those saints who came from the pentecostarion. getTone() Returns the Tone of the day as a String, based on the locale of the object. E.g.: In en locale, returns ’Tone VIII’ getFastingInstructions() Returns the Fasting instructions of the day as a String, basd on the locale of the object. E.g. In en locale, returns ’Xerophagy’ getFastingCode() Returns the raw code of the fasting instruction, e.g., 000001. E.g., the following test if meat is allowed on a given day:

split(//, $ponomar->getFastingCode())[0] == 1 getReadings( [$type, $Src] ) Returns an array with references to all Readings objects associated with the date

Optional parameters $type and $Src

$Src limits returned array to source of commemoration (e.g., pentecostar- ion, menaion); $type limits returned array to type of service (e.g., vespers, liturgy) loadBible([$version])

6 Loads the Bible in this language. Optional parameter $version specifies which version of the Bible to load if multiple versions are available in a language. If $version is not specified, the default version is used.

You can get a list of available versions by calling getBibleVersions().

Returns a reference to a new Ponomar::Bible object.

Constant: NO_LUCAN_JUMP Deprecated.

Constant: LUCAN_JUMP Deprecated. setLectionaryStyle ( $style ) Deprecated. getLectionaryStyle() Deprecated.

7 3 Ponomar::Bible

Ponomar::Bible - a Bible object for the Ponomar API

METHODS new( Lang => $language )

Creates a new instance of the Bible. Parameter Lang specifies the language. Reads the appropriate bible.xml file and loads the Bible information for that language into memory. getBookNameShort( $book )

Returns the short form of the name of the book $book in the current Bible getBookName ( $book )

Returns the full form of the name of the book $book in the current Bible exists ( $book )

Returns true if the book $book exists in the current Bible getPassage ($reading) Returns a set of verse objects with the text of the Bible passage given by the Reading object $reading

8 4 Ponomar::Cu

Ponomar::Cu -- Church Slavonic support in the Ponomar API

DESCRIPTION This is not an Object Oriented class, but rather a set of utility functions forwork- ing with Church Slavonic texts. This code is DEPRECATED and will be deleted. Use Lingua::CU class instead. TODO: The Ponomar API needs to be rewritten so that everything uses Lingua::CU.

METHODS hip2unicode ( $string )

Takes $string, a string in HIP (Hyperinvariant Presentation), and converts it to Unicode ucs2unicode ( $string )

Takes $string, a string in UCS (Universal Church Slavonic), and converts it to Unicode resolveTitli ( $string )

Takes $string, a string of Church Slavonic text in Unicode, and resolves any Titli present, including numerals. cu2arabic ( $string )

Takes $string, a numeral in Church Slavonic, and converts it to a numeral in Arabic numerals. arabic2cu ( $number )

Takes $number, a number, and returns its representation in Slavonic nu- merals.

9 5 Ponomar::I18n

Ponomar::I18n - Internationalization components for Ponomar

DESCRIPTION This class handles I18n in Ponomar. Note that at least Perl 5.007 is required, as we must have Unicode support.

METHODS load($location) Loads the YAML data in the file, the path of which is $location getLocaleKey($key, $locale) Returns the appropriate $key in the given $language; e.g.:

Ponomar::I18n::getLocaleKey(’february’, ’en’)

returns ’February’ getAvailableLanguages() Returns an array with the available languages in the Ponomar YAML file unload() Unloads the localization, freeing up the memory and destroying the inter- nal YAML::Tiny object. dateToStringFull($jdate, [$locale]) Returns the fullstring representation of $jdate, a Ponomar::JDate object, in the specified locale. If locale is not specified, en is assumed. dateToString($jdate, [$locale]) Returns a short string representation of $jdate, a Ponomar::JDate object, in the specified $locale. If $locale is not specified, ’en’ is assumed. dateToStringGregorian($jdate, [$locale]) Returns a string representation of $jdate, a Ponomar::JDate object, in the specified $locale on the Gregorian calendar. If $locale is not specified, en is assumed.

10 6 Ponomar::JDate

Ponomar::JDate - A module for working with dates on the (proleptic) Julian Cal- endar

SYNOPSIS

use Ponomar::JDate; $date = new Ponomar::JDate(2, 1, 2001); # RETURNS February 1, 2001 $date2 = $date->addDays(1); # returns February 2, 2001 $date2->equals(new Ponomar::JDate(2, 2, 2001)); # RETURNS true

By convention January is treated as Month 1. Sunday is treated as day of week 0. January 1 is treated as day of year 0.

METHODS new($julian_day) OR new($month, $day, $year) Creates a new instance of Ponomar::JDate either set to Month, Day, Year where Month, Day, Year is a calendar date on the Julian Calendar or to the Julian Day. The months begin with 1 for January and run to 12 for December. Though years BC are generally not used, if necessary, the code is defined so that the $year before AD 1 is -1 (NOT 0). The $day may have a fractional com- ponent. Note that Julian Days begin at Noon UTC, so we usually have a 0.5 around, which is a bit annoying, but is done this way to keep all formulae in this code the same as in Meuss, Astronomical Algorithms (1st edition). getYear() Returns the Year of the JDate object Example:

$date = new Ponomar::JDate(2, 1, 2001); $date->getYear(); # returns 2001 getYearAM() Returns the Year from the (Byzantine) Creation of the World (anno mundi). The Creation of the World took place on March 1, 5508 BC.

11 getMonth() Returns the Month of the JDate object. Example:

$date = new Ponomar::JDate(2, 1, 2001); $date->getMonth(); # returns 2

NB: January is month 1 getDay() Returns the day of the month of the JDate object. Example:

$date = new Ponomar::JDate(2, 1, 2001); $date->getDay(); # returns 1 getHour() Returns the hour component of the JDate object (which may be fractional). This is useful for such things as vernal equinox, sunrize and moon calcu- lations. The result is always in hours since midnight UTC, and by convention the JDay starts at noon UTC. getMinute() Returns the minute component of the JDate object (which may be frac- tional). This is useful for such things as vernal equinox, sunrize and moon calcu- lations. The result is always in minutes since the last hour UTC. getSecond() Returns the second component of the JDate object (which may be frac- tional). This is useful for such things as vernal equinox, sunrize and moon calcu- lations. The result is always in seconds since the last minute UTC.

12 getDayOfWeek() Returns the day of the week of the JDate object. Example:

$date = new Ponomar::JDate(2, 1, 2001); $date->getDayOfWeek(); # returns 2

NB: Sunday is day of week 0. getDayOfWeekString Returns the day of the week as a string. getDoy() Returns the day of the year (doy) of a JDate object. Note that January 1 is doy 0. February 29, if it exists, is doy 366. getYearGregorian() Returns the year of the JDate object according to the (proleptic) Gregorian calendar. getMonthGregorian() Returns the month of the JDate object according to the (proleptic) Grego- rian calendar. getDayGregorian() Returns the day of the Month of the JDate object according to the (prolep- tic) Gregorian calendar. getDaysSince($date)

Returns the number of days since $date, another JDate object. getDaysUntil($date)

Returns the number of days until $date, another JDate object. getWeeksSince($date)

Returns the number of weeks since $date, another JDate object.

13 getWeeksUntil($date)

Returns the number of weeks until $date, another JDate object. addDays($integer)

Returns a new JDate object, advanced by $integer days. addOneDay() Returns a new JDate object, advanced by one day. addMonths($integer)

Returns a new JDate object, advanced by $integer months. subtractDays($integer)

Returns a new JDate object, diminished by $integer days. subtractOneDay() Returns a new JDate object, diminished by one day. subtractMonths($integer)

Returns a new JDate object, diminished by $integer months. equals($date)

Returns true of this JDate object and the object $date are the same Julian day. Returns false otherwise. before($date)

Returns true if self is before $date. after($date)

Returns true if self is after $date. getJulianDay() Returns the Julian day of the object. getNearestSunday() Returns a new JDate object with the nearest Sunday to a JDate object. NB: if the JDate object is a Sunday, returns itself.

14 getPreviousSunday() Returns a new JDate object with the previous Sunday to a JDate object. NB: if the JDate object is a Sunday, returns one week before. getNextSunday() Returns a new JDate object with the next Sunday to a JDate object. NB: if the JDate object is a Sunday, returns one week later. times($n)

Returns the a new JDate object with the Julian Day multiplied by $n. Prob- ably not very useful. divide($n)

Returns the closest JDate object to the current JDate object divided by $n. This may be marginally useful in finding out things like midpoints oftime periods. module($n)

Returns the JDate object mod $n. Probably completely useless. getSunrise($longitude, $latitude, $TimeZone, [$DST, $ALT]) Return the sunrise/sunset for a given day.

Eastern longitude is entered as a positive number Western longitude is entered as a negative number Northern latitude is entered as a positive number Southern latitude is entered as a negative number

Example:

($sunrise, $sunset) = $date->getSunrise($longitude, $latitude, $TimeZone, $DST, $ALT);

Returns the sunrise and sunset times, in HH:MM format. Note: $Time Zone is the offset from UTC and $ is daylight saving time (1 means DST is in effect and 0 means it is not). If $ALT is not specified, a default altitude of -.0833 is used. Note that adding 1 to $TimeZone during DST and specifying $DST as 0 is the same as indicating the Time Zone correctly and specifying $DST as 1.

15 There are a number of values of $ALT to choose from. The default is -0.833 because this is what most countries use. Here is the list of other common values:

0 degrees Center of Sun’s disk touches a mathematical horizon -0.25 degrees Sun’s upper limb touches a mathematical horizon -0.583 degrees Center of Sun’s disk touches the horizon; atmospheric refraction ac- counted for -0.833 degrees, DEFAULT Sun’s supper limb touches the horizon; atmospheric refraction ac- counted for -6 degrees, CIVIL Civil twilight (one can no longer read outside without artificial illu- mination) -12 degrees, NAUTICAL Nautical twilight (navigation using a sea horizon no longer possible) -15 degrees, AMATEUR Amateur astronomical twilight (the sky is dark enough for most as- tronomical observations) -18 degrees, ASTRONOMICAL Astronomical twilight (the sky is completely dark) getMilankovichYear() Returns the year of the JDate object according to the (proleptic) Mi- lankovich calendar. getMilankovichMonth() Returns the month of the JDate object according to the (proleptic) Mi- lankovich calendar. getMilankovichDay() Returns the day of the JDate object according to the (proleptic) Mi- lankovich calendar.

16 7 Ponomar::Reading

Ponomar::Reading - a scripture reading object for the Ponomar API.

METHODS new( %attributes ) Creates a new Reading object. A Reading object has the following elements:

Reading => The Reading (e.g., Gen_1:1-13) Pericope => The Pericope number (e.g., 103 -- OPTIONAL) EffWeek => The effective week of the reading (e.g., 17 -- OPTIONAL) Type => The Type of reading. This is use- less, except for Liturgy, where it must be one of apostol or gospel. saint => The Assigned Saint or Commemoration ID. getReading()

Returns the Reading of the object (e.g., Gen_1:1-13) setReading($string)

Sets the Reading of the object to $string. equals($other)

Tests to see if this Reading object is equal to another object $other. The following are equivalent:

1. $ReadingsA is equal to $ReadingsB

2. $ReadingsA->{Reading} = $ReadingsB->{Reading} and $ReadingsA->{Saint} = $ReadingsB->{Saint}

Note that this method has been overloaded as ==; thus, you can write $ReadingsA == $ReadingsB. notEquals($other) Tests to see if this Reading object is not equal to another Reading object $other. The following are equivalent:

1. $ReadingsA is not equal to $ReadingsB.

17 2. $ReadingsA->{Reading} != $ReadingsB->{Reading} OR $ReadingsA->{Saint} != $ReadingsB->{Saint}

Note that this method has been overloaded as != Thus, you can write $ReadingsA != $ReadingsB.

18 8 Ponomar::Saint

Ponomar::Saint : a Commemoration / Saint object for the Ponomar API.

METHODS new (%attrs)

Creates a new Saint object and runs init(), loading XML data for this Saint. Saint objects have the following properties

CId : the Commemoration ID. Name: A hash of Names for the Saint, which contains keys Nominative, Genetive, Short, etc. Src: the source of the com- memoration (one of triodion, pentecostarion, or menaion). Services: an array containing the Services defined for this Saint. CURRENT LIMITATION: ONLY ONE SET OF SERVICES IS ALLOWED FOR A SAINT. THIS NEEDS TO BE FIXED! getKey($key)

Returns the value of the $key in this Saint, e.g.,

$self->getKey(’CId’)

returns the CId of the Saint setKey($key, $value) Sets the $key of the Saint equal to $value, e.g.,

$self->setKey(’CId’, 9000) equals($other)

Tests to see if this saint is equal to $other saint The following are equivalent:

1. $saintA is equal to $saintB.

2. $saintA->{Cid} is equal to $saintB->{Cid}.

This method has been overloaded as ==, so you can write $saintA == $saintB.

19 notEquals($other)

Tests to see if this saint is not equal to $other saint The following are equivalent:

1. $saintA is not equal to $saintB.

2. $saintA->{Cid} is not equal to $saintB->{Cid}.

This method has been overloaded as !=, so you can write $saintA != $saintB. stringify() Returns a string. By definition, the stringification of the Saint is hisCId. This method is overloaded as ””, so you can write ”$saint”. addService($type)

Adds a Service object of type $type to self. Type here is the type of service, e.g., one of vespers, matins, liturgy, etc. getServices( [$type] )

Returns an array of Service objects associated with this saint. If $type is specified, returns only those Service objects of a particular $type. $type is the Type of service, e.g., one of vespers, matins, liturgy, etc. hasServices( [$type] ) Returns true if the Saint has associated Service objects and false otherwise. If $type is specified, returns true if Saint has associated Service of $type and false otherwise. hasIcons ( [$lang] )

Returns true if the Saint has associated Icons. If $lang is specified, returns true if the Saint has associated Icons in language $lang. Otherwise, returns false.

20 9 Ponomar::Service

Ponomar::Service - A service object for the Ponomar API.

METHODS new(%properties) Creates a new Service object Service objects have the following properties

type: The type of service (e.g., e.g., oneof vespers, matins, liturgy, etc.) dRank: The Rank of this service (NOT the rank of the day). parent: a ref- erence to a Saint object which begat this Service object. addReading($reading)

Assings a Reading object $reading to the service. deleteReading($reading)

Removes a Reading object $reading from the service. getReadings() Returns the set of Readings objects assigned to the service hasReadings() Returns true if the Service has Readings objects assigned getType()

Returns the Type of the service (e.g., one of vespers, matins, liturgy, etc.). clearCommands ( [$commandName] )

Removes all liturgical commands named $commandName from this service object. If $commandName is undef, removes all liturgical commands. Returns nothing. addCommands ( $name, $value )

Given a liturgical command with name $name and boolean test $value, adds this command to the list of available commands for this Service. See the documentation for DivineLiturgy.xml for the list of allowable com- mand names. Note that $value is not checked for correct syntax. (THIS IS

21 A BUG). If either $name or $value is undef, the method will croak. Returns nothing. execCommands( $dRank )

Given the $dRank of the day, executes a set of commands associated with this service. These commands may generate instructions, rearrange read- ings or do other operations. Presently, only one set of Commands is supported, this is the set of com- mands recorded in DivineLiturgy.xml. Thus, if the type of the service ob- ject is liturgy, the file DivineLiturgy.xml is processed and readings are suppressed or transferred as necessary.

If the type is not liturgy, nothing is done and undef is returned.

If $self->{parent} is not a reference to a Saint, which is the parent of this Service, the method will croak.

22 10 Ponomar::Sunrise

Ponomar::Sunrise - Module that provides functions for the computation of Sun- rise and Sunset. Do not call directly, rather use JDate::getSunrise().

DESCRIPTION

Modified version of Astro::Sunrise to work with dates on the Julian Calen- dar. Dependency on DateTime removed. Removed wrapper methods, which have been moved to Ponomar::JDate. Basic computation kept in tact.

SPECIAL THANKS

Modified from Astro::Sunrise, which is by Ron Hill rkhill@firstlight.net.

COPYRIGHT and LICENSE Here is the copyright information provided by Ron Hill in Astro::Sunrise: Written as DAYLEN.C, 1989-08-16. Modified to SUNRISET.C, 1992-12-01. Copyright Paul Schlyter, 1989, 1992. Released to the public domain by Paul Schlyter, December 1992. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ”Software”), to deal inthe Software without restriction, including without limitation the rights to use,copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject tothe following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED ”AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WAR- RANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN AC- TION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

23 SEE ALSO Astro::Sunrise

24 11 Ponomar::Util

Ponomar::Util - Exports utility functions for Ponomar API.

DESCRIPTION This is not an Object Oriented class, but rather is a set of utility functions forthe Ponomar API. All useful methods are exported from this class via the Exporter interface.

METHODS findBottomUp ($language, $file) THIS ALGORITHM IMPLEMENTS BOTTOM-UP READING OF FILES IN THE XML PATH. THE FULL IMPLEMENTATION IS DESCRIBED BY YURI IN ”A Description”, p. 27.

Basically, we begin with basepath//