Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Have the number of bits be part of the type #2

Closed
mratsim opened this issue Mar 5, 2018 · 6 comments
Closed

Feature: Have the number of bits be part of the type #2

mratsim opened this issue Mar 5, 2018 · 6 comments

Comments

@mratsim
Copy link
Contributor

mratsim commented Mar 5, 2018

Currently the type for Uint256 is MpUint[MpUint[uint64]]. Also making the jump from MpUint[uint32] to uint64 is logic but not intuitive.

A better way would be to use MpUint[256] or MpUint[64] for example, so have the number of bits be part of the type. Still being able to retrieve the underlying type (uint64 or MpUint64) eases the implementation a lot.

Here is a proposed solution, it is currently blocked by the following Nim bug: nim-lang/Nim#7230

import macros, typetraits


macro getBase*(bits: static[int]): untyped =

  # Check if power of 2 greater or equal to 16
  assert bits >= 16, $bits & " should be greater or equal 16"
  assert (bits and (bits-1)) == 0, $bits & " is not a power of 2"

  case bits:
  of 128: result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint64"))
  of 64:  result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint32")) # This is useful for test purposes to compare with actual uint64
  of 32:  result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint16"))
  of 16:  result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint8"))
  else:
    result = newTree(nnkBracketExpr, ident("MpUint"), newIntLitNode(bits div 2))

type
  BaseUint* = SomeUnsignedInt or MpUintBase

  MpUintBase*[BaseUint] = object
      lo*, hi*: BaseUint

  ## This gets type mismatch
  # MpUint*[bits: static[int]] = getBase(bits)
  MpUint[N: static[int]] = object

## This works fine
var foo: getBase(256)
echo foo.type.name
@mratsim
Copy link
Contributor Author

mratsim commented Mar 5, 2018

Actually this is even simpler:

import macros, typetraits


macro getBase*(bits: static[int]): untyped =

  # Check if power of 2 greater or equal to 16
  assert bits >= 16, $bits & " should be greater or equal 16"
  assert (bits and (bits-1)) == 0, $bits & " is not a power of 2"

  case bits:
  of 128: result = ident("uint64")
  of 64:  result = ident("uint32") # This is useful for test purposes to compare with actual uint64
  of 32:  result = ident("uint16")
  of 16:  result = ident("uint8")
  else:
    result = newTree(nnkBracketExpr, ident("MpUint"), newIntLitNode(bits div 2))

#type
#  MpUint*[bits: static[int]] = object
#      lo*, hi*: getBase(bits)

type MpUint*[bits: static[int]] = object

## This works fine
var foo: getBase(128)
echo foo.type.name

We can always get the subtype by using type foo.lo

@mratsim
Copy link
Contributor Author

mratsim commented Mar 20, 2018

New try, but stuck again, upstreamed: nim-lang/Nim#7378

type
  MpUint*[bits: static[int]] = object
    when system.cpuEndian == littleEndian:
      when bits == 128: lo*, hi*: uint64
      elif bits ==  64: lo*, hi*: uint32
      elif bits ==  32: lo*, hi*: uint16
      elif bits ==  16: lo*, hi*: uint8
      else:
        lo*, hi*: MpUint[bits div 2]
    else:
      when bits == 128: hi*, lo*: uint64
      elif bits ==  64: hi*, lo*: uint32
      elif bits ==  32: hi*, lo*: uint16
      elif bits ==  16: hi*, lo*: uint8
      else:
        hi*, lo*: MpUint[bits div 2]


proc toMpUint*(n: uint16): MpUint[16] =
  cast[type result](n)

let a = 10'u16
var b = a.toMpUint()

echo b # works

## uncomment the following for failure
# proc fails_always(x: MpUint, y: MpUint) =
#   echo x
# Error: cannot generate code for: bits

proc fails_if_called*[bits: static[int]](x: MpUint[bits], y: MpUint[bits]) =
  echo x

## uncomment the following for failure
# fails_if_called(b, b) # Error: cannot generate code for: bits

fails_if_called[16](b, b) # This works but we shouldn't have to tell the size each time

@mratsim
Copy link
Contributor Author

mratsim commented Mar 20, 2018

Another try, it's when that cannot access the static int: nim-lang/Nim#7378

type
  BaseUint* = SomeUnsignedInt or MpUintBase

  MpUintBase*[BaseUint] = object
      lo*, hi*: BaseUint

type
  # works
  Foo*[bits: static[int]] = (
    when true:
      MpUintBase[uint64]
    )

type
  # Cannot generate bits
  Bar*[bits: static[int]] = (
    when bits == 128: # Error points to the bits here
       MpUintBase[uint64]
    )

@mratsim
Copy link
Contributor Author

mratsim commented Mar 20, 2018

Yet another try, upstreamed: nim-lang/Nim#7379

import macros

macro works(): untyped =
  result = getType(int)

macro fails(bits: static[int]): untyped =
  result = getType(int)

type
  Foo*[bits: static[int]] = works()
  Bar*[bits: static[int]] = fails(bits)
Error: type mismatch: got <bits>
but expected one of:
macro fails(bits: static[int]): untyped

expression: fails(bits)

@mratsim
Copy link
Contributor Author

mratsim commented Mar 20, 2018

Another bug raised, non-blocking but impacts performance (we can't inline type conversion otherwise it might triggers memory corruption): nim-lang/Nim#7382

@mratsim
Copy link
Contributor Author

mratsim commented Mar 27, 2018

Implemented by #6

@mratsim mratsim closed this as completed Mar 27, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant