private ExchangeRate smartLookup(Currency source, Currency target, Date date, List <int> forbidden)
        {
            // direct exchange rates are preferred.
            ExchangeRate direct = fetch(source, target, date);

            if (direct.HasValue)
            {
                return(direct);
            }

            // if none is found, turn to smart lookup. The source currency
            // is forbidden to subsequent lookups in order to avoid cycles.
            forbidden.Add(source.numericCode);

            foreach (KeyValuePair <int, List <Entry> > i in data_)
            {
                // we look for exchange-rate data which involve our source
                // currency...
                if (hashes(i.Key, source) && (i.Value.Count != 0))
                {
                    // ...whose other currency is not forbidden...
                    Entry    e     = i.Value[0];// front();
                    Currency other = source == e.rate.source ? e.rate.target : e.rate.source;
                    if (!forbidden.Contains(other.numericCode))
                    {
                        // ...and which carries information for the requested date.
                        ExchangeRate head = fetch(source, other, date);
                        if (((Nullable <double>)head.rate).HasValue)
                        {
                            // if we can get to the target from here...
                            try
                            {
                                ExchangeRate tail = smartLookup(other, target, date, forbidden);
                                // ..we're done.
                                return(ExchangeRate.chain(head, tail));
                            }
                            catch (Exception)
                            {
                                // otherwise, we just discard this rate.
                                ;
                            }
                        }
                    }
                }
            }
            // if the loop completed, we have no way to return the requested rate.
            throw new Exception("no conversion available from " + source.code + " to " + target.code + " for " + date);
        }
        /// <summary>
        /// Lookup the exchange rate between two currencies at a given
        /// date.  If the given type is Direct, only direct exchange
        /// rates will be returned if available; if Derived, direct
        /// rates are still preferred but derived rates are allowed.
        /// </summary>
        /// <remarks>
        /// if two or more exchange-rate chains are possible
        /// which allow to specify a requested rate, it is
        /// unspecified which one is returned.
        /// </remarks>
        /// <param name="source"></param>
        /// <param name="target"></param>
        /// <param name="date"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public ExchangeRate lookup(Currency source, Currency target, Date date, ExchangeRate.Type type)
        {
            if (source == target)
            {
                return(new ExchangeRate(source, target, 1.0));
            }

            if (date == new Date())
            {
                date = Settings.evaluationDate();
            }

            if (type == ExchangeRate.Type.Direct)
            {
                return(directLookup(source, target, date));
            }
            else if (!source.triangulationCurrency.empty())
            {
                Currency link = source.triangulationCurrency;
                if (link == target)
                {
                    return(directLookup(source, link, date));
                }
                else
                {
                    return(ExchangeRate.chain(directLookup(source, link, date), lookup(link, target, date)));
                }
            }
            else if (!target.triangulationCurrency.empty())
            {
                Currency link = target.triangulationCurrency;
                if (source == link)
                {
                    return(directLookup(link, target, date));
                }
                else
                {
                    return(ExchangeRate.chain(lookup(source, link, date), directLookup(link, target, date)));
                }
            }
            else
            {
                return(smartLookup(source, target, date));
            }
        }