using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using miew.Enumerable;
using miew.Memory;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// IHotValue(T) : a push-forward computation mesh
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace miew.Mesh
{
using Math = System.Math;
using String = System.String;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
///
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public interface IHotValueListener<T>
{
void Recalculate();
/// <summary>
/// This convenience method should be implemented like so:
/// public void ListenTo(IHotValue(T) bv) { bv.AddListener(this); }
/// </summary>
void ListenTo(IHotValue<T> bv);
/// <summary>
/// This convenience method should be implemented like so:
/// public void StopListeningTo(IHotValue<T> bv) { bv.RemoveListener(this); }
/// </summary>
void StopListeningTo(IHotValue<T> bv);
};
public interface IHotValue<T>
{
void AddListener(IHotValueListener<T> bv);
void RemoveListener(IHotValueListener<T> bv);
T Value { get; }
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// An IHotValue(T) instance which supports a value of T that never changes.
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public class HotValueConst<T> : IHotValue<T>
{
public static HotValueConst<T> Default = new HotValueConst<T>(default(T));
#if DEBUG
readonly
#endif
T val;
public HotValueConst(T value) { this.val = value; }
public T Value { get { return val; } }
/// Never have to notify, so there is no point in keeping track of listeners
public void AddListener(IHotValueListener<T> bv) { }
public void RemoveListener(IHotValueListener<T> bv) { }
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Abstract implementation helper for an IHotValue(T) which needs to notify subscribers of changes to its
/// value because that value is subject to change (i.e. perhaps because the IHotValue(T) is itself a subscriber
/// to other HotValues although this is not a specific requirement here)
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public abstract class HotValueNotifier<T> : IHotValue<T>
where T : IEquatable<T>
{
List<WeakReference<IHotValueListener<T>>> listeners = null;
public void AddListener(IHotValueListener<T> hv)
{
if (hv == null)
return;
listeners = listeners ?? new List<WeakReference<IHotValueListener<T>>>();
listeners.Add(new WeakReference<IHotValueListener<T>>(hv));
}
public void RemoveListener(IHotValueListener<T> hv)
{
if (listeners == null || hv == null)
return;
for (int i = 0; i < listeners.Count; i++)
{
if (hv == listeners[i].Target)
{
listeners.RemoveAt(i);
return;
}
}
}
public void Notify()
{
if (listeners == null)
return;
for (int i = 0; i < listeners.Count; )
{
IHotValueListener<T> bv = listeners[i].Target;
if (bv == null)
listeners.RemoveAt(i);
else
{
bv.Recalculate();
i++;
}
}
}
public abstract T Value { get; }
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Implementation of an IHotValue(T) that can (only) be explicitly changed. Such manual changes trigger notifications
/// to this object's subscribers.
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public class HotValueStatic<T> : HotValueNotifier<T>
where T : IEquatable<T>
{
T val;
public HotValueStatic(T initial_value)
{
this.val = initial_value;
}
public override T Value { get { return val; } }
public void SetValue(T value)
{
if (!value.Equals(this.val))
{
this.val = value;
Notify();
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Implementation of an IHotValue(T) repeater which allows the same notification targets to be maintained when the source
/// IHotValueTarget(T) is replaced. To change the source, call the 'ChangeSource' method. As an optimization, the last
/// reported value of the source is cached and if the value of the new source is equal the previous value, notifications
/// are not sent.
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public class HotValueDynamic<T> : HotValueNotifier<T>, IHotValueListener<T>
where T : IEquatable<T>
{
public HotValueDynamic(IHotValue<T> source)
{
this.src = source;
this.val = src == null ? default(T) : src.Value;
ListenTo(src);
}
IHotValue<T> src;
T val;
public override T Value { get { return src.Value; } }
public void Recalculate()
{
T new_val = src == null ? default(T) : src.Value;
if (!new_val.Equals(val))
{
val = new_val;
Notify();
}
}
public void ListenTo(IHotValue<T> hv) { hv.AddListener(this); }
public void StopListeningTo(IHotValue<T> hv) { hv.RemoveListener(this); }
public IHotValue<T> Source
{
get { return src; }
set
{
StopListeningTo(src);
src = value;
ListenTo(src);
Recalculate();
}
}
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Abstract implementation helper for a HotValueCalc(S,T) instance:
/// (a.) maintains a single cached value of type 'T' which is automatically computed based on zero or more IHotValue(S)
/// input values, and
/// (b.) itself implements IHotValue(T) allowing it to be used in an heterotypical automatic calculation mesh.
/// Derived classes must override the 'Calculate' function to implement their cutomized calculation.
/// </summary>
/// <remarks>
/// Changes to any of the input values automatically trigger recalculation and notification of this object's subscribers.
/// The number of IHotValue(S) input sources is set upon construction and cannot be modified, but these arguments can be
/// replaced by calling 'ChangeArg'
/// </remarks>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public abstract class HotValueCalc<S, T> : HotValueNotifier<T>, IHotValueListener<S>
where T : IEquatable<T>
{
T val;
readonly IHotValue<S>[] args;
static bool f_double;
static HotValueCalc()
{
f_double = default(T).GetType() == typeof(Double);
}
public HotValueCalc(params IHotValue<S>[] args)
: this(true, args)
{
}
public HotValueCalc(bool f_calc, params IHotValue<S>[] args)
{
this.args = args;
foreach (IHotValue<S> bv in args)
ListenTo(bv);
/// calculate and store the initial value, bypassing the check for vacuous value setting.
/// also skip broadcasting since there can be no subscribers initially
if (f_calc)
this.val = Calculate();
}
public void ListenTo(IHotValue<S> bv) { bv.AddListener(this); }
public void StopListeningTo(IHotValue<S> bv) { bv.RemoveListener(this); }
public override T Value { get { return val; } }
public void Recalculate()
{
T value = Calculate();
//Debug.WriteLine(value);
//Debug.WriteLine();
// if (new StackTrace().FrameCount > 200)
// Debugger.Break();
//Debug.WriteLine(new StackTrace().FrameCount);
if (f_double)
{
if (Math.Abs(Convert.ToDouble(this.val) - Convert.ToDouble(value)) < .00001)
return;
}
if (!value.Equals(this.val))
{
this.val = value;
Notify();
}
}
protected IHotValue<S>[] Args { get { return args; } }
protected S Arg0 { get { return args[0] == null ? default(S) : args[0].Value; } }
protected S Arg1 { get { return args[1] == null ? default(S) : args[1].Value; } }
protected S Arg2 { get { return args[2] == null ? default(S) : args[2].Value; } }
protected S Arg3 { get { return args[3] == null ? default(S) : args[3].Value; } }
protected S Arg4 { get { return args[4] == null ? default(S) : args[4].Value; } }
protected S Arg5 { get { return args[5] == null ? default(S) : args[5].Value; } }
public void ChangeArg(IHotValue<S> old_arg, IHotValue<S> new_arg)
{
if (old_arg == null || new_arg == null)
throw new ArgumentException("Cannot be null.");
int ix = System.Array.IndexOf<IHotValue<S>>(args, old_arg);
if (ix == -1)
throw new ArgumentException("The argument to replace was not found.");
if (old_arg == new_arg)
return;
do
{
StopListeningTo(old_arg);
args[ix] = new_arg;
ListenTo(new_arg);
}
while ((ix = System.Array.IndexOf<IHotValue<S>>(args, old_arg, ix)) != -1);
Recalculate();
}
protected abstract T Calculate();
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Abstract implementation helper for calculated IHotValue(T) values where the input type is the same as the type
/// of the calculation output
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public abstract class HotValueCalc<T> : HotValueCalc<T, T>
where T : IEquatable<T>
{
public HotValueCalc(params IHotValue<T>[] args)
: base(true, args)
{
}
public HotValueCalc(bool f_calc, params IHotValue<T>[] args)
: base(f_calc, args)
{
}
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// IHvDouble - supporting a mesh of double-precision calculations
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public interface IHvDouble : IHotValue<Double> { }
[DebuggerDisplay("{ToString(),nq}")]
public class HvdConst : HotValueConst<Double>, IHvDouble
{
public HvdConst(Double value)
: base(value)
{ }
public override string ToString() { return String.Format("{0:X8} Constant = {1}", GetHashCode(), Value); }
public static HvdConst NegativeInfinity = new HvdConst(double.NegativeInfinity);
public static HvdConst PositiveInfinity = new HvdConst(double.PositiveInfinity);
public static HvdConst Zero = new HvdConst(0);
public static HvdConst NaN = new HvdConst(double.NaN);
};
[DebuggerDisplay("{ToString(),nq}")]
public class HvdStatic : HotValueStatic<Double>, IHvDouble
{
public HvdStatic(Double value)
: base(value)
{ }
public override string ToString() { return String.Format("{0:X8} Static = {1}", GetHashCode(), Value); }
};
[DebuggerDisplay("{ToString(),nq}")]
public class HvdDynamic : HotValueDynamic<Double>, IHvDouble
{
public HvdDynamic(IHvDouble bv_arg)
: base(bv_arg)
{ }
public override string ToString() { return String.Format("{0:X8} Dynamic = {1}", GetHashCode(), Value); }
};
[DebuggerDisplay("{ToString(),nq}")]
public class HvdAdd : HotValueCalc<Double>, IHvDouble
{
public HvdAdd(params IHvDouble[] args)
: base(args)
{ }
protected override Double Calculate() { return Args.Sum(x => x.Value); }
public override string ToString() { return String.Format("{0:X8} {1} + {2} = {3}", GetHashCode(), Arg0, Arg1, Value); }
};
[DebuggerDisplay("{ToString(),nq}")]
public class HvdAddConst : HotValueCalc<Double>, IHvDouble
{
public HvdAddConst(Double dVal, params IHvDouble[] args)
: base(false, args)
{
this.dVal = dVal;
Recalculate();
}
Double dVal;
protected override Double Calculate() { return dVal + Args.Sum(x => x.Value); }
public override string ToString()
{
return String.Format("{0:X8} /{1}/ + {2} = {3}", GetHashCode(), dVal, Args.Select(x => x.Value.ToString()).StringJoin(" + "), Value);
}
};
[DebuggerDisplay("{ToString(),nq}")]
public class HvdMax : HotValueCalc<Double>, IHvDouble
{
public HvdMax(IHvDouble bv0, IHvDouble bv1)
: base(bv0, bv1)
{ }
protected override Double Calculate() { return Math.Max(Arg0, Arg1); }
public override string ToString() { return String.Format("{0:X8} Max({1},{2}) = {3}", GetHashCode(), Arg0, Arg1, Value); }
};
public class HvdMultiMax : HotValueCalc<Double>, IHvDouble
{
public HvdMultiMax(params IHvDouble[] args)
: base(args)
{ }
protected override Double Calculate() { return Args.Max(x => x.Value); }
};
[DebuggerDisplay("{ToString(),nq}")]
public class HvdMin : HotValueCalc<Double>, IHvDouble
{
public HvdMin(IHvDouble bv0, IHvDouble bv1)
: base(bv0, bv1)
{ }
protected override Double Calculate() { return Math.Min(Arg0, Arg1); }
public override string ToString() { return String.Format("{0:X8} Min({1},{2}) = {3}", GetHashCode(), Arg0, Arg1, Value); }
};
public class HvdMultiMin : HotValueCalc<Double>, IHvDouble
{
public HvdMultiMin(params IHvDouble[] args)
: base(args)
{ }
protected override Double Calculate() { return Args.Min(x => x.Value); }
};
public class HvdAverage : HotValueCalc<Double>, IHvDouble
{
public HvdAverage(params IHvDouble[] args)
: base(args)
{ }
protected override Double Calculate() { return Args.Average(x => x.Value); }
};
public class HvdAddHalf : HotValueCalc<Double>, IHvDouble
{
public HvdAddHalf(IHvDouble bv0, IHvDouble bv1)
: base(bv0, bv1)
{ }
protected override Double Calculate() { return Arg0 + (Arg1 / 2); }
};
public class HvdSubHalf : HotValueCalc<Double>, IHvDouble
{
public HvdSubHalf(IHvDouble bv0, IHvDouble bv1)
: base(bv0, bv1)
{ }
protected override Double Calculate() { return Arg0 - (Arg1 / 2); }
};
public class HvdCenterNode : HotValueCalc<Double>, IHvDouble
{
public HvdCenterNode(IHvDouble bv0, IHvDouble bv1, IHvDouble width)
: base(bv0, bv1, width)
{ }
protected override Double Calculate() { return Math.Abs(Arg1 - Arg0) / 2 - Arg2 / 2; }
};
public class HvdHalfSpan : HotValueCalc<Double>, IHvDouble
{
public HvdHalfSpan(IHvDouble bv0, IHvDouble bv1)
: base(bv0, bv1)
{ }
protected override Double Calculate() { return Math.Abs(Arg1 - Arg0) / 2; }
};
public class HvdAddHalfSpan : HotValueCalc<Double>, IHvDouble
{
public HvdAddHalfSpan(IHvDouble bv0, IHvDouble bv1, IHvDouble bv2)
: base(bv0, bv1, bv2)
{ }
protected override Double Calculate() { return Arg0 + (Arg2 - Arg1) / 2; }
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// IHvInterval - supporting a mesh of calculations involving intervals
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public interface IHvInterval : IHotValue<IHvInterval>
{
IHvDouble HvdX1 { get; }
IHvDouble HvdX2 { get; }
IHvDouble HvdLength { get; }
IHvDouble HvdCenter { get; }
Double X1 { get; }
Double X2 { get; }
Double Length { get; }
Double Center { get; }
};
public class HvIntervalBuilder : IHotValueListener<Double>, IHvInterval, IHotValueListener<IHvInterval>
{
public HvIntervalBuilder()
{
x1 = new HvdDynamic(null);
x2 = new HvdDynamic(null);
x1.AddListener(this);
x2.AddListener(this);
}
List<WeakReference<IHotValueListener<IHvInterval>>> listeners = null;
HvdDynamic x1 = null;
HvdDynamic x2 = null;
HvdStatic length = null;
HvdStatic center = null;
public IHvDouble HvdX1
{
get { return x1; }
}
public IHvDouble HvdX2
{
get { return x2; }
}
public IHvDouble HvdLength
{
get { return length = length ?? new HvdStatic(Length); }
}
public IHvDouble HvdCenter
{
get { return center = center ?? new HvdStatic(Center); }
}
public Double X1 { get { return x1.Value; } }
public Double X2 { get { return x2.Value; } }
public Double Length { get { return x2.Value - x1.Value; } }
public Double Center { get { return x1.Value + (x2.Value - x1.Value) / 2.0; } }
void IHotValueListener<Double>.Recalculate()
{
Notify();
}
void IHotValueListener<IHvInterval>.Recalculate()
{
Notify();
}
void IHotValueListener<Double>.ListenTo(IHotValue<Double> bv) { bv.AddListener(this); }
void IHotValueListener<IHvInterval>.ListenTo(IHotValue<IHvInterval> bv) { bv.AddListener(this); }
void IHotValueListener<Double>.StopListeningTo(IHotValue<Double> bv) { bv.RemoveListener(this); }
void IHotValueListener<IHvInterval>.StopListeningTo(IHotValue<IHvInterval> bv) { bv.AddListener(this); }
public void AddListener(IHotValueListener<IHvInterval> hv)
{
if (hv == null)
return;
listeners = listeners ?? new List<WeakReference<IHotValueListener<IHvInterval>>>();
listeners.Add(new WeakReference<IHotValueListener<IHvInterval>>(hv));
}
public void RemoveListener(IHotValueListener<IHvInterval> hv)
{
if (listeners == null || hv == null)
return;
for (int i = 0; i < listeners.Count; i++)
{
if (hv == listeners[i].Target)
{
listeners.RemoveAt(i);
return;
}
}
}
public void Notify()
{
if (listeners == null)
return;
for (int i = 0; i < listeners.Count; )
{
IHotValueListener<IHvInterval> bv = listeners[i].Target;
if (bv == null)
listeners.RemoveAt(i);
else
{
bv.Recalculate();
i++;
}
}
}
public IHvInterval Value
{
get { return this; }
}
};
}