Exemplo n.º 1
0
        /// <summary>
        ///     Produces a hash for the paths of adjacent bnodes for a bnode,
        ///     incorporating all information about its subgraph of bnodes.
        /// </summary>
        /// <remarks>
        ///     Produces a hash for the paths of adjacent bnodes for a bnode,
        ///     incorporating all information about its subgraph of bnodes. This method
        ///     will recursively pick adjacent bnode permutations that produce the
        ///     lexicographically-least 'path' serializations.
        /// </remarks>
        /// <param name="id">the ID of the bnode to hash paths for.</param>
        /// <param name="bnodes">the map of bnode quads.</param>
        /// <param name="namer">the canonical bnode namer.</param>
        /// <param name="pathNamer">the namer used to assign names to adjacent bnodes.</param>
        /// <param name="callback">(err, result) called once the operation completes.</param>
        private static HashResult HashPaths(string id,
                                            IDictionary <string, IDictionary <string, object> > bnodes,
                                            UniqueNamer namer,
                                            UniqueNamer pathNamer)
        {
#if !PORTABLE
            MessageDigest md = null;

            try
            {
                // create SHA-1 digest
                md = MessageDigest.GetInstance("SHA-1");
                var            groups = new JObject();
                IList <string> groupHashes;
                var            quads = (IList <RdfDataset.Quad>)bnodes[id]["quads"];
                for (var hpi = 0;; hpi++)
                {
                    if (hpi == quads.Count)
                    {
                        // done , hash groups
                        groupHashes = new List <string>(groups.GetKeys());
                        ((List <string>)groupHashes).Sort(StringComparer.CurrentCultureIgnoreCase);
                        for (var hgi = 0;; hgi++)
                        {
                            if (hgi == groupHashes.Count)
                            {
                                var res = new HashResult();
                                res.hash      = EncodeHex(md.Digest());
                                res.pathNamer = pathNamer;
                                return(res);
                            }

                            // digest group hash
                            var groupHash = groupHashes[hgi];
                            md.Update(JavaCompat.GetBytesForString(groupHash, "UTF-8"));

                            // choose a path and namer from the permutations
                            string      chosenPath  = null;
                            UniqueNamer chosenNamer = null;
                            var         permutator  = new Permutator((JArray)groups[groupHash]);
                            while (true)
                            {
                                var contPermutation = false;
                                var breakOut        = false;
                                var permutation     = permutator.Next();
                                var pathNamerCopy   = pathNamer.Clone();

                                // build adjacent path
                                var path    = string.Empty;
                                var recurse = new JArray();
                                foreach (string bnode in permutation)
                                {
                                    // use canonical name if available
                                    if (namer.IsNamed(bnode))
                                    {
                                        path += namer.GetName(bnode);
                                    }
                                    else
                                    {
                                        // recurse if bnode isn't named in the path
                                        // yet
                                        if (!pathNamerCopy.IsNamed(bnode))
                                        {
                                            recurse.Add(bnode);
                                        }
                                        path += pathNamerCopy.GetName(bnode);
                                    }

                                    // skip permutation if path is already >= chosen
                                    // path
                                    if (chosenPath != null && path.Length >= chosenPath.Length && string.CompareOrdinal(path, chosenPath) > 0)
                                    {
                                        // return nextPermutation(true);
                                        if (permutator.HasNext())
                                        {
                                            contPermutation = true;
                                        }
                                        else
                                        {
                                            // digest chosen path and update namer
                                            md.Update(JavaCompat.GetBytesForString(chosenPath, "UTF-8"));
                                            pathNamer = chosenNamer;

                                            // hash the nextGroup
                                            breakOut = true;
                                        }

                                        break;
                                    }
                                }

                                // if we should do the next permutation
                                if (contPermutation)
                                {
                                    continue;
                                }

                                // if we should stop processing this group
                                if (breakOut)
                                {
                                    break;
                                }

                                // does the next recursion
                                for (var nrn = 0;; nrn++)
                                {
                                    if (nrn == recurse.Count)
                                    {
                                        // return nextPermutation(false);
                                        if (chosenPath == null || string.CompareOrdinal(path, chosenPath) < 0)
                                        {
                                            chosenPath  = path;
                                            chosenNamer = pathNamerCopy;
                                        }

                                        if (!permutator.HasNext())
                                        {
                                            // digest chosen path and update namer
                                            md.Update(JavaCompat.GetBytesForString(chosenPath, "UTF-8"));
                                            pathNamer = chosenNamer;

                                            // hash the nextGroup
                                            breakOut = true;
                                        }

                                        break;
                                    }

                                    // do recursion
                                    var bnode_1 = (string)recurse[nrn];
                                    var result  = HashPaths(bnode_1, bnodes, namer, pathNamerCopy);
                                    path         += pathNamerCopy.GetName(bnode_1) + "<" + result.hash + ">";
                                    pathNamerCopy = result.pathNamer;

                                    // skip permutation if path is already >= chosen
                                    // path
                                    if (chosenPath != null && path.Length >= chosenPath.Length && string.CompareOrdinal(path, chosenPath) > 0)
                                    {
                                        // return nextPermutation(true);
                                        if (!permutator.HasNext())
                                        {
                                            // digest chosen path and update namer
                                            md.Update(JavaCompat.GetBytesForString(chosenPath, "UTF-8"));
                                            pathNamer = chosenNamer;

                                            // hash the nextGroup
                                            breakOut = true;
                                        }

                                        break;
                                    }
                                }

                                // do next recursion
                                // if we should stop processing this group
                                if (breakOut)
                                {
                                    break;
                                }
                            }
                        }
                    }

                    // get adjacent bnode
                    var quad    = (IDictionary <string, object>)quads[hpi];
                    var bnode_2 = GetAdjacentBlankNodeName((IDictionary <string, object>)quad["subject"
                                                           ],
                                                           id);
                    string direction = null;
                    if (bnode_2 != null)
                    {
                        // normal property
                        direction = "p";
                    }
                    else
                    {
                        bnode_2 = GetAdjacentBlankNodeName((IDictionary <string, object>)quad["object"],
                                                           id
                                                           );
                        if (bnode_2 != null)
                        {
                            direction = "r";
                        }
                    }

                    if (bnode_2 != null)
                    {
                        // get bnode name (try canonical, path, then hash)
                        string name;
                        if (namer.IsNamed(bnode_2))
                        {
                            name = namer.GetName(bnode_2);
                        }
                        else
                        {
                            if (pathNamer.IsNamed(bnode_2))
                            {
                                name = pathNamer.GetName(bnode_2);
                            }
                            else
                            {
                                name = HashQuads(bnode_2, bnodes, namer);
                            }
                        }

                        // hash direction, property, end bnode name/hash
                        using (var md1 = MessageDigest.GetInstance("SHA-1"))
                        {
                            // String toHash = direction + (String) ((Map<String,
                            // Object>) quad.get("predicate")).get("value") + name;
                            md1.Update(JavaCompat.GetBytesForString(direction, "UTF-8"));
                            md1.Update(JavaCompat.GetBytesForString((string)((IDictionary <string, object>)quad["predicate"])["value"], "UTF-8"));
                            md1.Update(JavaCompat.GetBytesForString(name, "UTF-8"));
                            var groupHash = EncodeHex(md1.Digest());
                            if (groups.ContainsKey(groupHash))
                            {
                                ((JArray)groups[groupHash]).Add(bnode_2);
                            }
                            else
                            {
                                var tmp = new JArray();
                                tmp.Add(bnode_2);
                                groups[groupHash] = tmp;
                            }
                        }
                    }
                }
            }
            catch
            {
                // TODO: i don't expect that SHA-1 is even NOT going to be
                // available?
                // look into this further
                throw;
            }
            finally
            {
                md?.Dispose();
            }
#else
            throw new PlatformNotSupportedException();
#endif
        }
        /// <summary>
        /// Returns the Threat Level after assigning <see cref="Force"/> to
        /// <see cref="CityBlock"/>. No work is done here to remember state of any sort,
        /// only the allocated threat level is calculated and returned. It is not the most
        /// optimized thing that can be done, but given the timeframe and problem domain,
        /// this is pretty close to being good enough.
        /// </summary>
        /// <returns></returns>
        private int Allocate()
        {
            //var comparer = new ForceComparer();

            var threatLevel = Blocks.Sum(x => x.ThreatLevel);

            /* Really, really (REALLY) avoid the R# help here: that could
             * be potentially very, very (VERY) expensive. */

            var permutator = new Permutator<Force>(Forces);

            do
            {
                var localForces = (from x in permutator.Values select x.Clone())
                    .OfType<Force>().ToList();

                // ReSharper disable once PossibleMultipleEnumeration
                var localBlocks = (from x in Blocks.ToArray()
                    select (CityBlock) x.Clone()).ToArray();

                // Assign all the possible forces we can to the best possible locations.
                foreach (var force in localForces
                    .TakeWhile(x => x.TryPatrol(localBlocks))
                    .Where(x => localBlocks.All(y => y.IsPatrolled)))
                {
                    break;
                }

                threatLevel = Math.Min(threatLevel,
                    localBlocks.Sum(x => x.ThreatLevel));

                // This is as low as we can go, no point in continuing through the (many) other permutations.
                if (threatLevel == 0) break;

            } while (permutator.Next());

            //// Permute the forces themselves.
            //var forcePermutations = Forces.Permute().ToArray();

            //// ReSharper disable once LoopCanBePartlyConvertedToQuery
            //foreach (var forces in forcePermutations)
            //{
            //    var localForces = (from x in forces
            //        select x.Clone()).OfType<Force>().ToArray();

            //    // ReSharper disable once PossibleMultipleEnumeration
            //    var localBlocks = (from x in Blocks.ToArray()
            //        select (CityBlock) x.Clone()).ToArray();

            //    // Assign all the possible forces we can to the best possible locations.
            //    foreach (var force in localForces
            //        .TakeWhile(x => x.TryPatrol(localBlocks))
            //        .Where(x => localBlocks.All(y => y.IsPatrolled)))
            //    {
            //        break;
            //    }

            //    threatLevel = Math.Min(threatLevel,
            //        localBlocks.Sum(x => x.ThreatLevel));

            //    // This is as low as we can go, no point in continuing through the (many) other permutations.
            //    if (threatLevel == 0) break;
            //}

            return threatLevel;
        }