
/*
	Licensed to the Apache Software Foundation (ASF) under one
	or more contributor license agreements.  See the NOTICE file
	distributed with this work for additional information
	regarding copyright ownership.  The ASF licenses this file
	to you under the Apache License, Version 2.0 (the
	"License"); you may not use this file except in compliance
	with the License.  You may obtain a copy of the License at
	
	http://www.apache.org/licenses/LICENSE-2.0

	Unless required by applicable law or agreed to in writing,
	software distributed under the License is distributed on an
	"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
	KIND, either express or implied.  See the License for the
	specific language governing permissions and limitations
	under the License.
*/

//
//  dbig.swift
//
//  Created by Michael Scott on 13/06/2015.
//  Copyright (c) 2015 Michael Scott. All rights reserved.
//

    public struct DBIG{
    var w=[Chunk](repeating: 0,count: CONFIG_BIG.DNLEN)
    public init() {
        for i in 0 ..< CONFIG_BIG.DNLEN {w[i]=0}
    }
    public init(_ x: Int)
    {
        w[0]=Chunk(x);
        for i in 1 ..< CONFIG_BIG.DNLEN {w[i]=0}
    }
    public init(_ x: BIG)
    {
        for i in 0 ..< CONFIG_BIG.NLEN {w[i]=x.w[i]}
        w[CONFIG_BIG.NLEN-1]=x.w[CONFIG_BIG.NLEN-1]&CONFIG_BIG.BMASK
        w[CONFIG_BIG.NLEN]=x.w[CONFIG_BIG.NLEN-1]>>Chunk(CONFIG_BIG.BASEBITS)
        for i in CONFIG_BIG.NLEN+1 ..< CONFIG_BIG.DNLEN {w[i]=0}
    }
    public init(_ x: DBIG)
    {
        for i in 0 ..< CONFIG_BIG.DNLEN {w[i]=x.w[i]}
    }
    public init(_ x: [Chunk])
    {
        for i in 0 ..< CONFIG_BIG.DNLEN {w[i]=x[i]}
    }

    mutating func cmove(_ g: DBIG,_ d: Int)
    {
        let b = Chunk(-d)
    
        for i in 0 ..< CONFIG_BIG.DNLEN
        {
            w[i]^=(w[i]^g.w[i])&b;
        }
    }

/* Copy from another DBIG */
    mutating func copy(_ x: DBIG)
    {
        for i in 0 ..< CONFIG_BIG.DNLEN {w[i] = x.w[i]}
    }

    mutating func ucopy(_ x: BIG)
    {
        for i in 0 ..< CONFIG_BIG.NLEN {w[i] = 0}
        for i in CONFIG_BIG.NLEN ..< CONFIG_BIG.DNLEN {w[i] = x.w[i-CONFIG_BIG.NLEN]}        
    }

    /* this+=x */
    mutating func add(_ x: DBIG)
    {
        for i in 0 ..< CONFIG_BIG.DNLEN
        {
            w[i]+=x.w[i]
        }
    }

    /* this-=x */
    mutating func sub(_ x: DBIG)
    {
        for i in 0 ..< CONFIG_BIG.DNLEN
        {
            w[i]-=x.w[i]
        }
    }

    /* this-=x */
    mutating func rsub(_ x: DBIG)
    {
        for i in 0 ..< CONFIG_BIG.DNLEN
        {
            w[i]=x.w[i]-w[i]
        }
    }    
    /* general shift left */
    mutating func shl(_ k: UInt)
    {
        let n=k%CONFIG_BIG.BASEBITS
        let m=Int(k/CONFIG_BIG.BASEBITS)
        w[CONFIG_BIG.DNLEN-1]=((w[CONFIG_BIG.DNLEN-1-m]<<Chunk(n)))|(w[CONFIG_BIG.DNLEN-m-2]>>Chunk(CONFIG_BIG.BASEBITS-n))
        for i in (m+1...CONFIG_BIG.DNLEN-2).reversed()
        {
            w[i]=((w[i-m]<<Chunk(n))&CONFIG_BIG.BMASK)|(w[i-m-1]>>Chunk(CONFIG_BIG.BASEBITS-n))
        }
        w[m]=(w[0]<<Chunk(n))&CONFIG_BIG.BMASK
        for i in 0 ..< m {w[i]=0}
    }
    /* general shift right */
    mutating func shr(_ k: UInt)
    {
        let n=k%CONFIG_BIG.BASEBITS
        let m=Int(k/CONFIG_BIG.BASEBITS)
        for i in 0 ..< CONFIG_BIG.DNLEN-m-1
        {
            w[i]=(w[m+i]>>Chunk(n))|((w[m+i+1]<<Chunk(CONFIG_BIG.BASEBITS-n))&CONFIG_BIG.BMASK)
        }
        w[CONFIG_BIG.DNLEN - m - 1]=w[CONFIG_BIG.DNLEN-1]>>Chunk(n)
        for i in CONFIG_BIG.DNLEN - m ..< CONFIG_BIG.DNLEN {w[i]=0}
    }
    /* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
    static func comp(_ a: DBIG,_ b: DBIG) -> Int
    {
        for i in (0...CONFIG_BIG.DNLEN-1).reversed()
        {
            if (a.w[i]==b.w[i]) {continue}
            if (a.w[i]>b.w[i]) {return 1}
            else  {return -1}
        }
        return 0;
    }
    /* set x = x mod 2^m */
    mutating func mod2m(_ m: UInt)
    {
        let wd=Int(m/CONFIG_BIG.BASEBITS)
        let bt=m%CONFIG_BIG.BASEBITS
        let msk=Chunk(1<<bt)-1;
        w[wd]&=msk;
        for i in wd+1 ..< CONFIG_BIG.DNLEN {w[i]=0}
    }
    /* normalise BIG - force all digits < 2^BASEBITS */
    mutating func norm()
    {
        var carry:Chunk=0
        for i in 0 ..< CONFIG_BIG.DNLEN-1
        {
            let d=w[i]+carry
            w[i]=d&CONFIG_BIG.BMASK
            carry=d>>Chunk(CONFIG_BIG.BASEBITS)
        }
        w[CONFIG_BIG.DNLEN-1]+=carry
    }
    /* reduces this DBIG mod a BIG, and returns the BIG */
    mutating func mod(_ c: BIG) -> BIG
    {
        var k:Int=0
        norm()
        var m=DBIG(c)
        var r=DBIG(0)
    
        if DBIG.comp(self,m)<0 {return BIG(self)}
    
        repeat
        {
            m.shl(1)
            k += 1
        }
        while (DBIG.comp(self,m)>=0);
    
        while (k>0)
        {
            m.shr(1)

		r.copy(self)
		r.sub(m)
		r.norm()
		cmove(r,Int(1-((r.w[CONFIG_BIG.DNLEN-1]>>Chunk(CONFIG_BIG.CHUNK-1))&1)))

            k -= 1
        }
        return BIG(self)
    }
    /* return this/c */
    mutating func div(_ c:BIG) -> BIG
    {
        var k:Int=0
        var m=DBIG(c)
        var a=BIG(0)
        var e=BIG(1)
        var r=BIG(0)
        var dr=DBIG(0)

        norm()
    
        while (DBIG.comp(self,m)>=0)
        {
            e.fshl(1)
            m.shl(1)
            k += 1
        }
    
        while (k>0)
        {
            m.shr(1)
            e.shr(1)

		dr.copy(self)
		dr.sub(m)
		dr.norm()
		let d=Int(1-((dr.w[CONFIG_BIG.DNLEN-1]>>Chunk(CONFIG_BIG.CHUNK-1))&1))
		cmove(dr,d)
		r.copy(a)
		r.add(e)
		r.norm()
		a.cmove(r,d)

            k -= 1
        }
        return a
    }
    
    /* split DBIG at position n, return higher half, keep lower half */
    mutating func split(_ n: UInt) -> BIG
    {
        var t=BIG(0)
        let m=n%CONFIG_BIG.BASEBITS
        var carry=w[CONFIG_BIG.DNLEN-1]<<Chunk(CONFIG_BIG.BASEBITS-m)
    
        for i in (CONFIG_BIG.NLEN-1...CONFIG_BIG.DNLEN-2).reversed()
        {
            let nw=(w[i]>>Chunk(m))|carry;
            carry=(w[i]<<Chunk(CONFIG_BIG.BASEBITS-m))&CONFIG_BIG.BMASK;
            t.set(i-CONFIG_BIG.NLEN+1,nw);
        }
        w[CONFIG_BIG.NLEN-1]&=((1<<Chunk(m))-1);
        return t;
    }
    /* return number of bits */
    func nbits() -> Int
    {
        var k=(CONFIG_BIG.DNLEN-1)
        var t=BIG(self)        
        t.norm()
        while k>=0 && t.w[k]==0 {k -= 1}
        if k<0 {return 0}
        var bts=Int(CONFIG_BIG.BASEBITS)*k
        var c=t.w[k];
        while c != 0 {c/=2; bts+=1}
        return bts
    }
    /* Convert to Hex String */
    public func toString() -> String
    {
        _ = DBIG()
        var s:String=""
        var len=nbits()
        if len%4 == 0 {len/=4}
        else {len/=4; len += 1}
        
        for i in (0...len-1).reversed()
        {
            var b = DBIG(self)
            b.shr(UInt(i*4))
            let n=String(b.w[0]&15,radix:16,uppercase:false)
            s+=n
        }
        
        return s
    }
    
}
