func NewHotp(secret []byte, count int64) (*Hotp, error) {
	otp, err := NewOtp(secret)
	if err != nil {
		return nil, err
	}
	return &Hotp{
		count,
		otp,
	}, err
}
NewHotp : Generate new hotp structure
package otp
import (
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha1"
	"encoding/binary"
	"fmt"
	"hash"
	"math"
	"math/big"
	"sync/atomic"
	"time"
)
const (
	debug = false
	defaultNumOfDigits = 6
	defaultIntervalSec = 30
	minNumOfDigits = 6  
	minSecretLen   = 4  
	maxSecretLen   = 255
	minIntervalSec = 10
	maxIntervalSec = 60
)
var defaultHashFunc = sha1.New
type Otp struct {
	Secret []byte            
	Digits int               
	digest func() hash.Hash  
}
func (otp Otp) String() string {
	return fmt.Sprintf("Otp: Digits: %v", otp.Digits)
}
func isNumberOfDigitsValid(val int) error {
	if val < minNumOfDigits {
		return fmt.Errorf("The OTP struct is not valid: Number of digits used (%d) is less than the minimum (%d)", val, minNumOfDigits)
	}
	return nil
}
func isDigestValid(digest func() hash.Hash) error {
	if digest == nil {
		return fmt.Errorf("The OTP struct is not valid: It must have a hash function, but the current hash is nil")
	}
	return nil
}
func isSecretValid(secret []byte) error {
	if len(secret) < minSecretLen || len(secret) > maxSecretLen {
		return fmt.Errorf("The secret key has an illegal length (%d): The length must be between %d and %d", len(secret), minSecretLen, maxSecretLen)
	}
	return nil
}
func (otp Otp) isDataValid() error {
	err := isNumberOfDigitsValid(otp.Digits)
	if err != nil {
		return err
	}
	err = isDigestValid(otp.digest)
	if err != nil {
		return err
	}
	return nil
}
func NewOtpAdvance(secret []byte, numOfDigits int, digest func() hash.Hash) (*Otp, error) {
	err := isSecretValid(secret)
	if err != nil {
		return nil, err
	}
	return &Otp{secret, numOfDigits, digest}, nil
}
func NewOtp(secret []byte) (*Otp, error) {
	err := isSecretValid(secret)
	if err != nil {
		return nil, err
	}
	return &Otp{secret, defaultNumOfDigits, defaultHashFunc}, nil
}
func (otp Otp) Generate(seed int64) (string, error) {
	if err := otp.isDataValid(); err != nil {
		return "", err
	}
	data := make([]byte, 8)
	binary.BigEndian.PutUint64(data, uint64(seed))
	return otp.GenerateHmac(data), nil
}
func (otp Otp) GenerateHmac(data []byte) string {
	hmacHash := hmac.New(otp.digest, otp.Secret)
	hmacHash.Write(data)
	digest := hmacHash.Sum(nil)
	mask := byte(0xf)
	len := byte(hmacHash.Size())
	if len < mask+4 {
		mask = byte(len - 4)
	}
	offset := int(digest[hmacHash.Size()-1] & mask)
	code := int32(digest[offset]&0x7f)<<24 |
		int32(digest[offset+1]&0xff)<<16 |
		int32(digest[offset+2]&0xff)<<8 |
		int32(digest[offset+3]&0xff)
	code = int32(int64(code) % int64(math.Pow10(otp.Digits)))
	if debug {
		fmt.Printf("Seed %x data %x code %x digest %x\n", otp.Secret, data, code, digest)
	}
	return fmt.Sprintf("%0*d", otp.Digits, code)
}
type Totp struct {
	Interval time.Duration  
	BaseOtp  *Otp
}
func (totp Totp) String() string {
	return fmt.Sprintf("Totp: Interval: %v, %v", totp.Interval, totp.BaseOtp)
}
func validInterval(val time.Duration) bool {
	if val.Seconds() < minIntervalSec || val.Seconds() > maxIntervalSec {
		return false
	}
	return true
}
func (totp Totp) isDataValid() error {
	if !validInterval(totp.Interval) {
		return fmt.Errorf("TOTP struct is not valid: Time interval should be between %vs and %vs, but the current interval is %v", minIntervalSec, maxIntervalSec, totp.Interval.Seconds())
	}
	return nil
}
func NewTotp(secret []byte) (*Totp, error) {
	otp, err := NewOtp(secret)
	if err != nil {
		return nil, err
	}
	return &Totp{
		time.Second * defaultIntervalSec,
		otp,
	}, nil
}
func (totp Totp) Now() (string, error) {
	return totp.AtTime(time.Now())  
}
func (totp Totp) AtTime(t time.Time) (string, error) {
	if err := totp.isDataValid(); err != nil {
		return "", err
	}
	return totp.BaseOtp.Generate(totp.timeCode(t))
}
func (totp Totp) timeCode(t time.Time) int64 {
	return (t.Unix() / int64(totp.Interval.Seconds()))
}
type Hotp struct {
	Count   int64
	BaseOtp *Otp
}
func (hp Hotp) String() string {
	return fmt.Sprintf("Hotp: count: %v, %v", hp.Count, hp.BaseOtp)
}