[インデックス 19513] ファイルの概要
このコミットは、Go言語のmath/big
パッケージにRat.Float32
メソッドを実装するものです。math/big
パッケージは、任意精度の数値演算を可能にするためのもので、この変更により、任意精度の有理数(big.Rat
型)をIEEE 754単精度浮動小数点数(float32
型)に変換する機能が追加されます。これは、big.Rat
で表現された数値を、より一般的な浮動小数点形式で利用できるようにするための重要な機能拡張です。
コミット
commit be91bc29a43ae582b6ca7f6adf561cfb25bd6911
Author: Robert Griesemer <gri@golang.org>
Date: Wed Jun 11 09:10:49 2014 -0700
math/big: implement Rat.Float32
Pending CL 101750048.
For submission after the 1.3 release.
Fixes #8065.
LGTM=adonovan
R=adonovan
CC=golang-codereviews
https://golang.org/cl/93550043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/be91bc29a43ae582b6ca7f6adf561cfb25bd6911
元コミット内容
このコミットは、Go言語の標準ライブラリであるmath/big
パッケージに、Rat
型からfloat32
型への変換メソッドFloat32
を追加することを目的としています。コミットメッセージには、この変更がCL 101750048
という保留中の変更リストに関連しており、Go 1.3リリース後に提出される予定であることが示されています。また、Fixes #8065
という記述がありますが、このIssueに関する公開情報は確認できませんでした。
変更の背景
Go言語のmath/big
パッケージは、標準のint
やfloat64
型では扱いきれない非常に大きな数値や高精度な計算を必要とする場面で利用されます。これまでのbig.Rat
型は、有理数を任意精度で表現できましたが、それを標準のfloat32
型に変換する直接的なメソッドが存在しませんでした。
float32
は、グラフィックス処理や機械学習など、精度よりもパフォーマンスやメモリ効率が重視される多くのアプリケーションで広く使用されています。big.Rat
で計算された高精度な結果を、これらのアプリケーションで利用するためには、float32
への効率的かつ正確な変換機能が不可欠でした。
このコミットは、big.Rat
で表現された有理数をIEEE 754標準に準拠したfloat32
形式に変換する機能を提供することで、math/big
パッケージの汎用性と実用性を向上させることを目的としています。特に、浮動小数点数の変換において重要な「最近接偶数への丸め(round-half-to-even)」規則を正確に実装することが求められました。
前提知識の解説
math/big
パッケージ
math/big
パッケージは、Go言語で任意精度の数値演算を可能にするためのパッケージです。Goの組み込み型(int64
やfloat64
など)には表現できる数値の範囲に限界がありますが、math/big
パッケージを使用することで、その限界を超える非常に大きな整数、有理数、浮動小数点数を扱うことができます。
big.Int
: 任意精度の符号付き整数を扱います。big.Rat
: 任意精度の有理数(分数)を扱います。分子と分母をbig.Int
で保持します。big.Float
: 任意精度の浮動小数点数を扱います。
これらの型は、通常の算術演算子(+
, -
, *
, /
など)ではなく、専用のメソッド(Add
, Sub
, Mul
, Div
など)を使用して演算を行います。
IEEE 754浮動小数点数表現(float32
とfloat64
)
IEEE 754は、浮動小数点数のコンピュータ上での表現方法と演算方法を標準化したものです。Go言語のfloat32
とfloat64
は、それぞれIEEE 754の単精度(binary32)と倍精度(binary64)浮動小数点数に準拠しています。
-
float32
(単精度): 32ビットで構成されます。- 符号部 (1ビット): 数値の符号(0: 正、1: 負)を表します。
- 指数部 (8ビット): 数値の大きさを表します。バイアス(127)が加えられた形で格納されます。
- 仮数部 (23ビット): 数値の精度を表します。正規化された数では、先頭に暗黙の「1」が仮定されるため、実質24ビットの精度を持ちます。
-
float64
(倍精度): 64ビットで構成されます。- 符号部 (1ビット)
- 指数部 (11ビット): バイアス(1023)が加えられた形で格納されます。
- 仮数部 (52ビット): 先頭に暗黙の「1」が仮定されるため、実質53ビットの精度を持ちます。
最近接偶数への丸め(Round Half To Even)
浮動小数点数演算において、正確に表現できない数値を丸める際に使用される規則の一つです。IEEE 754標準のデフォルトの丸めモードとして採用されています。
- 規則: ちょうど中間点にある数値(例: 2.5, 3.5)を丸める場合、最も近い偶数に丸めます。
- 例: 2.5 は 2 に丸められます(2は偶数)。
- 例: 3.5 は 4 に丸められます(4は偶数)。
- 例: -2.5 は -2 に丸められます。
- 例: -3.5 は -4 に丸められます。
この丸め方法は、単純な四捨五入(常に切り上げ)と比較して、多数の計算を行った際に発生する累積誤差の偏りを最小限に抑える効果があります。
技術的詳細
このコミットの核心は、math/big/rat.go
に追加されたquotToFloat32
関数と、Rat.Float32
メソッドです。
quotToFloat32
関数
quotToFloat32
は、非負の有理数 a/b
を最も近いfloat32
値に変換する内部関数です。quotToFloat64
(旧quotToFloat
)と同様のロジックで、float32
の特性に合わせて定数(Fsize
, Msize
, Esize
, Ebias
など)が調整されています。
この関数の主要なステップは以下の通りです。
-
正規化とシフト:
a
とb
のビット長を計算し、商a/b
がfloat32
の仮数部(Msize)の範囲に収まるように、a
またはb
を左シフトします。これにより、浮動小数点数の正規化された形式に近づけます。Msize1
は仮数部のビット幅に暗黙の1ビットを加えたもの(24ビット)、Msize2
はさらに1ビット加えたもの(25ビット)で、丸め処理のために余分な精度を保持します。
-
商と剰余の計算:
- シフトされた
a2
とb2
を用いて、a2
をb2
で除算し、商q
と剰余r
を計算します。 mantissa
には商q
の最下位32ビットが格納されます。haveRem
は剰余が存在するかどうかを示します。
- シフトされた
-
仮数部の調整:
- 計算された
mantissa
がMsize2
ビットに収まらない場合(つまり、mantissa
が1<<Msize2
以上の場合)、mantissa
を右シフトし、指数exp
をインクリメントして調整します。これは、正規化された浮動小数点数の形式に合わせるための処理です。 mantissa
がMsize1
ビットに収まらない場合はパニックを発生させます。
- 計算された
-
丸め処理:
- 非正規化数(Denormalized numbers)の処理: 指数
exp
が最小指数Emin
の範囲内にある場合、非正規化数として扱われます。この場合、精度が失われるため、mantissa
を右シフトし、haveRem
を更新します。 - 最近接偶数への丸め:
mantissa
の最下位ビットが1の場合、exact
をfalse
に設定します。mantissa
の最下位ビットが1で、かつ剰余がある場合、またはmantissa
の2番目のビットが1の場合(つまり、ちょうど中間点にある場合)、mantissa
をインクリメントします。- インクリメントの結果、
mantissa
が1<<Msize2
以上になった場合(桁上がり)、mantissa
を右シフトし、指数exp
をインクリメントします。 - 最後に、丸め処理に使用したビットを破棄するために
mantissa
を1ビット右シフトします。
- 非正規化数(Denormalized numbers)の処理: 指数
-
float32
値の生成:math.Ldexp
関数を使用して、調整されたmantissa
とexp
から最終的なfloat32
値を生成します。- 結果が無限大(
Inf
)になる場合、exact
をfalse
に設定します。
Rat.Float32
メソッド
Rat.Float32
メソッドは、big.Rat
型のレシーバx
に対して呼び出され、その有理数をfloat32
値に変換します。
- 分母の処理:
x
の分母x.b.abs
がゼロの場合、natOne
(値1を表す内部定数)を設定して分母を実体化します。これは、ゼロ除算を避けるための措置です。 quotToFloat32
の呼び出し:x
の分子の絶対値x.a.abs
と、調整された分母b
を引数としてquotToFloat32
を呼び出し、float32
値f
とexact
フラグを取得します。- 符号の適用:
x
の分子x.a
が負の場合、f
の符号を反転させます。これにより、結果のfloat32
値が元の有理数の符号と一致するようにします。 - 結果の返却: 最終的な
float32
値f
と、変換が正確であったかを示すexact
フラグを返します。
テストの追加
このコミットでは、src/pkg/math/big/rat_test.go
にTestFloat32SpecialCases
とTestFloat32Distribution
という新しいテスト関数が追加されています。
TestFloat32SpecialCases
:float64inputs
という既存のテストデータセットを利用して、Rat.Float32
がstrconv.ParseFloat
と一貫した結果を返すか、最も近い近似値であるか、そして非損失的なラウンドトリップ(float32
->Rat
->float32
)が可能であるかを検証します。TestFloat32Distribution
:float32
の範囲を超える広範な(符号、仮数、指数)の組み合わせを生成し、Rat.Float32
が常に最も近いfloat32
近似値を選択するかを検証します。これにより、様々な数値に対する変換の正確性を保証します。
また、既存のcheckNonLossyRoundtrip
とcheckIsBestApprox
関数が、float32
とfloat64
の両方に対応するようにcheckNonLossyRoundtrip32
/64
、checkIsBestApprox32
/64
に分割・汎用化されています。
コアとなるコードの変更箇所
src/pkg/math/big/int.go
// low32 returns the least significant 32 bits of z.
func low32(z nat) uint32 {
if len(z) == 0 {
return 0
}
return uint32(z[0])
}
// low64 returns the least significant 64 bits of z.
func low64(z nat) uint64 {
if len(z) == 0 {
return 0
}
v := uint64(z[0])
if _W == 32 && len(z) > 1 {
v |= uint64(z[1]) << 32
}
return v
}
// Int64 returns the int64 representation of x.
// If x cannot be represented in an int64, the result is undefined.
func (x *Int) Int64() int64 {
- v := int64(x.Uint64())
+ v := int64(low64(x.abs))
if x.neg {
v = -v
}
return v
}
// Uint64 returns the uint64 representation of x.
// If x cannot be represented in a uint64, the result is undefined.
func (x *Int) Uint64() uint64 {
- if len(x.abs) == 0 {
- return 0
- }
- v := uint64(x.abs[0])
- if _W == 32 && len(x.abs) > 1 {
- v |= uint64(x.abs[1]) << 32
- }
- return v
+ return low64(x.abs)
}
src/pkg/math/big/rat.go
// quotToFloat32 returns the non-negative float32 value
// nearest to the quotient a/b, using round-to-even in
// halfway cases. It does not mutate its arguments.
// Preconditions: b is non-zero; a and b have no common factors.
func quotToFloat32(a, b nat) (f float32, exact bool) {
const (
// float size in bits
Fsize = 32
// mantissa
Msize = 23
Msize1 = Msize + 1 // incl. implicit 1
Msize2 = Msize1 + 1
// exponent
Esize = Fsize - Msize1
Ebias = 1<<(Esize-1) - 1
Emin = 1 - Ebias
Emax = Ebias
)
// TODO(adonovan): specialize common degenerate cases: 1.0, integers.
alen := a.bitLen()
if alen == 0 {
return 0, true
}
blen := b.bitLen()
if blen == 0 {
panic("division by zero")
}
// 1. Left-shift A or B such that quotient A/B is in [1<<Msize1, 1<<(Msize2+1)
// (Msize2 bits if A < B when they are left-aligned, Msize2+1 bits if A >= B).
// This is 2 or 3 more than the float32 mantissa field width of Msize:
// - the optional extra bit is shifted away in step 3 below.
// - the high-order 1 is omitted in "normal" representation;
// - the low-order 1 will be used during rounding then discarded.
exp := alen - blen
var a2, b2 nat
a2 = a2.set(a)
b2 = b2.set(b)
if shift := Msize2 - exp; shift > 0 {
a2 = a2.shl(a2, uint(shift))
} else if shift < 0 {
b2 = b2.shl(b2, uint(-shift))
}
// 2. Compute quotient and remainder (q, r). NB: due to the
// extra shift, the low-order bit of q is logically the
// high-order bit of r.
var q nat
q, r := q.div(a2, a2, b2) // (recycle a2)
mantissa := low32(q)
haveRem := len(r) > 0 // mantissa&1 && !haveRem => remainder is exactly half
// 3. If quotient didn't fit in Msize2 bits, redo division by b2<<1
// (in effect---we accomplish this incrementally).
if mantissa>>Msize2 == 1 {
if mantissa&1 == 1 {
haveRem = true
}
mantissa >>= 1
exp++
}
if mantissa>>Msize1 != 1 {
panic(fmt.Sprintf("expected exactly %d bits of result", Msize2))
}
// 4. Rounding.
if Emin-Msize <= exp && exp <= Emin {
// Denormal case; lose 'shift' bits of precision.
shift := uint(Emin - (exp - 1)) // [1..Esize1)
lostbits := mantissa & (1<<shift - 1)
haveRem = haveRem || lostbits != 0
mantissa >>= shift
exp = 2 - Ebias // == exp + shift
}
// Round q using round-half-to-even.
exact = !haveRem
if mantissa&1 != 0 {
exact = false
if haveRem || mantissa&2 != 0 {
if mantissa++; mantissa >= 1<<Msize2 {
// Complete rollover 11...1 => 100...0, so shift is safe
mantissa >>= 1
exp++
}
}
}
mantissa >>= 1 // discard rounding bit. Mantissa now scaled by 1<<Msize1.
f = float32(math.Ldexp(float64(mantissa), exp-Msize1))
if math.IsInf(float64(f), 0) {
exact = false
}
return
}
// quotToFloat64 returns the non-negative float64 value
// nearest to the quotient a/b, using round-to-even in
// halfway cases. It does not mutate its arguments.
// Preconditions: b is non-zero; a and b have no common factors.
func quotToFloat64(a, b nat) (f float64, exact bool) {
const (
// float size in bits
Fsize = 64
// mantissa
Msize = 52
Msize1 = Msize + 1 // incl. implicit 1
Msize2 = Msize1 + 1
// exponent
Esize = Fsize - Msize1
Ebias = 1<<(Esize-1) - 1
Emin = 1 - Ebias
Emax = Ebias
)
// ... (rest of quotToFloat64, similar to quotToFloat32 but with float64 constants)
}
// Float32 returns the nearest float32 value for x and a bool indicating
// whether f represents x exactly. If the magnitude of x is too large to
// be represented by a float32, f is an infinity and exact is false.
// The sign of f always matches the sign of x, even if f == 0.
func (x *Rat) Float32() (f float32, exact bool) {
b := x.b.abs
if len(b) == 0 {
b = b.set(natOne) // materialize denominator
}
f, exact = quotToFloat32(x.a.abs, b)
if x.a.neg {
f = -f
}
return
}
// Float64 returns the nearest float64 value for x and a bool indicating
// whether f represents x exactly. If the magnitude of x is too large to
// be represented by a float64, f is an infinity and exact is false.
// The sign of f always matches the sign of x, even if f == 0.
func (x *Rat) Float64() (f float64, exact bool) {
b := x.b.abs
if len(b) == 0 {
b = b.set(natOne) // materialize denominator
}
f, exact = quotToFloat64(x.a.abs, b)
if x.a.neg {
f = -f
}
return
}
src/pkg/math/big/rat_test.go
// isFinite reports whether f represents a finite rational value.
// It is equivalent to !math.IsNan(f) && !math.IsInf(f, 0).
func isFinite(f float64) bool {
return math.Abs(f) <= math.MaxFloat64
}
func TestFloat32SpecialCases(t *testing.T) {
for _, input := range float64inputs {
// ... (test logic for float32 special cases)
}
}
func TestFloat32Distribution(t *testing.T) {
// ... (test logic for float32 distribution)
}
// checkNonLossyRoundtrip32 checks that a float->Rat->float roundtrip is
// non-lossy for finite f.
func checkNonLossyRoundtrip32(t *testing.T, f float32) {
// ... (logic for float32 roundtrip check)
}
// checkNonLossyRoundtrip64 checks that a float->Rat->float roundtrip is
// non-lossy for finite f.
func checkNonLossyRoundtrip64(t *testing.T, f float64) {
// ... (logic for float64 roundtrip check)
}
// checkIsBestApprox32 checks that f is the best possible float32
// approximation of r.
// Returns true on success.
func checkIsBestApprox32(t *testing.T, f float32, r *Rat) bool {
// ... (logic for float32 best approximation check)
}
// checkIsBestApprox64 checks that f is the best possible float64
// approximation of r.
// Returns true on success.
func checkIsBestApprox64(t *testing.T, f float64, r *Rat) bool {
// ... (logic for float64 best approximation check)
}
func isEven32(f float32) bool { return math.Float32bits(f)&1 == 0 }
func isEven64(f float64) bool { return math.Float64bits(f)&1 == 0 }
コアとなるコードの解説
src/pkg/math/big/int.go
の変更
low32
関数とlow64
関数が新しく追加されました。これらは、nat
型(math/big
パッケージ内部で数値を表現するために使われるスライス)の最下位32ビットまたは64ビットを取得するためのヘルパー関数です。Int64()
とUint64()
メソッドの内部実装が変更され、新しく追加されたlow64
関数を利用するように簡素化されました。これにより、コードの重複が減り、可読性が向上しています。
src/pkg/math/big/rat.go
の変更
quotToFloat32
関数の追加:- この関数は、
big.Rat
の分子と分母(nat
型)を受け取り、それらの商をfloat32
に変換します。 Fsize
,Msize
,Msize1
,Msize2
,Esize
,Ebias
,Emin
,Emax
といった定数は、float32
のIEEE 754表現の特性(ビット幅、バイアスなど)を正確に反映しています。- 変換ロジックは、有理数を浮動小数点数に変換する際の正規化、シフト、除算、そして最も重要な「最近接偶数への丸め」を厳密に実装しています。特に、
mantissa
の調整とhaveRem
フラグの管理によって、丸め処理の精度を保証しています。 math.Ldexp
は、仮数部と指数部から浮動小数点数を構築するために使用されます。
- この関数は、
quotToFloat64
関数の変更:- 既存の
quotToFloat
関数がquotToFloat64
にリネームされ、float64
のIEEE 754表現に対応する定数を使用するように変更されました。これにより、float32
とfloat64
の変換ロジックが明確に分離され、それぞれの浮動小数点形式に特化した処理が可能になりました。
- 既存の
Rat.Float32
メソッドの追加:- このメソッドは、
big.Rat
インスタンスに対して呼び出され、その値をfloat32
に変換します。 - 内部で
quotToFloat32
を呼び出し、計算されたfloat32
値と、変換が正確であったかを示すブール値を返します。 - 元の
big.Rat
が負の値である場合、結果のfloat32
値の符号を適切に反転させます。
- このメソッドは、
Rat.Float64
メソッドの変更:- 既存の
Rat.Float64
メソッドが、新しくリネームされたquotToFloat64
関数を呼び出すように変更されました。
- 既存の
src/pkg/math/big/rat_test.go
の変更
TestFloat32SpecialCases
の追加:float32
変換における特殊なケース(例: ゼロ、非常に小さい数、非常に大きい数、境界値など)をテストします。strconv.ParseFloat
との比較、最も近い近似値であることの確認、そしてfloat32
からRat
への変換、そして再びfloat32
への変換(ラウンドトリップ)が非損失的であることの検証を行います。
TestFloat32Distribution
の追加:float32
の表現範囲全体にわたる広範な有理数に対して、Rat.Float32
が常に最も近いfloat32
近似値を選択するかを検証します。これは、ランダムに生成された数値の分布をテストすることで、変換ロジックの堅牢性を確認します。
- ヘルパー関数の汎用化:
checkNonLossyRoundtrip
とcheckIsBestApprox
が、float32
とfloat64
の両方に対応するように、それぞれcheckNonLossyRoundtrip32
/64
、checkIsBestApprox32
/64
に分割されました。これにより、テストコードの再利用性が高まり、それぞれの浮動小数点形式に特化したテストが可能になりました。isEven
関数もisEven32
とisEven64
に分割され、それぞれの浮動小数点形式のビット表現に基づいて偶数性をチェックします。
これらの変更により、math/big
パッケージは、任意精度の有理数をfloat32
に正確かつ効率的に変換する機能を提供し、Go言語の数値計算能力をさらに向上させます。
関連リンク
- Go言語
math/big
パッケージドキュメント: https://pkg.go.dev/math/big - Go言語
math
パッケージドキュメント: https://pkg.go.dev/math - IEEE 754 - Wikipedia: https://ja.wikipedia.org/wiki/IEEE_754
参考にした情報源リンク
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFnfUqu_wxH7GnjKpRedqP0twJNQ59zrL96RNHasbaB9sIrgzYMRWSw6tl7qtC9YsmU6jPmgBYIu2clPjSyQ5XnOm6OTBTl4SHeFrfsJDJKnjGP9969ssas9_PIaZpoYw==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFWHEyb6HI6PMT-cth-M98-JH_7yyOHJUDITM5cBh8bV-d5nHuHil8EX68scFwyp5uIPXwHMhudeD40CuPc1d4BCsgHMQsnAwnVrJsahPdJoJj_8_ZK
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGzX4vYO5haqbV4YA9Pr8PrWbF6NclfWt-271OIEUPESJMjSpTPlYjOJMuE7vrSYVVGC_8bl5S_hswg0QUzoEybhiVRunl_NM6EvLhG_5aKNXGwJGWVVu4FR4CKInrQBsHu9jbhfsGLzw_2Lo1nE0orBx43RmVyyw_79r4ye94wHH92E6C1dduS9qU8fNg2U2EWV1emXDWfEYXWF2zvPvVdYkrcPyyB
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFM-k7CDVK2as4D92OVKTSrCOJeNuypOa3_zYot2_Pn-sMzYsyF5d2Veh8i21VWod7UmKxStIOF7VUzHfJyHF9mrHaiwLMVgDcEiZ38C_dbDWkyP-HFwjmDDGnN9sXrIZm1FLsc-m7jKw==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFnxvyCYGPc07eaMmmcXsfR7BJUt1LPQGyz9szFZY-RpIhMAng39YZ8bzdivTM5hSKgxSlOCw7f_Rrl3wOD8phzvoiBWHrmOjfMVAfbp0Ou7vhWM4cyf8fWrSMwJo0uiHHr6V7u0l6ZWd0ARRObFSLLvTJXvXuHSP71BQCZUfdVqyxowVBFi0FHEq1Ms3Kaq-Y8t9fT
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEXseGgUbjf1eOi7t4ykrDKsq1AQvbPaaCqxNgdjQ-DnXiHHzpboa_94EMVTyOKT7Or3aHXfk6K4wrVxRbKxYTE5YwkUMJ23TxKpmpBUbOD4ha52BNq2p7NZrdbA2uCXqGrQGJag4SQcbEcxGbaXIKTw_10BhbZVMyebmOgnx3WlKfWjia5i2QWe6dmGyW9zUx-3hJRrZNsu9o-xnJvUoXrJW96k0Vw
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHSbiBAJfEBeImfK2_uJFh6hBRuwSxdlH8vxkKNpiyH43yfdXc_LuKPKZ9Mo1M-5RhoUCcVPBkPjaGpSDvuwTPF9JbwD5z1_1a3rqZ2B33u8_tdYG8NV2FUCaGRxBdBNc2R1E6zwplyuQsre4E-gjUL1wtmyDqmctD1JR_oBHD2
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGfuWYWpgG1Is0z6lWujEP8Moxz2nynCl0stRU0lrmE7FJlKx-mVWPejq3VC7Vg-ywviKptZyxF9mtAhShlFF-YAHbxgcfEsYeuLhVrvn5gLXkDLYSUfDJLJO9fF1E3XtO0fF3U1mmNfYpNytwL0d6odbXCWyWDKErXn4KZcOrVDS2kXolfZCz9qU8fNg2U2EWV1emXDWfEYXWF2zvPvVdYkrcPyyB
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH9V0tC46dBIISc0y15mnckVoGVNmzLQyIVN_eo5HKJpt2r7AMezfIsF4iuHm2fg2XsWYttlRt0p40EdMncuSE7-2cdzpVkE4tczw-muyYw_oaMmtvbApyAvoS8rakodb_sXC07kk5RCQveJsprYwwPCihVoa-Bc5T5ModAyQHpxQK5VI-8R4LV0O7Q6kLZ3aidyBNLdG2LZ7Rx2RPOZOpeEqOtE5ggAuBzlEPEQywKvibb7NddJLeAHKMMQzioJDZlDYndj0g5Nf-TWCTDiQbfUkU_mKdOKLRzRVMVVOhb2NLUtW0mJyNmk75cHKVXrgRy3aGvKbkPmOOgt0bSANXJV2TG4wmz
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGAC-7C1DGjePY0eCWHOD6o0PZbh8VLM6ZIkC8X4uDiCsD98tRvL80aBqlN4G4paLAhDW5kxzOoXEPaLFTgDVAYTMmVDNA-VnoRYv5-Ie3USnpLTHFlfKfzHldPLv0_JIql
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFKRuLKNENQEekvSIyQ39nvKJFfgFnIlHwY17_SkC2z3SdLlPAMDa0AJnDt0Kly3ure9HPn3x7TEkmejVbPLwPJhLp0L5O0gt_PcBevh6kf-V1pR5QjGRyakC8p2_MV3SDEalHFpVCPXZXnsQodniM-GTpbeIBuQMkNVHtZJYZ3fqh4CehToG8JxvueeH4b5RMxdqosim59AFXAPikZRFDq
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFDN98GuLuGBuLVsel_ScrkvezCGSA2JvQxU-eZcQePapGuPY_b1WTCjjes_Y34U5_BjOUMBCAVZE_ltANQ_vESbHaHRCFT0n1AERWpukcIOV-lENbOPCtVvjYc8Xx6orcVy8yQdM129FkOThrojXhc18uV670eTLtGghrYoK8MTqnjChulqpdw=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQF8ZFLFJWrUPhF3bl5MUMzlPHiRtRz_uMfOjdD0CqL6E9bIlborLC9DVg4zw1FlD8IhqkQbtEWJF7dgLYH--rZ6S4_qQACnP3UHCta0AmRVlQwcXHO5QhAdZ3fno5hqsiyS5XCbu3xxwSVdtYF0lGQ4Ck8XwBjz8qvldw==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFZ6_uJ9ML2BzXOnjO7GWDoHZqwrmBbEjpR_yQ4Bk0ohqUbi30CiEQlpXOITkWHdHfFDNfPs0u4OWOt7OkncmLB96chiTRb28a8dTQIdbzHcLXgi615oIQ6hStkAjwmTvvZHp0Kdxkb-mnpvF7y02yXX5dnZBYNnFm4qjiYzKQ6oTZG0yrq3e9nODVlvyIcP8br95xJyw==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGoaaQVj-ohJmipi8HQu07uwSV7yt_ZP805H0b2oenuV7ZC63ShOTKYfve8i-scECbyyW_B0m22AYYKhXTmqFeVbVEdT12ZCTgos8v93rc5wMRN8RHXNJxhSdSpfv0=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEXcQbzRoTT-ft0XWDnyil_m97xJIfTydgLYXIAVA_V2D4FYXmUIasfnfliWl1ZjxIenAkuxXwWrnuAmExZ-S-fFzXzjAk3SCZRIwu6DU4aDVHyixwxCQ==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGzX4vYO5haqbV4YA9Pr8PrWbF6NclfWt-271OIEUPESJMjSpTPlYjOJMuE7vrSYVVGC_8bl5S_hswg0QUzoEybhiVRunl_NM6EvLhG_5aKNXGwJGWVVu4FR4CKInrQBsHu9jbhfsGLzw_2Lo1nE0orBx43RmVyyw_79r4ye94wHH92E6C1dduS9qU8fNg2U2EWV1emXDWfEYXWF2zvPvVdYkrcPyyB
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFM-k7CDVK2as4D92OVKTSrCOJeNuypOa3_zYot2_Pn-sMzYsyF5d2Veh8i21VWod7UmKxStIOF7VUzHfJyHF9mrHaiwLMVgDcEiZ38C_dbDWkyP-HFwjmDDGnN9sXrIZm1FLsc-m7jKw==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFnxvyCYGPc07eaMmmcXsfR7BJUt1LPQGyz9szFZY-RpIhMAng39YZ8bzdivTM5hSKgxSlOCw7f_Rrl3wOD8phzvoiBWHrmOjfMVAfbp0Ou7vhWM4cyf8fWrSMwJo0uiHHr6V7u0l6ZWd0ARRObFSLLvTJXvXuHSP71BQCZUfdVqyxowVBFi0FHEq1Ms3Kaq-Y8t9fT
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEXseGgUbjf1eOi7t4ykrDKsq1AQvbPaaCqxNgdjQ-DnXiHHzpboa_94EMVTyOKT7Or3aHXfk6K4wrVxRbKxYTE5YwkUMJ23TxKpmpBUbOD4ha52BNq2p7NZrdbA2uCXqGrQGJag4SQcbEcxGbaXIKTw_10BhbZVMyebmOgnx3WlKfWjia5i2QWe6dmGyW9zUx-3hJRrZNsu9o-xnJvUoXrJW96k0Vw
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHSbiBAJfEBeImfK2_uJFh6hBRuwSxdlH8vxkKNpiyH43yfdXc_LuKPKZ9Mo1M-5RhoUCcVPBkPjaGpSDvuwTPF9JbwD5z1_1a3rqZ2B33u8_tdYG8NV2FUCaGRxBdBNc2R1E6zwplyuQsre4E-gjUL1wtmyDqmctD1JR_oBHD2
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGfuWYWpgG1Is0z6lWujEP8Moxz2nynCl0stRU0lrmE7FJlKx-mVWPejq3VC7Vg-ywviKptZyxF9mtAhShlFF-YAHbxgcfEsYeuLhVrvn5gLXkDLYSUfDJLJO9fF1E3XtO0fF3U1mmNfYpNytwL0d6odbXCWyWDKErXn4KZcOrVDS2kXolfZCz9qU8fNg2U2EWV1emXDWfEYXWF2zvPvVdYkrcPyyB
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH9V0tC46dBIISc0y15mnckVoGVNmzLQyIVN_eo5HKJpt2r7AMezfIsF4iuHm2fg2XsWYttlRt0p40EdMncuSE7-2cdzpVkE4tczw-muyYw_oaMmtvbApyAvoS8rakodb_sXC07kk5RCQveJsprYwwPCihVoa-Bc5T5ModAyQHpxQK5VI-8R4LV0O7Q6kLZ3aidyBNLdG2LZ7Rx2RPOZOpeEqOtE5ggAuBzlEPEQywKvibb7NddJLeAHKMMQzioJDZlDYndj0g5Nf-TWCTDiQbfUkU_mKdOKLRzRVMVVOhb2NLUtW0mJyNmk75cHKVXrgRy3aGvKbkPmOOgt0bSANXJV2TG4wmz
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGAC-7C1DGjePY0eCWHOD6o0PZbh8VLM6ZIkC8X4uDiCsD98tRvL80aBqlN4G4paLAhDW5kxzOoXEPaLFTgDVAYTMmVDNA-VnoRYv5-Ie3USnpLTHFlfKfzHldPLv0_JIql
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFKRuLKNENQEekvSIyQ39nvKJFfgFnIlHwY17_SkC2z3SdLlPAMDa0AJnDt0Kly3ure9HPn3x7TEkmejVbPLwPJhLp0L5O0gt_PcBevh6kf-V1pR5QjGRyakC8p2_MV3SDEalHFpVCPXZXnsQodniM-GTpbeIBuQMkNVHtZJYZ3fqh4CehToG8JxvueeH4b5RMxdqosim59AFXAPikZRFDq
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFDN98GuLuGBuLVsel_ScrkvezCGSA2JvQxU-eZcQePapGuPY_b1WTCjjes_Y34U5_BjOUMBCAVZE_ltANQ_vESbHaHRCFT0n1AERWpukcIOV-lENbOPCtVvjYc8Xx6orcVy8yQdM129FkOThrojXhc18uV670eTLtGghrYoK8MTqnjChulqpdw=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQF8ZFLFJWrUPhF3bl5MUMzlPHiRtRz_uMfOjdD0CqL6E9bIlborLC9DVg4zw1FlD8IhqkQbtEWJF7dgLYH--rZ6S4_qQACnP3UHCta0AmRVlQwcXHO5QhAdZ3fno5hqsiyS5XCbu3xxwSVdtYF0lGQ4Ck8XwBjz8qvldw==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFZ6_uJ9ML2BzXOnjO7GWDoHZqwrmBbEjpR_yQ4Bk0ohqUbi30CiEQlpXOITkWHdHfFDNfPs0u4OWOt7OkncmLB96chiTRb28a8dTQIdbzHcLXgi615oIQ6hStkAjwmTvvZHp0Kdxkb-mnpvF7y02XW5dnZBYNnFm4qjiYzKQ6oTZG0yrq3e9nODVlvyIcP8br95xJyw==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGoaaQVj-ohJmipi8HQu07uwSV7yt_ZP805H0b2oenuV7ZC63ShOTKYfve8i-scECbyyW_B0m22AYYKhXTmqFeVbVEdT12ZCTgos8v93rc5wMRN8RHXNJxhSdSpfv0=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEXcQbzRoTT-ft0XWDnyil_m97xJIfTydgLYXIAVA_V2D4FYXmUIasfnfliWl1ZjxIenAkuxXwWrnuAmExZ-S-fFzXzjAk3SCZRIwu6DU4aDVHyixwxCQ==