[Scipy-svn] r2975 - trunk/Lib/sandbox/timeseries/src
scipy-svn at scipy.org
scipy-svn at scipy.org
Wed May 9 13:09:42 EDT 2007
Author: mattknox_ca
Date: 2007-05-09 12:09:36 -0500 (Wed, 09 May 2007)
New Revision: 2975
Added:
trunk/Lib/sandbox/timeseries/src/c_lib.c
trunk/Lib/sandbox/timeseries/src/c_tdates.c
trunk/Lib/sandbox/timeseries/src/c_tseries.c
Log:
new files related to restructuring of C code
Added: trunk/Lib/sandbox/timeseries/src/c_lib.c
===================================================================
--- trunk/Lib/sandbox/timeseries/src/c_lib.c 2007-05-09 17:08:29 UTC (rev 2974)
+++ trunk/Lib/sandbox/timeseries/src/c_lib.c 2007-05-09 17:09:36 UTC (rev 2975)
@@ -0,0 +1,204 @@
+#include "c_lib.h"
+#include "arrayobject.h"
+
+// Numpy UFUNCS
+static PyObject *NP_ADD, *NP_MULTIPLY, *NP_SUBTRACT, *NP_SQRT,
+ *NP_GREATER, *NP_GREATER_EQUAL;
+
+/*********************************************************
+** Convenience wrappers for numpy UFUNCS **
+*********************************************************/
+PyObject*
+np_add(PyObject *left_val, PyObject *right_val) {
+
+ PyObject *result;
+
+ result = PyObject_CallFunction(
+ NP_ADD, "OO",
+ (PyArrayObject*)left_val,
+ right_val);
+ return result;
+}
+
+PyObject*
+np_subtract(PyObject *left_val, PyObject *right_val) {
+
+ PyObject *result;
+
+ result = PyObject_CallFunction(
+ NP_SUBTRACT, "OO",
+ (PyArrayObject*)left_val,
+ right_val);
+ return result;
+}
+
+PyObject*
+np_multiply(PyObject *left_val, PyObject *right_val) {
+
+ PyObject *result;
+
+ result = PyObject_CallFunction(
+ NP_MULTIPLY, "OO",
+ (PyArrayObject*)left_val,
+ right_val);
+ return result;
+}
+
+PyObject*
+np_sqrt(PyObject *val) {
+ return PyObject_CallFunction(NP_SQRT, "(O)", val);
+}
+
+int np_greater(PyObject *left_val, PyObject *right_val) {
+
+ PyObject *temp;
+ int result;
+
+ temp = PyObject_CallFunction(
+ NP_GREATER, "OO",
+ (PyArrayObject*)left_val,
+ right_val);
+
+ result = (int)PyInt_AsLong(temp);
+ Py_DECREF(temp);
+ return result;
+}
+
+int np_greater_equal(PyObject *left_val, PyObject *right_val) {
+
+ PyObject *temp;
+ int result;
+
+ temp = PyObject_CallFunction(
+ NP_GREATER_EQUAL, "OO",
+ (PyArrayObject*)left_val,
+ right_val);
+
+ result = (int)PyInt_AsLong(temp);
+ Py_DECREF(temp);
+ return result;
+}
+
+char *str_uppercase(char *str) {
+ if (str) {
+ int i, len=strlen(str);
+ char *result;
+ if((result = malloc((len + 1)*sizeof(char))) == NULL) {
+ return (char *)PyErr_NoMemory();
+ }
+ strcpy(result, str);
+
+ for (i=0;i<len;i++) {
+ switch(result[i])
+ {
+ case 'a': { result[i]='A'; break; }
+ case 'b': { result[i]='B'; break; }
+ case 'c': { result[i]='C'; break; }
+ case 'd': { result[i]='D'; break; }
+ case 'e': { result[i]='E'; break; }
+ case 'f': { result[i]='F'; break; }
+ case 'g': { result[i]='G'; break; }
+ case 'h': { result[i]='H'; break; }
+ case 'i': { result[i]='I'; break; }
+ case 'j': { result[i]='J'; break; }
+ case 'k': { result[i]='K'; break; }
+ case 'l': { result[i]='L'; break; }
+ case 'm': { result[i]='M'; break; }
+ case 'n': { result[i]='N'; break; }
+ case 'o': { result[i]='O'; break; }
+ case 'p': { result[i]='P'; break; }
+ case 'q': { result[i]='Q'; break; }
+ case 'r': { result[i]='R'; break; }
+ case 's': { result[i]='S'; break; }
+ case 't': { result[i]='T'; break; }
+ case 'u': { result[i]='U'; break; }
+ case 'v': { result[i]='V'; break; }
+ case 'w': { result[i]='W'; break; }
+ case 'x': { result[i]='X'; break; }
+ case 'y': { result[i]='Y'; break; }
+ case 'z': { result[i]='Z'; break; }
+ }
+ }
+
+ return result;
+ } else { return NULL; }
+}
+
+char *str_replace(const char *s, const char *old, const char *new) {
+ char *ret;
+ int i, count = 0;
+ size_t newlen = strlen(new);
+ size_t oldlen = strlen(old);
+
+ for (i = 0; s[i] != '\0'; i++) {
+ if (strstr(&s[i], old) == &s[i]) {
+ count++;
+ i += oldlen - 1;
+ }
+ }
+
+ ret = malloc(i + 1 + count * (newlen - oldlen));
+ if (ret == NULL) {return (char *)PyErr_NoMemory();}
+
+ i = 0;
+ while (*s) {
+ if (strstr(s, old) == s) {
+ strcpy(&ret[i], new);
+ i += newlen;
+ s += oldlen;
+ } else {
+ ret[i++] = *s++;
+ }
+ }
+ ret[i] = '\0';
+
+ return ret;
+}
+
+
+PyObject *
+set_callback(PyObject *args, PyObject **callback)
+{
+ PyObject *result = NULL;
+ PyObject *temp;
+
+ if (PyArg_ParseTuple(args, "O:set_callback", &temp)) {
+
+ if (!PyCallable_Check(temp)) {
+ PyErr_SetString(PyExc_TypeError, "parameter must be callable");
+ return NULL;
+ }
+
+ Py_XINCREF(temp); // Add a reference to new callback
+ Py_XDECREF(*callback); // Dispose of previous callback
+ *callback = temp; // Remember new callback
+ // Boilerplate to return "None"
+ Py_INCREF(Py_None);
+ result = Py_None;
+ }
+ return result;
+}
+
+void import_c_lib(PyObject *m) {
+ PyObject *ops_dict;
+
+ import_array();
+
+ ops_dict = PyArray_GetNumericOps();
+
+ NP_ADD = PyDict_GetItemString(ops_dict, "add");
+ NP_MULTIPLY = PyDict_GetItemString(ops_dict, "multiply");
+ NP_SUBTRACT = PyDict_GetItemString(ops_dict, "subtract");
+ NP_SQRT = PyDict_GetItemString(ops_dict, "sqrt");
+ NP_GREATER = PyDict_GetItemString(ops_dict, "greater");
+ NP_GREATER_EQUAL = PyDict_GetItemString(ops_dict, "greater_equal");
+
+ Py_INCREF(NP_ADD);
+ Py_INCREF(NP_MULTIPLY);
+ Py_INCREF(NP_SUBTRACT);
+ Py_INCREF(NP_SQRT);
+ Py_INCREF(NP_GREATER);
+ Py_INCREF(NP_GREATER_EQUAL);
+
+ Py_DECREF(ops_dict);
+}
Added: trunk/Lib/sandbox/timeseries/src/c_tdates.c
===================================================================
--- trunk/Lib/sandbox/timeseries/src/c_tdates.c 2007-05-09 17:08:29 UTC (rev 2974)
+++ trunk/Lib/sandbox/timeseries/src/c_tdates.c 2007-05-09 17:09:36 UTC (rev 2975)
@@ -0,0 +1,2557 @@
+#include "c_tdates.h"
+#include <datetime.h>
+#include <time.h>
+
+
+int get_freq_group(int freq) { return (freq/1000)*1000; }
+
+static asfreq_info NULL_AF_INFO;
+
+/*********************************************************
+** Python callbacks. These functions must be called by **
+** the module __init__ script **
+*********************************************************/
+
+static PyObject *DateFromString = NULL;
+PyObject *
+set_callback_DateFromString(PyObject *dummy, PyObject *args) {
+ return set_callback(args, &DateFromString);
+}
+
+static PyObject *DateTimeFromString = NULL;
+PyObject *
+set_callback_DateTimeFromString(PyObject *dummy, PyObject *args) {
+ return set_callback(args, &DateTimeFromString);
+}
+
+//DERIVED FROM mx.DateTime
+/*
+=====================================================
+== Functions in the following section are borrowed ==
+== from mx.DateTime, and in many cases slightly ==
+== modified ==
+=====================================================
+*/
+
+#define Py_AssertWithArg(x,errortype,errorstr,a1) {if (!(x)) {PyErr_Format(errortype,errorstr,a1);goto onError;}}
+#define Py_Error(errortype,errorstr) {PyErr_SetString(errortype,errorstr);goto onError;}
+
+ /* Error Exception objects */
+static PyObject *DateCalc_Error;
+static PyObject *DateCalc_RangeError;
+
+#define GREGORIAN_CALENDAR 0
+#define JULIAN_CALENDAR 1
+
+#define SECONDS_PER_DAY ((double) 86400.0)
+
+/* Table with day offsets for each month (0-based, without and with leap) */
+static int month_offset[2][13] = {
+ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+};
+
+/* Table of number of days in a month (0-based, without and with leap) */
+static int days_in_month[2][12] = {
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+struct date_info {
+ long absdate;
+ double abstime;
+
+ double second;
+ int minute;
+ int hour;
+ int day;
+ int month;
+ int quarter;
+ int year;
+ int day_of_week;
+ int day_of_year;
+ int calendar;
+};
+
+
+/* Return 1/0 iff year points to a leap year in calendar. */
+static
+int dInfoCalc_Leapyear(register long year,
+ int calendar)
+{
+ if (calendar == GREGORIAN_CALENDAR) {
+ return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
+ } else {
+ return (year % 4 == 0);
+ }
+}
+
+static
+int dInfoCalc_ISOWeek(struct date_info *dinfo)
+{
+ int week;
+
+ /* Estimate */
+ week = (dinfo->day_of_year-1) - dinfo->day_of_week + 3;
+ if (week >= 0) week = week / 7 + 1;
+
+ /* Verify */
+ if (week < 0) {
+ /* The day lies in last week of the previous year */
+ if ((week > -2) ||
+ (week == -2 && dInfoCalc_Leapyear(dinfo->year-1, dinfo->calendar)))
+ week = 53;
+ else
+ week = 52;
+ } else if (week == 53) {
+ /* Check if the week belongs to year or year+1 */
+ if (31-dinfo->day + dinfo->day_of_week < 3) {
+ week = 1;
+ }
+ }
+
+ return week;
+}
+
+
+/* Return the day of the week for the given absolute date. */
+static
+int dInfoCalc_DayOfWeek(register long absdate)
+{
+ int day_of_week;
+
+ if (absdate >= 1) {
+ day_of_week = (absdate - 1) % 7;
+ } else {
+ day_of_week = 6 - ((-absdate) % 7);
+ }
+ return day_of_week;
+}
+
+/* Return the year offset, that is the absolute date of the day
+ 31.12.(year-1) in the given calendar.
+
+ Note:
+ For the Julian calendar we shift the absdate (which is measured
+ using the Gregorian Epoch) value by two days because the Epoch
+ (0001-01-01) in the Julian calendar lies 2 days before the Epoch in
+ the Gregorian calendar. */
+static
+int dInfoCalc_YearOffset(register long year,
+ int calendar)
+{
+ year--;
+ if (calendar == GREGORIAN_CALENDAR) {
+ if (year >= 0 || -1/4 == -1)
+ return year*365 + year/4 - year/100 + year/400;
+ else
+ return year*365 + (year-3)/4 - (year-99)/100 + (year-399)/400;
+ }
+ else if (calendar == JULIAN_CALENDAR) {
+ if (year >= 0 || -1/4 == -1)
+ return year*365 + year/4 - 2;
+ else
+ return year*365 + (year-3)/4 - 2;
+ }
+ Py_Error(DateCalc_Error, "unknown calendar");
+ onError:
+ return -1;
+}
+
+
+/* Set the instance's value using the given date and time. calendar
+ may be set to the flags: GREGORIAN_CALENDAR,
+ JULIAN_CALENDAR to indicate the calendar to be used. */
+
+static
+int dInfoCalc_SetFromDateAndTime(struct date_info *dinfo,
+ int year,
+ int month,
+ int day,
+ int hour,
+ int minute,
+ double second,
+ int calendar)
+{
+
+ /* Calculate the absolute date */
+ {
+ int leap;
+ long yearoffset,absdate;
+
+ /* Range check */
+ Py_AssertWithArg(year > -(INT_MAX / 366) && year < (INT_MAX / 366),
+ DateCalc_RangeError,
+ "year out of range: %i",
+ year);
+
+ /* Is it a leap year ? */
+ leap = dInfoCalc_Leapyear(year,calendar);
+
+ /* Negative month values indicate months relative to the years end */
+ if (month < 0) month += 13;
+ Py_AssertWithArg(month >= 1 && month <= 12,
+ DateCalc_RangeError,
+ "month out of range (1-12): %i",
+ month);
+
+ /* Negative values indicate days relative to the months end */
+ if (day < 0) day += days_in_month[leap][month - 1] + 1;
+ Py_AssertWithArg(day >= 1 && day <= days_in_month[leap][month - 1],
+ DateCalc_RangeError,
+ "day out of range: %i",
+ day);
+
+ yearoffset = dInfoCalc_YearOffset(year,calendar);
+ if (PyErr_Occurred()) goto onError;
+
+ absdate = day + month_offset[leap][month - 1] + yearoffset;
+
+ dinfo->absdate = absdate;
+
+ dinfo->year = year;
+ dinfo->month = month;
+ dinfo->quarter = ((month-1)/3)+1;
+ dinfo->day = day;
+
+ dinfo->day_of_week = dInfoCalc_DayOfWeek(absdate);
+ dinfo->day_of_year = (short)(absdate - yearoffset);
+
+ dinfo->calendar = calendar;
+ }
+
+ /* Calculate the absolute time */
+ {
+ Py_AssertWithArg(hour >= 0 && hour <= 23,
+ DateCalc_RangeError,
+ "hour out of range (0-23): %i",
+ hour);
+ Py_AssertWithArg(minute >= 0 && minute <= 59,
+ DateCalc_RangeError,
+ "minute out of range (0-59): %i",
+ minute);
+ Py_AssertWithArg(second >= (double)0.0 &&
+ (second < (double)60.0 ||
+ (hour == 23 && minute == 59 &&
+ second < (double)61.0)),
+ DateCalc_RangeError,
+ "second out of range (0.0 - <60.0; <61.0 for 23:59): %f",
+ second);
+
+ dinfo->abstime = (double)(hour*3600 + minute*60) + second;
+
+ dinfo->hour = hour;
+ dinfo->minute = minute;
+ dinfo->second = second;
+ }
+ return 0;
+ onError:
+ return -1;
+}
+
+static int monthToQuarter(int month) { return ((month-1)/3)+1; }
+
+/* Sets the date part of the date_info struct using the indicated
+ calendar.
+
+ XXX This could also be done using some integer arithmetics rather
+ than with this iterative approach... */
+static
+int dInfoCalc_SetFromAbsDate(register struct date_info *dinfo,
+ long absdate,
+ int calendar)
+{
+ register long year;
+ long yearoffset;
+ int leap,dayoffset;
+ int *monthoffset;
+
+ /* Approximate year */
+ if (calendar == GREGORIAN_CALENDAR) {
+ year = (long)(((double)absdate) / 365.2425);
+ } else if (calendar == JULIAN_CALENDAR) {
+ year = (long)(((double)absdate) / 365.25);
+ } else {
+ Py_Error(DateCalc_Error, "unknown calendar");
+ }
+ if (absdate > 0) year++;
+
+ /* Apply corrections to reach the correct year */
+ while (1) {
+ /* Calculate the year offset */
+ yearoffset = dInfoCalc_YearOffset(year,calendar);
+ if (PyErr_Occurred())
+ goto onError;
+
+ /* Backward correction: absdate must be greater than the
+ yearoffset */
+ if (yearoffset >= absdate) {
+ year--;
+ continue;
+ }
+
+ dayoffset = absdate - yearoffset;
+ leap = dInfoCalc_Leapyear(year,calendar);
+
+ /* Forward correction: non leap years only have 365 days */
+ if (dayoffset > 365 && !leap) {
+ year++;
+ continue;
+ }
+ break;
+ }
+
+ dinfo->year = year;
+ dinfo->calendar = calendar;
+
+ /* Now iterate to find the month */
+ monthoffset = month_offset[leap];
+ {
+ register int month;
+
+ for (month = 1; month < 13; month++) {
+ if (monthoffset[month] >= dayoffset)
+ break;
+ }
+
+ dinfo->month = month;
+ dinfo->quarter = monthToQuarter(month);
+ dinfo->day = dayoffset - month_offset[leap][month-1];
+ }
+
+
+ dinfo->day_of_week = dInfoCalc_DayOfWeek(absdate);
+ dinfo->day_of_year = dayoffset;
+ dinfo->absdate = absdate;
+
+ return 0;
+
+ onError:
+ return -1;
+}
+
+/* Sets the time part of the DateTime object. */
+static
+int dInfoCalc_SetFromAbsTime(struct date_info *dinfo,
+ double abstime)
+{
+ int inttime;
+ int hour,minute;
+ double second;
+
+ inttime = (int)abstime;
+ hour = inttime / 3600;
+ minute = (inttime % 3600) / 60;
+ second = abstime - (double)(hour*3600 + minute*60);
+
+ dinfo->hour = hour;
+ dinfo->minute = minute;
+ dinfo->second = second;
+
+ dinfo->abstime = abstime;
+
+ return 0;
+}
+
+/* Set the instance's value using the given date and time. calendar
+ may be set to the flags: GREGORIAN_CALENDAR, JULIAN_CALENDAR to
+ indicate the calendar to be used. */
+static
+int dInfoCalc_SetFromAbsDateTime(struct date_info *dinfo,
+ long absdate,
+ double abstime,
+ int calendar)
+{
+
+ /* Bounds check */
+ Py_AssertWithArg(abstime >= 0.0 && abstime <= SECONDS_PER_DAY,
+ DateCalc_Error,
+ "abstime out of range (0.0 - 86400.0): %f",
+ abstime);
+
+ /* Calculate the date */
+ if (dInfoCalc_SetFromAbsDate(dinfo,
+ absdate,
+ calendar))
+ goto onError;
+
+ /* Calculate the time */
+ if (dInfoCalc_SetFromAbsTime(dinfo,
+ abstime))
+ goto onError;
+
+ return 0;
+ onError:
+ return -1;
+}
+
+/*
+====================================================
+== End of section borrowed from mx.DateTime ==
+====================================================
+*/
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////
+
+// helpers for frequency conversion routines //
+
+static long DtoB_weekday(long fromDate) { return (((fromDate) / 7) * 5) + (fromDate)%7; }
+
+static long DtoB_WeekendToMonday(long absdate, int day_of_week) {
+
+ if (day_of_week > 4) {
+ //change to Monday after weekend
+ absdate += (7 - day_of_week);
+ }
+ return DtoB_weekday(absdate);
+}
+
+static long DtoB_WeekendToFriday(long absdate, int day_of_week) {
+
+ if (day_of_week > 4) {
+ //change to friday before weekend
+ absdate -= (day_of_week - 4);
+ }
+ return DtoB_weekday(absdate);
+}
+
+static long absdate_from_ymd(int y, int m, int d) {
+ struct date_info tempDate;
+ if (dInfoCalc_SetFromDateAndTime(&tempDate, y, m, d, 0, 0, 0, GREGORIAN_CALENDAR)) return INT_ERR_CODE;
+ return tempDate.absdate;
+}
+
+
+///////////////////////////////////////////////
+
+// frequency specifc conversion routines
+// each function must take an integer fromDate and a char relation ('B' or 'A' for 'BEFORE' or 'AFTER')
+
+//************ FROM DAILY ***************
+
+static long asfreq_DtoA(long fromDate, char relation, asfreq_info *af_info) {
+
+ struct date_info dinfo;
+ if (dInfoCalc_SetFromAbsDate(&dinfo, fromDate,
+ GREGORIAN_CALENDAR)) return INT_ERR_CODE;
+ if (dinfo.month > af_info->to_a_year_end) { return (long)(dinfo.year + 1); }
+ else { return (long)(dinfo.year); }
+}
+
+static long DtoQ_yq(long fromDate, asfreq_info *af_info,
+ int *year, int *quarter) {
+ struct date_info dinfo;
+ if (dInfoCalc_SetFromAbsDate(&dinfo, fromDate,
+ GREGORIAN_CALENDAR)) return INT_ERR_CODE;
+ if (af_info->to_q_year_end != 12) {
+ dinfo.month -= af_info->to_q_year_end;
+ if (dinfo.month <= 0) { dinfo.month += 12; }
+ else { dinfo.year += 1; }
+ dinfo.quarter = monthToQuarter(dinfo.month);
+ }
+
+ *year = dinfo.year;
+ *quarter = dinfo.quarter;
+
+ return 0;
+}
+
+
+static long asfreq_DtoQ(long fromDate, char relation, asfreq_info *af_info) {
+
+ int year, quarter;
+
+ if (DtoQ_yq(fromDate, af_info, &year, &quarter) == INT_ERR_CODE)
+ { return INT_ERR_CODE; }
+
+ return (long)((year - 1) * 4 + quarter);
+}
+
+static long asfreq_DtoM(long fromDate, char relation, asfreq_info *af_info) {
+
+ struct date_info dinfo;
+ if (dInfoCalc_SetFromAbsDate(&dinfo, fromDate,
+ GREGORIAN_CALENDAR)) return INT_ERR_CODE;
+ return (long)((dinfo.year - 1) * 12 + dinfo.month);
+}
+
+static long asfreq_DtoW(long fromDate, char relation, asfreq_info *af_info) {
+ return (fromDate - (1 + af_info->to_week_end))/7 + 1;
+}
+
+static long asfreq_DtoB(long fromDate, char relation, asfreq_info *af_info) {
+
+ struct date_info dinfo;
+ if (dInfoCalc_SetFromAbsDate(&dinfo, fromDate,
+ GREGORIAN_CALENDAR)) return INT_ERR_CODE;
+
+ if (relation == 'B') {
+ return DtoB_WeekendToFriday(dinfo.absdate, dinfo.day_of_week);
+ } else {
+ return DtoB_WeekendToMonday(dinfo.absdate, dinfo.day_of_week);
+ }
+}
+
+static long asfreq_DtoB_forConvert(long fromDate, char relation, asfreq_info *af_info) {
+
+ struct date_info dinfo;
+ if (dInfoCalc_SetFromAbsDate(&dinfo, fromDate,
+ GREGORIAN_CALENDAR)) return INT_ERR_CODE;
+
+ if (dinfo.day_of_week > 4) {
+ return -1;
+ } else {
+ return DtoB_weekday(fromDate);
+ }
+}
+
+// needed for getDateInfo function
+static long asfreq_DtoD(long fromDate, char relation, asfreq_info *af_info) { return fromDate; }
+
+static long asfreq_DtoHIGHFREQ(long fromDate, char relation, long periodsPerDay) {
+ if (fromDate >= HIGHFREQ_ORIG) {
+ if (relation == 'B') { return (fromDate - HIGHFREQ_ORIG)*(periodsPerDay) + 1; }
+ else { return (fromDate - HIGHFREQ_ORIG + 1)*(periodsPerDay); }
+ } else { return -1; }
+}
+
+static long asfreq_DtoH(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoHIGHFREQ(fromDate, relation, 24); }
+static long asfreq_DtoT(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoHIGHFREQ(fromDate, relation, 24*60); }
+static long asfreq_DtoS(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoHIGHFREQ(fromDate, relation, 24*60*60); }
+
+//************ FROM SECONDLY ***************
+
+static long asfreq_StoD(long fromDate, char relation, asfreq_info *af_info)
+ { return (fromDate - 1)/(60*60*24) + HIGHFREQ_ORIG; }
+
+static long asfreq_StoA(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoA(asfreq_StoD(fromDate, relation, &NULL_AF_INFO), relation, af_info); }
+static long asfreq_StoQ(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoQ(asfreq_StoD(fromDate, relation, &NULL_AF_INFO), relation, af_info); }
+static long asfreq_StoM(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoM(asfreq_StoD(fromDate, relation, &NULL_AF_INFO), relation, &NULL_AF_INFO); }
+static long asfreq_StoW(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoW(asfreq_StoD(fromDate, relation, &NULL_AF_INFO), relation, af_info); }
+static long asfreq_StoB(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoB(asfreq_StoD(fromDate, relation, &NULL_AF_INFO), relation, &NULL_AF_INFO); }
+static long asfreq_StoB_forConvert(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoB_forConvert(asfreq_StoD(fromDate, relation, &NULL_AF_INFO), relation, &NULL_AF_INFO); }
+static long asfreq_StoT(long fromDate, char relation, asfreq_info *af_info)
+ { return (fromDate - 1)/60 + 1; }
+static long asfreq_StoH(long fromDate, char relation, asfreq_info *af_info)
+ { return (fromDate - 1)/(60*60) + 1; }
+
+//************ FROM MINUTELY ***************
+
+static long asfreq_TtoD(long fromDate, char relation, asfreq_info *af_info)
+ { return (fromDate - 1)/(60*24) + HIGHFREQ_ORIG; }
+
+static long asfreq_TtoA(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoA(asfreq_TtoD(fromDate, relation, &NULL_AF_INFO), relation, af_info); }
+static long asfreq_TtoQ(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoQ(asfreq_TtoD(fromDate, relation, &NULL_AF_INFO), relation, af_info); }
+static long asfreq_TtoM(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoM(asfreq_TtoD(fromDate, relation, &NULL_AF_INFO), relation, &NULL_AF_INFO); }
+static long asfreq_TtoW(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoW(asfreq_TtoD(fromDate, relation, &NULL_AF_INFO), relation, af_info); }
+static long asfreq_TtoB(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoB(asfreq_TtoD(fromDate, relation, &NULL_AF_INFO), relation, &NULL_AF_INFO); }
+
+static long asfreq_TtoB_forConvert(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoB_forConvert(asfreq_TtoD(fromDate, relation, &NULL_AF_INFO), relation, &NULL_AF_INFO); }
+
+static long asfreq_TtoH(long fromDate, char relation, asfreq_info *af_info)
+ { return (fromDate - 1)/60 + 1; }
+static long asfreq_TtoS(long fromDate, char relation, asfreq_info *af_info) {
+ if (relation == 'B') { return fromDate*60 - 59; }
+ else { return fromDate*60; }}
+
+//************ FROM HOURLY ***************
+
+static long asfreq_HtoD(long fromDate, char relation, asfreq_info *af_info)
+ { return (fromDate - 1)/24 + HIGHFREQ_ORIG; }
+static long asfreq_HtoA(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoA(asfreq_HtoD(fromDate, relation, &NULL_AF_INFO), relation, af_info); }
+static long asfreq_HtoQ(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoQ(asfreq_HtoD(fromDate, relation, &NULL_AF_INFO), relation, af_info); }
+static long asfreq_HtoM(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoM(asfreq_HtoD(fromDate, relation, &NULL_AF_INFO), relation, &NULL_AF_INFO); }
+static long asfreq_HtoW(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoW(asfreq_HtoD(fromDate, relation, &NULL_AF_INFO), relation, af_info); }
+static long asfreq_HtoB(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoB(asfreq_HtoD(fromDate, relation, &NULL_AF_INFO), relation, &NULL_AF_INFO); }
+
+static long asfreq_HtoB_forConvert(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoB_forConvert(asfreq_HtoD(fromDate, relation, &NULL_AF_INFO), relation, &NULL_AF_INFO); }
+
+// calculation works out the same as TtoS, so we just call that function for HtoT
+static long asfreq_HtoT(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_TtoS(fromDate, relation, &NULL_AF_INFO); }
+static long asfreq_HtoS(long fromDate, char relation, asfreq_info *af_info) {
+ if (relation == 'B') { return fromDate*60*60 - 60*60 + 1; }
+ else { return fromDate*60*60; }}
+
+//************ FROM BUSINESS ***************
+
+static long asfreq_BtoD(long fromDate, char relation, asfreq_info *af_info)
+ { return ((fromDate-1)/5)*7 + (fromDate-1)%5 + 1; }
+
+static long asfreq_BtoA(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoA(asfreq_BtoD(fromDate, relation, &NULL_AF_INFO), relation, af_info); }
+
+static long asfreq_BtoQ(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoQ(asfreq_BtoD(fromDate, relation, &NULL_AF_INFO), relation, af_info); }
+
+static long asfreq_BtoM(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoM(asfreq_BtoD(fromDate, relation, &NULL_AF_INFO), relation, &NULL_AF_INFO); }
+
+static long asfreq_BtoW(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoW(asfreq_BtoD(fromDate, relation, &NULL_AF_INFO), relation, af_info); }
+
+static long asfreq_BtoH(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoH(asfreq_BtoD(fromDate, relation, &NULL_AF_INFO), relation, &NULL_AF_INFO); }
+
+static long asfreq_BtoT(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoT(asfreq_BtoD(fromDate, relation, &NULL_AF_INFO), relation, &NULL_AF_INFO); }
+
+static long asfreq_BtoS(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoS(asfreq_BtoD(fromDate, relation, &NULL_AF_INFO), relation, &NULL_AF_INFO); }
+
+//************ FROM WEEKLY ***************
+
+static long asfreq_WtoD(long fromDate, char relation, asfreq_info *af_info) {
+ if (relation == 'B') { return fromDate * 7 - 6 + af_info->from_week_end;}
+ else { return fromDate * 7 + af_info->from_week_end; }
+}
+
+static long asfreq_WtoA(long fromDate, char relation, asfreq_info *af_info) {
+ return asfreq_DtoA(asfreq_WtoD(fromDate, 'A', af_info), relation, af_info); }
+static long asfreq_WtoQ(long fromDate, char relation, asfreq_info *af_info) {
+ return asfreq_DtoQ(asfreq_WtoD(fromDate, 'A', af_info), relation, af_info); }
+static long asfreq_WtoM(long fromDate, char relation, asfreq_info *af_info) {
+ return asfreq_DtoM(asfreq_WtoD(fromDate, 'A', af_info), relation, &NULL_AF_INFO); }
+
+static long asfreq_WtoW(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoW(asfreq_WtoD(fromDate, relation, af_info), relation, af_info); }
+
+static long asfreq_WtoB(long fromDate, char relation, asfreq_info *af_info) {
+
+ struct date_info dinfo;
+ if (dInfoCalc_SetFromAbsDate(&dinfo, asfreq_WtoD(fromDate, relation, af_info),
+ GREGORIAN_CALENDAR)) return INT_ERR_CODE;
+
+ if (relation == 'B') { return DtoB_WeekendToMonday(dinfo.absdate, dinfo.day_of_week); }
+ else { return DtoB_WeekendToFriday(dinfo.absdate, dinfo.day_of_week); }
+}
+
+static long asfreq_WtoH(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoH(asfreq_WtoD(fromDate, relation, af_info), relation, &NULL_AF_INFO); }
+static long asfreq_WtoT(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoT(asfreq_WtoD(fromDate, relation, af_info), relation, &NULL_AF_INFO); }
+static long asfreq_WtoS(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoS(asfreq_WtoD(fromDate, relation, af_info), relation, &NULL_AF_INFO); }
+
+//************ FROM MONTHLY ***************
+
+static void MtoD_ym(long fromDate, long *y, long *m) {
+ *y = (fromDate - 1) / 12 + 1;
+ *m = fromDate - 12 * (*y) - 1;
+}
+
+static long asfreq_MtoD(long fromDate, char relation, asfreq_info *af_info) {
+
+ long y, m, absdate;
+
+ if (relation == 'B') {
+ MtoD_ym(fromDate, &y, &m);
+ if ((absdate = absdate_from_ymd(y, m, 1)) == INT_ERR_CODE) return INT_ERR_CODE;
+ return absdate;
+ } else {
+ MtoD_ym(fromDate+1, &y, &m);
+ if ((absdate = absdate_from_ymd(y, m, 1)) == INT_ERR_CODE) return INT_ERR_CODE;
+ return absdate-1;
+ }
+}
+
+static long asfreq_MtoA(long fromDate, char relation, asfreq_info *af_info) {
+ return asfreq_DtoA(asfreq_MtoD(fromDate, 'A', &NULL_AF_INFO), relation, af_info); }
+
+static long asfreq_MtoQ(long fromDate, char relation, asfreq_info *af_info) {
+ return asfreq_DtoQ(asfreq_MtoD(fromDate, 'A', &NULL_AF_INFO), relation, af_info); }
+
+static long asfreq_MtoW(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoW(asfreq_MtoD(fromDate, relation, &NULL_AF_INFO), relation, af_info); }
+
+static long asfreq_MtoB(long fromDate, char relation, asfreq_info *af_info) {
+
+ struct date_info dinfo;
+ if (dInfoCalc_SetFromAbsDate(&dinfo, asfreq_MtoD(fromDate, relation, &NULL_AF_INFO),
+ GREGORIAN_CALENDAR)) return INT_ERR_CODE;
+
+ if (relation == 'B') { return DtoB_WeekendToMonday(dinfo.absdate, dinfo.day_of_week); }
+ else { return DtoB_WeekendToFriday(dinfo.absdate, dinfo.day_of_week); }
+}
+
+static long asfreq_MtoH(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoH(asfreq_MtoD(fromDate, relation, &NULL_AF_INFO), relation, &NULL_AF_INFO); }
+static long asfreq_MtoT(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoT(asfreq_MtoD(fromDate, relation, &NULL_AF_INFO), relation, &NULL_AF_INFO); }
+static long asfreq_MtoS(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoS(asfreq_MtoD(fromDate, relation, &NULL_AF_INFO), relation, &NULL_AF_INFO); }
+
+//************ FROM QUARTERLY ***************
+
+static void QtoD_ym(long fromDate, long *y, long *m, asfreq_info *af_info) {
+
+ *y = (fromDate - 1) / 4 + 1;
+ *m = (fromDate + 4) * 3 - 12 * (*y) - 2;
+
+ if (af_info->from_q_year_end != 12) {
+ *m += af_info->from_q_year_end;
+ if (*m > 12) { *m -= 12; }
+ else { *y -= 1; }
+ }
+}
+
+static long asfreq_QtoD(long fromDate, char relation, asfreq_info *af_info) {
+
+ long y, m, absdate;
+
+ if (relation == 'B') {
+ QtoD_ym(fromDate, &y, &m, af_info);
+ if ((absdate = absdate_from_ymd(y, m, 1)) == INT_ERR_CODE) return INT_ERR_CODE;
+ return absdate;
+ } else {
+ QtoD_ym(fromDate+1, &y, &m, af_info);
+ if ((absdate = absdate_from_ymd(y, m, 1)) == INT_ERR_CODE) return INT_ERR_CODE;
+ return absdate - 1;
+ }
+}
+
+static long asfreq_QtoQ(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoQ(asfreq_QtoD(fromDate, relation, af_info), relation, af_info); }
+
+static long asfreq_QtoA(long fromDate, char relation, asfreq_info *af_info) {
+ return asfreq_DtoA(asfreq_QtoD(fromDate, relation, af_info), relation, af_info); }
+
+static long asfreq_QtoM(long fromDate, char relation, asfreq_info *af_info) {
+ return asfreq_DtoM(asfreq_QtoD(fromDate, relation, af_info), relation, &NULL_AF_INFO); }
+
+static long asfreq_QtoW(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoW(asfreq_QtoD(fromDate, relation, af_info), relation, af_info); }
+
+static long asfreq_QtoB(long fromDate, char relation, asfreq_info *af_info) {
+
+ struct date_info dinfo;
+ if (dInfoCalc_SetFromAbsDate(&dinfo, asfreq_QtoD(fromDate, relation, af_info),
+ GREGORIAN_CALENDAR)) return INT_ERR_CODE;
+
+ if (relation == 'B') { return DtoB_WeekendToMonday(dinfo.absdate, dinfo.day_of_week); }
+ else { return DtoB_WeekendToFriday(dinfo.absdate, dinfo.day_of_week); }
+}
+
+
+static long asfreq_QtoH(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoH(asfreq_QtoD(fromDate, relation, af_info), relation, &NULL_AF_INFO); }
+static long asfreq_QtoT(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoT(asfreq_QtoD(fromDate, relation, af_info), relation, &NULL_AF_INFO); }
+static long asfreq_QtoS(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoS(asfreq_QtoD(fromDate, relation, af_info), relation, &NULL_AF_INFO); }
+
+
+//************ FROM ANNUAL ***************
+
+static long asfreq_AtoD(long fromDate, char relation, asfreq_info *af_info) {
+ long absdate, year, final_adj;
+ int month = (af_info->from_a_year_end + 1) % 12;
+
+ if (relation == 'B') {
+ if (af_info->from_a_year_end == 12) {year = fromDate;}
+ else {year = fromDate - 1;}
+ final_adj = 0;
+ } else {
+ if (af_info->from_a_year_end == 12) {year = fromDate+1;}
+ else {year = fromDate;}
+ final_adj = -1;
+ }
+ absdate = absdate_from_ymd(year, month, 1);
+ if (absdate == INT_ERR_CODE) return INT_ERR_CODE;
+ return absdate + final_adj;
+}
+
+static long asfreq_AtoA(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoA(asfreq_AtoD(fromDate, relation, af_info), relation, af_info); }
+
+static long asfreq_AtoQ(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoQ(asfreq_AtoD(fromDate, relation, af_info), relation, af_info); }
+
+static long asfreq_AtoM(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoM(asfreq_AtoD(fromDate, relation, af_info), relation, af_info); }
+
+static long asfreq_AtoW(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoW(asfreq_AtoD(fromDate, relation, af_info), relation, af_info); }
+
+static long asfreq_AtoB(long fromDate, char relation, asfreq_info *af_info) {
+
+ struct date_info dinfo;
+ if (dInfoCalc_SetFromAbsDate(&dinfo, asfreq_AtoD(fromDate, relation, af_info),
+ GREGORIAN_CALENDAR)) return INT_ERR_CODE;
+
+ if (relation == 'B') { return DtoB_WeekendToMonday(dinfo.absdate, dinfo.day_of_week); }
+ else { return DtoB_WeekendToFriday(dinfo.absdate, dinfo.day_of_week); }
+}
+
+static long asfreq_AtoH(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoH(asfreq_AtoD(fromDate, relation, af_info), relation, &NULL_AF_INFO); }
+static long asfreq_AtoT(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoT(asfreq_AtoD(fromDate, relation, af_info), relation, &NULL_AF_INFO); }
+static long asfreq_AtoS(long fromDate, char relation, asfreq_info *af_info)
+ { return asfreq_DtoS(asfreq_AtoD(fromDate, relation, af_info), relation, &NULL_AF_INFO); }
+
+static long nofunc(long fromDate, char relation, asfreq_info *af_info) { return -1; }
+
+// end of frequency specific conversion routines
+
+// return a pointer to appropriate conversion function
+long (*get_asfreq_func(int fromFreq, int toFreq, int forConvert))(long, char, asfreq_info*) {
+
+ int fromGroup = get_freq_group(fromFreq);
+ int toGroup = get_freq_group(toFreq);
+
+ if (fromGroup == FR_UND) { fromGroup = FR_DAY; }
+
+ switch(fromGroup)
+ {
+ case FR_ANN:
+ switch(toGroup)
+ {
+ case FR_ANN: return &asfreq_AtoA;
+ case FR_QTR: return &asfreq_AtoQ;
+ case FR_MTH: return &asfreq_AtoM;
+ case FR_WK: return &asfreq_AtoW;
+ case FR_BUS: return &asfreq_AtoB;
+ case FR_DAY: return &asfreq_AtoD;
+ case FR_HR: return &asfreq_AtoH;
+ case FR_MIN: return &asfreq_AtoT;
+ case FR_SEC: return &asfreq_AtoS;
+ default: return &nofunc;
+ }
+
+ case FR_QTR:
+ switch(toGroup)
+ {
+ case FR_ANN: return &asfreq_QtoA;
+ case FR_QTR: return &asfreq_QtoQ;
+ case FR_MTH: return &asfreq_QtoM;
+ case FR_WK: return &asfreq_QtoW;
+ case FR_BUS: return &asfreq_QtoB;
+ case FR_DAY: return &asfreq_QtoD;
+ case FR_HR: return &asfreq_QtoH;
+ case FR_MIN: return &asfreq_QtoT;
+ case FR_SEC: return &asfreq_QtoS;
+ default: return &nofunc;
+ }
+
+ case FR_MTH:
+ switch(toGroup)
+ {
+ case FR_ANN: return &asfreq_MtoA;
+ case FR_QTR: return &asfreq_MtoQ;
+ case FR_WK: return &asfreq_MtoW;
+ case FR_BUS: return &asfreq_MtoB;
+ case FR_DAY: return &asfreq_MtoD;
+ case FR_HR: return &asfreq_MtoH;
+ case FR_MIN: return &asfreq_MtoT;
+ case FR_SEC: return &asfreq_MtoS;
+ default: return &nofunc;
+ }
+
+ case FR_WK:
+ switch(toGroup)
+ {
+ case FR_ANN: return &asfreq_WtoA;
+ case FR_QTR: return &asfreq_WtoQ;
+ case FR_MTH: return &asfreq_WtoM;
+ case FR_WK: return &asfreq_WtoW;
+ case FR_BUS: return &asfreq_WtoB;
+ case FR_DAY: return &asfreq_WtoD;
+ case FR_HR: return &asfreq_WtoH;
+ case FR_MIN: return &asfreq_WtoT;
+ case FR_SEC: return &asfreq_WtoS;
+ default: return &nofunc;
+ }
+
+ case FR_BUS:
+ switch(toGroup)
+ {
+ case FR_ANN: return &asfreq_BtoA;
+ case FR_QTR: return &asfreq_BtoQ;
+ case FR_MTH: return &asfreq_BtoM;
+ case FR_WK: return &asfreq_BtoW;
+ case FR_DAY: return &asfreq_BtoD;
+ case FR_HR: return &asfreq_BtoH;
+ case FR_MIN: return &asfreq_BtoT;
+ case FR_SEC: return &asfreq_BtoS;
+ default: return &nofunc;
+ }
+
+ case FR_DAY:
+ switch(toGroup)
+ {
+ case FR_ANN: return &asfreq_DtoA;
+ case FR_QTR: return &asfreq_DtoQ;
+ case FR_MTH: return &asfreq_DtoM;
+ case FR_WK: return &asfreq_DtoW;
+ case FR_BUS:
+ if (forConvert) { return &asfreq_DtoB_forConvert; }
+ else { return &asfreq_DtoB; }
+ case FR_DAY: return &asfreq_DtoD;
+ case FR_HR: return &asfreq_DtoH;
+ case FR_MIN: return &asfreq_DtoT;
+ case FR_SEC: return &asfreq_DtoS;
+ default: return &nofunc;
+ }
+
+ case FR_HR:
+ switch(toGroup)
+ {
+ case FR_ANN: return &asfreq_HtoA;
+ case FR_QTR: return &asfreq_HtoQ;
+ case FR_MTH: return &asfreq_HtoM;
+ case FR_WK: return &asfreq_HtoW;
+ case FR_BUS:
+ if (forConvert) { return &asfreq_HtoB_forConvert; }
+ else { return &asfreq_HtoB; }
+ case FR_DAY: return &asfreq_HtoD;
+ case FR_MIN: return &asfreq_HtoT;
+ case FR_SEC: return &asfreq_HtoS;
+ default: return &nofunc;
+ }
+
+ case FR_MIN:
+ switch(toGroup)
+ {
+ case FR_ANN: return &asfreq_TtoA;
+ case FR_QTR: return &asfreq_TtoQ;
+ case FR_MTH: return &asfreq_TtoM;
+ case FR_WK: return &asfreq_TtoW;
+ case FR_BUS:
+ if (forConvert) { return &asfreq_TtoB_forConvert; }
+ else { return &asfreq_TtoB; }
+ case FR_DAY: return &asfreq_TtoD;
+ case FR_HR: return &asfreq_TtoH;
+ case FR_SEC: return &asfreq_TtoS;
+ default: return &nofunc;
+ }
+
+ case FR_SEC:
+ switch(toGroup)
+ {
+ case FR_ANN: return &asfreq_StoA;
+ case FR_QTR: return &asfreq_StoQ;
+ case FR_MTH: return &asfreq_StoM;
+ case FR_WK: return &asfreq_StoW;
+ case FR_BUS:
+ if (forConvert) { return &asfreq_StoB_forConvert; }
+ else { return &asfreq_StoB; }
+ case FR_DAY: return &asfreq_StoD;
+ case FR_HR: return &asfreq_StoH;
+ case FR_MIN: return &asfreq_StoT;
+ default: return &nofunc;
+ }
+ default: return &nofunc;
+ }
+}
+
+static int calc_a_year_end(int freq, int group) {
+ int result = (freq - group) % 12;
+ if (result == 0) {return 12;}
+ else {return result;}
+}
+
+static int calc_week_end(int freq, int group) {
+ return freq - group;
+}
+
+void get_asfreq_info(int fromFreq, int toFreq, asfreq_info *af_info) {
+
+ int fromGroup = get_freq_group(fromFreq);
+ int toGroup = get_freq_group(toFreq);
+
+ switch(fromGroup)
+ {
+ case FR_WK: {
+ af_info->from_week_end = calc_week_end(fromFreq, fromGroup);
+ } break;
+ case FR_ANN: {
+ af_info->from_a_year_end = calc_a_year_end(fromFreq, fromGroup);
+ } break;
+ case FR_QTR: {
+ af_info->from_q_year_end = calc_a_year_end(fromFreq, fromGroup);
+ } break;
+
+ }
+
+ switch(toGroup)
+ {
+ case FR_WK: {
+ af_info->to_week_end = calc_week_end(toFreq, toGroup);
+ } break;
+ case FR_ANN: {
+ af_info->to_a_year_end = calc_a_year_end(toFreq, toGroup);
+ } break;
+ case FR_QTR: {
+ af_info->to_q_year_end = calc_a_year_end(toFreq, toGroup);
+ } break;
+ }
+
+}
+
+static double getAbsTime(int freq, long dailyDate, long originalDate) {
+
+ long startOfDay, periodsPerDay;
+
+ switch(freq)
+ {
+ case FR_HR:
+ periodsPerDay = 24;
+ break;
+ case FR_MIN:
+ periodsPerDay = 24*60;
+ break;
+ case FR_SEC:
+ periodsPerDay = 24*60*60;
+ break;
+ default:
+ return 0;
+ }
+
+ startOfDay = asfreq_DtoHIGHFREQ(dailyDate, 'B', periodsPerDay);
+ return (24*60*60)*((double)(originalDate - startOfDay))/((double)periodsPerDay);
+}
+
+/************************************************************
+** Date type definition
+************************************************************/
+
+typedef struct {
+ PyObject_HEAD
+ int freq; /* frequency of date */
+ int value; /* integer representation of date */
+ PyObject* cached_vals;
+} DateObject;
+
+/* Forward declarations */
+static PyTypeObject DateType;
+#define DateObject_Check(op) PyObject_TypeCheck(op, &DateType)
+
+static void
+DateObject_dealloc(DateObject* self) {
+ Py_XDECREF(self->cached_vals);
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+
+static PyObject *freq_dict, *freq_dict_rev, *freq_constants;
+
+#define DICT_SETINT_STRKEY(dict, key, val) \
+ {PyObject *pyval = PyInt_FromLong(val); \
+ PyDict_SetItemString(dict, key, pyval); \
+ Py_DECREF(pyval); }
+
+#define ADD_FREQ_CONSTANT(const_name, val) \
+ DICT_SETINT_STRKEY(freq_constants, const_name, val)
+
+#define INIT_FREQ(const_name, key, aliases) \
+ {PyObject *pykey = PyInt_FromLong(key); \
+ PyDict_SetItem(freq_dict, pykey, aliases); \
+ PyDict_SetItemString(freq_constants, const_name, pykey); \
+ Py_DECREF(pykey); \
+ Py_DECREF(aliases); }
+
+
+static int init_freq_group(int num_items, int num_roots, int base_const,
+ char item_abbrevs[][2][10], char group_prefixes[][15],
+ char item_const_names[][15]) {
+
+ int i;
+
+ for (i = 0; i < num_items; i++) {
+
+ PyObject *aliases;
+ int j, size, k;
+
+ if (i == 0) { k = 3; } else { k = 2; }
+
+ size = num_roots * k;
+
+ aliases = PyTuple_New(size);
+
+ for (j = 0; j < num_roots; j++) {
+ PyObject *alias_v1, *alias_v2;
+ char *root, *alt;
+
+ if ((root = malloc((30) * sizeof(char))) == NULL) return INT_ERR_CODE;
+ if ((alt = malloc((30) * sizeof(char))) == NULL) return INT_ERR_CODE;
+
+ strcpy(root, group_prefixes[j]);
+ strcpy(alt, group_prefixes[j]);
+
+ if (i == 0) {
+ PyObject *alias = PyString_FromString(root);
+ PyTuple_SET_ITEM(aliases, j*k + 2, alias);
+ }
+
+ strcat(root, "-");
+ strcat(root, item_abbrevs[i][0]);
+ strcat(alt, "-");
+ strcat(alt, item_abbrevs[i][1]);
+
+ alias_v1 = PyString_FromString(root);
+ alias_v2 = PyString_FromString(alt);
+
+ free(root);
+ free(alt);
+
+ PyTuple_SET_ITEM(aliases, j*k, alias_v1);
+ PyTuple_SET_ITEM(aliases, j*k + 1, alias_v2);
+ }
+
+ INIT_FREQ(item_const_names[i], base_const+i, aliases);
+ }
+
+ return 0;
+}
+
+/* take a dictionary with integer keys and tuples of strings for values,
+ and populate a dictionary with all the strings as keys and integers
+ for values */
+static int reverse_dict(PyObject *source, PyObject *dest) {
+
+ PyObject *key, *value;
+
+ Py_ssize_t pos = 0;
+
+ while (PyDict_Next(source, &pos, &key, &value)) {
+ PyObject *tuple_iter;
+ PyObject *item;
+
+ if((tuple_iter = PyObject_GetIter(value)) == NULL) return INT_ERR_CODE;
+
+ while ((item = PyIter_Next(tuple_iter)) != NULL) {
+ PyDict_SetItem(dest, item, key);
+ Py_DECREF(item);
+ }
+ Py_DECREF(tuple_iter);
+ }
+ return 0;
+}
+
+static int build_freq_dict(void) {
+
+ char ANN_prefixes[8][15] = { "A", "Y", "ANN", "ANNUAL", "ANNUALLY",
+ "YR", "YEAR", "YEARLY" };
+
+ char QTRE_prefixes[8][15] = { "Q", "QTR", "QUARTER", "QUARTERLY", "Q-E",
+ "QTR-E", "QUARTER-E", "QUARTERLY-E"};
+ char QTRS_prefixes[4][15] = { "Q-S", "QTR-S", "QUARTER-S", "QUARTERLY-S" };
+
+ char WK_prefixes[4][15] = { "W", "WK", "WEEK", "WEEKLY" };
+
+ /* Note: order of this array must match up with how the Annual
+ frequency constants are lined up */
+ char month_names[12][2][10] = {
+ { "DEC", "DECEMBER" },
+ { "JAN", "JANUARY" },
+ { "FEB", "FEBRUARY" },
+ { "MAR", "MARCH" },
+ { "APR", "APRIL" },
+ { "MAY", "MAY" },
+ { "JUN", "JUNE" },
+ { "JUL", "JULY" },
+ { "AUG", "AUGUST" },
+ { "SEP", "SEPTEMBER" },
+ { "OCT", "OCTOBER" },
+ { "NOV", "NOVEMBER" }};
+
+ char day_names[7][2][10] = {
+ { "SUN", "SUNDAY" },
+ { "MON", "MONDAY" },
+ { "TUE", "TUESDAY" },
+ { "WED", "WEDNESDAY" },
+ { "THU", "THURSDAY" },
+ { "FRI", "FRIDAY" },
+ { "SAT", "SATURDAY" }};
+
+ char ANN_const_names[12][15] = {
+ "FR_ANNDEC",
+ "FR_ANNJAN",
+ "FR_ANNFEB",
+ "FR_ANNMAR",
+ "FR_ANNAPR",
+ "FR_ANNMAY",
+ "FR_ANNJUN",
+ "FR_ANNJUL",
+ "FR_ANNAUG",
+ "FR_ANNSEP",
+ "FR_ANNOCT",
+ "FR_ANNNOV"};
+
+ char QTRE_const_names[12][15] = {
+ "FR_QTREDEC",
+ "FR_QTREJAN",
+ "FR_QTREFEB",
+ "FR_QTREMAR",
+ "FR_QTREAPR",
+ "FR_QTREMAY",
+ "FR_QTREJUN",
+ "FR_QTREJUL",
+ "FR_QTREAUG",
+ "FR_QTRESEP",
+ "FR_QTREOCT",
+ "FR_QTRENOV"};
+
+ char QTRS_const_names[12][15] = {
+ "FR_QTRSDEC",
+ "FR_QTRSJAN",
+ "FR_QTRSFEB",
+ "FR_QTRSMAR",
+ "FR_QTRSAPR",
+ "FR_QTRSMAY",
+ "FR_QTRSJUN",
+ "FR_QTRSJUL",
+ "FR_QTRSAUG",
+ "FR_QTRSSEP",
+ "FR_QTRSOCT",
+ "FR_QTRSNOV"};
+
+ char WK_const_names[7][15] = {
+ "FR_WKSUN",
+ "FR_WKMON",
+ "FR_WKTUE",
+ "FR_WKWED",
+ "FR_WKTHU",
+ "FR_WKFRI",
+ "FR_WKSAT"};
+
+ PyObject *aliases;
+
+ freq_dict = PyDict_New();
+ freq_dict_rev = PyDict_New();
+ freq_constants = PyDict_New();
+
+ aliases = Py_BuildValue("(ssss)", "M", "MTH", "MONTH", "MONTHLY");
+ INIT_FREQ("FR_MTH", FR_MTH, aliases);
+
+ aliases = Py_BuildValue("(ssss)", "B", "BUS", "BUSINESS", "BUSINESSLY");
+ INIT_FREQ("FR_BUS", FR_BUS, aliases);
+
+ aliases = Py_BuildValue("(ssss)", "D", "DAY", "DLY", "DAILY");
+ INIT_FREQ("FR_DAY", FR_DAY, aliases);
+
+ aliases = Py_BuildValue("(sssss)", "H", "HR", "HOUR", "HRLY", "HOURLY");
+ INIT_FREQ("FR_HR", FR_HR, aliases);
+
+ aliases = Py_BuildValue("(ssss)", "T", "MIN", "MINUTE", "MINUTELY");
+ INIT_FREQ("FR_MIN", FR_MIN, aliases);
+
+ aliases = Py_BuildValue("(ssss)", "S", "SEC", "SECOND", "SECONDLY");
+ INIT_FREQ("FR_SEC", FR_SEC, aliases);
+
+ aliases = Py_BuildValue("(ssss)", "U", "UND", "UNDEF", "UNDEFINED");
+ INIT_FREQ("FR_UND", FR_UND, aliases);
+
+ ADD_FREQ_CONSTANT("FR_ANN", FR_ANN);
+
+ if(init_freq_group(12, 8, FR_ANN,
+ month_names, ANN_prefixes, ANN_const_names) == INT_ERR_CODE) {
+ return INT_ERR_CODE;
+ }
+
+ ADD_FREQ_CONSTANT("FR_QTR", FR_QTR);
+
+ if(init_freq_group(12, 8, FR_QTREDEC,
+ month_names, QTRE_prefixes, QTRE_const_names) == INT_ERR_CODE) {
+ return INT_ERR_CODE;
+ }
+
+ if(init_freq_group(12, 4, FR_QTRSDEC,
+ month_names, QTRS_prefixes, QTRS_const_names) == INT_ERR_CODE) {
+ return INT_ERR_CODE;
+ }
+
+ ADD_FREQ_CONSTANT("FR_WK", FR_WK);
+
+ if(init_freq_group(7, 4, FR_WK,
+ day_names, WK_prefixes, WK_const_names) == INT_ERR_CODE) {
+ return INT_ERR_CODE;
+ }
+
+ if(reverse_dict(freq_dict, freq_dict_rev) == INT_ERR_CODE) {
+ return INT_ERR_CODE;
+ }
+
+ return 0;
+}
+
+
+/* take user specified frequency and convert to int representation
+ of the frequency */
+int check_freq(PyObject *freq_spec) {
+
+ if (PyInt_Check(freq_spec)) {
+ return (int)PyInt_AsLong(freq_spec);
+ } else if (PyString_Check(freq_spec)) {
+ char *freq_str, *freq_str_uc;
+ PyObject *freq_val;
+
+ freq_str = PyString_AsString(freq_spec);
+ if((freq_str_uc = str_uppercase(freq_str)) == NULL) {return INT_ERR_CODE;}
+
+ freq_val = PyDict_GetItemString(freq_dict_rev, freq_str_uc);
+
+ free(freq_str_uc);
+
+ if (freq_val == NULL) {
+ PyErr_SetString(PyExc_ValueError, "invalid frequency specification");
+ return INT_ERR_CODE;
+ } else {
+ int ret_val = (int)PyInt_AsLong(freq_val);
+ return ret_val;
+ }
+ } else if (freq_spec == Py_None) {
+ return FR_UND;
+ } else {
+ int retval = (int)PyInt_AsLong(freq_spec);
+ if (PyErr_Occurred()) {
+ PyErr_SetString(PyExc_ValueError, "invalid frequency specification");
+ return INT_ERR_CODE;
+ } else { return retval; }
+ }
+
+}
+
+static PyObject *
+DateObject_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
+
+ DateObject *self;
+
+ self = (DateObject*)type->tp_alloc(type, 0);
+ if (self != NULL) {
+ // initialize attributes that need initializing in here
+ self->freq = FR_UND;
+ self->value = -1;
+ }
+
+ return (PyObject *)self;
+}
+
+/* for use in C code */
+static DateObject *
+DateObject_New(void) {
+ PyObject *dummy;
+ return (DateObject*)DateObject_new(&DateType, dummy, dummy);
+}
+
+#define INIT_ERR(errortype, errmsg) PyErr_SetString(errortype,errmsg);return -1
+
+static int
+DateObject_init(DateObject *self, PyObject *args, PyObject *kwds) {
+
+ PyObject *freq=NULL, *value=NULL, *datetime=NULL, *string=NULL;
+ char *INSUFFICIENT_MSG = "insufficient parameters to initialize Date";
+
+ int def_info=INT_ERR_CODE;
+
+ int year=def_info, month=def_info, day=def_info, quarter=def_info,
+ hour=def_info, minute=def_info, second=def_info;
+
+ int free_dt=0;
+
+ static char *kwlist[] = {"freq", "value", "string",
+ "year", "month", "day", "quarter",
+ "hour", "minute", "second",
+ "datetime", NULL};
+
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "O|OOiiiiiiiO", kwlist,
+ &freq, &value, &string,
+ &year, &month, &day, &quarter,
+ &hour, &minute, &second,
+ &datetime)) return -1;
+
+ if (PyObject_HasAttrString(freq, "freq")) {
+ PyObject *freq_attr = PyObject_GetAttrString(freq, "freq");
+ self->freq = PyInt_AS_LONG(freq_attr);
+ Py_DECREF(freq_attr);
+ } else {
+ if((self->freq = check_freq(freq)) == INT_ERR_CODE) return -1;
+ }
+
+ if ((value && PyString_Check(value)) || string) {
+
+ PyObject *string_arg = PyTuple_New(1);
+ int freq_group = get_freq_group(self->freq);
+
+ free_dt = 1;
+
+ if (!string) {
+ string = value;
+ }
+
+ PyTuple_SET_ITEM(string_arg, 0, string);
+ Py_INCREF(string);
+
+ if (freq_group == FR_HR ||
+ freq_group == FR_MIN ||
+ freq_group == FR_SEC)
+ { datetime = PyEval_CallObject(DateTimeFromString, string_arg); }
+ else { datetime = PyEval_CallObject(DateFromString, string_arg); }
+
+ Py_DECREF(string_arg);
+
+ value = NULL;
+ }
+
+ if (value) {
+ self->value = PyInt_AsLong(value);
+ } else {
+
+ int freq_group = get_freq_group(self->freq);
+
+ if (datetime) {
+ year=PyDateTime_GET_YEAR(datetime);
+ month=PyDateTime_GET_MONTH(datetime);
+ day=PyDateTime_GET_DAY(datetime);
+ hour=PyDateTime_DATE_GET_HOUR(datetime);
+ minute=PyDateTime_DATE_GET_MINUTE(datetime);
+ second=PyDateTime_DATE_GET_SECOND(datetime);
+ }
+
+ if (!datetime) {
+
+ // First, some basic checks.....
+ if (year == def_info) {
+ INIT_ERR(PyExc_ValueError, INSUFFICIENT_MSG);
+ }
+ if (self->freq == FR_BUS ||
+ self->freq == FR_DAY ||
+ self->freq == FR_WK ||
+ self->freq == FR_UND) {
+ if (month == def_info || day == def_info) {
+ INIT_ERR(PyExc_ValueError, INSUFFICIENT_MSG);
+ }
+
+ // if FR_BUS, check for week day
+
+ } else if (self->freq == FR_MTH) {
+ if (month == def_info) {
+ INIT_ERR(PyExc_ValueError, INSUFFICIENT_MSG);
+ }
+ } else if (freq_group == FR_QTR) {
+ if (quarter == def_info) {
+ INIT_ERR(PyExc_ValueError, INSUFFICIENT_MSG);
+ }
+ } else if (self->freq == FR_SEC) {
+ if (month == def_info ||
+ day == def_info ||
+ second == def_info) {
+ INIT_ERR(PyExc_ValueError, INSUFFICIENT_MSG);
+ }
+ if (hour == def_info) {
+ hour = second/3600;
+ minute = (second % 3600)/60;
+ second = second % 60;
+ } else if (minute == def_info) {
+ INIT_ERR(PyExc_ValueError, INSUFFICIENT_MSG);
+ }
+ } else if (self->freq == FR_MIN) {
+ if (month == def_info ||
+ day == def_info ||
+ minute == def_info) {
+ INIT_ERR(PyExc_ValueError, INSUFFICIENT_MSG);
+ }
+ if (hour == def_info) {
+ hour = minute/60;
+ minute = minute % 60;
+ }
+ } else if (self->freq == FR_HR) {
+ if (month == def_info ||
+ day == def_info ||
+ hour == def_info) {
+ INIT_ERR(PyExc_ValueError, INSUFFICIENT_MSG);
+ }
+ }
+
+ }
+
+ if (self->freq == FR_SEC) {
+ long absdays, delta;
+ absdays = absdate_from_ymd(year, month, day);
+ delta = (absdays - HIGHFREQ_ORIG);
+ self->value = (int)(delta*86400 + hour*3600 + minute*60 + second + 1);
+ } else if (self->freq == FR_MIN) {
+ long absdays, delta;
+ absdays = absdate_from_ymd(year, month, day);
+ delta = (absdays - HIGHFREQ_ORIG);
+ self->value = (int)(delta*1440 + hour*60 + minute + 1);
+ } else if (self->freq == FR_HR) {
+ long absdays, delta;
+ if((absdays = absdate_from_ymd(year, month, day)) == INT_ERR_CODE) return -1;
+ delta = (absdays - HIGHFREQ_ORIG);
+ self->value = (int)(delta*24 + hour + 1);
+ } else if (self->freq == FR_DAY) {
+ if((self->value = (int)absdate_from_ymd(year, month, day)) == INT_ERR_CODE) return -1;
+ } else if (self->freq == FR_UND) {
+ if((self->value = (int)absdate_from_ymd(year, month, day)) == INT_ERR_CODE) return -1;
+ } else if (self->freq == FR_BUS) {
+ long weeks, days;
+ if((days = absdate_from_ymd(year, month, day)) == INT_ERR_CODE) return -1;
+ weeks = days/7;
+ self->value = (int)(days - weeks*2);
+ } else if (freq_group == FR_WK) {
+ int adj_ordinal, ordinal, day_adj;
+ if((ordinal = (int)absdate_from_ymd(year, month, day)) == INT_ERR_CODE) return -1;
+ day_adj = (7 - (self->freq - FR_WK)) % 7;
+ adj_ordinal = ordinal + ((7 - day_adj) - ordinal % 7) % 7;
+ self->value = adj_ordinal/7;
+ } else if (self->freq == FR_MTH) {
+ self->value = (year-1)*12 + month;
+ } else if (freq_group == FR_QTR) {
+ if ((self->freq - freq_group) > 12) {
+ // quarterly frequency with year determined by ending period
+ self->value = year*4 + quarter;
+ } else {
+ /* quarterly frequency with year determined by ending period
+ or has December year end*/
+ self->value = (year-1)*4 + quarter;
+ }
+ } else if (freq_group == FR_ANN) {
+ self->value = year;
+ }
+
+ }
+
+ if (free_dt) { Py_DECREF(datetime); }
+
+ return 0;
+}
+
+static PyMemberDef DateObject_members[] = {
+ {"freq", T_INT, offsetof(DateObject, freq), 0,
+ "frequency"},
+ {"value", T_INT, offsetof(DateObject, value), 0,
+ "integer representation of the Date"},
+ {NULL} /* Sentinel */
+};
+
+static char DateObject_toordinal_doc[] =
+"Return the proleptic Gregorian ordinal of the date, where January 1 of\n"
+"year 1 has ordinal 1";
+static PyObject *
+DateObject_toordinal(DateObject* self)
+{
+ if (self->freq == FR_DAY) {
+ return PyInt_FromLong(self->value);
+ } else {
+ long (*toDaily)(long, char, asfreq_info*) = NULL;
+ asfreq_info af_info;
+
+ toDaily = get_asfreq_func(self->freq, FR_DAY, 0);
+ get_asfreq_info(self->freq, FR_DAY, &af_info);
+
+ return PyInt_FromLong(toDaily(self->value, 'A', &af_info));
+ }
+}
+
+static char DateObject_asfreq_doc[] =
+"Returns a date converted to a specified frequency.\n\n"
+":Parameters:\n"
+" - freq : string/int\n"
+" Frequency to convert the Date to. Accepts any valid frequency\n"
+" specification (string or integer)\n"
+" - relation :string *['After']*\n"
+" Applies only when converting a lower frequency Date to a higher\n"
+" frequency Date, or when converting a weekend Date to a business\n"
+" frequency Date. Valid values are 'before', 'after', 'b', and 'a'.";
+static PyObject *
+DateObject_asfreq(DateObject *self, PyObject *args, PyObject *kwds)
+{
+
+ PyObject *freq=NULL;
+ char *relation_raw=NULL;
+ char *relation_uc;
+ char relation;
+ int invalid_relation=0;
+ int toFreq;
+ int result_val;
+ DateObject *result = DateObject_New();
+
+ static char *kwlist[] = {"freq", "relation", NULL};
+
+ long (*asfreq_func)(long, char, asfreq_info*) = NULL;
+ asfreq_info af_info;
+
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "O|s", kwlist,
+ &freq, &relation_raw)) return NULL;
+
+ if(relation_raw) {
+ if (strlen(relation_raw) > 0) {
+ if((relation_uc = str_uppercase(relation_raw)) == NULL)
+ {return PyErr_NoMemory();}
+
+ if (strcmp(relation_uc, "BEFORE") == 0 ||
+ strcmp(relation_uc, "B") == 0 ||
+ strcmp(relation_uc, "AFTER") == 0 ||
+ strcmp(relation_uc, "A") == 0) {
+ relation = relation_uc[0];
+ } else { invalid_relation=1; }
+ } else {
+ invalid_relation=1;
+ }
+
+ if (invalid_relation) {
+ PyErr_SetString(PyExc_ValueError,"Invalid relation specification");
+ return NULL;
+ }
+ } else {
+ relation = 'A';
+ }
+
+ if ((toFreq = check_freq(freq)) == INT_ERR_CODE) return NULL;
+
+ get_asfreq_info(self->freq, toFreq, &af_info);
+ asfreq_func = get_asfreq_func(self->freq, toFreq, 0);
+
+ result_val = asfreq_func(self->value, relation, &af_info);
+
+ result->freq = toFreq;
+ result->value = result_val;
+
+ return (PyObject*)result;
+
+}
+
+static char DateObject_strfmt_doc[] =
+"Returns string representation of Date object according to format specified.\n\n"
+":Parameters:\n"
+" - fmt : string\n"
+" Formatting string. Uses the same directives as in the time.strftime\n"
+" function in the standard Python time module. In addition, a few other\n"
+" directives are supported:\n"
+" %q - the 'quarter' of the date\n"
+" %f - Year without century as a decimal number [00,99]. The\n"
+" 'year' in this case is the year of the date determined by\n"
+" the year for the current quarter. This is the same as %y\n"
+" unless the Date is one of the 'qtr-s' frequencies\n"
+" %F - Year with century as a decimal number. The 'year' in this\n"
+" case is the year of the date determined by the year for\n"
+" the current quarter. This is the same as %Y unless the\n"
+" Date is one of the 'qtr-s' frequencies\n";
+static PyObject *
+DateObject_strfmt(DateObject *self, PyObject *args)
+{
+
+ char *orig_fmt_str, *fmt_str;
+ char *result;
+
+ int num_extra_fmts = 3;
+
+ char extra_fmts[3][2][10] = {{"%q", "^`AB`^"},
+ {"%f", "^`CD`^"},
+ {"%F", "^`EF`^"}};
+
+ int extra_fmts_found[3] = {0,0,0};
+ int extra_fmts_found_one = 0;
+ struct tm c_date;
+ struct date_info tempDate;
+ long absdate;
+ double abstime;
+ int i, result_len;
+ PyObject *py_result;
+
+ long (*toDaily)(long, char, asfreq_info*) = NULL;
+ asfreq_info af_info;
+
+ if (!PyArg_ParseTuple(args, "s:strfmt(fmt)", &orig_fmt_str)) return NULL;
+
+ toDaily = get_asfreq_func(self->freq, FR_DAY, 0);
+ get_asfreq_info(self->freq, FR_DAY, &af_info);
+
+ absdate = toDaily(self->value, 'A', &af_info);
+ abstime = getAbsTime(self->freq, absdate, self->value);
+
+ if(dInfoCalc_SetFromAbsDateTime(&tempDate, absdate, abstime,
+ GREGORIAN_CALENDAR)) return NULL;
+
+ // populate standard C date struct with info from our date_info struct
+ c_date.tm_sec = (int)tempDate.second;
+ c_date.tm_min = tempDate.minute;
+ c_date.tm_hour = tempDate.hour;
+ c_date.tm_mday = tempDate.day;
+ c_date.tm_mon = tempDate.month - 1;
+ c_date.tm_year = tempDate.year - 1900;
+ c_date.tm_wday = tempDate.day_of_week;
+ c_date.tm_yday = tempDate.day_of_year;
+ c_date.tm_isdst = -1;
+
+ result_len = strlen(orig_fmt_str) + 50;
+ if ((result = malloc(result_len * sizeof(char))) == NULL) {return PyErr_NoMemory();}
+
+ fmt_str = orig_fmt_str;
+
+ // replace any special format characters with their place holder
+ for(i=0; i < num_extra_fmts; i++) {
+ char *special_loc;
+ if ((special_loc = strstr(fmt_str,extra_fmts[i][0])) != NULL) {
+ char *tmp_str = fmt_str;
+ fmt_str = str_replace(fmt_str, extra_fmts[i][0],
+ extra_fmts[i][1]);
+ /* only free the previous loop value if this is not the first
+ special format string found */
+ if (extra_fmts_found_one) { free(tmp_str); }
+
+ if (fmt_str == NULL) {return NULL;}
+
+ extra_fmts_found[i] = 1;
+ extra_fmts_found_one = 1;
+ }
+ }
+
+ strftime(result, result_len, fmt_str, &c_date);
+ if (extra_fmts_found_one) { free(fmt_str); }
+
+ // replace any place holders with the appropriate value
+ for(i=0; i < num_extra_fmts; i++) {
+ if (extra_fmts_found[i]) {
+ char *tmp_str = result;
+ char *extra_str;
+
+ if (strcmp(extra_fmts[i][0], "%q") == 0 ||
+ strcmp(extra_fmts[i][0], "%f") == 0 ||
+ strcmp(extra_fmts[i][0], "%F") == 0) {
+
+ asfreq_info af_info;
+ int qtr_freq, year, quarter, year_len;
+
+ if (get_freq_group(self->freq) == FR_QTR) {
+ qtr_freq = self->freq;
+ } else { qtr_freq = FR_QTR; }
+ get_asfreq_info(FR_DAY, qtr_freq, &af_info);
+
+ if(DtoQ_yq(absdate, &af_info, &year, &quarter) == INT_ERR_CODE)
+ { return NULL; }
+
+ if(strcmp(extra_fmts[i][0], "%q") == 0) {
+ if ((extra_str = malloc(2 * sizeof(char))) == NULL) {
+ free(tmp_str);
+ return PyErr_NoMemory();
+ }
+ sprintf(extra_str, "%i", quarter);
+ } else {
+ if ((qtr_freq % 1000) > 12) { year -= 1; }
+
+ if (strcmp(extra_fmts[i][0], "%f") == 0) {
+ year_len = 2;
+ year = year % 100;
+ } else { year_len = 4; }
+
+ if ((extra_str = malloc((year_len+1) * sizeof(char))) == NULL) {
+ free(tmp_str);
+ return PyErr_NoMemory();
+ }
+
+ if (year_len == 2 && year < 10) {
+ sprintf(extra_str, "0%i", year);
+ } else { sprintf(extra_str, "%i", year); }
+ }
+
+ } else {
+ PyErr_SetString(PyExc_RuntimeError,"Unrecogized fmt string");
+ return NULL;
+ }
+
+ result = str_replace(result, extra_fmts[i][1], extra_str);
+ free(tmp_str);
+ free(extra_str);
+ if (result == NULL) { return NULL; }
+ }
+ }
+
+ py_result = PyString_FromString(result);
+ free(result);
+
+ return py_result;
+}
+
+static PyObject *
+DateObject___str__(DateObject* self)
+{
+
+ int freq_group = get_freq_group(self->freq);
+ PyObject *string_arg, *retval;
+
+ string_arg = NULL;
+ if (freq_group == FR_ANN) { string_arg = Py_BuildValue("(s)", "%Y"); }
+ else if (freq_group == FR_QTR) { string_arg = Py_BuildValue("(s)", "%FQ%q"); }
+ else if (freq_group == FR_MTH) { string_arg = Py_BuildValue("(s)", "%b-%Y"); }
+ else if (freq_group == FR_DAY ||
+ freq_group == FR_BUS ||
+ freq_group == FR_WK ||
+ freq_group == FR_UND) { string_arg = Py_BuildValue("(s)", "%d-%b-%Y"); }
+ else if (freq_group == FR_HR) { string_arg = Py_BuildValue("(s)", "%d-%b-%Y %H:00"); }
+ else if (freq_group == FR_MIN) { string_arg = Py_BuildValue("(s)", "%d-%b-%Y %H:%M"); }
+ else if (freq_group == FR_SEC) { string_arg = Py_BuildValue("(s)", "%d-%b-%Y %H:%M:%S"); }
+
+ if (string_arg == NULL) { return NULL; }
+
+ retval = DateObject_strfmt(self, string_arg);
+ Py_DECREF(string_arg);
+
+ return retval;
+}
+
+static PyObject *
+DateObject_freqstr(DateObject *self, void *closure) {
+ PyObject *key = PyInt_FromLong(self->freq);
+ PyObject *freq_aliases = PyDict_GetItem(freq_dict, key);
+ PyObject *main_alias = PyTuple_GET_ITEM(freq_aliases, 0);
+ Py_DECREF(key);
+ Py_INCREF(main_alias);
+ return main_alias;
+}
+
+
+static PyObject *
+DateObject___repr__(DateObject* self)
+{
+ PyObject *py_str_rep, *py_freqstr, *py_repr;
+ char *str_rep, *freqstr, *repr;
+ int repr_len;
+
+ py_str_rep = DateObject___str__(self);
+ if (py_str_rep == NULL) { return NULL; }
+
+ py_freqstr = DateObject_freqstr(self, NULL);
+
+ str_rep = PyString_AsString(py_str_rep);
+ freqstr = PyString_AsString(py_freqstr);
+
+ repr_len = strlen(str_rep) + strlen(freqstr) + 6;
+
+ if((repr = malloc((repr_len + 1) * sizeof(char))) == NULL)
+ { return PyErr_NoMemory(); }
+
+ strcpy(repr, "<");
+ strcat(repr, freqstr);
+ strcat(repr, " : ");
+ strcat(repr, str_rep);
+ strcat(repr, ">");
+
+ py_repr = PyString_FromString(repr);
+
+ Py_DECREF(py_str_rep);
+ Py_DECREF(py_freqstr);
+
+ free(repr);
+
+ return py_repr;
+}
+
+/******************************
+ These methods seem rather useless. May or may not implement them.
+fromordinal(self, ordinal):
+ return Date(self.freq, datetime=dt.datetime.fromordinal(ordinal))
+tostring(self):
+ return str(self)
+toobject(self):
+ return self
+isvalid(self):
+ return True
+*******************************/
+
+
+static DateObject *
+DateObject_FromFreqAndValue(int freq, int value) {
+
+ DateObject *result = DateObject_New();
+
+ PyObject *args = PyTuple_New(0);
+ PyObject *kw = PyDict_New();
+ PyObject *py_freq = PyInt_FromLong(freq);
+ PyObject *py_value = PyInt_FromLong(value);
+
+ PyDict_SetItemString(kw, "freq", py_freq);
+ PyDict_SetItemString(kw, "value", py_value);
+
+ Py_DECREF(py_freq);
+ Py_DECREF(py_value);
+
+ DateObject_init(result, args, kw);
+
+ Py_DECREF(args);
+ Py_DECREF(kw);
+
+ return result;
+}
+
+static PyObject *
+DateObject_date_plus_int(PyObject *date, PyObject *pyint) {
+ DateObject *dateobj = (DateObject*)date;
+ if (DateObject_Check(pyint)) {
+ PyErr_SetString(PyExc_TypeError, "Cannot add two Date objects");
+ return NULL;
+ }
+
+ return (PyObject*)DateObject_FromFreqAndValue(dateobj->freq, PyInt_AsLong(pyint) + dateobj->value);
+}
+
+static PyObject *
+DateObject___add__(PyObject *left, PyObject *right)
+{
+ if (DateObject_Check(left)) {
+ return DateObject_date_plus_int(left, right);
+ } else {
+ return DateObject_date_plus_int(right, left);
+ }
+}
+
+static PyObject *
+DateObject___subtract__(PyObject *left, PyObject *right)
+{
+ int result;
+ DateObject *dleft;
+ if (!DateObject_Check(left)) {
+ PyErr_SetString(PyExc_ValueError, "Cannot subtract Date from non-Date value");
+ return NULL;
+ }
+
+ dleft = (DateObject*)left;
+
+ if (DateObject_Check(right)) {
+ DateObject *dright = (DateObject*)right;
+ if (dleft->freq != dright->freq) {
+ PyErr_SetString(PyExc_ValueError, "Cannot subtract Dates with different frequency");
+ return NULL;
+ }
+ result = dleft->value - dright->value;
+ return PyInt_FromLong(result);
+ } else {
+ result = dleft->value - PyInt_AsLong(right);
+ return (PyObject*)DateObject_FromFreqAndValue(dleft->freq, result);
+ }
+}
+
+static int
+DateObject___compare__(DateObject * obj1, DateObject * obj2)
+{
+ if (obj1->freq != obj2->freq) {
+ PyErr_SetString(PyExc_ValueError,
+ "Cannot compare dates with different frequency");
+ return -1;
+ }
+
+ if (obj1->value < obj2->value) return -1;
+ if (obj1->value > obj2->value) return 1;
+ if (obj1->value == obj2->value) return 0;
+ return -1;
+}
+
+static long
+DateObject___hash__(DateObject *self)
+{
+ register int freq_group = get_freq_group(self->freq);
+
+ /* within a given frequency, hash values are guaranteed to be unique
+ for different dates. For different frequencies, we make a reasonable
+ effort to ensure hash values will be unique, but it is not guaranteed */
+ if (freq_group == FR_BUS) {
+ return self->value + 10000000;
+ } else if (freq_group == FR_WK) {
+ return self->value + 100000000;
+ } else { return self->value; }
+}
+
+static PyObject *
+DateObject___int__(DateObject *self)
+{
+ return PyInt_FromLong(self->value);
+}
+
+static PyObject *
+DateObject___float__(DateObject *self)
+{
+ return PyFloat_FromDouble((double)(self->value));
+}
+
+/***************************************************
+ ====== Date Properties ======
+****************************************************/
+
+// helper function for date property funcs
+static int
+DateObject_set_date_info(DateObject *self, struct date_info *dinfo) {
+ PyObject *daily_obj = DateObject_toordinal(self);
+ long absdate = PyInt_AsLong(daily_obj);
+
+ Py_DECREF(daily_obj);
+
+ if(dInfoCalc_SetFromAbsDate(dinfo, absdate,
+ GREGORIAN_CALENDAR)) return -1;
+
+ return 0;
+}
+
+// helper function for date property funcs
+static int
+DateObject_set_date_info_wtime(DateObject *self, struct date_info *dinfo) {
+ PyObject *daily_obj = DateObject_toordinal(self);
+ long absdate = PyInt_AsLong(daily_obj);
+ double abstime;
+
+ Py_DECREF(daily_obj);
+
+ abstime = getAbsTime(self->freq, absdate, self->value);
+
+ if(dInfoCalc_SetFromAbsDateTime(dinfo, absdate, abstime,
+ GREGORIAN_CALENDAR)) return -1;
+
+ return 0;
+}
+
+static PyObject *
+DateObject_year(DateObject *self, void *closure) {
+ struct date_info dinfo;
+ if(DateObject_set_date_info(self, &dinfo) == -1) return NULL;
+ return PyInt_FromLong(dinfo.year);
+}
+
+static int _DateObject_quarter_year(DateObject *self, int *year, int *quarter) {
+
+ PyObject *daily_obj;
+ long absdate;
+
+ asfreq_info af_info;
+ int qtr_freq;
+
+ daily_obj = DateObject_toordinal(self);
+ absdate = PyInt_AsLong(daily_obj);
+ Py_DECREF(daily_obj);
+
+ if (get_freq_group(self->freq) == FR_QTR) {
+ qtr_freq = self->freq;
+ } else { qtr_freq = FR_QTR; }
+ get_asfreq_info(FR_DAY, qtr_freq, &af_info);
+
+ if(DtoQ_yq(absdate, &af_info, year, quarter) == INT_ERR_CODE)
+ { return INT_ERR_CODE; }
+
+ if ((qtr_freq % 1000) > 12) { *year -= 1; }
+
+ return 0;
+}
+
+static PyObject *
+DateObject_qyear(DateObject *self, void *closure) {
+ int year, quarter;
+ if(_DateObject_quarter_year(self,
+ &year, &quarter) == INT_ERR_CODE) { return NULL; }
+ return PyInt_FromLong(year);
+}
+
+static PyObject *
+DateObject_quarter(DateObject *self, void *closure) {
+ int year, quarter;
+ if(_DateObject_quarter_year(self,
+ &year, &quarter) == INT_ERR_CODE) { return NULL; }
+ return PyInt_FromLong(quarter);
+}
+
+static PyObject *
+DateObject_month(DateObject *self, void *closure) {
+ struct date_info dinfo;
+ if(DateObject_set_date_info(self, &dinfo) == -1) return NULL;
+ return PyInt_FromLong(dinfo.month);
+}
+
+static PyObject *
+DateObject_day(DateObject *self, void *closure) {
+ struct date_info dinfo;
+ if(DateObject_set_date_info(self, &dinfo) == -1) return NULL;
+ return PyInt_FromLong(dinfo.day);
+}
+
+static PyObject *
+DateObject_day_of_week(DateObject *self, void *closure) {
+ struct date_info dinfo;
+ if(DateObject_set_date_info(self, &dinfo) == -1) return NULL;
+ return PyInt_FromLong(dinfo.day_of_week);
+}
+
+static PyObject *
+DateObject_day_of_year(DateObject *self, void *closure) {
+ struct date_info dinfo;
+ if(DateObject_set_date_info(self, &dinfo) == -1) return NULL;
+ return PyInt_FromLong(dinfo.day_of_year);
+}
+
+static PyObject *
+DateObject_week(DateObject *self, void *closure) {
+ struct date_info dinfo;
+ if(DateObject_set_date_info(self, &dinfo) == -1) return NULL;
+ return PyInt_FromLong(dInfoCalc_ISOWeek(&dinfo));
+}
+
+static PyObject *
+DateObject_hour(DateObject *self, void *closure) {
+ struct date_info dinfo;
+ if(DateObject_set_date_info_wtime(self, &dinfo) == -1) return NULL;
+ return PyInt_FromLong(dinfo.hour);
+}
+
+static PyObject *
+DateObject_minute(DateObject *self, void *closure) {
+ struct date_info dinfo;
+ if(DateObject_set_date_info_wtime(self, &dinfo) == -1) return NULL;
+ return PyInt_FromLong(dinfo.minute);
+}
+
+static PyObject *
+DateObject_second(DateObject *self, void *closure) {
+ struct date_info dinfo;
+ if(DateObject_set_date_info_wtime(self, &dinfo) == -1) return NULL;
+ return PyInt_FromLong((int)dinfo.second);
+}
+
+static PyObject *
+DateObject_datetime(DateObject *self, void *closure) {
+ PyObject *datetime;
+ struct date_info dinfo;
+ if(DateObject_set_date_info_wtime(self, &dinfo) == -1) return NULL;
+ datetime = PyDateTime_FromDateAndTime(dinfo.year, dinfo.month,
+ dinfo.day, dinfo.hour,
+ dinfo.minute, (int)dinfo.second, 0);
+ return datetime;
+}
+
+static int
+DateObject_ReadOnlyErr(DateObject *self, PyObject *value, void *closure) {
+ PyErr_SetString(PyExc_AttributeError, "Cannot set read-only property");
+ return -1;
+}
+
+static PyGetSetDef DateObject_getseters[] = {
+ {"year", (getter)DateObject_year, (setter)DateObject_ReadOnlyErr,
+ "Returns the year.", NULL},
+ {"qyear", (getter)DateObject_qyear, (setter)DateObject_ReadOnlyErr,
+ "For quarterly frequency dates, returns the year corresponding to the\n"
+ "year end (start) month. When using QTR or QTR-E based quarterly\n"
+ "frequencies, this is the fiscal year in a financial context.\n\n"
+ "For non-quarterly dates, this simply returns the year of the date.",
+ NULL},
+ {"quarter", (getter)DateObject_quarter, (setter)DateObject_ReadOnlyErr,
+ "Returns the quarter.", NULL},
+ {"month", (getter)DateObject_month, (setter)DateObject_ReadOnlyErr,
+ "Returns the month.", NULL},
+ {"week", (getter)DateObject_week, (setter)DateObject_ReadOnlyErr,
+ "Returns the week.", NULL},
+ {"day", (getter)DateObject_day, (setter)DateObject_ReadOnlyErr,
+ "Returns the day of month.", NULL},
+ {"day_of_week", (getter)DateObject_day_of_week, (setter)DateObject_ReadOnlyErr,
+ "Returns the day of week.", NULL},
+ {"day_of_year", (getter)DateObject_day_of_year, (setter)DateObject_ReadOnlyErr,
+ "Returns the day of year.", NULL},
+ {"second", (getter)DateObject_second, (setter)DateObject_ReadOnlyErr,
+ "Returns the second.", NULL},
+ {"minute", (getter)DateObject_minute, (setter)DateObject_ReadOnlyErr,
+ "Returns the minute.", NULL},
+ {"hour", (getter)DateObject_hour, (setter)DateObject_ReadOnlyErr,
+ "Returns the hour.", NULL},
+
+ {"freqstr", (getter)DateObject_freqstr, (setter)DateObject_ReadOnlyErr,
+ "Returns the string representation of frequency.", NULL},
+ {"datetime", (getter)DateObject_datetime, (setter)DateObject_ReadOnlyErr,
+ "Returns the Date object converted to standard python datetime object",
+ NULL},
+
+ {NULL} /* Sentinel */
+};
+
+
+static PyNumberMethods DateObject_as_number = {
+ (binaryfunc)DateObject___add__, /* nb_add */
+ (binaryfunc)DateObject___subtract__, /* nb_subtract */
+ 0, /* nb_multiply */
+ 0, /* nb_divide */
+ 0, /* nb_remainder */
+ 0, /* nb_divmod */
+ 0, /* nb_power */
+ 0, /* nb_negative */
+ 0, /* nb_positive */
+ 0, /* nb_absolute */
+ 0, /* nb_nonzero */
+ 0, /* nb_invert */
+ 0, /* nb_lshift */
+ 0, /* nb_rshift */
+ 0, /* nb_and */
+ 0, /* nb_xor */
+ 0, /* nb_or */
+ 0, /* nb_coerce */
+ (unaryfunc)DateObject___int__, /* nb_int */
+ (unaryfunc)0, /* nb_long */
+ (unaryfunc)DateObject___float__, /* nb_float */
+ (unaryfunc)0, /* nb_oct */
+ (unaryfunc)0, /* nb_hex */
+};
+
+static PyMethodDef DateObject_methods[] = {
+ {"toordinal", (PyCFunction)DateObject_toordinal, METH_NOARGS,
+ DateObject_toordinal_doc},
+ {"strfmt", (PyCFunction)DateObject_strfmt, METH_VARARGS,
+ DateObject_strfmt_doc},
+ {"asfreq", (PyCFunction)DateObject_asfreq, METH_VARARGS | METH_KEYWORDS,
+ DateObject_asfreq_doc},
+ {NULL} /* Sentinel */
+};
+
+
+static PyTypeObject DateType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "timeseries.Date", /* tp_name */
+ sizeof(DateObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)DateObject_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ (cmpfunc)DateObject___compare__, /* tp_compare */
+ (reprfunc)DateObject___repr__, /* tp_repr */
+ &DateObject_as_number, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)DateObject___hash__, /* tp_hash */
+ 0, /* tp_call*/
+ (reprfunc)DateObject___str__, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | /* tp_flags */
+ Py_TPFLAGS_CHECKTYPES |
+ Py_TPFLAGS_BASETYPE,
+ "Defines a Date object, as the combination of a date and a frequency.\n"
+ "Several options are available to construct a Date object explicitly:\n\n"
+ "- Give appropriate values to the `year`, `month`, `day`, `quarter`, `hours`,\n"
+ " `minutes`, `seconds` arguments.\n\n"
+ " >>> td.Date(freq='Q',year=2004,quarter=3)\n"
+ " >>> td.Date(freq='D',year=2001,month=1,day=1)\n\n"
+ "- Use the `string` keyword. This method uses a modified version of the\n"
+ " mx.DateTime parser submodule. More information is available in its\n"
+ " documentation.\n\n"
+ " >>> ts.Date('D', '2007-01-01')\n\n"
+ "- Use the `datetime` keyword with an existing datetime.datetime object.\n\n"
+ " >>> td.Date('D', datetime=datetime.datetime.now())", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ DateObject_methods, /* tp_methods */
+ DateObject_members, /* tp_members */
+ DateObject_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)DateObject_init, /* tp_init */
+ 0, /* tp_alloc */
+ DateObject_new, /* tp_new */
+};
+
+
+///////////////////////////////////////////////////////////////////////
+
+char c_tdates_check_freq_doc[] =
+"translate user specified frequency into frequency constant";
+PyObject *
+c_tdates_check_freq(PyObject *self, PyObject *args) {
+
+ PyObject *freq;
+ int freq_val;
+
+ if (!PyArg_ParseTuple(args, "O:check_freq(freq)", &freq)) return NULL;
+ if ((freq_val = check_freq(freq)) == INT_ERR_CODE) return NULL;
+
+ return PyInt_FromLong(freq_val);
+}
+
+char c_tdates_check_freq_str_doc[] =
+"translate user specified frequency into standard string representation";
+PyObject *
+c_tdates_check_freq_str(PyObject *self, PyObject *args) {
+
+ PyObject *alias_tuple, *result, *freq_key;
+
+ if ((freq_key = c_tdates_check_freq(self, args)) == NULL) return NULL;
+
+ alias_tuple = PyDict_GetItem(freq_dict, freq_key);
+ result = PyTuple_GET_ITEM(alias_tuple, 0);
+
+ Py_INCREF(result);
+
+ Py_DECREF(freq_key);
+
+ return result;
+}
+
+char c_tdates_get_freq_group_doc[] =
+"translate user specified frequency into frequency group constant";
+PyObject *
+c_tdates_get_freq_group(PyObject *self, PyObject *args) {
+
+ PyObject *freq;
+ int freq_val;
+
+ if (!PyArg_ParseTuple(args, "O:get_freq_group(freq)", &freq)) return NULL;
+ if ((freq_val = check_freq(freq)) == INT_ERR_CODE) return NULL;
+
+ return PyInt_FromLong(get_freq_group(freq_val));
+}
+
+char c_tdates_thisday_doc[] =
+"Returns today's date, at the given frequency\n\n"
+":Parameters:\n"
+" - freq : string/int\n"
+" Frequency to convert the Date to. Accepts any valid frequency\n"
+" specification (string or integer)\n";
+PyObject *
+c_tdates_thisday(PyObject *self, PyObject *args) {
+
+ PyObject *freq, *init_args, *init_kwargs;
+ time_t rawtime;
+ struct tm *timeinfo;
+ int freq_val;
+
+ DateObject *secondly_date;
+
+ if (!PyArg_ParseTuple(args, "O:thisday(freq)", &freq)) return NULL;
+
+ if ((freq_val = check_freq(freq)) == INT_ERR_CODE) return NULL;
+
+ time(&rawtime);
+ timeinfo = localtime(&rawtime);
+
+ init_args = PyTuple_New(0);
+ init_kwargs = PyDict_New();
+
+ DICT_SETINT_STRKEY(init_kwargs, "freq", FR_SEC);
+ DICT_SETINT_STRKEY(init_kwargs, "year", timeinfo->tm_year+1900);
+ DICT_SETINT_STRKEY(init_kwargs, "month", timeinfo->tm_mon+1);
+ DICT_SETINT_STRKEY(init_kwargs, "day", timeinfo->tm_mday);
+ DICT_SETINT_STRKEY(init_kwargs, "hour", timeinfo->tm_hour);
+ DICT_SETINT_STRKEY(init_kwargs, "minute", timeinfo->tm_min);
+ DICT_SETINT_STRKEY(init_kwargs, "second", timeinfo->tm_sec);
+
+ secondly_date = DateObject_New();
+ DateObject_init(secondly_date, init_args, init_kwargs);
+
+ Py_DECREF(init_args);
+ Py_DECREF(init_kwargs);
+
+ if (freq_val != FR_SEC) {
+ DateObject *result = DateObject_New();
+
+ long (*asfreq_func)(long, char, asfreq_info*) = NULL;
+ asfreq_info af_info;
+
+ int date_val;
+
+ get_asfreq_info(FR_SEC, freq_val, &af_info);
+ asfreq_func = get_asfreq_func(FR_SEC, freq_val, 0);
+
+ date_val = asfreq_func(secondly_date->value, 'A', &af_info);
+
+ Py_DECREF(secondly_date);
+
+ result->freq = freq_val;
+ result->value = date_val;
+
+ return (PyObject*)result;
+
+ } else { return (PyObject*)secondly_date; }
+}
+
+
+PyObject *
+DateArray_asfreq(PyObject *self, PyObject *args)
+{
+ PyArrayObject *fromDates, *toDates;
+ PyArrayIterObject *iterFrom, *iterTo;
+ PyObject *fromDateObj, *toDateObj;
+ char *relation;
+ int fromFreq, toFreq;
+ long fromDate, toDate;
+ long (*asfreq_main)(long, char, asfreq_info*) = NULL;
+ asfreq_info af_info;
+
+ if (!PyArg_ParseTuple(args,
+ "Oiis:asfreq(fromDates, fromfreq, tofreq, relation)",
+ &fromDates, &fromFreq, &toFreq, &relation)) return NULL;
+
+ get_asfreq_info(fromFreq, toFreq, &af_info);
+
+ asfreq_main = get_asfreq_func(fromFreq, toFreq, 0);
+
+ toDates = (PyArrayObject *)PyArray_Copy(fromDates);
+
+ iterFrom = (PyArrayIterObject *)PyArray_IterNew((PyObject *)fromDates);
+ if (iterFrom == NULL) return NULL;
+
+ iterTo = (PyArrayIterObject *)PyArray_IterNew((PyObject *)toDates);
+ if (iterTo == NULL) return NULL;
+
+ while (iterFrom->index < iterFrom->size) {
+
+ fromDateObj = PyArray_GETITEM(fromDates, iterFrom->dataptr);
+ fromDate = PyInt_AsLong(fromDateObj);
+ CHECK_ASFREQ(toDate = asfreq_main(fromDate, relation[0], &af_info));
+ toDateObj = PyInt_FromLong(toDate);
+
+ PyArray_SETITEM(toDates, iterTo->dataptr, toDateObj);
+
+ Py_DECREF(fromDateObj);
+ Py_DECREF(toDateObj);
+
+ PyArray_ITER_NEXT(iterFrom);
+ PyArray_ITER_NEXT(iterTo);
+ }
+
+ Py_DECREF(iterFrom);
+ Py_DECREF(iterTo);
+
+ return (PyObject *)toDates;
+
+}
+
+PyObject *
+DateArray_getDateInfo(PyObject *self, PyObject *args)
+{
+ int freq;
+ char *info;
+
+ PyArrayObject *array;
+ PyArrayObject *newArray;
+ PyArrayIterObject *iterSource, *iterResult;
+
+ PyObject* (*getDateInfo)(DateObject*, void*) = NULL;
+
+ if (!PyArg_ParseTuple(args, "Ois:getDateInfo(array, freq, info)", &array, &freq, &info)) return NULL;
+ newArray = (PyArrayObject *)PyArray_Copy(array);
+
+ iterSource = (PyArrayIterObject *)PyArray_IterNew((PyObject *)array);
+ iterResult = (PyArrayIterObject *)PyArray_IterNew((PyObject *)newArray);
+
+
+ switch(*info)
+ {
+ case 'Y': //year
+ getDateInfo = &DateObject_year;
+ break;
+ case 'F': //"fiscal" year
+ getDateInfo = &DateObject_qyear;
+ break;
+ case 'Q': //quarter
+ getDateInfo = &DateObject_quarter;
+ break;
+ case 'M': //month
+ getDateInfo = &DateObject_month;
+ break;
+ case 'D': //day
+ getDateInfo = &DateObject_day;
+ break;
+ case 'R': //day of year
+ getDateInfo = &DateObject_day_of_year;
+ break;
+ case 'W': //day of week
+ getDateInfo = &DateObject_day_of_week;
+ break;
+ case 'I': //week of year
+ getDateInfo = &DateObject_week;
+ break;
+ case 'H': //hour
+ getDateInfo = &DateObject_hour;
+ break;
+ case 'T': //minute
+ getDateInfo = &DateObject_minute;
+ break;
+ case 'S': //second
+ getDateInfo = &DateObject_second;
+ break;
+ default:
+ return NULL;
+ }
+
+ while (iterSource->index < iterSource->size) {
+ DateObject *curr_date;
+ PyObject *val, *dInfo;
+
+ val = PyArray_GETITEM(array, iterSource->dataptr);
+ curr_date = DateObject_FromFreqAndValue(freq, PyInt_AsLong(val));
+ dInfo = getDateInfo(curr_date, NULL);
+
+ PyArray_SETITEM(newArray, iterResult->dataptr, dInfo);
+
+ Py_DECREF(val);
+ Py_DECREF(curr_date);
+ Py_DECREF(dInfo);
+
+ PyArray_ITER_NEXT(iterSource);
+ PyArray_ITER_NEXT(iterResult);
+ }
+
+ Py_DECREF(iterSource);
+ Py_DECREF(iterResult);
+
+ return (PyObject *) newArray;
+}
+
+
+void import_c_tdates(PyObject *m)
+{
+
+ if (PyType_Ready(&DateType) < 0) return;
+
+ DateCalc_Error =
+ PyErr_NewException("c_tdates.DateCalc_Error", NULL, NULL);
+ DateCalc_RangeError =
+ PyErr_NewException("c_tdates.DateCalc_RangeError", NULL, NULL);
+
+ import_array();
+ PyDateTime_IMPORT;
+
+ Py_INCREF(&DateType);
+ PyModule_AddObject(m, "Date", (PyObject *)(&DateType));
+
+ if(build_freq_dict() == INT_ERR_CODE) {
+ PyErr_SetString( \
+ PyExc_ImportError, \
+ "initialization of module timeseries.c_tdates failed");
+ return;
+ };
+
+ PyModule_AddObject(m, "freq_dict", freq_dict);
+ PyModule_AddObject(m, "freq_dict_rev", freq_dict_rev);
+ PyModule_AddObject(m, "freq_constants", freq_constants);
+
+ PyModule_AddObject(m, "DateCalc_Error", DateCalc_Error);
+ PyModule_AddObject(m, "DateCalc_RangeError", DateCalc_RangeError);
+
+}
Added: trunk/Lib/sandbox/timeseries/src/c_tseries.c
===================================================================
--- trunk/Lib/sandbox/timeseries/src/c_tseries.c 2007-05-09 17:08:29 UTC (rev 2974)
+++ trunk/Lib/sandbox/timeseries/src/c_tseries.c 2007-05-09 17:09:36 UTC (rev 2975)
@@ -0,0 +1,835 @@
+#include "c_tdates.h"
+#include "c_tseries.h"
+
+/* Helper function for TimeSeries_convert:
+ determine the size of the second dimension for the resulting
+ converted array */
+static long get_height(int fromFreq, int toFreq) {
+
+ int maxBusDaysPerYear, maxBusDaysPerQuarter, maxBusDaysPerMonth;
+ int maxDaysPerYear, maxDaysPerQuarter, maxDaysPerMonth;
+
+ int fromGroup = get_freq_group(fromFreq);
+ int toGroup = get_freq_group(toFreq);
+
+ if (fromGroup == FR_UND) { fromGroup = FR_DAY; }
+
+ maxBusDaysPerYear = 262;
+ maxBusDaysPerQuarter = 66;
+ maxBusDaysPerMonth = 23;
+
+ maxDaysPerYear = 366;
+ maxDaysPerQuarter = 92;
+ maxDaysPerMonth = 31;
+
+ switch(fromGroup)
+ {
+ case FR_ANN: return 1;
+ case FR_QTR:
+ switch(toGroup)
+ {
+ case FR_ANN: return 4;
+ default: return 1;
+ }
+ case FR_MTH: //monthly
+ switch(toGroup)
+ {
+ case FR_ANN: return 12;
+ case FR_QTR: return 3;
+ default: return 1;
+ }
+ case FR_WK: //weekly
+ switch(toGroup)
+ {
+ case FR_ANN: return 53;
+ case FR_QTR: return 13;
+ case FR_MTH: return 4;
+ default: return 1;
+ }
+ case FR_BUS: //business
+ switch(toGroup)
+ {
+ case FR_ANN: return maxBusDaysPerYear;;
+ case FR_QTR: return maxBusDaysPerQuarter;
+ case FR_MTH: return maxBusDaysPerMonth;
+ case FR_WK: return 5;
+ default: return 1;
+ }
+ case FR_DAY: //daily
+ switch(toGroup)
+ {
+ case FR_ANN: return maxDaysPerYear;;
+ case FR_QTR: return maxDaysPerQuarter;
+ case FR_MTH: return maxDaysPerMonth;
+ case FR_WK: return 7;
+ default: return 1;
+ }
+ case FR_HR: //hourly
+ switch(toGroup)
+ {
+ case FR_ANN: return 24 * maxDaysPerYear;;
+ case FR_QTR: return 24 * maxDaysPerQuarter;
+ case FR_MTH: return 24 * maxDaysPerMonth;
+ case FR_WK: return 24 * 7;
+ case FR_DAY: return 24;
+ case FR_BUS: return 24;
+ default: return 1;
+ }
+ case FR_MIN: //minutely
+ switch(toGroup)
+ {
+ case FR_ANN: return 24 * 60 * maxDaysPerYear;;
+ case FR_QTR: return 24 * 60 * maxDaysPerQuarter;
+ case FR_MTH: return 24 * 60 * maxDaysPerMonth;
+ case FR_WK: return 24 * 60 * 7;
+ case FR_DAY: return 24 * 60;
+ case FR_BUS: return 24 * 60;
+ case FR_HR: return 60;
+ default: return 1;
+ }
+ case FR_SEC: //minutely
+ switch(toGroup)
+ {
+ case FR_ANN: return 24 * 60 * 60 * maxDaysPerYear;;
+ case FR_QTR: return 24 * 60 * 60 * maxDaysPerQuarter;
+ case FR_MTH: return 24 * 60 * 60 * maxDaysPerMonth;
+ case FR_WK: return 24 * 60 * 60 * 7;
+ case FR_DAY: return 24 * 60 * 60;
+ case FR_BUS: return 24 * 60 * 60;
+ case FR_HR: return 60 * 60;
+ case FR_MIN: return 60;
+ default: return 1;
+ }
+ default: return 1;
+ }
+}
+
+PyObject *
+TimeSeries_convert(PyObject *self, PyObject *args)
+{
+ PyObject *arrayTest;
+ PyArrayObject *array, *newArray;
+ PyArrayObject *mask, *newMask;
+
+ PyObject *returnVal = NULL;
+ PyObject *start_index_retval;
+
+ long startIndex;
+ long newStart, newStartTemp;
+ long newEnd, newEndTemp;
+ long newLen, newHeight;
+ int i;
+ long currIndex, prevIndex;
+ long nd;
+ npy_intp *dim, *newIdx;
+ long currPerLen;
+ char *position;
+ PyObject *fromFreq_arg, *toFreq_arg;
+ int fromFreq, toFreq;
+ char relation;
+ asfreq_info af_info;
+
+ PyObject *val, *valMask;
+
+ long (*asfreq_main)(long, char, asfreq_info*) = NULL;
+ long (*asfreq_endpoints)(long, char, asfreq_info*) = NULL;
+ long (*asfreq_reverse)(long, char, asfreq_info*) = NULL;
+
+ returnVal = PyDict_New();
+
+ if (!PyArg_ParseTuple(args,
+ "OOOslO:convert(array, fromfreq, tofreq, position, startIndex, mask)",
+ &array, &fromFreq_arg, &toFreq_arg,
+ &position, &startIndex, &mask)) return NULL;
+
+ if((fromFreq = check_freq(fromFreq_arg)) == INT_ERR_CODE) return NULL;
+ if((toFreq = check_freq(toFreq_arg)) == INT_ERR_CODE) return NULL;
+
+ if (toFreq == fromFreq)
+ {
+ PyObject *sidx;
+ newArray = (PyArrayObject *)PyArray_Copy(array);
+ newMask = (PyArrayObject *)PyArray_Copy(mask);
+ sidx = PyInt_FromLong(startIndex);
+
+ PyDict_SetItemString(returnVal, "values", (PyObject*)newArray);
+ PyDict_SetItemString(returnVal, "mask", (PyObject*)newMask);
+ PyDict_SetItemString(returnVal, "startindex", sidx);
+
+ Py_DECREF(newArray);
+ Py_DECREF(newMask);
+ Py_DECREF(sidx);
+
+ return returnVal;
+ }
+
+ switch(position[0])
+ {
+ case 'S':
+ // start -> before
+ relation = 'B';
+ break;
+ case 'E':
+ // end -> after
+ relation = 'A';
+ break;
+ default:
+ return NULL;
+ break;
+ }
+
+ get_asfreq_info(fromFreq, toFreq, &af_info);
+
+ asfreq_main = get_asfreq_func(fromFreq, toFreq, 1);
+ asfreq_endpoints = get_asfreq_func(fromFreq, toFreq, 0);
+
+ //convert start index to new frequency
+ CHECK_ASFREQ(newStartTemp = asfreq_main(startIndex, 'B', &af_info));
+ if (newStartTemp < 1) {
+ CHECK_ASFREQ(newStart = asfreq_endpoints(startIndex, 'A', &af_info));
+ }
+ else { newStart = newStartTemp; }
+
+ //convert end index to new frequency
+ CHECK_ASFREQ(newEndTemp = asfreq_main(startIndex+array->dimensions[0]-1, 'A', &af_info));
+ if (newEndTemp < 1) {
+ CHECK_ASFREQ(newEnd = asfreq_endpoints(startIndex+array->dimensions[0]-1, 'B', &af_info));
+ }
+ else { newEnd = newEndTemp; }
+
+ if (newStart < 1) {
+ PyErr_SetString(PyExc_ValueError, "start_date outside allowable range for destination frequency");
+ return NULL;
+ }
+
+ newLen = newEnd - newStart + 1;
+ newHeight = get_height(fromFreq, toFreq);
+
+ if (newHeight > 1) {
+ long tempval;
+ asfreq_info af_info_rev;
+
+ get_asfreq_info(toFreq, fromFreq, &af_info_rev);
+ asfreq_reverse = get_asfreq_func(toFreq, fromFreq, 0);
+
+ CHECK_ASFREQ(tempval = asfreq_reverse(newStart, 'B', &af_info_rev));
+ currPerLen = startIndex - tempval;
+
+ nd = 2;
+ dim = PyDimMem_NEW(nd);
+ dim[0] = (npy_intp)newLen;
+ dim[1] = (npy_intp)newHeight;
+ } else {
+ nd = 1;
+ dim = PyDimMem_NEW(nd);
+ dim[0] = (npy_intp)newLen;
+ }
+
+ newIdx = PyDimMem_NEW(nd);
+ arrayTest = PyArray_SimpleNew(nd, dim, array->descr->type_num);
+ if (arrayTest == NULL) { return NULL; }
+ newArray = (PyArrayObject*)arrayTest;
+ newMask = (PyArrayObject*)PyArray_SimpleNew(nd, dim, mask->descr->type_num);
+
+ PyDimMem_FREE(dim);
+
+ PyArray_FILLWBYTE(newArray,0);
+ PyArray_FILLWBYTE(newMask,1);
+
+ prevIndex = newStart;
+
+ //set values in the new array
+ for (i = 0; i < array->dimensions[0]; i++) {
+
+ val = PyArray_GETITEM(array, PyArray_GetPtr(array, &i));
+ valMask = PyArray_GETITEM(mask, PyArray_GetPtr(mask, &i));
+
+ CHECK_ASFREQ(currIndex = asfreq_main(startIndex + i, relation, &af_info));
+
+ newIdx[0] = currIndex-newStart;
+
+ if (newHeight > 1) {
+
+ if (currIndex != prevIndex)
+ {
+ //reset period length
+ currPerLen = 0;
+ prevIndex = currIndex;
+ }
+
+ newIdx[1] = currPerLen;
+ currPerLen++;
+ }
+
+ if (newIdx[0] > -1) {
+ PyArray_SETITEM(newArray, PyArray_GetPtr(newArray, newIdx), val);
+ PyArray_SETITEM(newMask, PyArray_GetPtr(newMask, newIdx), valMask);
+ }
+
+ Py_DECREF(val);
+ Py_DECREF(valMask);
+
+ }
+
+ PyDimMem_FREE(newIdx);
+
+ start_index_retval = (PyObject*)PyInt_FromLong(newStart);
+
+ PyDict_SetItemString(returnVal, "values", (PyObject*)newArray);
+ PyDict_SetItemString(returnVal, "mask", (PyObject*)newMask);
+ PyDict_SetItemString(returnVal, "startindex", start_index_retval);
+
+ Py_DECREF(newArray);
+ Py_DECREF(newMask);
+ Py_DECREF(start_index_retval);
+
+ return returnVal;
+}
+
+
+/* This function is directly copied from direct copy of function in */
+/* Return typenumber from dtype2 unless it is NULL, then return
+ NPY_DOUBLE if dtype1->type_num is integer or bool
+ and dtype1->type_num otherwise.
+*/
+static int
+_get_type_num_double(PyArray_Descr *dtype1, PyArray_Descr *dtype2)
+{
+ if (dtype2 != NULL)
+ return dtype2->type_num;
+
+ /* For integer or bool data-types */
+ if (dtype1->type_num < NPY_FLOAT) {
+ return NPY_DOUBLE;
+ }
+ else {
+ return dtype1->type_num;
+ }
+}
+
+#define _CHKTYPENUM(typ) ((typ) ? (typ)->type_num : PyArray_NOTYPE)
+
+/* validates the standard arguments to moving functions and set the original
+ mask, original ndarray, and mask for the result */
+static PyObject *
+check_mov_args(PyObject *orig_arrayobj, int span, int min_win_size,
+ PyObject **orig_ndarray, PyObject **result_mask) {
+
+ PyObject *orig_mask=NULL;
+ PyArrayObject **orig_ndarray_tmp, **result_mask_tmp;
+ int *raw_result_mask;
+
+ if (!PyArray_Check(orig_arrayobj)) {
+ PyErr_SetString(PyExc_ValueError, "array must be a valid subtype of ndarray");
+ return NULL;
+ }
+
+ // check if array has a mask, and if that mask is an array
+ if (PyObject_HasAttrString(orig_arrayobj, "_mask")) {
+ PyObject *tempMask = PyObject_GetAttrString(orig_arrayobj, "_mask");
+ if (PyArray_Check(tempMask)) {
+ orig_mask = PyArray_EnsureArray(tempMask);
+ } else {
+ Py_DECREF(tempMask);
+ }
+ }
+
+ *orig_ndarray = PyArray_EnsureArray(orig_arrayobj);
+ orig_ndarray_tmp = (PyArrayObject**)orig_ndarray;
+
+ if ((*orig_ndarray_tmp)->nd != 1) {
+ PyErr_SetString(PyExc_ValueError, "array must be 1 dimensional");
+ return NULL;
+ }
+
+ if (span < min_win_size) {
+ char *error_str;
+ error_str = malloc(60 * sizeof(char));
+ MEM_CHECK(error_str)
+ sprintf(error_str,
+ "span must be greater than or equal to %i",
+ min_win_size);
+ PyErr_SetString(PyExc_ValueError, error_str);
+ free(error_str);
+ return NULL;
+ }
+
+ raw_result_mask = malloc((*orig_ndarray_tmp)->dimensions[0] * sizeof(int));
+ MEM_CHECK(raw_result_mask)
+
+ {
+ PyArrayObject *orig_mask_tmp;
+ int i, valid_points=0, is_masked;
+
+ orig_mask_tmp = (PyArrayObject*)orig_mask;
+
+ for (i=0; i<((*orig_ndarray_tmp)->dimensions[0]); i++) {
+
+ is_masked=0;
+
+ if (orig_mask != NULL) {
+ PyObject *valMask;
+ valMask = PyArray_GETITEM(orig_mask_tmp,
+ PyArray_GetPtr(orig_mask_tmp, &i));
+ is_masked = (int)PyInt_AsLong(valMask);
+ Py_DECREF(valMask);
+ }
+
+ if (is_masked) {
+ valid_points=0;
+ } else {
+ if (valid_points < span) { valid_points += 1; }
+ if (valid_points < span) { is_masked = 1; }
+ }
+
+ raw_result_mask[i] = is_masked;
+ }
+ }
+
+ *result_mask = PyArray_SimpleNewFromData(
+ 1, (*orig_ndarray_tmp)->dimensions,
+ PyArray_INT32, raw_result_mask);
+ MEM_CHECK(*result_mask)
+ result_mask_tmp = (PyArrayObject**)result_mask;
+ (*result_mask_tmp)->flags = ((*result_mask_tmp)->flags) | NPY_OWNDATA;
+ return 0;
+}
+
+/* computation portion of moving sum. Appropriate mask is overlayed on top
+ afterwards */
+static PyObject*
+calc_mov_sum(PyArrayObject *orig_ndarray, int span, int rtype)
+{
+ PyArrayObject *result_ndarray=NULL;
+ int i;
+
+ result_ndarray = (PyArrayObject*)PyArray_ZEROS(
+ orig_ndarray->nd,
+ orig_ndarray->dimensions,
+ rtype, 0);
+ ERR_CHECK(result_ndarray)
+
+ for (i=0; i<orig_ndarray->dimensions[0]; i++) {
+
+ PyObject *val=NULL, *mov_sum_val=NULL;
+
+ val = PyArray_GETITEM(orig_ndarray, PyArray_GetPtr(orig_ndarray, &i));
+
+ if (i == 0) {
+ mov_sum_val = val;
+ } else {
+ int prev_idx = i-1;
+ PyObject *mov_sum_prevval;
+ mov_sum_prevval= PyArray_GETITEM(result_ndarray,
+ PyArray_GetPtr(result_ndarray, &prev_idx));
+ mov_sum_val = np_add(val, mov_sum_prevval);
+ Py_DECREF(mov_sum_prevval);
+ ERR_CHECK(mov_sum_val)
+
+ if (i >= span) {
+ PyObject *temp_val, *rem_val;
+ int rem_idx = i-span;
+ temp_val = mov_sum_val;
+ rem_val = PyArray_GETITEM(orig_ndarray,
+ PyArray_GetPtr(orig_ndarray, &rem_idx));
+
+ mov_sum_val = np_subtract(temp_val, rem_val);
+ ERR_CHECK(mov_sum_val)
+
+ Py_DECREF(temp_val);
+ Py_DECREF(rem_val);
+ }
+ }
+
+ PyArray_SETITEM(result_ndarray,
+ PyArray_GetPtr(result_ndarray, &i),
+ mov_sum_val);
+
+ if (mov_sum_val != val) { Py_DECREF(val); }
+
+ Py_DECREF(mov_sum_val);
+ }
+
+ return (PyObject*)result_ndarray;
+
+}
+
+PyObject *
+MaskedArray_mov_sum(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *orig_arrayobj=NULL, *orig_ndarray=NULL,
+ *result_ndarray=NULL, *result_mask=NULL,
+ *result_dict=NULL;
+ PyArray_Descr *dtype=NULL;
+
+ int rtype, span;
+
+ static char *kwlist[] = {"array", "span", "dtype", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds,
+ "Oi|O&:mov_sum(array, span, dtype)", kwlist,
+ &orig_arrayobj, &span,
+ PyArray_DescrConverter2, &dtype)) return NULL;
+
+ check_mov_args(orig_arrayobj, span, 1,
+ &orig_ndarray, &result_mask);
+
+ rtype = _CHKTYPENUM(dtype);
+
+ result_ndarray = calc_mov_sum((PyArrayObject*)orig_ndarray,
+ span, rtype);
+ ERR_CHECK(result_ndarray)
+
+ result_dict = PyDict_New();
+ MEM_CHECK(result_dict)
+ PyDict_SetItemString(result_dict, "array", result_ndarray);
+ PyDict_SetItemString(result_dict, "mask", result_mask);
+
+ Py_DECREF(result_ndarray);
+ Py_DECREF(result_mask);
+ return result_dict;
+}
+
+PyObject *
+MaskedArray_mov_average(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *orig_arrayobj=NULL, *orig_ndarray=NULL,
+ *result_ndarray=NULL, *result_mask=NULL,
+ *result_dict=NULL,
+ *mov_sum=NULL, *denom=NULL;
+ PyArray_Descr *dtype=NULL;
+
+ int rtype, span;
+
+ static char *kwlist[] = {"array", "span", "dtype", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds,
+ "Oi|O&:mov_average(array, span, dtype)", kwlist,
+ &orig_arrayobj, &span,
+ PyArray_DescrConverter2, &dtype)) return NULL;
+
+
+ check_mov_args(orig_arrayobj, span, 2,
+ &orig_ndarray, &result_mask);
+
+ rtype = _get_type_num_double(((PyArrayObject*)orig_ndarray)->descr, dtype);
+
+ mov_sum = calc_mov_sum((PyArrayObject*)orig_ndarray, span, rtype);
+ ERR_CHECK(mov_sum)
+
+ denom = PyFloat_FromDouble(1.0/(double)(span));
+
+ result_ndarray = np_multiply(mov_sum, denom);
+ ERR_CHECK(result_ndarray)
+
+ Py_DECREF(mov_sum);
+ Py_DECREF(denom);
+
+ result_dict = PyDict_New();
+ MEM_CHECK(result_dict)
+ PyDict_SetItemString(result_dict, "array", result_ndarray);
+ PyDict_SetItemString(result_dict, "mask", result_mask);
+
+ Py_DECREF(result_ndarray);
+ Py_DECREF(result_mask);
+ return result_dict;
+}
+
+
+/* computation portion of moving median. Appropriate mask is overlayed on top
+ afterwards.
+
+ The algorithm used here is based on the code found at:
+ http://cran.r-project.org/src/contrib/Devel/runStat_1.1.tar.gz
+
+ This code was originally released under the GPL, but the author
+ (David Brahm) has granted me (and scipy) permission to use it under the BSD
+ license. */
+PyObject*
+calc_mov_median(PyArrayObject *orig_ndarray, int span, int rtype)
+{
+ PyArrayObject *result_ndarray=NULL;
+ PyObject **result_array, **ref_array, **even_array=NULL;
+ PyObject *new_val, *old_val;
+ PyObject *temp_add, *one_half;
+ int a, i, k, R, arr_size, z;
+ int *r;
+
+ arr_size = orig_ndarray->dimensions[0];
+
+ result_ndarray = (PyArrayObject*)PyArray_ZEROS(
+ orig_ndarray->nd,
+ orig_ndarray->dimensions,
+ rtype, 0);
+ ERR_CHECK(result_ndarray)
+
+ if (arr_size >= span) {
+ result_array = calloc(arr_size, sizeof(PyObject*));
+ MEM_CHECK(result_array)
+
+ /* this array will be used for quick access to the data in the original
+ array (so PyArray_GETITEM doesn't have to be used over and over in the
+ main loop) */
+ ref_array = malloc(arr_size * sizeof(PyObject*));
+ MEM_CHECK(ref_array)
+
+ for (i=0; i<arr_size; i++) {
+ ref_array[i] = PyArray_GETITEM(orig_ndarray, PyArray_GetPtr(orig_ndarray, &i));
+ }
+
+ /* this array wll be used for keeping track of the "ranks" of the values
+ in the current window */
+ r = malloc(span * sizeof(int));
+ MEM_CHECK(r)
+
+ for (i=0; i < span; i++) {
+ r[i] = 1;
+ }
+
+ if ((span % 2) == 0) {
+ // array to store two median values when span is an even #
+ even_array = calloc(2, sizeof(PyObject*));
+ MEM_CHECK(even_array)
+ }
+
+ R = (span + 1)/2;
+ one_half = PyFloat_FromDouble(0.5);
+
+ z = arr_size - span;
+
+ /* Calculate initial ranks "r" */
+ for (i=0; i < span; i++) {
+
+ for (k=0; k < i; k++) {
+ if (np_greater_equal(ref_array[z+i], ref_array[z+k])) {
+ r[i]++;
+ }
+ }
+ for (k=i+1; k < span; k++) {
+ if (np_greater(ref_array[z+i], ref_array[z+k])) {
+ r[i]++;
+ }
+ }
+
+ /* If rank=R, this is the median */
+ if (even_array != NULL) {
+ if (r[i]==R) {
+ even_array[0] = ref_array[z+i];
+ } else if (r[i] == (R+1)) {
+ even_array[1] = ref_array[z+i];
+ }
+ } else {
+ if (r[i]==R) {
+ result_array[arr_size-1] = ref_array[z+i];
+ }
+ }
+ }
+
+ if (even_array != NULL) {
+ temp_add = np_add(even_array[0], even_array[1]);
+ result_array[arr_size-1] = np_multiply(temp_add, one_half);
+ Py_DECREF(temp_add);
+ }
+
+ for (i=arr_size-2; i >= span-1; i--) {
+ a = span;
+ z = i - span + 1;
+ old_val = ref_array[i+1];
+ new_val = ref_array[i-span+1];
+
+ for (k=span-1; k > 0; k--) {
+ r[k] = r[k-1]; /* Shift previous iteration's ranks */
+ if (np_greater_equal(ref_array[z+k], new_val)) {r[k]++; a--;}
+ if (np_greater(ref_array[z+k], old_val)) {r[k]--;}
+
+ if (r[k]==R) {
+ result_array[i] = ref_array[z+k];
+ }
+
+ if (even_array != NULL) {
+ if (r[k]==R) {
+ even_array[0] = ref_array[z+k];
+ } else if (r[k] == (R+1)) {
+ even_array[1] = ref_array[z+k];
+ }
+ } else {
+ if (r[k]==R) {
+ result_array[i] = ref_array[z+k];
+ }
+ }
+
+ }
+
+ r[0] = a;
+
+ if (even_array != NULL) {
+ if (a==R) {
+ even_array[0] = new_val;
+ } else if (a == (R+1)) {
+ even_array[1] = new_val;
+ }
+
+ temp_add = np_add(even_array[0], even_array[1]);
+ result_array[i] = np_multiply(temp_add, one_half);;
+ Py_DECREF(temp_add);
+
+ } else {
+ if (a==R) {
+ result_array[i] = new_val;
+ }
+ }
+
+ }
+
+ Py_DECREF(one_half);
+
+ for (i=span-1; i<arr_size; i++) {
+ PyArray_SETITEM(result_ndarray,
+ PyArray_GetPtr(result_ndarray, &i),
+ result_array[i]);
+ }
+
+ for (i=0; i<arr_size; i++) {
+ Py_DECREF(ref_array[i]);
+ }
+
+ if (even_array != NULL) {
+ for (i=span-1; i<arr_size; i++) {
+ Py_DECREF(result_array[i]);
+ }
+ }
+
+ free(ref_array);
+ free(result_array);
+ }
+
+ return (PyObject*)result_ndarray;
+
+}
+
+PyObject *
+MaskedArray_mov_median(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *orig_arrayobj=NULL, *orig_ndarray=NULL,
+ *result_ndarray=NULL, *result_mask=NULL, *result_dict=NULL;
+ PyArray_Descr *dtype=NULL;
+
+ int rtype, span;
+
+ static char *kwlist[] = {"array", "span", "dtype", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds,
+ "Oi|O&:mov_median(array, span, dtype)", kwlist,
+ &orig_arrayobj, &span,
+ PyArray_DescrConverter2, &dtype)) return NULL;
+
+ check_mov_args(orig_arrayobj, span, 1,
+ &orig_ndarray, &result_mask);
+
+ rtype = _CHKTYPENUM(dtype);
+
+ result_ndarray = calc_mov_median((PyArrayObject*)orig_ndarray,
+ span, rtype);
+ ERR_CHECK(result_ndarray)
+
+ result_dict = PyDict_New();
+ MEM_CHECK(result_dict)
+ PyDict_SetItemString(result_dict, "array", result_ndarray);
+ PyDict_SetItemString(result_dict, "mask", result_mask);
+
+ Py_DECREF(result_ndarray);
+ Py_DECREF(result_mask);
+ return result_dict;
+}
+
+
+PyObject *
+MaskedArray_mov_stddev(PyObject *self, PyObject *args, PyObject *kwds)
+{
+
+ PyObject *orig_ndarray=NULL, *orig_arrayobj=NULL,
+ *result_ndarray=NULL, *result_mask=NULL,
+ *result_dict=NULL,
+ *result_temp1=NULL, *result_temp2=NULL, *result_temp3=NULL,
+ *mov_sum=NULL, *mov_sum_sq=NULL,
+ *denom1=NULL, *denom2=NULL;
+
+ PyArray_Descr *dtype=NULL;
+
+ int rtype, span, is_variance, bias;
+
+ static char *kwlist[] = {"array", "span", "is_variance", "bias", "dtype", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds,
+ "Oiii|O&:mov_stddev(array, span, is_variance, bias, dtype)",
+ kwlist, &orig_arrayobj, &span, &is_variance, &bias,
+ PyArray_DescrConverter2, &dtype)) return NULL;
+
+
+ check_mov_args(orig_arrayobj, span, 2,
+ &orig_ndarray, &result_mask);
+
+ rtype = _get_type_num_double(((PyArrayObject*)orig_ndarray)->descr, dtype);
+
+ mov_sum = calc_mov_sum((PyArrayObject*)orig_ndarray, span, rtype);
+ ERR_CHECK(mov_sum)
+
+ result_temp1 = np_multiply(orig_ndarray, orig_ndarray);
+ ERR_CHECK(result_temp1)
+
+ mov_sum_sq = calc_mov_sum((PyArrayObject*)result_temp1, span, rtype);
+ Py_DECREF(result_temp1);
+ ERR_CHECK(mov_sum_sq)
+
+
+ /*
+ formulas from:
+ http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods
+ */
+ if (bias == 0) {
+ denom1 = PyFloat_FromDouble(1.0/(double)(span-1));
+ denom2 = PyFloat_FromDouble(1.0/(double)(span*(span-1)));
+ } else {
+ denom1 = PyFloat_FromDouble(1.0/(double)span);
+ denom2 = PyFloat_FromDouble(1.0/(double)(span*span));
+ }
+
+ result_temp1 = np_multiply(mov_sum_sq, denom1);
+ ERR_CHECK(result_temp1)
+ Py_DECREF(mov_sum_sq);
+ Py_DECREF(denom1);
+
+ result_temp3 = np_multiply(mov_sum, mov_sum);
+ ERR_CHECK(result_temp3)
+ Py_DECREF(mov_sum);
+
+ result_temp2 = np_multiply(result_temp3, denom2);
+ ERR_CHECK(result_temp2)
+ Py_DECREF(result_temp3);
+ Py_DECREF(denom2);
+
+ result_temp3 = np_subtract(result_temp1, result_temp2);
+ ERR_CHECK(result_temp3)
+ Py_DECREF(result_temp1);
+ Py_DECREF(result_temp2);
+
+ if (is_variance) {
+ result_ndarray = result_temp3;
+ } else {
+ result_temp1 = np_sqrt(result_temp3);
+ ERR_CHECK(result_temp1)
+ Py_DECREF(result_temp3);
+ result_ndarray = result_temp1;
+ }
+
+ result_dict = PyDict_New();
+ MEM_CHECK(result_dict)
+ PyDict_SetItemString(result_dict, "array", result_ndarray);
+ PyDict_SetItemString(result_dict, "mask", result_mask);
+
+ Py_DECREF(result_ndarray);
+ Py_DECREF(result_mask);
+ return result_dict;
+}
+
+void import_c_tseries(PyObject *m) { import_array(); }
More information about the Scipy-svn
mailing list