2016-07-02 18:35:49 +01:00
|
|
|
// Copyright (c) 2016, Google Inc.
|
|
|
|
//
|
|
|
|
// Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
// purpose with or without fee is hereby granted, provided that the above
|
|
|
|
// copyright notice and this permission notice appear in all copies.
|
|
|
|
//
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
|
|
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
|
|
|
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
|
|
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"math/big"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
type test struct {
|
|
|
|
LineNumber int
|
|
|
|
Type string
|
|
|
|
Values map[string]*big.Int
|
|
|
|
}
|
|
|
|
|
|
|
|
type testScanner struct {
|
|
|
|
scanner *bufio.Scanner
|
|
|
|
lineNo int
|
|
|
|
err error
|
|
|
|
test test
|
|
|
|
}
|
|
|
|
|
|
|
|
func newTestScanner(r io.Reader) *testScanner {
|
|
|
|
return &testScanner{scanner: bufio.NewScanner(r)}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *testScanner) scanLine() bool {
|
|
|
|
if !s.scanner.Scan() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
s.lineNo++
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *testScanner) addAttribute(line string) (key string, ok bool) {
|
|
|
|
fields := strings.SplitN(line, "=", 2)
|
|
|
|
if len(fields) != 2 {
|
|
|
|
s.setError(errors.New("invalid syntax"))
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
|
|
|
|
key = strings.TrimSpace(fields[0])
|
|
|
|
value := strings.TrimSpace(fields[1])
|
|
|
|
|
|
|
|
valueInt, ok := new(big.Int).SetString(value, 16)
|
|
|
|
if !ok {
|
|
|
|
s.setError(fmt.Errorf("could not parse %q", value))
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
if _, dup := s.test.Values[key]; dup {
|
|
|
|
s.setError(fmt.Errorf("duplicate key %q", key))
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
s.test.Values[key] = valueInt
|
|
|
|
return key, true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *testScanner) Scan() bool {
|
|
|
|
s.test = test{
|
|
|
|
Values: make(map[string]*big.Int),
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scan until the first attribute.
|
|
|
|
for {
|
|
|
|
if !s.scanLine() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if len(s.scanner.Text()) != 0 && s.scanner.Text()[0] != '#' {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var ok bool
|
|
|
|
s.test.Type, ok = s.addAttribute(s.scanner.Text())
|
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
s.test.LineNumber = s.lineNo
|
|
|
|
|
|
|
|
for s.scanLine() {
|
|
|
|
if len(s.scanner.Text()) == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if s.scanner.Text()[0] == '#' {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := s.addAttribute(s.scanner.Text()); !ok {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return s.scanner.Err() == nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *testScanner) Test() test {
|
|
|
|
return s.test
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *testScanner) Err() error {
|
|
|
|
if s.err != nil {
|
|
|
|
return s.err
|
|
|
|
}
|
|
|
|
return s.scanner.Err()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *testScanner) setError(err error) {
|
|
|
|
s.err = fmt.Errorf("line %d: %s", s.lineNo, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkKeys(t test, keys ...string) bool {
|
|
|
|
var foundErrors bool
|
|
|
|
|
|
|
|
for _, k := range keys {
|
|
|
|
if _, ok := t.Values[k]; !ok {
|
|
|
|
fmt.Fprintf(os.Stderr, "Line %d: missing key %q.\n", t.LineNumber, k)
|
|
|
|
foundErrors = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, _ := range t.Values {
|
|
|
|
var found bool
|
|
|
|
for _, k2 := range keys {
|
|
|
|
if k == k2 {
|
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found {
|
|
|
|
fmt.Fprintf(os.Stderr, "Line %d: unexpected key %q.\n", t.LineNumber, k)
|
|
|
|
foundErrors = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return !foundErrors
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkResult(t test, expr, key string, r *big.Int) {
|
|
|
|
if t.Values[key].Cmp(r) != 0 {
|
|
|
|
fmt.Fprintf(os.Stderr, "Line %d: %s did not match %s.\n\tGot %s\n", t.LineNumber, expr, key, r.Text(16))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
if len(os.Args) != 2 {
|
|
|
|
fmt.Fprintf(os.Stderr, "Usage: %s bn_tests.txt\n", os.Args[0])
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
in, err := os.Open(os.Args[1])
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintf(os.Stderr, "Error opening %s: %s.\n", os.Args[0], err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
defer in.Close()
|
|
|
|
|
|
|
|
scanner := newTestScanner(in)
|
|
|
|
for scanner.Scan() {
|
|
|
|
test := scanner.Test()
|
|
|
|
switch test.Type {
|
|
|
|
case "Sum":
|
|
|
|
if checkKeys(test, "A", "B", "Sum") {
|
|
|
|
r := new(big.Int).Add(test.Values["A"], test.Values["B"])
|
|
|
|
checkResult(test, "A + B", "Sum", r)
|
|
|
|
}
|
|
|
|
case "LShift1":
|
|
|
|
if checkKeys(test, "A", "LShift1") {
|
|
|
|
r := new(big.Int).Add(test.Values["A"], test.Values["A"])
|
|
|
|
checkResult(test, "A + A", "LShift1", r)
|
|
|
|
}
|
|
|
|
case "LShift":
|
|
|
|
if checkKeys(test, "A", "N", "LShift") {
|
|
|
|
r := new(big.Int).Lsh(test.Values["A"], uint(test.Values["N"].Uint64()))
|
|
|
|
checkResult(test, "A << N", "LShift", r)
|
|
|
|
}
|
|
|
|
case "RShift":
|
|
|
|
if checkKeys(test, "A", "N", "RShift") {
|
|
|
|
r := new(big.Int).Rsh(test.Values["A"], uint(test.Values["N"].Uint64()))
|
|
|
|
checkResult(test, "A >> N", "RShift", r)
|
|
|
|
}
|
|
|
|
case "Square":
|
|
|
|
if checkKeys(test, "A", "Square") {
|
|
|
|
r := new(big.Int).Mul(test.Values["A"], test.Values["A"])
|
|
|
|
checkResult(test, "A * A", "Square", r)
|
|
|
|
}
|
|
|
|
case "Product":
|
|
|
|
if checkKeys(test, "A", "B", "Product") {
|
|
|
|
r := new(big.Int).Mul(test.Values["A"], test.Values["B"])
|
|
|
|
checkResult(test, "A * B", "Product", r)
|
|
|
|
}
|
|
|
|
case "Quotient":
|
|
|
|
if checkKeys(test, "A", "B", "Quotient", "Remainder") {
|
|
|
|
q, r := new(big.Int).QuoRem(test.Values["A"], test.Values["B"], new(big.Int))
|
|
|
|
checkResult(test, "A / B", "Quotient", q)
|
|
|
|
checkResult(test, "A % B", "Remainder", r)
|
|
|
|
}
|
2016-07-03 01:33:07 +01:00
|
|
|
case "ModMul":
|
|
|
|
if checkKeys(test, "A", "B", "M", "ModMul") {
|
|
|
|
r := new(big.Int).Mul(test.Values["A"], test.Values["B"])
|
|
|
|
r = r.Mod(r, test.Values["M"])
|
|
|
|
checkResult(test, "A * B (mod M)", "ModMul", r)
|
|
|
|
}
|
2016-07-03 15:05:56 +01:00
|
|
|
case "ModExp":
|
|
|
|
if checkKeys(test, "A", "E", "M", "ModExp") {
|
|
|
|
r := new(big.Int).Exp(test.Values["A"], test.Values["E"], test.Values["M"])
|
|
|
|
checkResult(test, "A ^ E (mod M)", "ModExp", r)
|
|
|
|
}
|
2016-07-03 16:52:58 +01:00
|
|
|
case "Exp":
|
|
|
|
if checkKeys(test, "A", "E", "Exp") {
|
|
|
|
r := new(big.Int).Exp(test.Values["A"], test.Values["E"], nil)
|
|
|
|
checkResult(test, "A ^ E", "Exp", r)
|
|
|
|
}
|
2016-07-03 20:19:20 +01:00
|
|
|
case "ModSqrt":
|
|
|
|
bigOne := new(big.Int).SetInt64(1)
|
|
|
|
bigTwo := new(big.Int).SetInt64(2)
|
|
|
|
|
|
|
|
if checkKeys(test, "A", "P", "ModSqrt") {
|
|
|
|
test.Values["A"].Mod(test.Values["A"], test.Values["P"])
|
|
|
|
|
|
|
|
r := new(big.Int).Mul(test.Values["ModSqrt"], test.Values["ModSqrt"])
|
|
|
|
r = r.Mod(r, test.Values["P"])
|
|
|
|
checkResult(test, "ModSqrt ^ 2 (mod P)", "A", r)
|
|
|
|
|
2016-07-27 21:44:31 +01:00
|
|
|
if test.Values["P"].Cmp(bigTwo) > 0 {
|
2016-07-03 20:19:20 +01:00
|
|
|
pMinus1Over2 := new(big.Int).Sub(test.Values["P"], bigOne)
|
|
|
|
pMinus1Over2.Rsh(pMinus1Over2, 1)
|
|
|
|
|
|
|
|
if test.Values["ModSqrt"].Cmp(pMinus1Over2) > 0 {
|
|
|
|
fmt.Fprintf(os.Stderr, "Line %d: ModSqrt should be minimal.\n", test.LineNumber)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-07-26 01:20:32 +01:00
|
|
|
case "ModInv":
|
|
|
|
if checkKeys(test, "A", "M", "ModInv") {
|
|
|
|
r := new(big.Int).ModInverse(test.Values["A"], test.Values["M"])
|
|
|
|
checkResult(test, "A ^ -1 (mod M)", "ModInv", r)
|
|
|
|
}
|
2016-07-02 18:35:49 +01:00
|
|
|
default:
|
|
|
|
fmt.Fprintf(os.Stderr, "Line %d: unknown test type %q.\n", test.LineNumber, test.Type)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if scanner.Err() != nil {
|
|
|
|
fmt.Fprintf(os.Stderr, "Error reading tests: %s.\n", scanner.Err())
|
|
|
|
}
|
|
|
|
}
|