/**
         * Uses the specified {@link Connections} object to Build the structural
         * anatomy needed by this {@code TemporalMemory} to implement its algorithms.
         *
         * The connections object holds the {@link Column} and {@link Cell} infrastructure,
         * and is used by both the {@link SpatialPooler} and {@link TemporalMemory}. Either of
         * these can be used separately, and therefore this Connections object may have its
         * Columns and Cells initialized by either the init method of the SpatialPooler or the
         * init method of the TemporalMemory. We check for this so that complete initialization
         * of both Columns and Cells occurs, without either being redundant (initialized more than
         * once). However, {@link Cell}s only get created when initializing a TemporalMemory, because
         * they are not used by the SpatialPooler.
         *
         * @param   c       {@link Connections} object
         */


        public void init(Connections conn)
        {
            this.connections = conn;

            SparseObjectMatrix <Column> matrix = this.connections.getMemory() == null ?
                                                 new SparseObjectMatrix <Column>(this.connections.getColumnDimensions()) :
                                                 (SparseObjectMatrix <Column>) this.connections.getMemory();

            this.connections.setMemory(matrix);

            int numColumns = matrix.getMaxIndex() + 1;

            this.connections.setNumColumns(numColumns);
            int cellsPerColumn = this.connections.getCellsPerColumn();

            Cell[] cells = new Cell[numColumns * cellsPerColumn];

            //Used as flag to determine if Column objects have been created.
            Column colZero = matrix.getObject(0);

            for (int i = 0; i < numColumns; i++)
            {
                Column column = colZero == null ? new Column(cellsPerColumn, i, this.connections.getSynPermConnected(), this.connections.NumInputs) : matrix.getObject(i);
                for (int j = 0; j < cellsPerColumn; j++)
                {
                    cells[i * cellsPerColumn + j] = column.Cells[j];
                }
                //If columns have not been previously configured
                if (colZero == null)
                {
                    matrix.set(i, column);
                }
            }
            //Only the TemporalMemory initializes cells so no need to test for redundancy
            this.connections.setCells(cells);
        }
        /// <summary>
        /// Implements muticore initialization of pooler.
        /// </summary>
        /// <param name="c"></param>
        protected override void ConnectAndConfigureInputs(Connections c)
        {
            List <KeyPair> colList = new List <KeyPair>();

            ConcurrentDictionary <int, KeyPair> colList2 = new ConcurrentDictionary <int, KeyPair>();

            int numColumns = c.HtmConfig.NumColumns;

            // Parallel implementation of initialization
            ParallelOptions opts = new ParallelOptions();

            //int synapseCounter = 0;

            Parallel.For(0, numColumns, opts, (indx) =>
            {
                Random rnd = new Random(42);

                int colIndex = (int)indx;
                var data     = new ProcessingData
                {
                    // Gets RF
                    Potential = HtmCompute.MapPotential(c.HtmConfig, colIndex, rnd /*(c.getRandom()*/),
                    Column    = c.GetColumn(colIndex)
                };

                // This line initializes all synases in the potential pool of synapses.
                // It creates the pool on proximal dendrite segment of the column.
                // After initialization permancences are set to zero.
                data.Column.CreatePotentialPool(c.HtmConfig, data.Potential, -1);
                //connectColumnToInputRF(c.HtmConfig, data.Potential, data.Column);

                //Interlocked.Add(ref synapseCounter, data.Column.ProximalDendrite.Synapses.Count);

                //colList.Add(new KeyPair() { Key = i, Value = column });

                data.Perm = HtmCompute.InitSynapsePermanences(c.HtmConfig, data.Potential, c.HtmConfig.Random);

                data.AvgConnected = GetAvgSpanOfConnectedSynapses(c, colIndex);

                HtmCompute.UpdatePermanencesForColumn(c.HtmConfig, data.Perm, data.Column, data.Potential, true);

                if (!colList2.TryAdd(colIndex, new KeyPair()
                {
                    Key = colIndex, Value = data
                }))
                {
                }
            });

            //c.setProximalSynapseCount(synapseCounter);

            List <double> avgSynapsesConnected = new List <double>();

            foreach (var item in colList2.Values)
            //for (int i = 0; i < numColumns; i++)
            {
                int i = (int)item.Key;

                ProcessingData data = (ProcessingData)item.Value;
                //ProcessingData data = new ProcessingData();

                // Debug.WriteLine(i);
                //data.Potential = mapPotential(c, i, c.isWrapAround());

                //var st = string.Join(",", data.Potential);
                //Debug.WriteLine($"{i} - [{st}]");

                //var counts = c.getConnectedCounts();

                //for (int h = 0; h < counts.getDimensions()[0]; h++)
                //{
                //    // Gets the synapse mapping between column-i with input vector.
                //    int[] slice = (int[])counts.getSlice(h);
                //    Debug.Write($"{slice.Count(y => y == 1)} - ");
                //}
                //Debug.WriteLine(" --- ");
                // Console.WriteLine($"{i} - [{String.Join(",", ((ProcessingData)item.Value).Potential)}]");

                // This line initializes all synases in the potential pool of synapses.
                // It creates the pool on proximal dendrite segment of the column.
                // After initialization permancences are set to zero.
                //var potPool = data.Column.createPotentialPool(c, data.Potential);
                //connectColumnToInputRF(c, data.Potential, data.Column);

                //data.Perm = initPermanence(c.getSynPermConnected(), c.getSynPermMax(),
                //      c.getRandom(), c.getSynPermTrimThreshold(), c, data.Potential, data.Column, c.getInitConnectedPct());

                //updatePermanencesForColumn(c, data.Perm, data.Column, data.Potential, true);

                avgSynapsesConnected.Add(data.AvgConnected);

                colList.Add(new KeyPair()
                {
                    Key = i, Value = data.Column
                });
            }

            SparseObjectMatrix <Column> mem = (SparseObjectMatrix <Column>)c.HtmConfig.Memory;

            if (mem.IsRemotelyDistributed)
            {
                // Pool is created and attached to the local instance of Column.
                // Here we need to update the pool on remote Column instance.
                mem.set(colList);
            }

            // The inhibition radius determines the size of a column's local
            // neighborhood.  A cortical column must overcome the overlap score of
            // columns in its neighborhood in order to become active. This radius is
            // updated every learning round. It grows and shrinks with the average
            // number of connected synapses per column.
            UpdateInhibitionRadius(c, avgSynapsesConnected);
        }