/// <summary> /// Multiplies two Tnums together. /// </summary> public static Tnum operator * (Tnum tn1, Tnum tn2) { Tnum result = new Tnum(); foreach(KeyValuePair<DateTime,List<Hval>> slice in TimePointValues(tn1,tn2)) { Hstate top = PrecedingState(slice.Value); decimal val1 = Convert.ToDecimal(slice.Value [0].Val); decimal val2 = Convert.ToDecimal(slice.Value [1].Val); // Short circuit 1 if (val1 == 0 || val2 == 0) { result.AddState(slice.Key, new Hval(0)); } else if (top != Hstate.Known) // Short circuit 2 { result.AddState(slice.Key, new Hval(null,top)); } else // Do the math { decimal prod = val1 * val2; result.AddState(slice.Key, new Hval(prod)); } } return result.Lean; }
/// <summary> /// Returns the total elapsed days that a Tvar has a given value, /// for each of a given set of intervals. /// Example: meetsAnnualTest = var.ElapsedDaysPerInterval(theYear) > 183; /// </summary> public Tnum TotalElapsedDaysPer(Tnum period) { // If base Tnum is ever unknown during the time period, return // the state with the proper precedence Hstate baseState = PrecedenceForMissingTimePeriods(this); Hstate periodState = PrecedenceForMissingTimePeriods(period); Hstate top = PrecedingState(baseState, periodState); if (top != Hstate.Known) return new Tnum(top); Tnum result = new Tnum(); int count = period.IntervalValues.Count; for (int i=0; i < count; i++) { DateTime spanEnd = Time.EndOf; if (i < count-1) { spanEnd = period.IntervalValues.Keys[i+1]; } TimeSpan time = this.TotalElapsedTime(period.IntervalValues.Keys[i], spanEnd); // Add the state, but not if it's at the end of time if (period.IntervalValues.Keys[i] < Time.EndOf) { result.AddState(period.IntervalValues.Keys[i], time.TotalDays); } } return result.Lean; }
public static Tnum IntervalsSince(Tdate startDate, Tdate endDate, IntervalType interval, int? startAt) { // Handle unknowns Hstate top = PrecedingState(startDate.FirstValue, endDate.FirstValue); if (top != Hstate.Known) return new Tnum(new Hval(null,top)); DateTime start = startDate.ToDateTime; DateTime end = endDate.ToDateTime; Tnum result = new Tnum(); if (start != Time.DawnOf) { result.AddState(Time.DawnOf,0); } DateTime indexDate = start; int? indexNumber = startAt; while (indexDate < end) { result.AddState(indexDate,Convert.ToDecimal(indexNumber)); indexNumber++; indexDate = indexDate.AddInterval(interval, 1); } if (end < Time.EndOf) result.AddState(end, 0); return result; }
/// <summary> /// Takes a Tnum representing some value per unit time, and sums or /// accumulates it over a given type of time interval to obtain a /// running total. /// </summary> /// <example> /// Calculate lifetime accrued income, given a person's annual income: /// /// AccruedIncome = AnnualIncome.RunningSummedIntervals(TheYear) /// /// The time units cancel: [$/year] * [year] yields [$]. /// </example> public Tnum RunningSummedIntervals(Tnum interval) { // TODO: Review how uncertainty is handled here: // Handle unknowns Hstate top = PrecedingState(this.FirstValue, interval.FirstValue); if (top != Hstate.Known) return new Tnum(new Hval(null,top)); // If base Tnum is ever unknown during the time period, return // the state with the proper precedence Hstate baseState = PrecedenceForMissingTimePeriods(this); if (baseState != Hstate.Known) return new Tnum(baseState); // Start accumulating... Tnum result = new Tnum(0); decimal total = 0; decimal previousVal = 0; // Iterate through the intervals, totaling for (int i=1; i < interval.TimeLine.Count-1; i++) { total += Convert.ToDecimal(this.ObjectAsOf(interval.TimeLine.Keys[i]).Val); // Only add changepoint if value actually changes if (total != previousVal) { result.AddState(interval.TimeLine.Keys[i+1], total); } // Set for next iteration previousVal = total; } return result; }
/// <summary> /// Finds the Tset associated with a given Tnum value on a given date. /// </summary> private static Hval AssociatedTset(List<Tuple<Tset,Tnum>> setFcnVals, Tnum val, DateTime asOfDate) { foreach(Tuple<Tset,Tnum> t in setFcnVals) { // Handle uncertainty Hval s = t.Item2.AsOf(asOfDate).FirstValue; Hval v = val.AsOf(asOfDate).FirstValue; if (s.IsUncertain && v.IsUncertain) return new Hval(null); // Not hitting this for some reason... if (s.IsUnstated && v.IsUnstated) return new Hval(null, Hstate.Unstated); if (s.IsStub && v.IsStub) return new Hval(null, Hstate.Stub); // Compare numeric values if (t.Item2.AsOf(asOfDate) == val.AsOf(asOfDate)) { // Tsets created in Combos() are eternal, so FirstValue is ok here return t.Item1.FirstValue; } } return new Hval(null, Hstate.Stub); }
/// <summary> /// Provides a running count of how many whole intervals a Tbool /// has been continuously true. /// </summary> /// <remarks> /// Example: /// tb = <--FTFTTF--> /// tb.ICT = <--010120--> /// /// Use judiciously with TheDay and TheCalendarWeek, as they have thousands of time intervals. /// </remarks> public Tnum ContinuousElapsedIntervals(Tnum interval) { // If base Tnum is ever unknown during the time period, return // the state with the proper precedence Hstate baseState = PrecedenceForMissingTimePeriods(this); if (baseState != Hstate.Known) return new Tnum(baseState); int intervalCount = 0; DateTime dateNextTrue = this.DateNextTrue(Time.DawnOf); DateTime dateNextTrueIntervalEnds = this.NextChangeDate(dateNextTrue.AddTicks(1)); Tnum result = new Tnum(0); // Iterate through the time intervals in the input Tnum for (int i=0; i < interval.IntervalValues.Count-1; i++) { DateTime start = interval.IntervalValues.Keys[i]; DateTime end = interval.IntervalValues.Keys[i+1]; // If base Tbool is always true during the interval, increment the count if (end <= dateNextTrueIntervalEnds) { if (start >= dateNextTrue) { intervalCount++; result.AddState(end, intervalCount); continue; } } else { // Otherwise, skip to next true interval intervalCount = 0; result.AddState(end, intervalCount); dateNextTrue = this.DateNextTrue(end); dateNextTrueIntervalEnds = this.NextChangeDate(dateNextTrue.AddTicks(1)); } } return result; }
/// <summary> /// Finds the total of a Tnum, summed over the given intervals between two /// dates (start and end). /// </summary> /// <example> /// AnnualIncome2014 = MonthlyIncome.TotalSummedIntervals(TheMonth, 2014-01-01, 2014-12-31) /// </example> public Tnum TotalSummedIntervals(Tnum interval, Tdate start, Tdate end) { Tnum rsi = RunningSummedIntervals(interval); return rsi.AsOf(end) - rsi.AsOf(start); }
public static Tnum IntervalsUntil(Tdate startDate, Tdate endDate, IntervalType interval, int startAt) { // Handle unknowns Hstate top = PrecedingState(startDate.FirstValue, endDate.FirstValue); if (top != Hstate.Known) return new Tnum(new Hval(null,top)); DateTime start = startDate.ToDateTime; DateTime end = endDate.ToDateTime; Tnum result = new Tnum(); DateTime indexDate = end; int indexNumber = startAt-1; while (indexDate > start) { if (indexNumber != startAt-1) { result.AddState(indexDate,Convert.ToString(indexNumber)); } indexNumber++; indexDate = indexDate.SubtractInterval(interval); } result.AddState(Time.DawnOf, 0); result.AddState(start,Convert.ToString(indexNumber)); result.AddState(end, 0); return result; }
/// <summary> /// Maps a function to a timeline, starting on a given date. /// </summary> // TODO: Get rid of Time.IntervalType public static Tnum TemporalMap(Func<Tnum,Tnum> fcn, Tdate startDate, Tnum intervalCount, Time.IntervalType intervalType) { return fcn(Time.IntervalsSince(startDate, startDate.AddYears(intervalCount), intervalType)); }
/// <summary> /// Implements Tset.OptimalSubset(Tnum fcn). /// </summary> private static Tset OptimalSubsetCore(Tset theSet, Func<Tset,Tnum> fcn) { Tset result = new Tset(); // For each time period in the Tset for (int i=0; i < theSet.IntervalValues.Count; i++) { // Get some useful values Hval thisSetVal = theSet.IntervalValues.Values[i]; DateTime start = theSet.IntervalValues.Keys[i]; // Handle uncertainty of theSet if (!thisSetVal.IsKnown) { result.AddState(start, thisSetVal); } else { // Date parameters and set members of that time interval List<Thing> mems = (List<Thing>)thisSetVal.Val; DateTime end = Time.EndOf; try {end = theSet.IntervalValues.Keys[i+1];} catch {} // For each combination of set members, get the associated fcn val List<Tuple<Tset,Tnum>> setFcnVals = new List<Tuple<Tset,Tnum>>(); Tnum maxVal = new Tnum(Decimal.MinValue); foreach (Tset s in Combos(mems)) { // Invoke the fcn for that subset Tnum val = fcn(s); // Save the result of the fcn and the associated Tset setFcnVals.Add(Tuple.Create(s, val)); // Update the running maximum value maxVal = Max(maxVal, val); } // Foreach changepoint in maxVal, find the associated Tset for (int j=0; j < maxVal.IntervalValues.Count; j++) { DateTime mDate = maxVal.IntervalValues.Keys[j]; if (mDate >= start && mDate < end) { // Get the associated Tset Hval outSet = AssociatedTset(setFcnVals, maxVal, mDate); // Add the change point result.AddState(mDate, outSet); } } } } return result; }
/// <summary> /// Returns a Tbool in which the values are shifted in time relative to /// the dates. /// </summary> public Tbool Shift(int offset, Tnum temporalPeriod) { return this.Shift<Tbool>(offset, temporalPeriod); }
/// <summary> /// Cosine /// </summary> public static Tnum Cos(Tnum tn) { return ApplyFcnToTimeline<Tnum>(x => CoreCos(x), tn); }
/// <summary> /// A raised to the B power /// </summary> public static Tnum Pow(Tnum tnA, Tnum tnB) { return(ApplyFcnToTimeline <Tnum>(x => CorePow(x), tnA, tnB)); }
/// <summary> /// Adds a specified number of years to each DateTime in a Tdate. /// Negative values are subtracted. /// </summary> public Tdate AddYears(Tnum days) { return ApplyFcnToTimeline<Tdate>(x => CoreAddYears(x), this, days); }
/// <summary> /// Returns a Tset in which the values are shifted in time relative to /// the dates. /// </summary> public Tset Shift(int offset, Tnum temporalPeriod) { return this.Shift<Tset>(offset, temporalPeriod); }
/// <summary> /// Returns a Tdate in which the last value in a time period is the /// final value. /// </summary> public Tdate PeriodEndVal(Tnum temporalPeriod) { return(this.PeriodEndVal <Tdate>(temporalPeriod).Lean); }
/// <summary> /// Returns true in the period during which a given date falls /// Example: (2013-04-15).IsInPeriod(TheYear) is true for all of 2013, and otherwise false /// </summary> public Tbool IsInPeriod(Tnum interval) { // The event + 1 tick (temporal) Tbool theEvent = Time.IsBetween(this, new Tdate(this.ToDateTime.AddTicks(1))); // True during the interval when the event occurs return theEvent.EverPer(interval); }
/// <summary> /// Returns a Tdate in which the values are shifted in time relative to /// the dates. /// </summary> public Tdate Shift(int offset, Tnum temporalPeriod) { return(this.Shift <Tdate>(offset, temporalPeriod)); }
/// <summary> /// ArcTan /// </summary> public static Tnum ArcTan(Tnum tn) { return(ApplyFcnToTimeline <Tnum>(x => CoreArcTan(x), tn)); }
/// <summary> /// Cosine /// </summary> public static Tnum Cos(Tnum tn) { return(ApplyFcnToTimeline <Tnum>(x => CoreCos(x), tn)); }
/// <summary> /// Logarithm of n to base b /// </summary> public static Tnum Log(Tnum b, Tnum n) { return(ApplyFcnToTimeline <Tnum>(x => CoreLog(x), b, n)); }
/// <summary> /// Returns a Tset in which the last value in a time period is the /// final value. /// </summary> public Tset PeriodEndVal(Tnum temporalPeriod) { return this.PeriodEndVal<Tset>(temporalPeriod).Lean; }
/// <summary> /// Logarithm of n to base b /// </summary> public static Tnum Log(Tnum b, Tnum n) { return ApplyFcnToTimeline<Tnum>(x => CoreLog(x), b, n); }
/// <summary> /// Square root /// </summary> public static Tnum Sqrt(Tnum tn) { return(ApplyFcnToTimeline <Tnum>(x => CoreSqrt(x), tn)); }
/// <summary> /// ArcTan /// </summary> public static Tnum ArcTan(Tnum tn) { return ApplyFcnToTimeline<Tnum>(x => CoreArcTan(x), tn); }
// ******************************************************************** // ROUNDING FUNCTIONS // ******************************************************************** /// <summary> /// Rounds a value to the nearest multiple of a given number. By default, /// it rounds up in case of a tie. /// </summary> public Tnum RoundToNearest(Tnum multiple) { return RoundToNearest(multiple,false); }
/// <summary> /// Divides one Tnum by another. /// </summary> public static Tnum operator / (Tnum tn1, Tnum tn2) { Tnum result = new Tnum(); foreach(KeyValuePair<DateTime,List<Hval>> slice in TimePointValues(tn1,tn2)) { Hstate top = PrecedingState(slice.Value); decimal denominator = Convert.ToDecimal(slice.Value[1].Val); if (denominator == 0) // Short circuit 1: Div-by-zero { result.AddState(slice.Key, new Hval(null)); } else if (top != Hstate.Known) // Short circuit 2: Hstates { result.AddState(slice.Key, new Hval(null,top)); } else // Do the math { decimal r = Convert.ToDecimal(slice.Value[0].Val) / denominator; result.AddState(slice.Key, new Hval(r)); } } return result.Lean; }
public Tnum RoundToNearest(Tnum multiple, Tbool breakTieByRoundingDown) { Tnum diff = this % multiple; return Switch<Tnum>(() => diff > multiple / 2, () => this - diff + multiple, ()=> diff < multiple / 2 || breakTieByRoundingDown, ()=> this - diff, () => true, () => this - diff + multiple); }
/// <summary> /// Rounds a value down to the next multiple of a given number. /// </summary> public Tnum RoundDown(Tnum multiple) { return(Switch <Tnum>(() => this % multiple != 0, () => this - (this % multiple), () => true, () => this)); }
/// <summary> /// Takes a Tnum representing some value per unit time, and accumulates it /// over a given number of successive time intervals. /// </summary> /// <example> /// Calculate income accumulated over a three month period, where the /// person earned $3,000/month: /// /// Income = MonthlyIncome.SlidingSummedIntervals(TheMonth, 3) /// /// The time units cancel: [$/mo.] * [mo.] yields [$]. /// </example> public Tnum SlidingSummedIntervals(Tnum interval, Tnum windowSize) { // TODO: Review how uncertainty is handled here: // Handle unknowns Hstate top = PrecedingState(this.FirstValue, interval.FirstValue, windowSize.FirstValue); if (top != Hstate.Known) return new Tnum(new Hval(null,top)); // If base Tnum is ever unknown during the time period, return // the state with the proper precedence Hstate baseState = PrecedenceForMissingTimePeriods(this); if (baseState != Hstate.Known) return new Tnum(baseState); // Handle eternal values if (this.IsEternal) { return this * windowSize; } // Start accumulating... int num = windowSize.ToHardInt; // Get first accumulated value decimal firstVal = 0; for (int j=0; j<num; j++) { // Don't walk off the end of the timeline if (j < interval.TimeLine.Count) { firstVal += this.AsOf(interval.TimeLine.Keys [j]).ToHardDecimal; } } Tnum result = new Tnum(firstVal); // Iterate through the subsequent intervals decimal previousVal = firstVal; for (int i=1; i < interval.TimeLine.Count-num; i++) { // Take the value from the last iteration, and slide it the time window to the right, // subtracting the left interval and adding the new right one. decimal lastOldInterval = Convert.ToDecimal(this.ObjectAsOf(interval.TimeLine.Keys[i-1]).Val); decimal nextNewInterval = Convert.ToDecimal(this.ObjectAsOf(interval.TimeLine.Keys[i+num-1]).Val); decimal newVal = previousVal - lastOldInterval + nextNewInterval; // Only add changepoint if value actually changes if (newVal != previousVal) { // The value of an interval is counted after it has elapsed result.AddState(interval.TimeLine.Keys[i+num], newVal); } // Set for next iteration previousVal = newVal; } return result; }
/// <summary> /// Returns a Tbool in which the last value in a time period is the /// final value. /// </summary> public Tbool PeriodEndVal(Tnum temporalPeriod) { return this.PeriodEndVal<Tbool>(temporalPeriod).Lean; }
/// <summary> /// Returns the total number of elapsed intervals between two dates. /// </summary> public Tnum TotalElapsedIntervals(Tnum interval, Tdate start, Tdate end) { Tnum rei = RunningElapsedIntervals(interval); return rei.AsOf(end) - rei.AsOf(start); }
//********************************************************************* // TEMPORAL "RECURRENCE" FUNCTIONS //********************************************************************* /// <summary> /// Loops (cycles) through numbers over time (e.g. 1 2 3 4 1 2 3 4 ...) /// </summary> public static Tnum Recurrence(Tdate startDate, Tdate endDate, IntervalType interval, int min, int max) { // Handle unknowns Hstate top = PrecedingState(startDate.FirstValue, endDate.FirstValue); if (top != Hstate.Known) return new Tnum(new Hval(null,top)); DateTime start = startDate.ToDateTime; DateTime end = endDate.ToDateTime; Tnum result = new Tnum(); if (start != Time.DawnOf) { result.AddState(Time.DawnOf, 0); } DateTime indexDate = start; int indexNumber = min; while (indexDate < end) { result.AddState(indexDate,Convert.ToString(indexNumber)); indexDate = indexDate.AddInterval(interval, 1); // Reset sequence indexNumber++; if (indexNumber > max) { indexNumber = min; } } result.AddState(end, 0); return result; }
/// <summary> /// Finds the total of a Tnum, summed over the given intervals between two /// dates (start and end). /// </summary> /// <example> /// AnnualIncome2014 = MonthlyIncome.TotalSummedIntervals(TheMonth, 2014-01-01, 2014-12-31) /// </example> public Tnum TotalSummedIntervals(Tnum interval, Tdate start, Tdate end) { Tnum rsi = RunningSummedIntervals(interval); return(rsi.AsOf(end) - rsi.AsOf(start)); }
// ******************************************************************** // ROUNDING FUNCTIONS // ******************************************************************** /// <summary> /// Rounds a value to the nearest multiple of a given number. By default, /// it rounds up in case of a tie. /// </summary> public Tnum RoundToNearest(Tnum multiple) { return(RoundToNearest(multiple, false)); }
/// <summary> /// Natural logarithm /// </summary> public static Tnum Log(Tnum tn) { return ApplyFcnToTimeline<Tnum>(x => CoreNatLog(x), tn); }
/// <summary> /// Rounds a value down to the next multiple of a given number. /// </summary> public Tnum RoundDown(Tnum multiple) { return Switch<Tnum>(() => this % multiple != 0, () => this - (this % multiple), () => true, () => this); }
/// <summary> /// Natural logarithm /// </summary> public static Tnum Log(Tnum tn) { return(ApplyFcnToTimeline <Tnum>(x => CoreNatLog(x), tn)); }
/// <summary> /// Provides a running count of how many intervals (years, days, etc.) a Tbool /// has been true within some sliding window of time. At the end of that sliding /// window, this function returns the amount of time that has elapsed within the /// window during which the Tbool was true. /// </summary> /// <remarks> /// Example: For a given Tbool, at any given point in time, for how many days during the /// previous 3 days is the Tbool true? /// /// tb = <FTTTFTTTFFFF> /// tb.SEI(3, TheDay) = <001232223210> /// </remarks> public Tnum SlidingElapsedIntervals(Tnum interval, Tnum windowSize) { // If a Tbool is eternally true, return windowSize if (this.IsTrue) { return windowSize; } // The number of true intervals in a sliding window of time equals // the running count as of time1 minus the running count as of time0. Tnum r = this.RunningElapsedIntervals(interval); int size = Convert.ToInt32(windowSize.FirstValue.Val) * -1; // Counts the current inerval return r - r.Shift(size, interval); }
/// <summary> /// A raised to the B power /// </summary> public static Tnum Pow(Tnum tnA, Tnum tnB) { return ApplyFcnToTimeline<Tnum>(x => CorePow(x), tnA, tnB); }
/// <summary> /// Square root /// </summary> public static Tnum Sqrt(Tnum tn) { return ApplyFcnToTimeline<Tnum>(x => CoreSqrt(x), tn); }
/// <summary> /// Takes a Tnum representing some value per unit time, and accumulates it /// over a given number of successive time intervals. /// </summary> /// <example> /// Calculate income accumulated over a three month period, where the /// person earned $3,000/month: /// /// Income = MonthlyIncome.SlidingSummedIntervals(TheMonth, 3) /// /// The time units cancel: [$/mo.] * [mo.] yields [$]. /// </example> public Tnum SlidingSummedIntervals(Tnum interval, Tnum windowSize) { // TODO: Review how uncertainty is handled here: // Handle unknowns Hstate top = PrecedingState(this.FirstValue, interval.FirstValue, windowSize.FirstValue); if (top != Hstate.Known) { return(new Tnum(new Hval(null, top))); } // If base Tnum is ever unknown during the time period, return // the state with the proper precedence Hstate baseState = PrecedenceForMissingTimePeriods(this); if (baseState != Hstate.Known) { return(new Tnum(baseState)); } // Handle eternal values if (this.IsEternal) { return(this * windowSize); } // Start accumulating... int num = windowSize.ToHardInt; // Get first accumulated value decimal firstVal = 0; for (int j = 0; j < num; j++) { // Don't walk off the end of the timeline if (j < interval.TimeLine.Count) { firstVal += this.AsOf(interval.TimeLine.Keys [j]).ToHardDecimal; } } Tnum result = new Tnum(firstVal); // Iterate through the subsequent intervals decimal previousVal = firstVal; for (int i = 1; i < interval.TimeLine.Count - num; i++) { // Take the value from the last iteration, and slide it the time window to the right, // subtracting the left interval and adding the new right one. decimal lastOldInterval = Convert.ToDecimal(this.ObjectAsOf(interval.TimeLine.Keys[i - 1]).Val); decimal nextNewInterval = Convert.ToDecimal(this.ObjectAsOf(interval.TimeLine.Keys[i + num - 1]).Val); decimal newVal = previousVal - lastOldInterval + nextNewInterval; // Only add changepoint if value actually changes if (newVal != previousVal) { // The value of an interval is counted after it has elapsed result.AddState(interval.TimeLine.Keys[i + num], newVal); } // Set for next iteration previousVal = newVal; } return(result); }