| // Copyright 2016 The Go Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style | 
 | // license that can be found in the LICENSE file. | 
 |  | 
 | package currency | 
 |  | 
 | import ( | 
 | 	"sort" | 
 | 	"time" | 
 |  | 
 | 	"golang.org/x/text/language" | 
 | ) | 
 |  | 
 | // QueryIter represents a set of Units. The default set includes all Units that | 
 | // are currently in use as legal tender in any Region. | 
 | type QueryIter interface { | 
 | 	// Next returns true if there is a next element available. | 
 | 	// It must be called before any of the other methods are called. | 
 | 	Next() bool | 
 |  | 
 | 	// Unit returns the unit of the current iteration. | 
 | 	Unit() Unit | 
 |  | 
 | 	// Region returns the Region for the current iteration. | 
 | 	Region() language.Region | 
 |  | 
 | 	// From returns the date from which the unit was used in the region. | 
 | 	// It returns false if this date is unknown. | 
 | 	From() (time.Time, bool) | 
 |  | 
 | 	// To returns the date up till which the unit was used in the region. | 
 | 	// It returns false if this date is unknown or if the unit is still in use. | 
 | 	To() (time.Time, bool) | 
 |  | 
 | 	// IsTender reports whether the unit is a legal tender in the region during | 
 | 	// the specified date range. | 
 | 	IsTender() bool | 
 | } | 
 |  | 
 | // Query represents a set of Units. The default set includes all Units that are | 
 | // currently in use as legal tender in any Region. | 
 | func Query(options ...QueryOption) QueryIter { | 
 | 	it := &iter{ | 
 | 		end:  len(regionData), | 
 | 		date: 0xFFFFFFFF, | 
 | 	} | 
 | 	for _, fn := range options { | 
 | 		fn(it) | 
 | 	} | 
 | 	return it | 
 | } | 
 |  | 
 | // NonTender returns a new query that also includes matching Units that are not | 
 | // legal tender. | 
 | var NonTender QueryOption = nonTender | 
 |  | 
 | func nonTender(i *iter) { | 
 | 	i.nonTender = true | 
 | } | 
 |  | 
 | // Historical selects the units for all dates. | 
 | var Historical QueryOption = historical | 
 |  | 
 | func historical(i *iter) { | 
 | 	i.date = hist | 
 | } | 
 |  | 
 | // A QueryOption can be used to change the set of unit information returned by | 
 | // a query. | 
 | type QueryOption func(*iter) | 
 |  | 
 | // Date queries the units that were in use at the given point in history. | 
 | func Date(t time.Time) QueryOption { | 
 | 	d := toDate(t) | 
 | 	return func(i *iter) { | 
 | 		i.date = d | 
 | 	} | 
 | } | 
 |  | 
 | // Region limits the query to only return entries for the given region. | 
 | func Region(r language.Region) QueryOption { | 
 | 	p, end := len(regionData), len(regionData) | 
 | 	x := regionToCode(r) | 
 | 	i := sort.Search(len(regionData), func(i int) bool { | 
 | 		return regionData[i].region >= x | 
 | 	}) | 
 | 	if i < len(regionData) && regionData[i].region == x { | 
 | 		p = i | 
 | 		for i++; i < len(regionData) && regionData[i].region == x; i++ { | 
 | 		} | 
 | 		end = i | 
 | 	} | 
 | 	return func(i *iter) { | 
 | 		i.p, i.end = p, end | 
 | 	} | 
 | } | 
 |  | 
 | const ( | 
 | 	hist = 0x00 | 
 | 	now  = 0xFFFFFFFF | 
 | ) | 
 |  | 
 | type iter struct { | 
 | 	*regionInfo | 
 | 	p, end    int | 
 | 	date      uint32 | 
 | 	nonTender bool | 
 | } | 
 |  | 
 | func (i *iter) Next() bool { | 
 | 	for ; i.p < i.end; i.p++ { | 
 | 		i.regionInfo = ®ionData[i.p] | 
 | 		if !i.nonTender && !i.IsTender() { | 
 | 			continue | 
 | 		} | 
 | 		if i.date == hist || (i.from <= i.date && (i.to == 0 || i.date <= i.to)) { | 
 | 			i.p++ | 
 | 			return true | 
 | 		} | 
 | 	} | 
 | 	return false | 
 | } | 
 |  | 
 | func (r *regionInfo) Region() language.Region { | 
 | 	// TODO: this could be much faster. | 
 | 	var buf [2]byte | 
 | 	buf[0] = uint8(r.region >> 8) | 
 | 	buf[1] = uint8(r.region) | 
 | 	return language.MustParseRegion(string(buf[:])) | 
 | } | 
 |  | 
 | func (r *regionInfo) Unit() Unit { | 
 | 	return Unit{r.code &^ nonTenderBit} | 
 | } | 
 |  | 
 | func (r *regionInfo) IsTender() bool { | 
 | 	return r.code&nonTenderBit == 0 | 
 | } | 
 |  | 
 | func (r *regionInfo) From() (time.Time, bool) { | 
 | 	if r.from == 0 { | 
 | 		return time.Time{}, false | 
 | 	} | 
 | 	return fromDate(r.from), true | 
 | } | 
 |  | 
 | func (r *regionInfo) To() (time.Time, bool) { | 
 | 	if r.to == 0 { | 
 | 		return time.Time{}, false | 
 | 	} | 
 | 	return fromDate(r.to), true | 
 | } |