import itertools
import textwrap
import random as pyran
from pygments.lexers import PythonLexer
from pygments.lexers import c_cpp
from pygments.lexers import asm
from pygments.formatters import ImageFormatter
from pygments.filters import CodeTagFilter
from pygments import highlight
import json
from tracemalloc import stop
from riscvmodel.program.tests import *
from riscvmodel.program.tests_atomic import *
from riscvmodel.model import Model
from riscvmodel.variant import RV32I, RV32IM
from riscvmodel.isa import *
from riscvmodel import *
import math
import cache_sim as cs
from math import gcd, log2

def lcm(x, y):
     # or can import gcd from `math` in Python 3
    return x * y // math.gcd(x, y)

type = {"char": 1, "short": 2, "int": 4, "long long int": 8}
short_names = {"char": "c", "short": "sh", "int": "in", "long long int": "ll"}

base_address = 0x1000
array_sizes = [256,512,1024]

def log2ceil(x):
    if x%2 == 0:
        return int(log2(x))
    else:
        return int(log2(x)) + 1

def get_ratio(r1,r2):
    ratio = (r1/(r1+r2)).as_integer_ratio()
    return f"{ratio[0]}:{ratio[1]}"
    
def format_bytes(size):
    # 2**10 = 1024
    power = 2**10
    n = 0
    power_labels = {0 : '', 1: 'kilo', 2: 'mega', 3: 'giga', 4: 'tera'}
    while size > power:
        size /= power
        n += 1
    return size, power_labels[n]+'bytes'

# Loop 1 
# for (int i = 0; i < array_size; i = i + stride) {
#       a[i] = 0
#    }
def loop1(cache, size_type,array_size):
    base_address = 0x1000000
    for i in range(0,16):
        for j in range(0,256):
            idx = i*256+j
            cs.operateCache(base_address+idx*type[size_type],cache)

def loop2(cache, size_type,array_size):
    base_address = 0x1000000
    for j in range(0,256):
        for i in range(0,16):
            idx = i*256+j
            cs.operateCache(base_address+idx*type[size_type],cache)

def loop2_tiled(cache, size_type,array_size):
    base_address = 0x1000000
    t1 = 2
    t2 = 4
    for i in range(0,16//t1):
        for j in range(0,256//t2):
            for ii in range(0,t1):
                for jj in range(0,t2):
                    idx = ((i*t1+ii)*256)+ j*t2+jj
                    cs.operateCache(base_address+idx*type[size_type],cache)


def loop3(cache, size_type,array_size):
    base_address = 0x1000000
    for i in range(0,16):
        for j in range(0,256):
            idx1 = i*256+j
            idx2 = i*256 + j+1
            idx3 = (i+1)*256 + j    
            idx4 = (i+1)*256 + j+1
            cs.operateCache(base_address+idx1*type[size_type],cache)
            cs.operateCache(base_address+idx2*type[size_type],cache)
            cs.operateCache(base_address+idx3*type[size_type],cache)
            cs.operateCache(base_address+idx4*type[size_type],cache)


def loop4(cache, size_type,array_size):
    base_address = 0x1000000
    t1 = 2
    t2 = 4
    for i in range(0,16//t1):
        for j in range(0,256//t2):
            for ii in range(0,t1):
                for jj in range(0,t2):
                    idx1 = ((i*t1+ii)*256)+ j*t2+jj
                    idx2 = ((i*t1+ii)*256)+ j*t2+jj+1
                    idx3 = ((i*t1+ii+1)*256)+ j*t2+jj    
                    idx4 = ((i*t1+ii+1)*256)+ j*t2+jj+1
                    cs.operateCache(base_address+idx1*type[size_type],cache)
                    cs.operateCache(base_address+idx2*type[size_type],cache)
                    cs.operateCache(base_address+idx3*type[size_type],cache)
                    cs.operateCache(base_address+idx4*type[size_type],cache)

def printqs(qnumber, num_questions):   
    sa = cs.Cache()
    i = 0
    t = "int"
    # Cache 1 Parameters
    sa.blockBits = 4
    sa.linesPerSet = 4
    sa.setBits = 1#log2ceil(256//(1<<sa.blockBits*sa.linesPerSet)) 
    
    w1 = sa.linesPerSet
    s1 = 1 << sa.setBits
    b1 = 1 << sa.blockBits
    c1 = format_bytes(s1 * w1 * b1)
    print(c1)
    
    # Loop 1; Cache 1 simulation
    cs.cacheSetUp(sa)
    loop1(sa,t,256*16)
    hit_miss_evict_1_1 = (sa.hit_count,sa.miss_count,sa.eviction_count)
#    hit_ratio_1_1 = get_ratio(sa.hit_count,sa.miss_count)
    cs.deallocateCache(sa)    
    #####################
    print(hit_miss_evict_1_1)


    # Loop 2; Cache 1 simulation
    cs.cacheSetUp(sa)
    loop2(sa,t,256*16)
    hit_miss_evict_1_2 = (sa.hit_count,sa.miss_count,sa.eviction_count)
    cs.deallocateCache(sa)    
    #####################
    print(hit_miss_evict_1_2)
    
    # Loop 3; Cache 1 simulation
    cs.cacheSetUp(sa)
    loop2_tiled(sa,t,256*16)
    hit_miss_evict_1_2_tiled = (sa.hit_count,sa.miss_count,sa.eviction_count)
    cs.deallocateCache(sa)    
    #####################
    print(hit_miss_evict_1_2_tiled)
    
    
    # Loop 3; Cache 1 simulation
    cs.cacheSetUp(sa)
    loop3(sa,t,256*16)
    hit_miss_evict_1_3 = (sa.hit_count,sa.miss_count,sa.eviction_count)
    cs.deallocateCache(sa)    
    #####################
    print(hit_miss_evict_1_3)

    # Loop 4; Cache 1 simulation
    cs.cacheSetUp(sa)
    loop4(sa,t,256*16)
    hit_miss_evict_1_4 = (sa.hit_count,sa.miss_count,sa.eviction_count)
    cs.deallocateCache(sa)    
    #####################
    print(hit_miss_evict_1_4)
    


if __name__ == "__main__":
    printqs(2,2)
