#region copyright
// Copyright Andrew Rafas 2012.
// Distributed under the Eclipse Public License, Version 1.0.
// (See accompanying file LICENSE.txt or
// copy at http://www.eclipse.org/legal/epl-v10.html)
#endregion
using System;
using System.Collections.Generic;
namespace CSFP.Memoize
{
public abstract class Dependent : IDependent, ISource
{
protected int _notReadyCount; // if == 0 then we can calculate this dependent, otherwise the value will be invalid
protected bool _isCached; // can be false even if _uncachedCount == 0
protected DependentList _dependentList;
#if DEBUG
internal readonly ISource[] SourceList;
#endif
protected Dependent(params ISource[] sourceList)
{
#if DEBUG
SourceList = sourceList;
#endif
_notReadyCount = 0;
_isCached = false;
_dependentList = EmptyDependentList.Instance;
for (int i = 0; i < sourceList.Length; ++i) {
var d = sourceList[i];
d.AddDependent(this, i);
if (!d.IsReady)
++_notReadyCount;
}
}
void IDependent.Notify(int readyChange, int key)
{
#if DEBUG
Helper.Log("Notify({0}): {1} ({2} -> {3}, {4})", ++Helper.NotificationCount, this, _notReadyCount, _notReadyCount - readyChange, key);
#endif
var oldReady = _notReadyCount == 0;
_notReadyCount -= readyChange;
var newReady = _notReadyCount == 0;
// if !_isCached then all dependencies, and all their dependencies, and so forth have !IsCached...
// if oldReady == newReady then we do not have to do antyhing to keep this invariant...
if (oldReady != newReady) {
_isCached = false; // ready change implies invalidation
_dependentList.NotifyAll(newReady ? DependentList.BecomeReady : DependentList.BecomeNotReady);
} else if (_isCached) {
_isCached = false;
_dependentList.NotifyAll(DependentList.ValueChanged);
}
}
bool ISource.IsReady
{
get { return _notReadyCount == 0; }
}
void ISource.AddDependent(IDependent dependency, int key)
{
_dependentList = _dependentList.Add(new DepKey(dependency, key));
}
}
public abstract class FunctionBase<R> : Dependent, IValue<R>
{
protected R _cachedValue;
protected FunctionBase(params ISource[] sourceList)
: base(sourceList)
{
}
#if DEBUG
[System.Diagnostics.Conditional("DEBUG")]
void LogCall()
{
var sb = new System.Text.StringBuilder();
for (int i = 0; i < SourceList.Length; ++i) {
if (i != 0)
sb.Append(", ");
object value = "?";
sb.Append(SourceList[i].ToString());
}
Helper.Log("Calculate({0}): {1} ({2})", ++Helper.CalculationCount, this, sb.ToString());
}
#endif
public override string ToString()
{
return base._isCached ? _cachedValue.ToString() : "?";
}
protected abstract void UpdateCached();
public R Value
{
get
{
if (base._isCached)
return _cachedValue;
// We do not check IsReady, if the caller was so dumb to call us when we are not ready,
// then we will get an exception higher up in the function call chain.
#if DEBUG
LogCall();
#endif
UpdateCached();
base._isCached = true;
#if DEBUG
Helper.Log("Result: {0} := {1}", this, _cachedValue);
#endif
// The prior call will cause ValueChanged notifications if some value gets cached
// higher up in the function call chain, but since we are most likely called from a
// function below us, the notification will not propagate down as they are most
// likely !_isCached. (It can propagate though if we fan out downwards...)
base._dependentList.NotifyAll(DependentList.ValueChanged);
return _cachedValue;
}
}
}
public sealed class Function<T, R> : FunctionBase<R>
{
IValue<T> _param;
Func<T, R> _func;
public Function(IValue<T> param, Func<T, R> pureFunction)
: base(param)
{
_param = param;
_func = pureFunction;
}
protected override void UpdateCached()
{
base._cachedValue = _func(_param.Value);
}
}
public sealed class Function<T1, T2, R> : FunctionBase<R>
{
IValue<T1> _param1;
IValue<T2> _param2;
Func<T1, T2, R> _func;
public Function(IValue<T1> param1, IValue<T2> param2, Func<T1, T2, R> pureFunction)
: base(param1, param2)
{
_param1 = param1;
_param2 = param2;
_func = pureFunction;
}
protected override void UpdateCached()
{
base._cachedValue = _func(_param1.Value, _param2.Value);
}
}
public sealed class Function<T1, T2, T3, R> : FunctionBase<R>
{
IValue<T1> _param1;
IValue<T2> _param2;
IValue<T3> _param3;
Func<T1, T2, T3, R> _func;
public Function(IValue<T1> param1, IValue<T2> param2, IValue<T3> param3, Func<T1, T2, T3, R> pureFunction)
: base(param1, param2, param3)
{
_param1 = param1;
_param2 = param2;
_param3 = param3;
_func = pureFunction;
}
protected override void UpdateCached()
{
base._cachedValue = _func(_param1.Value, _param2.Value, _param3.Value);
}
}
public sealed class Function<T1, T2, T3, T4, R> : FunctionBase<R>
{
IValue<T1> _param1;
IValue<T2> _param2;
IValue<T3> _param3;
IValue<T4> _param4;
Func<T1, T2, T3, T4, R> _func;
public Function(IValue<T1> param1, IValue<T2> param2, IValue<T3> param3, IValue<T4> param4, Func<T1, T2, T3, T4, R> pureFunction)
: base(param1, param2, param3, param4)
{
_param1 = param1;
_param2 = param2;
_param3 = param3;
_param4 = param4;
_func = pureFunction;
}
protected override void UpdateCached()
{
base._cachedValue = _func(_param1.Value, _param2.Value, _param3.Value, _param4.Value);
}
}
}