Tensor Weighted Interpolated Transfer (TWIT)

One function that is at the core of the SRS system is where the activation of a N-Dimensional map of neurons (a Tensor) should stimulate some part or all of another tensors activation.  This has several parameters.
Source and Target dimensions.  In NumPy nomenclature this is the shape and slices of the source and target tensors.
For example the source tensor A might be (4,5,6) in shape and the target B might be (7,8) in shape.  They miss match on both count of dimensions and size of dimensions.
Also along each axis of the larger dimension tensor (in this case A) there is a start and end weight usually between -1.0 and 1.0.  We want to interpolate the weights along that dimension.

One may also select a subset of either or both A and B as the connected part.  The form of this is source, destination, and weight ranges for each dimension.  If some part is missing it is assumed to be the whole range of the tensor dimension, or for weights is assumed to be 1.0.

 Here is a screen shot of the current C# NuTank neural editor after one time tick.

On the left we have the input Image as a concept map with a dog. It is 100x100 grey scale. The TWIT function is the light blue boxes. The curly bracket { } strings are the source indices  and the underscore _ is between a range of numbers.  So 0_99 indicates the source range along that dimension. The angle brackets < > are the destination range, and the square brackets [ ] are the weight range.

The top TWIT is {0_99,0_99} <0_99, 0_99> [1_0, 1_1] which indicates the entire range of the 100x100 dog picture, the entire range of the destination which is 100x100, and the weight interpolates from 0 to 1 along the horizontal axis and is 1 all along the vertical.  The resulting picture is bright on the left and fades to black along the right. (There was a bug when I took the screen shot but the idea is still the same even though the numbers appear incorrect.)

The next TWIT is more interesting.  The pair take the source image at 1 weight to the destination and then the second TWIT takes the source image times -1.0 weight shifted over 1 pixel. This is an edge extraction.

There are two interpolations that can happen.  One is the weights interpolation along an axis, and the other is the index interpolation along the dimensions.

The code to do this in C# is a series of int and float iterators that generate an interpolation along a single axis.  The interpolator is given start and end value pairs for a source and destination range, and the number of steps to perform for the interpolation.
The single dimension interpolation iterator is then aggregated into a N dimensional interpolation iterator. It returns successive tuples of source, destination, and weight for each dimension of the source and destination.

Code:
The low level objects are a NIndex which is a 1D array of integer (Immutable).
We then have Range Primitives which are Range Int and Range Float which are a start and end range with some string and parse functions.
A Range Float Iterator is Enumerable and has a Start, End, how many divisions.  This iterator returns successive  float values and can deal with reversed ranges and such. (E.g. interpolate from 20 to 5 in 17 steps.)
Same for a Range Int Iterator.
The Connection Single Dimension Iterator aggregates two Range Int Iterators and a Range Float Iterator to give all three factors tracked along one dimension.
Then a Connection Multi Dim Iterator has a source map, destination map (Tensor) and a list of Connection Single Dimension Iterator that all combines to be a full TWIT.

Given the complexity of so many dimensions and so many interpolations going on at once, it was very difficult to do such on-the-fly in the MoveNext() function, so instead the entire set of tuples is generated in advance and then just stepped through.  This uses a bit more memory but is very performant and easy(er) to implement.


This elegantly lets one generate the connection of a TWIT between a source and destination concept map.

I am currently developing the same algorithm in TensorFlow.  Tricky.



Here is some code...
ConnectionStrand.cpp

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
 
namespace Cognate
{
    /// <summary>
    /// A single link (dendrite?) between a source ConceptMap to a destination ConceptMap with a weight along a single dimension of a N-dimensional Map.
    /// </summary>
    public class ConnectionLink
    {
        public ConnectionLink(int srcIdxint dstIdxfloat weight)
        {
            this.SrcIdx = srcIdx;
            this.DstIdx = dstIdx;
            this.Weight = weight;
        }
 
        public override string ToString()
        {
            return string.Format("src {0} dst {1} w {2}", SrcIdx, DstIdx, Weight);
        }
 
        public int SrcIdx { getset; }
 
        public int DstIdx { getset; }
 
        public float Weight { getset; }
 
        /// <summary>
        /// Weight = Weight / connectionCount
        /// If connectionCount == 0 then does nothing.
        /// </summary>
        /// <param name="connectionCount"></param>
        public void SetWeightForNormalization(float connectionCount)
        {
            if (connectionCount == 0)
            {
                return;
            }
            Weight /= (float)connectionCount;
        }
    }
 
    /// <summary>
    /// The definition of a connection between two maps along one dimension. Can be one or two actual Links if the float idx is between integers.
    /// </summary>
    public class ConnectionDefinitionSingleDimension : IPostLoadable
    {
        public RangeInt sourceRange { getset; }
        public RangeInt destinationRange { getset; }
        public RangeFloat weights { getset; }
 
        public ConnectionDefinitionSingleDimension()
        {
 
        }
 
        public ConnectionDefinitionSingleDimension(RangeInt sourceRangeRangeInt destinationRangeRangeFloat weights)
        {
            this.sourceRange = sourceRange;
            this.destinationRange = destinationRange;
            this.weights = weights;
        }
        /// <summary>
        /// Makes a connection ranged 0..sourceRange-1 and constant weights.
        /// </summary>
        /// <param name="sourceRange"></param>
        /// <param name="destinationRange"></param>
        /// <param name="weight"></param>
        public ConnectionDefinitionSingleDimension(int sourceRangeint destinationRangefloat weight)
        {
            this.sourceRange = new RangeInt(0sourceRange - 1);
            this.destinationRange = new RangeInt(0destinationRange - 1);
            this.weights = new RangeFloat(weight); ;
        }
 
        public virtual void PostLoad(CognateWorld wBrain b = nullobject parentHook = null)
        {
            // No action needed.
        }
    }
 
    public enum FanStyle
    {
        // Src and destination have the same count of integer indicies.
        Equal,
        // Src has more points than Dest.
        FanIn,
        // Dst has more pints than Src.
        FanOut
    };
 
    /// <summary>
    /// Iterate along a single dimension of multi dimensional src and dst Maps returning successive single links.
    /// </summary>
    public class ConnectionDefinitionSingleDimensionIterator : IEnumerator<ConnectionLink>
    {
        readonly RangeFloatIterator sourceIt;
        readonly RangeFloatIterator destIt;
        readonly RangeFloatIterator weightIt;
 
        readonly FanStyle fan;
 
        private readonly List<ConnectionLink> CachedConnectionLinks = new List<ConnectionLink>();
        private int CachedConnectionLinksIdx = 0;
        public bool AtStart { getprotected set; }
        public bool AtEnd { getprotected set; }
 
        public ConnectionDefinitionSingleDimensionIterator(RangeInt srcRangeRangeInt dstRangeRangeFloat weights)
        {
            AtStart = true;
            AtEnd = false;
            int counts = Math.Max(srcRange.AbsSpan, dstRange.AbsSpan);
            sourceIt = new RangeFloatIterator(srcRangecounts);
            destIt = new RangeFloatIterator(dstRangecounts);
            weightIt = new RangeFloatIterator(weightscounts);
            if (srcRange.AbsSpan == dstRange.AbsSpan)
            {
                fan = FanStyle.Equal;
            }
            else if (srcRange.AbsSpan < dstRange.AbsSpan)
            {
                fan = FanStyle.FanOut;
            }
            else
            {
                fan = FanStyle.FanIn;
            }
 
            PrepIterationList();
        }
 
        /// <summary>
        /// In the case of fan in to a smaller map we have to calculate how many connection go into
        /// a single point in the destination and divide weights by the count. So we make the entire list of values, then adjust them to 
        /// be normalized.
        /// </summary>
        private void PrepIterationList()
        {
            Reset();
            while (sourceIt.MoveNext() && destIt.MoveNext() && weightIt.MoveNext())
            {
                CachedConnectionLinks.Add(new ConnectionLink((int)Math.Round(sourceIt.Current), (int)Math.Round(destIt.Current), weightIt.Current));
            }
 
            if (CachedConnectionLinks.Count == 0)
            {
                // Should never have an empty iteration?
                AtEnd = true;
                return;
            }
 
            // If Equal or FanOut we just use the weights per connection link.
            // If FanIn (Src Map is wider than Dst Map) then we divide the weights by the count of
            // links to the destination single neuron.  This prevents overloading the destination map neuron levels
            // and gives the intuitive expected stimulus 'brightness'.
            // Make a copy so the original order is the same but the instances weights get normalized.
            List<ConnectionLinkcpy = new List<ConnectionLink>(CachedConnectionLinks);
            if (fan == FanStyle.FanIn)
            {
                cpy.Sort((ab=> a.DstIdx.CompareTo(b.DstIdx));
 
                int count = 0;
                int value = cpy[0].DstIdx;
                for (int i = 0i < cpy.Count; i++)
                {
                    ConnectionLink v = cpy[i];
                    if (value != v.DstIdx)
                    {
                        NormalizeSingleConnectioGroup(countvalue);
 
                        value = v.DstIdx;
                        count = 1;
                    }
                    else if (i == CachedConnectionLinks.Count - 1)
                    {
                        NormalizeSingleConnectioGroup(count + 1value);
                    }
                    else
                    {
                        count++;
                    }
                }
            }
        }
 
        private void NormalizeSingleConnectioGroup(int countint value)
        {
            if (count < 2)
            {
                // Nothing to do.
                return;
            }
            for (int k = 0k < CachedConnectionLinks.Count; k++)
            {
                ConnectionLink vv = CachedConnectionLinks[k];
                if (vv.DstIdx == value)
                {
                    vv.SetWeightForNormalization(count);
                }
            }
        }
 
        public override string ToString()
        {
            return "src " + sourceIt.ToString() + " : dst " + destIt.ToString() + " : w " + weightIt.ToString() +
                (AtStart ? " (at start)" : ""+ (AtEnd ? " (at end)" : "" + (CachedConnectionLinks.Count == 0 ? "ERROR - ZERO SIZE MAP?" : ""));
        }
 
        public ConnectionLink Current { getprivate set; }
 
        object IEnumerator.Current
        {
            get
            {
                return Current;
            }
        }
 
        public void Dispose()
        {
            sourceIt.Dispose();
            destIt.Dispose();
            weightIt.Dispose();
            CachedConnectionLinks.Clear();
            CachedConnectionLinksIdx = 0;
            AtEnd = true;
            AtStart = false;
        }
 
        public bool MoveNext()
        {
            if (AtEnd)
            {
                return false;
            }
            AtStart = false;
            Current = CachedConnectionLinks[CachedConnectionLinksIdx];
            CachedConnectionLinksIdx++;
            if (CachedConnectionLinksIdx >= CachedConnectionLinks.Count)
            {
                AtEnd = true;
            }
            return true;
        }
 
        public void Reset()
        {
            AtStart = true;
            AtEnd = false;
            CachedConnectionLinksIdx = 0;
        }
 
        public static void UnitTest()
        {
            RangeInt s = new RangeInt(05);
            RangeInt d = new RangeInt(011);
            RangeFloat w = new RangeFloat(0.2f0.7f);
            int N = Math.Max(s.AbsSpan, d.AbsSpan);
            ConnectionDefinitionSingleDimensionIterator it = new ConnectionDefinitionSingleDimensionIterator(sdw);
            for (int i = 0i < Ni++)
            {
                Debug.Assert(it.MoveNext());
            }
            Debug.Assert(it.MoveNext() == false);
        }
 
    }
 
    /// <summary>
    /// What the ConnectionStrandIterator returns each iteration.
    /// </summary>
    public class ConnectionSingleDimensionLink
    {
        public float sourceIdx;
        public float destinationIdx;
        public float weight;
 
        public ConnectionSingleDimensionLink(float sfloat dfloat w)
        {
            sourceIdx = s;
            destinationIdx = d;
            weight = w;
        }
    }
 
    /// <summary>
    /// Iterates the indicies as floats, and weights as floats along multiple dimensions as connection requests between a source and a destination ConeceptMaps
    /// </summary>
    public class ConnectionMultiDimIterator : IEnumerator<List<ConnectionLink>>
    {
        public ConceptMap Src { get; }
        public ConceptMap Dst { get; }
        public bool AtEnd { getprivate set; }
 
        List<ConnectionDefinitionSingleDimensionIterator> its = new List<ConnectionDefinitionSingleDimensionIterator>();
 
        public ConnectionMultiDimIterator(ConceptMap srcConceptMap dstList<RangeIntsrcIndexRange = nullList<RangeIntdstIndexRange = nullList<RangeFloatweights = null)
        {
            this.Src = src;
            this.Dst = dst;
            this.AtEnd = false;
 
            if (srcIndexRange == null)
            {
                srcIndexRange = new List<RangeInt>();
                for (int i = 0i < src.DimensionsCount; i++)
                {
                    srcIndexRange.Add(new RangeInt(0src.Dimension(i)));
                }
            }
            if (dstIndexRange == null)
            {
                dstIndexRange = new List<RangeInt>();
                for (int i = 0i < dst.DimensionsCount; i++)
                {
                    dstIndexRange.Add(new RangeInt(0dst.Dimension(i)));
                }
            }
            if (weights == null)
            {
                weights = new List<RangeFloat>();
            }
            Debug.Assert(srcIndexRange.Count <= src.DimensionsCount);
            Debug.Assert(dstIndexRange.Count <= dst.DimensionsCount);
 
            int maxIdx = Math.Max(src.DimensionsCount, dst.DimensionsCount);
            for (int i = 0i < maxIdxi++)
            {
                // If src and dst dimension counts unequal fill either with 1 dimension so they match.
                if (srcIndexRange.Count <= i)
                {
                    srcIndexRange.Add(new RangeInt(00));
                }
                if (dstIndexRange.Count <= i)
                {
                    dstIndexRange.Add(new RangeInt(00));
                }
                if (weights.Count <= i)
                {
                    // Default for higher dimensions
                    weights.Add(new RangeFloat(1.0f));
                }
 
                ConnectionDefinitionSingleDimensionIterator it = new ConnectionDefinitionSingleDimensionIterator(srcIndexRange[i], dstIndexRange[i], weights[i]);
                its.Add(it);
 
            }
        }
 
        /// <summary>
        /// Don't call Current, call one of CurrentSrcFlatIdx, CurrentDstFlatIdex, CurrentWeight.
        /// </summary>
        public List<ConnectionLink> Current => throw new NotImplementedException("Don't call Current, call one of CurrentSrcFlatIdx, CurrentDstFlatIdex, CurrentWeigh");
 
        /// <summary>
        /// Don't call Current, call one of CurrentSrcFlatIdx, CurrentDstFlatIdex, CurrentWeight.
        /// </summary>
        object IEnumerator.Current => throw new NotImplementedException("Don't call Current, call one of CurrentSrcFlatIdx, CurrentDstFlatIdex, CurrentWeigh");
 
        public int CurrentSrcFlatIdx
        {
            get
            {
                return Src.CalcIndexAsSrc(its);
            }
        }
 
        public int[] CurrentSrcIdxs
        {
            get
            {
                int[] ret = new int[its.Count];
                for (int i = 0i < ret.Length; i++)
                {
                    ret[i= its[i].Current.SrcIdx;
                }
                return ret;
            }
        }
 
        public int CurrentDstFlatIdx
        {
            get
            {
                return Dst.CalcIndexAsDst(its);
            }
        }
 
        public int[] CurrentDstIdxs
        {
            get
            {
                int[] ret = new int[its.Count];
                for (int i = 0i < ret.Length; i++)
                {
                    ret[i= its[i].Current.DstIdx;
                }
                return ret;
            }
        }
 
        public float[] CurrentWeights
        {
            get
            {
                float[] ret = new float[its.Count];
                for (int i = 0i < ret.Length; i++)
                {
                    ret[i= its[i].Current.Weight;
                }
                return ret;
            }
        }
 
        public float CurrentWeightsProduct
        {
            get
            {
                float ret = 1.0f;
                for (int i = 0i < its.Count; i++)
                {
                    ret *= its[i].Current.Weight;
                }
                return ret;
            }
        }
 
        public override string ToString()
        {
            return "MDI: src=" + CognateHelpers.ToCommaString(CurrentSrcIdxs) +
                " : dst=" + CognateHelpers.ToCommaString(CurrentDstIdxs) +
                " : wts=" + CognateHelpers.ToCommaString(CurrentWeights) +
                " srcCIdx=" + CurrentSrcFlatIdx +
                " dstCIdx=" + CurrentDstFlatIdx +
                " combW=" + CurrentWeightsProduct;
        }
 
        public void Dispose()
        {
        }
 
        public bool MoveNext()
        {
            if (AtEnd)
            {
                return false;
            }
 
            if (its[0].AtStart)
            {
                // Get all iterators valid.
                for (int i = 0i < its.Count; i++)
                {
                    its[i].MoveNext();
                }
                return true;
            }
 
            for (int i = 0i < its.Count; i++)
            {
                if (its[i].MoveNext())
                {
                    return true;
                }
                its[i].Reset();
                // Get the iterator valid after the reset.
                its[i].MoveNext();
            }
 
            // At end of full N-Dimensional iteration pass.
            AtEnd = true;
            return false;
        }
 
        public void Reset()
        {
            this.AtEnd = false;
            for (int i = 0i < its.Count; i++)
            {
                its[i].Reset();
            }
        }
 
        public static void UnitTest()
        {
            ConceptMap A = new ConceptMap(null75);
            ConceptMap B = new ConceptMap(null24);
            List<RangeIntsrcIdxRange = new List<RangeInt>
            {
                new RangeInt(07),
                new RangeInt(04)
            };
            List<RangeFloatw = new List<RangeFloat>
            {
                new RangeFloat(1.0f1.0f),
                new RangeFloat(1.0f1.0f)
            };
            ConnectionMultiDimIterator it = new ConnectionMultiDimIterator(ABweightsw);
            //ConnectionMultiDimIterator it = new ConnectionMultiDimIterator(A, B, srcIndexRange: srcIdxRange, weights: w);
 
            Console.WriteLine(string.Format("Map A {0}   Map B {1}"A.ToString(), B.ToString()));
 
            for (int i = 0i < 300i++)
            {
                if (it.MoveNext() == false)
                {
                    break;
                }
                int srcI = it.CurrentSrcFlatIdx;
                int dstI = it.CurrentDstFlatIdx;
                float weight = it.CurrentWeightsProduct;
                int[] srcIdxs = it.CurrentSrcIdxs;
                int[] dstIdxs = it.CurrentDstIdxs;
                float[] weights = it.CurrentWeights;
                Console.WriteLine(String.Format("{0} src: {1} dst: {2,6} srcI {3,6} dstI {4,6}, w {5,6:0.####}, wts {6}"iCognateHelpers.ToCommaString(srcIdxs), CognateHelpers.ToCommaString(dstIdxs), srcIdstIweightCognateHelpers.ToCommaString(weights)));
            }
            Console.WriteLine("Done.");
        }
    }
 
}

RangePrimitives.cpp


using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
 
namespace Cognate
{
    /// <summary>
    /// Integer pair
    /// </summary>
    public class RangeInt
    {
        /* Note: No IPostLoad needed */
        public int Start { getset; }
        public int End { getset; }
        public bool Reverse { getset; }
 
        public RangeInt()
        {
 
        }
 
        [JsonIgnore]
        public string AsString
        {
            get
            {
                return string.Format("{0}_{1}", Start, End);
            }
        }
 
        public RangeInt(int startint end)
        {
            this.Start = start;
            this.End = end;
            Reverse = Start > End;
        }
 
        public RangeInt(int i)
        {
            this.Start = this.End = i;
            Reverse = false;
        }
 
        /// <summary>
        /// Parse string s to this RangeInt. Valid s is like "", "3", "2_7" "_3" "1_" "5_2" being ranges. Reverse ranges allowed.
        /// Asserts limits Start <= End. If s is "" then defaults to defaultValue.  If one or the other number is not given defaults to defaultValue.
        /// Makes sure resulting two numbers are in limits range.
        /// </summary>
        /// <param name="s"></param>
        /// <param name="ri"></param>
        /// <param name="defaultValue"></param>
        /// <param name="limits"></param>
        /// <returns></returns>
        public static bool TryParse(string sout RangeInt riRangeInt defaultValueRangeInt limits)
        {
            Debug.Assert(defaultValue != null);
            Debug.Assert(s != null);
            Debug.Assert(limits != null);
            Debug.Assert(limits.Start <= limits.End, "Limits must have Start less than or equal to End.");
 
            Debug.Assert(Math.Min(defaultValue.Start, defaultValue.End) >= limits.Start, "DefaultValue must lie within limits.");
            Debug.Assert(Math.Max(defaultValue.Start, defaultValue.End) <= limits.End, "DefaultValue must lie within limits.");
 
            ri = defaultValue.MemberwiseClone() as RangeInt;
            if (s == null)
            {
                return false;
            }
            s = s.Trim();
            if (s.Length == 0)
            {
                ri = defaultValue;
                return true;
            }
 
            int dashidx = s.IndexOf('_');
            if (dashidx >= 0)
            {
                string[] nums = s.Split('_');
                if (nums.Length > 2)
                {
                    return false;
                }
                string A = nums[0].Trim();
                string B = nums[1].Trim();
                int Ai;
                int Bi;
 
                if (A.Length == 0)
                {
                    Ai = defaultValue.Start;
                }
                else if (int.TryParse(Aout Ai== false)
                {
                    return false;
                }
 
                if (B.Length == 0)
                {
                    Bi = defaultValue.End;
                }
                else if (int.TryParse(Bout Bi== false)
                {
                    return false;
                }
 
                if (Ai < 0 || Bi < 0)
                {
                    return false;
                }
 
                if (Math.Min(AiBi< limits.Start)
                {
                    return false;
                }
 
                if (Math.Max(AiBi> limits.End)
                {
                    return false;
                }
 
                ri = new RangeInt(AiBi);
            }
            else
            {
                int Ai;
 
                if (s.Length == 0)
                {
                    Ai = defaultValue.Start;
                }
                else if (int.TryParse(sout Ai== false)
                {
                    return false;
                }
 
                if (Ai < 0)
                {
                    return false;
                }
                ri = new RangeInt(Ai);
            }
 
            return true;
        }
 
        /// <summary>
        /// Abs(End - Start) + 1
        /// </summary>
        [JsonIgnore]
        public int AbsSpan
        {
            get
            {
                return Math.Abs(End - Start) + 1;
            }
        }
 
        public override string ToString()
        {
            return String.Format("{0} {1}", Start, End);
        }
    }
 
    /// <summary>
    /// Float pair. start is not necessarily larger than end.  Can be a reverse range.
    /// </summary>
    public class RangeFloat
    {
        /* Note: No IPostLoad needed */
 
        public float Start { getset; }
        public float End { getset; }
        public bool Reverse { getset; }
        [JsonIgnore]
        public string AsString
        {
            get
            {
                return string.Format("{0}_{1}", Start, End);
            }
        }
 
        public RangeFloat()
        {
 
        }
 
        public RangeFloat(float startfloat end)
        {
            this.Start = start;
            this.End = end;
            Reverse = Start > End;
        }
 
        public RangeFloat(RangeInt ri)
        {
            this.Start = ri.Start;
            this.End = ri.End;
            Reverse = Start > End;
        }
 
        public RangeFloat(float f)
        {
            this.Start = this.End = f;
            Reverse = false;
        }
 
        /// <summary>
        /// Abs(End - Start)  NOTE: no +1 on end of this span calc.
        /// </summary>
        [JsonIgnore]
        public float AbsSpan
        {
            get
            {
                return (int)Math.Abs(End - Start);
            }
        }
 
        public override string ToString()
        {
            return String.Format("{0} {1}", Start, End);
        }
 
        /// <summary>
        /// Parse string s to this RangeFloat. Valid s is like "", "0.3", "0.0_1.0" "_3.0" "0.5_" "1.0_0.0" being ranges. Reverse ranges allowed.
        /// Different than RangeInt, does not check any range boundaries and defaults to 1.0f for all range. So the above "0.5_" is assumed to be "0.5_1.0"
        /// </summary>
        /// <param name="s"></param>
        /// <param name="ri"></param>
        /// <param name="defaultValue"></param>
        /// <param name="limits"></param>
        /// <returns></returns>
        public static bool TryParse(string sout RangeFloat rifloat defaultValue = 1.0f)
        {
            Debug.Assert(s != null);
 
            ri = new RangeFloat(defaultValue);
            if (s == null)
            {
                return true;
            }
            s = s.Trim();
            if (s.Length == 0)
            {
                return true;
            }
 
            int dashidx = s.IndexOf('_');
            if (dashidx >= 0)
            {
                string[] nums = s.Split('_');
                if (nums.Length > 2)
                {
                    return false;
                }
                string A = nums[0].Trim();
                string B = nums[1].Trim();
                float Ai;
                float Bi;
 
                if (A.Length == 0)
                {
                    Ai = defaultValue;
                }
                else if (float.TryParse(Aout Ai== false)
                {
                    return false;
                }
 
                if (B.Length == 0)
                {
                    Bi = defaultValue;
                }
                else if (float.TryParse(Bout Bi== false)
                {
                    return false;
                }
 
                ri = new RangeFloat(AiBi);
            }
            else
            {
                float Ai;
 
                if (s.Length == 0)
                {
                    Ai = defaultValue;
                }
                else if (float.TryParse(sout Ai== false)
                {
                    return false;
                }
 
                ri = new RangeFloat(Ai);
            }
 
            return true;
        }
    }
 
    /// <summary>
    /// An iterator that will return successive floats interpolated across divisions steps.
    /// </summary>
    public class RangeFloatIterator : IEnumerator<float>
    {
        public float Start { get; }
        public float End { get; }
        public int Divs { get; }
        public bool Reverse { get; }
        public float CurrentValue { getprivate set; }
        public bool AtStart { getprivate set; }
        public bool AtEnd { getprivate set; }
        private int currentIdx = 0;
 
        public RangeFloatIterator()
        {
 
        }
 
        public RangeFloatIterator(RangeFloat rfint divisions)
        {
            Start = rf.Start;
            End = rf.End;
            Reverse = rf.Reverse;
            Divs = divisions;
            Reset();
        }
        public RangeFloatIterator(RangeInt rfint divisions)
        {
            Start = rf.Start;
            End = rf.End;
            Reverse = rf.Reverse;
            Divs = divisions;
            Reset();
        }
 
        public RangeFloatIterator(float startfloat endint divisions)
        {
            Start = start;
            End = end;
            Reverse = start > end;
            Divs = divisions;
            Reset();
        }
 
        /// <summary>
        /// Range is like "" being a default value (1.0), or "2.3" being same start and end, or "1.1->2.1" being a start and end value.
        /// </summary>
        /// <param name="range"></param>
        /// <param name="divisions"></param>
        public RangeFloatIterator(String rangeint divisions)
        {
            if (range == null)
            {
                throw new NullReferenceException("Parse of floating point range specifier failed: null");
            }
            range = range.Trim();
            if (range.Length == 0)
            {
                Start = End = 1.0f;
            }
            else if (range.Contains("->"))
            {
                int i = range.IndexOf("->");
                if (i < 1)
                {
                    throw new CognateRangeFormatException("Parse of floating point range specifier failed, no first value before the '->': " + range);
                }
                string ss = range.Substring(0i).Trim();
                if (range.Length <= i + 2)
                {
                    throw new CognateRangeFormatException("Parse of floating point range specifier failed, no second value after the '->': " + range);
                }
                string es = range.Substring(i + 2).Trim();
 
                if (!float.TryParse(ssout float sf))
                {
                    throw new CognateRangeFormatException("Parse of floating point range specifier failed, first value not a float number: " + range);
                }
                if (!float.TryParse(esout float ef))
                {
                    throw new CognateRangeFormatException("Parse of floating point range specifier failed, second value not a float number: " + range);
                }
 
                Start = sf;
                End = ef;
            }
            else
            {
                float f;
                if (!float.TryParse(rangeout f))
                {
                    throw new CognateRangeFormatException("Parse of floating point range specifier failed: " + range);
                }
                Start = End = f;
            }
            Divs = divisions;
            Reset();
        }
 
        public float Current
        {
            get
            {
                if (AtStart)
                {
                    throw new InvalidOperationException("Must call MoveNext() before getting Current value.");
                }
                return CurrentValue;
            }
        }
 
        object IEnumerator.Current
        {
            get
            {
                if (AtStart)
                {
                    throw new InvalidOperationException("Must call MoveNext() before getting Current value.");
                }
                return CurrentValue;
            }
        }
 
        public void Dispose()
        {
        }
 
        public bool MoveNext()
        {
            if (AtEnd)
            {
                return false;
            }
            if (AtStart)
            {
                AtStart = false;
                CurrentValue = Start;
                return true;
            }
 
            currentIdx++;
            if (currentIdx >= Divs)
            {
                AtEnd = true;
                // Get it exact in case of rounding errors.
                CurrentValue = End;
                return false;
            }
 
            CurrentValue = Start + ((float)currentIdx / (float)(Divs - 1)) * (End - Start);
 
            return true;
        }
 
        public void Reset()
        {
            currentIdx = 0;
            AtStart = true;
            AtEnd = false;
            CurrentValue = Start;
        }
 
        public override string ToString()
        {
            if (AtStart)
            {
                return String.Format("Start {0} End {1} current (at start)", Start, End);
            }
            else
            {
                return String.Format("Start {0} End {1} current {2}", Start, End, Current);
            }
        }
 
        public static void UnitTest()
        {
            RangeFloatIterator it = new RangeFloatIterator(new RangeFloat(0.0f10.0f), 17);
            for (int i = 0i < 17i++)
            {
                Debug.Assert(it.MoveNext());
                Debug.Assert(Math.Abs(it.Current - i * 10.0f * (1.0f / 16.0f)) < 0.001f);
            }
            Debug.Assert(it.MoveNext() == false);
            Debug.Assert(it.Current == 10.0f);
 
            it = new RangeFloatIterator(new RangeFloat(2.0f0.0f), 4);
            for (int i = 0i < 4i++)
            {
                Debug.Assert(it.MoveNext());
                Debug.Assert(Math.Abs(it.Current - (2.0f - i * 2.0f * (1.0f / 3.0f))) < 0.001f);
            }
            Debug.Assert(it.MoveNext() == false);
            Debug.Assert(it.Current == 0.0f);
 
            it = new RangeFloatIterator(new RangeFloat(1.5f1.5f), 4);
            for (int i = 0i < 4i++)
            {
                Debug.Assert(it.MoveNext());
                Debug.Assert(Math.Abs(it.Current - 1.5f< 0.001f);
            }
            Debug.Assert(it.MoveNext() == false);
            Debug.Assert(it.Current == 1.5f);
        }
    }
 
    /// <summary>
    /// An iterator that will return successive ints interpolated across divisions steps.
    /// So, RangeIntIterator(new RangeInt(2, 4), 9) will return sequence 2, 2, 2, 3, 3, 3, 4, 4, 4
    /// 
    /// RangeIntIterator(new RangeInt(3, 1), 3) returns sequence 3, 2, 1
    /// 
    /// See RangeIntIterator::UnitTest()
    /// </summary>
    public class RangeIntIterator : IEnumerator<int>
    {
        readonly RangeFloatIterator rfi;
 
        public RangeIntIterator()
        {
 
        }
 
        public RangeIntIterator(RangeInt riint divisions)
        {
            rfi = new RangeFloatIterator(new RangeFloat(ri), divisions);
        }
 
        /// <summary>
        /// Range is like "" being a default value (0), or "2" being same start and end, or "1->5" being a start and end value range.
        /// Start may be equal or greater than end and that is just a constant or reversed sequence.
        /// </summary>
        /// <param name="range"></param>
        /// <param name="divisions"></param>
        public RangeIntIterator(String rangeint divisions)
        {
            if (range == null)
            {
                throw new NullReferenceException("Parse of floating point range specifier failed: null");
            }
 
            int start;
            int end;
            range = range.Trim();
            if (range.Length == 0)
            {
                start = end = 0;
            }
            else if (range.Contains("->"))
            {
                int i = range.IndexOf("->");
                if (i < 1)
                {
                    throw new CognateRangeFormatException("Parse of int range specifier failed, no first value before the '->': " + range);
                }
                string ss = range.Substring(0i).Trim();
                if (range.Length <= i + 2)
                {
                    throw new CognateRangeFormatException("Parse of int range specifier failed, no second value after the '->': " + range);
                }
                string es = range.Substring(i + 2).Trim();
 
                if (!int.TryParse(ssout start))
                {
                    throw new CognateRangeFormatException("Parse of int range specifier failed, first value not a float number: " + range);
                }
                if (!int.TryParse(esout end))
                {
                    throw new CognateRangeFormatException("Parse of int range specifier failed, second value not a float number: " + range);
                }
            }
            else
            {
                int f;
                if (!int.TryParse(rangeout f))
                {
                    throw new CognateRangeFormatException("Parse of int range specifier failed: " + range);
                }
                start = end = f;
            }
            rfi = new RangeFloatIterator(startenddivisions);
        }
 
        public bool AtStart
        {
            get
            {
                return rfi.AtStart;
            }
        }
 
        public int Current
        {
            get
            {
                return (int)(Math.Round(rfi.Current));
            }
        }
 
        object IEnumerator.Current
        {
            get
            {
                return (int)(Math.Round(rfi.Current));
            }
        }
 
        public void Dispose()
        {
            rfi.Dispose();
        }
 
        public bool MoveNext()
        {
            return rfi.MoveNext();
        }
 
        public void Reset()
        {
            rfi.Reset();
        }
 
        public override string ToString()
        {
            if (rfi.AtStart)
            {
                return String.Format("Start {0} End {1} current (at start)", rfi.Start, rfi.End);
            }
            else
            {
                return String.Format("Start {0} End {1} current {2}", rfi.Start, rfi.End, rfi.Current);
            }
        }
 
 
        public static void UnitTest()
        {
            RangeFloatIterator fit = new RangeFloatIterator("2.0->4.0"9);
            Debug.Assert(fit.MoveNext());
            Debug.Assert(CognateHelpers.CloseTo(fit.Current, 2.0f));
            Debug.Assert(fit.MoveNext());
            Debug.Assert(CognateHelpers.CloseTo(fit.Current, 2.25f));
            Debug.Assert(fit.MoveNext());
            Debug.Assert(CognateHelpers.CloseTo(fit.Current, 2.5f));
            Debug.Assert(fit.MoveNext());
            Debug.Assert(CognateHelpers.CloseTo(fit.Current, 2.75f));
            Debug.Assert(fit.MoveNext());
            Debug.Assert(CognateHelpers.CloseTo(fit.Current, 3.0f));
            Debug.Assert(fit.MoveNext());
            Debug.Assert(CognateHelpers.CloseTo(fit.Current, 3.25f));
            Debug.Assert(fit.MoveNext());
            Debug.Assert(CognateHelpers.CloseTo(fit.Current, 3.5f));
            Debug.Assert(fit.MoveNext());
            Debug.Assert(CognateHelpers.CloseTo(fit.Current, 3.75f));
            Debug.Assert(fit.MoveNext());
            Debug.Assert(CognateHelpers.CloseTo(fit.Current, 4.0f));
            Debug.Assert(fit.MoveNext() == false);
            fit.Reset();
            Debug.Assert(fit.MoveNext());
            Debug.Assert(CognateHelpers.CloseTo(fit.Current, 2.0f));
            Debug.Assert(fit.MoveNext());
            Debug.Assert(CognateHelpers.CloseTo(fit.Current, 2.25f));
            Debug.Assert(fit.MoveNext());
            Debug.Assert(CognateHelpers.CloseTo(fit.Current, 2.5f));
            Debug.Assert(fit.MoveNext());
            Debug.Assert(CognateHelpers.CloseTo(fit.Current, 2.75f));
            Debug.Assert(fit.MoveNext());
            Debug.Assert(CognateHelpers.CloseTo(fit.Current, 3.0f));
            Debug.Assert(fit.MoveNext());
            Debug.Assert(CognateHelpers.CloseTo(fit.Current, 3.25f));
            Debug.Assert(fit.MoveNext());
            Debug.Assert(CognateHelpers.CloseTo(fit.Current, 3.5f));
            Debug.Assert(fit.MoveNext());
            Debug.Assert(CognateHelpers.CloseTo(fit.Current, 3.75f));
            Debug.Assert(fit.MoveNext());
            Debug.Assert(CognateHelpers.CloseTo(fit.Current, 4.0f));
            Debug.Assert(fit.MoveNext() == false);
 
            RangeIntIterator it = new RangeIntIterator(new RangeInt(010), 7);
            bool hit = false;
            try
            {
                int n = it.Current;
            }
            catch (InvalidOperationException)
            {
                hit = true;
            }
            Debug.Assert(hit);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 0);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 2);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 3);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 5);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 7);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 8);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 10);
            Debug.Assert(it.MoveNext() == false);
 
            it = new RangeIntIterator(new RangeInt(03), 7);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 0);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 0);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 1);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 2);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 2);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 2);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 3);
            Debug.Assert(it.MoveNext() == false);
 
            it = new RangeIntIterator(new RangeInt(31), 7);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 3);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 3);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 2);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 2);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 2);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 1);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 1);
            Debug.Assert(it.MoveNext() == false);
 
            it = new RangeIntIterator(new RangeInt(31), 3);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 3);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 2);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 1);
            Debug.Assert(it.MoveNext() == false);
 
            it = new RangeIntIterator(new RangeInt(31), 4);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 3);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 2);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 2);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 1);
            Debug.Assert(it.MoveNext() == false);
 
            it = new RangeIntIterator(new RangeInt(24), 9);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 2);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 2);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 2);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 3);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 3);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 3);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 4);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 4);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 4);
            Debug.Assert(it.MoveNext() == false);
 
            it = new RangeIntIterator("2->4"9);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 2);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 2);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 2);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 3);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 3);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 3);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 4);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 4);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 4);
            Debug.Assert(it.MoveNext() == false);
 
            it = new RangeIntIterator("4->2"5);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 4);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 4);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 3);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 2);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 2);
            Debug.Assert(it.MoveNext() == false);
 
            it = new RangeIntIterator("4"5);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 4);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 4);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 4);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 4);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 4);
            Debug.Assert(it.MoveNext() == false);
 
            it = new RangeIntIterator(""5);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 0);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 0);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 0);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 0);
            Debug.Assert(it.MoveNext());
            Debug.Assert(it.Current == 0);
            Debug.Assert(it.MoveNext() == false);
        }
    }
}



Comments

Popular posts from this blog

UE4 Asset Directory Listing Blueprint

Subsumption and Neural Hiding - The 'Object Oriented Paradigm' of Artificial Intelligence

Self Cycling Asynchronous Neural Systems