├── go.mod ├── ph_config.go ├── LICENSE.md ├── ph_api.go ├── ph_test.go ├── README.md └── ph_work.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/claygod/PiHex 2 | 3 | go 1.16 -------------------------------------------------------------------------------- /ph_config.go: -------------------------------------------------------------------------------- 1 | package PiHex 2 | 3 | // PiHex 4 | // Configuration settings 5 | // Copyright © 2016-2025 Eduard Sesigin. All rights reserved. Contacts: 6 | 7 | // Editable parameters 8 | const ( 9 | // The number of hexadecimal digits generated in one step. 10 | // For 64-bit architecture can not exceed nine. 11 | // If you have doubts about the accuracy, you can reduce this number 12 | // (it's a bit to increase the work program). 13 | STEP = 9 // or 8 14 | ) 15 | 16 | // Non-editable parameters 17 | const ( 18 | // The limit for calculation 19 | CLIMIT = 10000000 20 | // The number of digits exponent 21 | NTP = 25 22 | // Special option 23 | EPS = 1e-17 24 | ) 25 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | LICENSE 2 | 3 | Copyright (c) 2016-2020 Eduard Sesigin. All rights reserved. Contacts: 4 | 5 | Permission is hereby granted, to any person obtaining a copy of this software and associated documentation files 6 | (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, 7 | modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom 8 | the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice (or reference to this permission notice) 11 | shall be included in all copies or portions of the Software. 12 | 13 | DISCLAIMER 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 16 | THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 18 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 19 | OTHER DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /ph_api.go: -------------------------------------------------------------------------------- 1 | package PiHex 2 | 3 | // PiHex 4 | // API 5 | // Copyright © 2016-2025 Eduard Sesigin. All rights reserved. Contacts: 6 | 7 | import ( 8 | "runtime" 9 | ) 10 | 11 | // PiHex Library generates a hexadecimal number sequence in the number Pi in 12 | // the range from 0 to 10,000,000. To calculate using "Bailey-Borwein-Plouffe" 13 | // algorithm, instructions that was published by David September 17, 2006. 14 | 15 | // New - create a new PiHex-struct 16 | func New() *Pi { 17 | pi := &Pi{} 18 | pi.genExp() 19 | pi.ch = make(chan float64) 20 | 21 | return pi 22 | } 23 | 24 | // Get - 'num' hexadecimal digits in the number Pi since 'start' position. 25 | // If the inputs exceed the permissible values, it returns an empty slice. 26 | // 27 | // Arguments: 28 | // 29 | // start - start number 30 | // num - how to calculate the numbers 31 | // 32 | // Return: 33 | // 34 | // slice bytes 0 to 15 35 | func (pi *Pi) Get(start int, num int) []byte { 36 | var out []byte 37 | if start <= CLIMIT && 38 | start+num < CLIMIT && 39 | start >= 0 && 40 | num > 0 { 41 | numcpu := runtime.NumCPU() 42 | runtime.GOMAXPROCS(numcpu) 43 | 44 | for i := 0; i < num; i = i + STEP { 45 | c := num - i 46 | 47 | if c > STEP { 48 | out = append(out, pi.genHex(start+i, STEP)...) 49 | } else { 50 | out = append(out, pi.genHex(start+i, c)...) 51 | } 52 | } 53 | } 54 | 55 | return out 56 | } 57 | -------------------------------------------------------------------------------- /ph_test.go: -------------------------------------------------------------------------------- 1 | package PiHex 2 | 3 | // PiHex 4 | // Test 5 | // Copyright © 2016-2025 Eduard Sesigin. All rights reserved. Contacts: 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | func TestLimitZero(t *testing.T) { 12 | pi := New() 13 | 14 | if len(pi.Get(0, 0)) != 0 { 15 | t.Error("Error two zeros in the argument /pi.Get(0, 0)") 16 | } 17 | } 18 | 19 | func TestLimitCount1(t *testing.T) { 20 | pi := New() 21 | 22 | if len(pi.Get(0, 5)) != 5 { 23 | t.Error("It returned the wrong number of digits /pi.Get(0, 5)") 24 | } 25 | } 26 | 27 | func TestLimitCount2(t *testing.T) { 28 | pi := New() 29 | 30 | if len(pi.Get(5, 5)) != 5 { 31 | t.Error("It returned the wrong number of digits /pi.Get(5, 5)") 32 | } 33 | } 34 | 35 | func TestLimitNegative1(t *testing.T) { 36 | pi := New() 37 | 38 | if len(pi.Get(-1, 5)) != 0 { 39 | t.Error("Adopted by Negative arguments /pi.Get(-1, 5)") 40 | } 41 | } 42 | 43 | func TestLimitNegative2(t *testing.T) { 44 | pi := New() 45 | 46 | if len(pi.Get(0, -5)) != 0 { 47 | t.Error("Adopted by Negative arguments /pi.Get(0, -5)") 48 | } 49 | } 50 | 51 | func TestLimitNegative3(t *testing.T) { 52 | pi := New() 53 | 54 | if len(pi.Get(-1, -5)) != 0 { 55 | t.Error("Adopted by Negative arguments /pi.Get(-1, -5)") 56 | } 57 | } 58 | 59 | func TestLimitMax1(t *testing.T) { 60 | pi := New() 61 | 62 | if len(pi.Get(0, 10000001)) != 0 { 63 | t.Error("The value argument exceeds the limit /pi.Get(0, 10000001)") 64 | } 65 | } 66 | 67 | func TestLimitMax2(t *testing.T) { 68 | pi := New() 69 | 70 | if len(pi.Get(9999999, 2)) != 0 { 71 | t.Error("The value argument exceeds the limit /pi.Get(9999999, 2)") 72 | } 73 | } 74 | 75 | func TestLimitResult1(t *testing.T) { 76 | pi := New() 77 | tru := []byte{2, 4, 3, 15, 6, 10, 8, 8, 8, 5, 10, 3, 0, 8, 13, 3, 1, 3, 1, 9} 78 | result := pi.Get(0, len(tru)) 79 | 80 | for i := 0; i < len(tru); i++ { 81 | if tru[i] != result[i] { 82 | t.Error("Wrong Result ", result[i], " at position ", i, " /pi.Get(0, len(tru)") 83 | } 84 | } 85 | } 86 | 87 | func TestLimitResult2(t *testing.T) { 88 | pi := New() 89 | tru := []byte{6, 12, 6, 5, 14, 5, 2, 12, 11, 4} 90 | result := pi.Get(1000000, len(tru)) 91 | 92 | for i := 0; i < len(tru); i++ { 93 | if tru[i] != result[i] { 94 | t.Error("Wrong Result ", result[i], " at position ", i, " /pi.Get(1000000, len(tru)") 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PiHex 2 | PiHex Library generates a hexadecimal number sequence in the number Pi in the range from 0 to 1.0e10000000. To calculate using "Bailey-Borwein-Plouffe" algorithm, instructions that was published by David H. Bailey September 17, 2006. 3 | 4 | [![API documentation](https://godoc.org/github.com/claygod/PiHex?status.svg)](https://godoc.org/github.com/claygod/PiHex) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/claygod/PiHex)](https://goreportcard.com/report/github.com/claygod/PiHex) 6 | [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go) 7 | 8 | # Usage 9 | 10 | An example of using the PiHex Library: 11 | ```go 12 | package main 13 | 14 | import ( 15 | "fmt" 16 | "github.com/claygod/PiHex" 17 | ) 18 | 19 | func main() { 20 | pi := PiHex.New() 21 | fmt.Print("The first 9 digits of Pi (hexadecimal): ", pi.Get(0, 9)) 22 | } 23 | ``` 24 | 25 | # Settings 26 | 27 | In the configuration file, you can change the constant STEP. This constant determines the amount generated in one step numbers. The reduction leads to a constant increase in the operating time of the program. 28 | 29 | Attention! This constant can not be more than 9! Limitation due to the 64-bit library architecture. 30 | 31 | The configuration file [config.go](https://github.com/claygod/PiHex/blob/master/ph_config.go) 32 | 33 | # Perfomance 34 | 35 | To optimize the run-time program, highly loaded sections of the library are performed in parallel (4 goroutines). 36 | 37 | # API 38 | 39 | Methods: 40 | - *New* - create a new PiHex 41 | - *Get* - receiving a sequence of hexadecimal digits starting at the specified position and in the right quantity. 42 | 43 | Example: 44 | 45 | ```go 46 | pi := PiHex.New() 47 | x := pi.Get(1000, 5) 48 | ``` 49 | 50 | # Algorithm 51 | 52 | The Bailey–Borwein–Plouffe formula (BBP formula) is a spigot algorithm for computing the nth binary digit of Pi using base 16 math. The formula can directly calculate the value of any given digit of π without calculating the preceding digits. The BBP is a summation-style formula that was discovered in 1995 by Simon Plouffe and was named after the authors of the paper in which the formula was published, David H. Bailey, Peter Borwein, and Simon Plouffe. 53 | 54 | # Implementation 55 | 56 | Plays Library is based on the publication "The BBP Algorithm for Pi" of David H. Bailey on September 17, 2006: http://www.davidhbailey.com/dhbpapers/bbp-alg.pdf 57 | 58 | ## Copyright 59 | 60 | Copyright © 2017-2025 Eduard Sesigin. All rights reserved. Contacts: claygod@yandex.ru 61 | -------------------------------------------------------------------------------- /ph_work.go: -------------------------------------------------------------------------------- 1 | package PiHex 2 | 3 | // PiHex 4 | // Work 5 | // Copyright © 2016-2025 Eduard Sesigin. All rights reserved. Contacts: 6 | 7 | import ( 8 | //"fmt" 9 | "math" 10 | ) 11 | 12 | // Pi structure 13 | type Pi struct { 14 | // Table exponents 15 | exponent *[NTP]float64 16 | // Channel to return the results of the goroutines 17 | ch chan float64 18 | } 19 | 20 | // genHex - generate 'num' hexadecimal digits in the number Pi since 'start' position 21 | // The most time consuming calculations are performed in parallel. Arguments: 22 | // start - start number 23 | // num - how to calculate the numbers 24 | func (pi *Pi) genHex(start int, num int) []byte { 25 | var d1 float64 26 | var d2 float64 27 | // Start of the goroutines 28 | go pi.series(1, start, 4.) 29 | go pi.series(4, start, 2.) 30 | go pi.series(5, start, 1.) 31 | go pi.series(6, start, 1.) 32 | // Getting the results of the goroutines 33 | for i := 0; i < 4; i++ { 34 | d2 = <-pi.ch 35 | 36 | if d2 > 8. { 37 | d1 += d2 38 | } else { 39 | d1 -= d2 40 | } 41 | } 42 | 43 | pid := d1 - 10. 44 | pid = pid - float64(int(pid)) + 1. 45 | 46 | return pi.ihex(pid, num) 47 | } 48 | 49 | // genExp - generate table exponents 50 | func (pi *Pi) genExp() { 51 | var exp [NTP]float64 52 | exp[0] = 0. 53 | exp[1] = 1. 54 | 55 | for i := 2; i < NTP; i++ { 56 | exp[i] = 2. * exp[i-1] 57 | } 58 | 59 | pi.exponent = &exp 60 | } 61 | 62 | // series - This routine evaluates the series sum 16^(id-k)/(8*k+m) 63 | // using the modular exponentiation technique. Arguments: 64 | // m - 1,4,5,6 only 65 | // id - start number 66 | // id - coefficient 67 | func (pi *Pi) series(m int, id int, kf float64) { 68 | var ak, p, s, t float64 69 | // Sum the series up to id 70 | for k := 0; k < id; k++ { 71 | ak = 8*float64(k) + float64(m) 72 | p = float64(id) - float64(k) 73 | t = pi.expm(p, ak) 74 | s += t / ak 75 | s = s - float64(int(s)) 76 | } 77 | // Compute a few terms where k >= id 78 | for k := id; k <= id+100; k++ { 79 | ak = 8*float64(k) + float64(m) 80 | t = math.Pow(16., float64(id-k)) / ak 81 | 82 | if t < EPS { 83 | break 84 | } 85 | 86 | s = s + t 87 | s = s - float64(int(s)) 88 | } 89 | 90 | s = s * kf 91 | 92 | if kf == 4. { 93 | s += 10. 94 | } 95 | 96 | pi.ch <- s 97 | } 98 | 99 | func (pi *Pi) expm(p float64, ak float64) float64 { 100 | var p1, pt, r float64 101 | 102 | if ak == 1. { 103 | return 0. 104 | } 105 | // Find the greatest power of two less than or equal to p 106 | i := 0 107 | for ; i < NTP; i++ { 108 | if pi.exponent[i] > p { 109 | break 110 | } 111 | } 112 | pt = pi.exponent[i-1] 113 | p1 = p 114 | r = 1. 115 | // Perform binary exponentiation algorithm modulo ak 116 | for j := 1; j <= i; j++ { 117 | if p1 >= pt { 118 | r = 16. * r 119 | r = r - float64(float64(int(r/ak))*ak) 120 | p1 = p1 - pt 121 | } 122 | 123 | pt = 0.5 * pt 124 | 125 | if pt >= 1. { 126 | r = r * r 127 | r = r - float64(float64(int(r/ak))*ak) 128 | } 129 | } 130 | 131 | return r 132 | } 133 | 134 | func (pi *Pi) ihex(x float64, num int) []byte { 135 | var out []byte 136 | var y float64 137 | y = math.Abs(x) 138 | 139 | for i := 0; i < num; i++ { 140 | y = 16. * (y - math.Floor(y)) 141 | out = append(out, byte(y)) 142 | } 143 | 144 | return out 145 | } 146 | --------------------------------------------------------------------------------